21 Commits

Author SHA1 Message Date
sp-lab
4dac89e313 fix(mailsync): impersonation 불가 메일박스는 스킵 (동기화 전체 실패 방지)
All checks were successful
build-and-push / build (push) Successful in 32s
멤버 목록에 개인 Gmail(theorose49@gmail.com) 등 Workspace 도메인 밖 계정이
섞여 있으면 도메인 전체 위임 token 교환이 unauthorized_client로 실패하는데,
기존엔 이 한 계정 실패가 firstErr로 잡혀 프로젝트 전체 동기화가 오류로
마크됐다(나머지 멤버 메일은 정상 수집됐는데도). 이제 해당 메일박스만
로그 남기고 건너뛴다.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:14:34 +00:00
sp-lab
654f17d487 fix(mailsync): AI 요약이 NULL summary 행을 못 잡던 버그 수정
All checks were successful
build-and-push / build (push) Successful in 32s
기존 메일 569건은 summary 컬럼 추가 이전부터 있던 행이라 값이 NULL인데,
요약 대상 선택 쿼리가 summary='' 만 검색해서(SQL에서 NULL='' 는 거짓)
영원히 선택되지 않았다. COALESCE(summary,'')='' 로 NULL·빈문자열 모두 포함.
회당 상한 40→150으로 백로그 드레인 가속, 요약 실패는 silent skip 대신 로그로 남김.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:53:14 +00:00
theorose49
ed4ef537e6 feat(calendar): 일정에 참여자(participants) — 그들 캘린더에도 표시
All checks were successful
build-and-push / build (push) Successful in 33s
- CalendarEvent.Participants(JSON 이메일 배열), patch 시 JSON 변환
- 목록은 전 구성원 공유(이미) → 프론트가 소유자 OR 참여자로 필터/표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:32:02 +09:00
theorose49
317db37a04 feat(calendar): 일정 목록을 전 구성원 공유로 반환(편집·삭제는 소유자만)
All checks were successful
build-and-push / build (push) Successful in 33s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:15:17 +09:00
theorose49
995dd36167 feat: 대시보드 내 이슈(/my/tasks) + 전체 캘린더(CalendarEvent) + 메일 AI 요약(OpenAI)
All checks were successful
build-and-push / build (push) Successful in 32s
- GET /my/tasks: 전 프로젝트에서 나에게 배정된 작업 + projectName (대시보드 JIRA 보드용)
  · fix: ORDER BY "end"(예약어) 따옴표 처리
- CalendarEvent 모델 + /calendar/events CRUD(본인 소유), nav에 캘린더 추가
- internal/ai(OpenAI, stdlib): 메일 동기화 시 신규 메일에 한 줄 AI 요약 생성(OPENAI_API_KEY)
  · ProjectMailMsg.Summary, 회당 40건 상한
- nav inbox 라벨 쪽지함으로 통일

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:06:55 +09:00
theorose49
d9ab9934c0 feat(mail): 본문 HTML 우선(메일앱처럼) + 스레드 순번(threadIndex/Count) 제공
All checks were successful
build-and-push / build (push) Successful in 33s
- GetFull: text/plain 대신 text/html 우선 → 서식 있는 본문 렌더
- handleListProjectMails: 스레드별 ts 정렬 순번 계산해 threadIndex(1=원문)/threadCount 반환

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:50:11 +09:00
theorose49
e0cd216800 feat(mail): 가시성=단일메일 참조여부로 복귀 + 메일 전문·첨부파일 온디맨드
All checks were successful
build-and-push / build (push) Successful in 33s
- 가시성을 스레드 단위 → 단일 메일 from/to/cc 기준으로 되돌림(답장도 개별 판정)
- mailsync.GetFull(rfc822msgid→format=full): 본문(text 우선/HTML) + 첨부 리스트 파싱
- mailsync.GetAttachment: 첨부 바이트 다운로드(요청자 메일함 impersonate)
- GET /mails/full, GET /mails/attachment — 권한: canSeeProject + 단일메일 참여자만(403)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:18:49 +09:00
theorose49
14e8a62f76 fix(mail): 답장도 리스트에 각각 표시(스레드 단위 가시성) + 동기화 진행 신호
All checks were successful
build-and-push / build (push) Successful in 32s
- 가시성을 메일 단위 → '내가 참여한 스레드 전체'로: 한 스레드에서 한 통이라도
  from/to/cc면 그 스레드의 원문·답장 모두 노출(각각의 행). 무관 스레드는 숨김.
