- User 엔티티 확장: passwordHash nullable, provider, emailVerified, 인증 토큰 해시/만료(select:false)
- 회원가입: 자동 로그인 폐지 → 미인증 계정 생성 + 인증 메일 발송(MailService 플레이스홀더, 백엔드 로그로 링크 출력)
- 이메일 인증: GET /auth/verify-email(해시 매칭+만료 검사) → 프론트로 리다이렉트, 재발송 엔드포인트
- 로그인 게이트: 미인증 로컬 403 차단, 소셜 전용 계정은 소셜 로그인 안내
- 소셜 OAuth: passport google/kakao 전략(키 미설정 시 자동 비활성), 이메일로 계정 매칭/생성
- 기존 로컬 계정 그랜드페더링(부팅 시 인증 완료 보정)
- 프론트: 가입 후 인증 메일 안내 화면, 로그인 소셜 버튼 실연동 + 인증 결과 배너
- env(APP_API_URL/FRONTEND_URL/GOOGLE_*/KAKAO_*) 문서화, 문서·계약 갱신
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- GET /repos/name-available?slug= 추가(:repoId 앞 선언, Relay slug 중복만 검사)
- 생성 폼: 형식 즉시 검사 + 350ms 디바운스 서버 조회로 상태별(빈칸/형식위반/확인중/사용가능/중복) 색·아이콘 표기
- 항상 떠 있던 정적 "사용 가능한 이름입니다" 표시 제거
- 조직 이니셜 소유자 아바타 제거
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
설정 탭/권한:
- findOne/update 응답에 현재 사용자 권한(canManage=admin, isOwner=소유자) 포함
(목록은 캐시·사용자 무관이라 미포함). RepoTabs 가 canManage 일 때만 설정 탭 노출,
비관리자의 설정 화면 직접 진입은 저장소 상세로 리다이렉트. 입력 비활성화 처리 제거.
- 저장소 삭제는 isOwner 일 때만(비소유자 admin 엔 비활성+안내).
설정 로직 버그/정리:
- update 가 이전 값과 비교해 실제 변경분만 활동/Gitea/캐시 반영
(무변경 저장 시 허위 'renamed'/'visibility_changed' 활동이 쌓이던 버그 수정).
- 위험구역의 미구현 'Gitea 저장소 이름 변경'(slug rename) 스텁 제거.
- 조직 owner 아바타(하드코딩 'M') 제거.
Gitea 역동기화(Gitea=SSOT):
- 동기화 매칭 시 slug(rename)·공개범위·기본 브랜치를 Gitea 기준으로 끌어옴
→ Gitea 에서 직접 바꿔도 Relay 가 따라가고 slugName 도 갱신돼 쓰기 경로 일치
(rename 후 옛 이름으로 쓰기 호출이 404 나던 desync 해소). 중복 매칭 가드 추가.
- 표시명/설명은 Gitea 단일 description 합본 구조라 Relay-SSOT 유지(외부 편집 시 깨짐 방지).
- GiteaService.updateRepo 에 default_branch 추가 + update 가 브랜치도 Gitea 에 푸시(양방향 일치).
검증: 백엔드 build/lint 0·20 tests, 프론트 type-check/lint/build 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
task.status_changed 를 일반 라벨('승인 대기로 변경')이 아닌 행위 중심 문구로
파생(statusFrom→statusTo): 시작/다시 시작·승인을 요청·수정을 요청·완료(승인).
업무 상세 활동 탭(task.store)·알림과 동일 표현으로 일관화. 안 쓰게 된
taskStatusLabel·TaskStatus import 제거.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 백엔드: User.avatarUrl(nullable) 추가 → PublicUser/toPublic 로 모든 유저 응답에 노출.
- 프론트: 공용 UserAvatar(이미지 있으면 img, 없으면 사람 실루엣 placeholder)로
전 화면 아바타 통일(상단바·저장소·멤버·담당자·활동·내업무·댓글·AI패널).
화면마다 달랐던 색상+이니셜/하드코딩 청록 아바타 제거.
- 죽은 코드 정리: 색상·이니셜 시스템(avatarColor/initialOf/AvatarColor 유틸·타입,
User/RepoActivityItem 의 initial/color, 전역·컴포넌트 .av-a~.av-f CSS) +
미사용 정적 목업 데이터(USERS/REPOS/REPO_TASKS/TASK_DETAIL/MY_TASKS_*/REPO_MEMBERS/
REPO_ACTIVITY 등) 전부 삭제. relay.mock.ts 는 뷰 타입 + countActive 만 유지.
avatar_url 컬럼은 dev synchronize 가 추가. 업로드(실제 사진 설정)는 추후 단계.
검증: 백엔드 build 0, 프론트 type-check/lint/build 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
미인증으로 보호 라우트 진입 시 가드가 붙이던 redirect 쿼리로 로그인 후 이전 화면에
복귀하던 동작 제거. LoginPage 는 항상 /repos 로 이동하고, 가드는 redirect 쿼리를
더 이상 보존하지 않는다(딥링크 복귀 제거).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
알림(8-2):
- Notification 모듈 + SSE 스트림(@Sse, @SkipTransform 으로 표준 래퍼 우회).
- 12종 알림 적재(업무 지시/해제/승인요청/승인/수정요청/시작/마감변경/삭제/댓글·답글,
멤버 초대/역할변경/제거). 행위자 제외 + 중복 dedup. AppShell 종(미읽음 뱃지·드롭다운).
- 실시간 fan-out: REDIS_HOST 시 ioredis pub/sub(다중 인스턴스), 미설정 시 인메모리 폴백.
Redis 캐싱(8-3):
- CacheModule → @keyv/redis(미설정 시 인메모리 폴백). 저장소 목록을 버전 네임스페이스로
캐시, 생성/수정/삭제·동기화·멤버 초대/제거 시 무효화(공유 버전 키라 다중 인스턴스 정합).
- enableShutdownHooks 로 종료 시 Redis 연결 graceful close.
검증: 백엔드 build/lint 0·20 tests, 프론트 build/lint/type-check 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 저장소 업무 목록(RepoDetailPage): 세그먼트/검색을 백엔드로 이전(클라 필터 제거),
응답 counts 로 세그먼트 배지, 더보기(오프셋) 추가. task.store 에 total/counts/hasMore/loadMore.
- 멤버: member.store.fetchList 가 페이지네이션 API 를 끝까지 순회해 전체 로드
(역할/검색 카운트 전수 + TaskCreatePage 담당자 후보 보장).
- 타입 신설: TaskSegment/TaskSegmentCounts/ApiRepoTaskListResult.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
페이지네이션 도입 후 백엔드 목록 API 가 { items, total, ... } 를 반환하는데
프론트 멤버·업무 소비처가 배열로 기대해 list.map 이 깨지고, 멤버 탭과 업무
목록이 비어 보이던 회귀를 수정.
- backend(task): 업무 목록 쿼리에서 to-many 조인 컬럼(checklist.sort_order)
으로 정렬해 페이지네이션 시 TypeORM 이 터지던 문제 제거(task.seq 만 정렬).
목록 응답은 체크리스트 [완료/전체] 수만 쓰므로 항목 순서가 불필요.
- frontend(member/task): list 컴포저블이 Paginated<T> 를 반환하도록 맞추고
스토어에서 .items 로 언래핑(저장소·내업무와 동일 계약).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 멤버 목록/초대/역할변경/제거 프론트 연동(RepoMembersPage), 초대 자동완성
- GET /users 사용자 검색 API + UserController
- RepoMembersPage 역할 변경 드롭다운 잘림(overflow) 수정
- InviteMember/UpdateMember DTO, RepoMember 엔티티 정비
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- GiteaRepoBar 컴포넌트(클론 주소 복사 + Gitea 열기)를 RepoSettingsPage 에 적용
- repo.store 뷰에 cloneUrl/htmlUrl/giteaMissing 매핑
- repo-sync: import 저장소 owner-admin 자동 지정(REPO_IMPORT_ADMIN_EMAIL)
- env example·docker-compose.dev 에 Gitea/import-admin 키 정비
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 프로젝트 설명을 생성 필수 항목으로 변경(CreateRepoDto.description required + 프론트 검증)
- 템플릿 복제: useTemplate=true 면 Gitea generate API 로 GITEA_TEMPLATE_OWNER/REPO
(기본 TtiPo/project-base) 저장소를 복제 생성(브랜치는 템플릿을 따름)
- GET /repos/owner → /repos/meta 로 확장({ owner, template }) — 생성폼 메타 일괄 제공
- 프론트 생성폼: 가짜 템플릿 목록 제거 → 실제 템플릿 옵션, 설명 필수 UI
- env.example/ api-contract/ 진행현황 문서 갱신
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
생성 폼/목록의 하드코딩된 'marketing-team' 을 실제 설정값으로 교체한다.
- 백엔드: GET /repos/owner ({ owner }) — GITEA_ORG(미연동 시 기본 상수) 반환
- 프론트: useRepo.owner() + repo.store owner/fetchOwner(캐시)
- RepoCreatePage 소유자 고정 표시, RepoListPage 배너를 실제 조직명으로
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>