- isSyncing(): 백필/수동 동기화 진행 중이면 mails 응답에 syncing=true → 프론트 폴링

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:01:33 +09:00
theorose49
7e1bb6606e feat(perm): 일반 구성원도 업체담당자·주의사항·작업 CRUD 허용 (비민감 정보)
All checks were successful
build-and-push / build (push) Successful in 33s
- 업체 담당자(ClientContact) upsert/delete를 requireAdmin → canSeeProject
- PATCH /projects/{id}/notes: 구성원이 주의사항·계약범위(글/그림)만 편집(화이트리스트)
- 작업(타임라인)은 기존대로 구성원 CRUD(삭제 포함, 백엔드 이미 허용)
- 유지(관리자 전용): 프로젝트 핵심정보(상태/PM/일정/업체) 수정, 작업자 기여도, 계약/정산

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 13:50:12 +09:00
theorose49
dce6bb215e feat(notify): 작업 배정·댓글·PM 지정 시 대상 유저에게 쪽지함 알림(+모바일 푸시)
All checks were successful
build-and-push / build (push) Successful in 32s
- 작업 생성/수정으로 담당자 배정 시 그 구성원에게 알림(type=task), 본인배정 제외
- 내 작업에 새 댓글 달리면 담당자에게 알림(본인 댓글 제외)
- 프로젝트 PM 지정/변경 시 PM에게 알림
- 모두 기존 s.notify() 사용 → Notification 생성 + 등록기기로 FCM 푸시(자격증명 시 자동)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 13:19:26 +09:00
theorose49
c865baccd2 feat(mail): DB 저장 + 주기 동기화 + 전체 히스토리 수집 + 메일 숨김
All checks were successful
build-and-push / build (push) Successful in 33s
- ProjectMailMsg(헤더 저장)·ProjectMailState(동기화 상태) 모델, AutoMigrate 등록
- mailsync.FetchForDomain: nextPageToken 따라 전체 히스토리 페이지네이션(maxPerBox=0=전부)
- 백그라운드 주기 동기화 StartMailSyncLoop(MAIL_SYNC_INTERVAL 기본 15m, 0=비활성)
  · 미동기화 프로젝트=full 백필, 이후=최신 페이지 top-up
- GET /mails는 DB에서 읽어 참여자(from/to/cc) 필터 + 공동 메모 인라인 결합 + lastSyncedAt
- POST /mails/sync(강제 풀싱크), PUT /mail-hide(프로젝트 단위 숨김)
- mailCache 제거(DB가 캐시), config MailSyncInterval

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:44:31 +09:00
theorose49
b4b47e5ed1 feat(mail): 전사 메일함 수집 + 요청자 참여(from/to/cc) 가시성 필터
All checks were successful
build-and-push / build (push) Successful in 32s
- 수집 대상을 프로젝트 팀 → 전사 모든 구성원 메일함으로 변경(프로젝트 캐시)
- Message.Cc 수집 + Involves(email): 요청자가 수신·참조된 메일만 노출
- 공동 메모는 그대로(프로젝트 구성원 공유)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 11:11:23 +09:00
theorose49
751aa8ed97 feat(mail): 프로젝트 고객사 도메인 메일 연동(Google Workspace 도메인위임) + 공동 메모
All checks were successful
build-and-push / build (push) Successful in 32s
- Project.ClientDomain 필드, MailNote(프로젝트 구성원 공동 메모) 모델
- internal/mailsync: 서비스계정+도메인위임으로 팀 메일함을 도메인 검색·집계(stdlib만, push 패턴 재사용)
  · GOOGLE_SA_CREDENTIALS_FILE 미설정 시 비활성(graceful)
- GET /projects/{id}/mails (3분 캐시), GET/PUT /projects/{id}/mail-notes
- fix: handlePatchProject map-key Updates가 camelCase 멀티워드 필드(consultingType·
  scopeText·pmEmail·clientDomain·날짜)를 컬럼에 못 맞춰 저장 실패하던 버그 → snakeKeys 변환

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 10:41:26 +09:00
theorose49
ce07aced0f feat(members): 전 구성원 공개 이름 디렉터리 엔드포인트
All checks were successful
build-and-push / build (push) Successful in 33s
- GET /members/directory: 모든 인증 사용자에게 {id,email,displayName,avatarKey} 반환
- PM/작업자/담당자 등을 이메일이 아닌 이름으로 표시하기 위함 (직급/연락처 미포함)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:47:43 +09:00
theorose49
f46f135dbf feat(task): JIRA형 작업 — 설명·우선순위·라벨 필드 + 댓글 스레드
All checks were successful
build-and-push / build (push) Successful in 33s
- ProjectTask에 description/priority/labels 추가
- TaskComment 모델 + 엔드포인트(GET/POST /tasks/{id}/comments, DELETE /comments/{id})
- 댓글 작성자=요청자, 삭제는 작성자 또는 관리자
- PATCH /tasks: labels/dependsOn JSON 슬라이스 map 패치 처리

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:08:02 +09:00
theorose49
dcf8b415db feat: 회사/제품/버전 PATCH·DELETE, 세금 DELETE, 기준정보 nav
All checks were successful
build-and-push / build (push) Successful in 32s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:45:43 +09:00
theorose49
db0f857f0a feat(project): 계약범위를 글/그림 자유 텍스트 2칸으로 변경 (드롭다운 제거)
All checks were successful
build-and-push / build (push) Successful in 33s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:23:38 +09:00
theorose49
df09d23662 feat: 인턴 직급 + 초과근무 관리자 집계화 + SSO 로그아웃 URL + 디바이스/FCM
All checks were successful
build-and-push / build (push) Successful in 35s
- 직급에 인턴 추가(기본 할당량 15), 직책(position)은 UI에서 제거(컬럼은 유지)
- 초과근무: 유저 신청 제거 → 관리자 근무관리에서 실제 출퇴근 기록 기반 자동 집계
- 로그아웃: infra 공통 LOGOUT_URL(/me로 전달) 사용 → oauth2-proxy 종료 + Keycloak end-session
- (이전 커밋 포함) Device 등록 + FCM HTTP v1 sender + notify 연동

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 10:55:53 +09:00
theorose49
6158888417 feat: 본인 표시이름 자가수정 허용 (계정 설정용)
All checks were successful
build-and-push / build (push) Successful in 32s
memberSelfPatch에 displayName 추가 — 유저가 계정 설정에서 표시 이름 변경 가능.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 09:52:10 +09:00
theorose49
a904cbf9b9 feat: 메일함·근무상태 기록·프로필 사진·자동 프로비저닝 + 인센티브 유저 노출 제한
All checks were successful
build-and-push / build (push) Successful in 33s
- 알림(Notification) 모델/이벤트 발행(프로젝트 추가·휴가/초과근무 승인·인센티브 반영/지급·정산 확정) + 메일함 API
- 근무상태 기록(WorkStatusEvent: 출근/퇴근/휴식/미팅/이동), 출퇴근은 Attendance도 갱신
- 남은 연차(소수점) 엔드포인트, 관리자 근무관리용 집계/로그 조회
- 프로필 사진(Member.AvatarKey) 업로드/스트리밍
- Keycloak 최초 로그인 자동 Member 프로비저닝(ensureMember, rank/부서 nullable)
- 프로젝트 scope=mine(나의 업무는 관리자도 본인 참여분만), nav에 메일함·근무관리·프로젝트관리·내프로필 추가
- 운영 안전: SEED 기본값 false(로컬만 SEED=true), ADMIN_GROUPS 기본 'admin'

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 09:38:33 +09:00
theorose49
f83724b995 feat: spin 백엔드 전체 구현 (근무·프로젝트·인센티브·회계)
All checks were successful
build-and-push / build (push) Successful in 39s
- config/db/storage/auth/router/perms: eQMS 규약 미러링, 권한 2-tier
  (관리자 전체 / 구성원 본인·신청만), oauth2-proxy 헤더 인증 + DEV_AUTH mock
- 모델: 구성원/부서, 근무(출퇴근·휴가·공가·초과), 프로젝트(회사/제품/버전·
  작업자portion·담당자·태스크·계약·첨부·분할입금), 인센티브(설정·단계·
  유저배분·분기정산), 회계(거래·세금)
- internal/worktime: 근로기준법 월 집계 엔진
- internal/incentive: BE/non-BE × 계약금/중도금/잔금 3단계 계산 + 시뮬레이션
- 시드 데이터, Go 멀티스테이지 Dockerfile
- ADMIN_GROUPS 기본값 'admin' (전 내부 앱 공통 그룹)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:57:35 +09:00