Commit Graph

229 Commits

Author SHA1 Message Date
ttipo 20f593bd5a feat: 수정 요청 모달을 '할 일 완료 검토' 디자인으로 개편
시안(수정 요청 모달.html)을 반영해 모달 UI를 재구성한다.

- 헤더(그라데이션 배지·안내)·스크롤 본문·고정 푸터 구조로 변경
- 항목별 완료/보완을 세그먼트 토글(완료=초록 / 보완=빨강 좌측 바)로 표시,
  카테고리 그룹·진행률 바·완료 카운트 동봉
- 보완 항목 수에 따라 안내 배너(warn/good)와 메시지 필수 여부가 전환
- 모든 항목 완료 시 버튼이 '승인 완료'(초록)로 바뀌어 곧바로 승인,
  보완 항목이 있으면 '수정 요청 보내기 (N건)'(빨강)으로 메시지와 함께 반려
- 모달 진입 시 전 항목을 '완료'로 초기화(담당자가 완료를 주장하며 요청)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:18:53 +09:00
ttipo 79e0c820ba feat: 수정 요청 시 할 일 항목별 완료 체크/X 검토 추가
지시자가 승인을 검토하며 수정 요청할 때, 단순 텍스트 입력에 더해
할 일 목록에서 어떤 항목이 완성/미완성인지 체크(✓)·X로 표시할 수 있게 한다.

- 백엔드: ApprovalChangesDto 에 항목별 검토 결과(checklist) 추가,
  requestChanges 가 전달된 항목만 done 상태로 반영(미전달 항목은 보존)
- 프론트: 수정 요청 모달에 카테고리별 체크/X 토글 UI 추가, 검토 결과를
  note 와 함께 전송
- 테스트: 전달된 항목만 완료 체크가 반영되는지 검증(9 tests)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 16:46:03 +09:00
ttipo c86c04f99e feat: 체크리스트를 필수·관리·보안·부가 카테고리 탭으로 구조화
업무 지시(생성/편집)·상세 화면의 '해야 할 일'을 AI 추천과 동일한
4개 영역(필수/관리/보안/부가)으로 분류한다.

- 백엔드: ChecklistItem 에 category 컬럼(기본 '필수') 추가, 생성/동기화
  DTO·서비스·응답에 category 반영
- 프론트: 생성/편집은 카테고리 탭 + 탭별 추가 입력, AI 추천 항목은
  소속 영역으로 매핑해 일괄 추가, 상세는 카테고리별 그룹 표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 16:18:37 +09:00
ttipo 93c8ae2a22 docs: 단독 실행(non-Docker) 관련 내용 전부 제거
Docker 만 사용하므로 standalone 안내를 정리:
- 프론트 env(.example): '단독 실행 시 주석 해제' 안내 + 주석 처리된 VITE 라인 제거
  (VITE_ 값은 compose build arg 로만 주입됨)
- CLAUDE.md: '단독 실행 시 주석 예시 활성화' 규칙 삭제
- README: 사전요구사항/cp 명령/‘단독 실행’ 섹션/env 표의 standalone 문구 제거
  (모바일은 compose 비대상이라 별도 실행 안내만 한 줄 유지)
- integration-progress: Redis 폴백 설명의 '단독 실행' 표현을 'REDIS_HOST 미설정 시'로 교정

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:53:31 +09:00
ttipo 1f2a93b01a chore: 모든 env 파일 구조·주석 정리 및 env↔example 규격 통일
- 죽은 설정 제거: GITEA_*/REPO_IMPORT_ADMIN_EMAIL (저장소·Gitea 기능 삭제됨)
- 섹션 헤더 스타일을 # === 블록으로 전 파일 통일(루트/백엔드/프론트)
- 루트: AI 섹션(AI_TIMEOUT_MS) 형식 통일 + production 파일에 누락됐던 AI 섹션 추가
- 백엔드: JWT/앱URL/OAuth/AI 4섹션으로 일관 정렬, production 에 누락됐던 URL/OAuth/AI 추가
- 프론트: 빌드 주입 변수 안내(VITE_API_BASE_URL·VITE_AI_TIMEOUT_MS) 명시
- 각 .env 와 .example 의 키·순서·주석을 동일하게(값만 실값/placeholder 차이)

(실제 .env.* 는 gitignore 라 미커밋 — 로컬에선 동일 구조로 함께 정리함)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:21:40 +09:00
ttipo 042d724b3a refactor: AI 타임아웃을 백엔드·프론트 동일 값 + 환경변수(AI_TIMEOUT_MS)로 통일
기존: 백엔드 30초(하드코딩 const)·프론트 60초(하드코딩)로 불일치
변경: 루트 .env 의 AI_TIMEOUT_MS(기본 60000)를 단일 소스로
- 백엔드 ai.service: REQUEST_TIMEOUT_MS const → ConfigService(AI_TIMEOUT_MS) 주입, 기본 60s
- 프론트 useAi: import.meta.env.VITE_AI_TIMEOUT_MS 사용, 기본 60s
- docker-compose: 백엔드 environment 에 AI_TIMEOUT_MS, 프론트 build arg 에 VITE_AI_TIMEOUT_MS=${AI_TIMEOUT_MS}
- 프론트 Dockerfile ARG/ENV, env.d.ts 타입, 루트 .env.example 추가
미설정/비정상 값이면 양쪽 모두 기본 60초로 폴백.

검증: 백엔드 build/lint 0, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:01:51 +09:00
ttipo ff33fb2e6c fix: AI 추천 프론트 타임아웃(10초) → 60초, 응답 잘림 없이 수신
근본 원인: useApi 기본 axios timeout 이 10초인데 suggestChecklist 가 이를
덮어쓰지 않아, AI 생성이 10초를 넘으면(항목 제한 해제 후 ~10.5초로 일관 초과)
백엔드가 200 으로 완료해도 프론트가 먼저 끊어 '추천을 가져오지 못했습니다' 발생.
(백엔드 에러 로그가 없던 이유 — 백엔드는 성공했음)
suggestChecklist 호출에 timeout 60000(백엔드 abort 30초보다 길게) 지정.

런타임 측정: 백엔드 suggest 응답 10.48초/HTTP 200 확인.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:54:40 +09:00
ttipo 0d47e989ba feat: AI 추천 카테고리당 항목 수 제한 해제
- 프롬프트의 '3~6개씩' 제한 제거 → 카테고리별로 필요한 만큼 제안
- parseSuggestions 의 카테고리당 15개 슬라이스 캡 제거(응답 길이는 max_tokens 가 자연 제한)
- 항목이 늘어 잘리지 않도록 suggest max_tokens 3072 → 4096

런타임 재현: 동일 입력에서 필수8·관리6·보안8·부가6(총 28개)로 더 풍부하게 반환.
백엔드 build/lint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:32:35 +09:00
ttipo f8ae813c05 fix: AI 추천 토큰 한도 부족으로 응답 잘림 → 추천 실패 수정
4개 카테고리(필수/관리/보안/부가) 구조화 JSON 응답이 max_tokens=1024 를
넘으면 중간에 잘려 JSON 파싱이 실패하고 '추천을 가져오지 못했습니다' 로 떴음.
- callClaude 에 maxTokens 파라미터 추가, suggestChecklist 는 3072 사용
- parseSuggestions 파싱 실패/빈 결과 시 원본 일부를 warn 로그로 남겨 진단 가능

런타임 재현: 동일 입력으로 필수5·관리5·보안5·부가4(총 19개) 정상 반환 확인.
백엔드 build/lint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:27:31 +09:00
ttipo 3379c54033 feat: AI 추천 모달 디자인 개편 + 4개 고정 카테고리(필수/관리/보안/부가)
백엔드:
- SUGGEST_SYSTEM_PROMPT 를 정확히 4개 카테고리(필수/관리/보안/부가 기능)로 고정
  (parseSuggestions 가 빈 카테고리는 자동 제외)

프론트(시안 'AI 추천 모달.html' 반영):
- 그라데이션 헤더(배지 아이콘 + '자동 생성' 뱃지 + 분석 요약 서브타이틀)
- 카테고리별 색상 아이콘 dot + 개수 pill + 전체 추가/해제 토글
- 항목을 체크 토글로 '선택' 후 푸터의 'N개 업무에 추가'로 일괄 반영(기존 즉시추가 → 선택→커밋 모델)
- 이미 체크리스트에 있는 항목은 잠금(added) 표시
- 푸터: 선택 개수 + 취소/추가 버튼(.ai-btn — 페이지 .btn 과 분리)

검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:19:03 +09:00
ttipo bd21557e39 fix: AI 추천 검증을 제목·내용 둘 다로 확장
AI 추천은 제목과 내용을 함께 분석하므로, 둘 중 하나라도 비면 해당 필드에
인라인 에러(빨강 테두리+메시지)를 표시하고 요청을 중단하도록 수정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:55:20 +09:00
ttipo 867f47ba8f feat: AI 추천 버튼도 비활성화 제거 + 인라인 검증으로 통일
기존: 제목 미입력 시 'AI 추천' 버튼 비활성화(canRequestAi)
변경: 제출 버튼과 동일 로직 — 버튼 상시 활성(분석 중 in-flight 만 disabled),
클릭 시 제목이 비어 있으면 titleError 로 인라인 안내(빨강 테두리+메시지) 후 중단.
사용 안 하게 된 canRequestAi computed 제거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:46:07 +09:00
ttipo 116560b549 style: 업무 제목 인풋도 에러 시 빨간 테두리 표시
borderless 제목 인풋에 has-error 시 빨간 테두리(+가로 패딩) 부여 —
에디터·담당자 필드와 일관. box-sizing:border-box 라 overflow 없음,
세로 패딩 유지로 레이아웃 점프 최소화.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:41:10 +09:00
ttipo 612d6c7de8 fix: 업무 내용 필수 검증 추가 + 제목 에러 표기 정리
- 업무 내용(*)이 표기상 필수인데 검증이 없던 문제 → 필수로 강제
  · 프론트: 인라인 검증(에디터 빨강 테두리 + '업무 내용을 입력해 주세요.'),
    입력 시 즉시 해제
  · 백엔드: CreateTaskDto.content 를 @ArrayNotEmpty 필수로(데이터 계약 일치)
- 제목 에러 표기 정리: 1.5rem 큰 placeholder 가 빨갛게 변하며 아래 메시지와
  이중으로 강조되던 것을 제거 → 작은 인라인 메시지만 표시(차분하게)

검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:36:58 +09:00
ttipo 9f66c6d0ec feat: 업무 지시 필드별 인라인 검증 + 제출 버튼 상시 활성화
기존: 제목·담당자 미입력 시 '지시 보내기' 버튼이 비활성화(이유 안내 없음)
변경:
- 버튼 비활성화 해제(제출 중 중복 클릭 방지용 submitting 만 유지)
- 제출 시 validateForm 으로 검증 → 미입력 필드에 인라인 에러 표시
  · 제목: 빨강 placeholder + '업무 제목을 입력해 주세요.'
  · 담당자: combo 빨강 테두리 + '담당자를 1명 이상 지정해 주세요.'
- 해당 필드를 채우면 watch 로 에러 즉시 해제

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:29:36 +09:00
ttipo ae6af518dd style: 업무 생성 AI 추천을 하단 인라인 패널 → 모달로 변경
기존: 체크리스트 아래에 인라인으로 펼쳐지던 AI 추천 패널
변경: Teleport 기반 중앙 모달(반투명 배경, 헤더 고정 + 본문 스크롤).
배경 클릭/× 로 닫기(분석 중에는 닫기 비활성). 추천 항목 추가 동작은 동일.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:50:41 +09:00
ttipo 544d49aa97 style: 업무 생성 첨부파일 선택을 강조 없는 일반 버튼으로 변경
기존: 점선 테두리 + 강조색(accent) 굵은 '클릭하여 파일 선택' 드롭존(강조 스타일)
변경: 클립 아이콘 + '파일 선택' 의 차분한 보조 버튼(.attach-btn). 미사용
.dropzone.is-disabled 잔여 스타일도 제거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:46:35 +09:00
ttipo 73eb53625c fix: 멤버 초대/역할변경/제거 알림에 actorName 누락 → '누군가' 표기 수정
ProjectMemberService 의 notify payload 에 actorName 이 빠져 있어, 프론트
notification.store 가 actorName 부재 시 '누군가'로 폴백하던 문제.
세 member.* 알림(invited/role_changed/removed) payload 에 actorName 추가
(actingUserId 로 사용자명 조회하는 actorName 헬퍼 신설). 활동 피드는 actor
관계로 별도 해결되므로 영향 없음.

런타임 검증: 초대 시 수신자 알림 payload.actorName = 초대자 이름으로 채워짐.
백엔드 build/lint 0·8 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:31:03 +09:00
ttipo 32a87d8f62 chore: 잔존 '저장소' 주석·문구를 '프로젝트'로 정리
저장소 기능 제거 후 남은 '저장소' 표현을 프로젝트로 통일:
- 백엔드: activity/notification/task 주석·ApiResponse 문구, AI 시스템 프롬프트
  (SUGGEST_SYSTEM_PROMPT 의 저장소→프로젝트, 무의미해진 'slug=영문이름' 지시 제거),
  CONTEXT_REPO_LIMIT→CONTEXT_PROJECT_LIMIT(프로젝트 컨텍스트 한도)
- 프론트: 페이지/스토어 주석 및 TaskCreate/TaskDetail 의 사용자 노출 문구('저장소'→'프로젝트')
- 단, auth OAuth state 'store' 의미의 '저장소'(cookie-state/strategies)는 정당하여 유지

검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:57:06 +09:00
ttipo be0a1bc9d1 refactor: 'Repo' 접두 타입·헬퍼 식별자를 'Project' 로 일괄 rename
저장소 기능 제거 후 남은 레거시 'Repo' 명칭을 프로젝트 의미에 맞게 정리(동작 변화 없음):
- 타입: ApiRepoMember→ApiProjectMember, ApiRepoTask→ApiProjectTask,
  ApiRepoTaskListResult→ApiProjectTaskListResult, RepoTask(View)→ProjectTask(View),
  RepoMember(Response)→ProjectMember(Response), RepoActivityItem/Day→ProjectActivityItem/Day,
  RepoTaskResponse/ListResult→ProjectTaskResponse/ListResult (백엔드 task.service 포함)
- 헬퍼: toRepoTaskView→toProjectTaskView, toRepoActivityItem→toProjectActivityItem,
  loadRepo→loadProject, filteredRepos→filteredProjects, removeRepo→removeProject
- 백엔드 spec mock repoRepo→projectRepo, 잔존 RepoDetailPage/저장소(Repo) 주석 정리

검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0 (식별자 충돌 없음).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:52:23 +09:00
ttipo ef1757181d refactor: 저장소(Repo)·Gitea 연동 기능 완전 제거 — 프로젝트 단독 구조
복잡도를 줄이기 위해 저장소 연동 기능을 전부 제거하고 프로젝트를 단독 단위로 운영한다.

백엔드:
- modules/repo, modules/gitea 모듈 전체 삭제(엔티티/서비스/컨트롤러/DTO/역방향 동기화)
- app.module 에서 RepoModule 제거
- ProjectService: repoCount 집계·repoRepo·"미분류" 시스템 프로젝트(ensureUnassigned/
  onApplicationBootstrap)·isSystem 게이트 전부 제거. ProjectResponse 에서 repoCount/isSystem 제거
- Project 엔티티 is_system 컬럼 제거(dev synchronize 가 DROP)
- ProjectModule forFeature 에서 Repo 제거
- Activity 타입 repo.linked/unlinked 제거
- env example 에서 Gitea/REPO_IMPORT 섹션 제거

프론트:
- ProjectReposPage, useRepo 삭제. ProjectTabs 의 '저장소' 탭·repoCount prop 제거
- 4개 페이지의 :repo-count 제거, 라우트 /projects/:id/repos 제거
- types/project·project.store·mock 에서 repoCount/isSystem·Repo 뷰 제거
- ProjectListPage 저장소수/시스템 배지·gitea CSS 제거, activity 의 repo.* 케이스 제거
- types/repo 는 공용 ApiMember 만 유지
- 잔여 '저장소'/Gitea 사용자 문구를 프로젝트로 정리

검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:44:15 +09:00
ttipo 861c8efa6b chore: 테스트 편의를 위해 rate limit 을 분당 1000회로 완화
세 겹의 제한을 모두 분당 1000회로 상향(테스트 중 429 회피):
- main.ts express-rate-limit: 15분 150회 → 1분 1000회
- 전역 ThrottlerModule: 분당 100회 → 1000회
- 라우트별 @Throttle(login/signup/resend/forgot/reset/ai): 3~20회 → 1000회

각 라우트 주석에 운영 권장값을 남겨 추후 복원/ env 화 용이.
운영 배포 전 적정값으로 되돌릴 것.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:27:41 +09:00
ttipo d0599787f7 style: 저장소 탭 breadcrumb 을 다른 탭과 동일하게 통일
ProjectReposPage 가 .crumb .lnk(accent색)·.sep(여백) 를 로컬로 덮어써 다른 탭과
달라 보이던 것을 제거 → 전역 relay.css 의 crumb 스타일(text-2 링크·opacity 구분자)
을 따르도록 통일.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:19:42 +09:00
ttipo 647336b14d style: 저장소 탭 가이드 문구를 멤버 탭과 동일한 형식(tab-note)으로 변경
기존 .lede 단락 → 멤버 탭의 안내(정보 아이콘 + 작은 회색 텍스트) 형식으로 통일

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:12:15 +09:00
ttipo 3667f48897 fix: 저장소 연동 추가 폼이 열려 있을 때 기존 저장소 목록 숨김
연동 추가 화면(showForm)에서는 하단의 연동 저장소 목록 섹션을 표시하지 않는다.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:06:43 +09:00
ttipo 4d943e8d0d refactor: 저장소 공개 범위(visibility) UI 제거 + 기본 공개(public) 생성
사용자 입력/표시에서 저장소 공개 범위를 제거하고 새 저장소는 공개로 생성한다.
(Gitea 역방향 동기화에 쓰이는 Repo 엔티티 visibility 컬럼은 유지)

백엔드:
- CreateRepoDto/UpdateRepoDto 에서 visibility 제거
- RepoService.create 가 visibility 를 'public' 으로 고정
- update 의 visibility 변경·Gitea private 푸시 로직 제거(공개범위는 Gitea 기준)
- RepoResponse/toResponse 에서 visibility 제거

프론트:
- types/repo ApiRepo/Create/Update 페이로드에서 visibility 제거
- ProjectReposPage 연동 폼의 공개 범위 입력 + 목록 배지 + 관련 CSS 제거
- mock Visibility 타입·orphan Repo 뷰의 visibility 제거

런타임 검증: 저장소 응답에 visibility 없음, repos.visibility 컬럼 유지(Gitea 동기화).
백엔드 build/lint 0·14 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:51:21 +09:00
ttipo 135cc0759f refactor: 저장소 연동 폼 — 표시 제목 최상단 + 템플릿 셀렉트 복원
- 필드 순서 변경: 표시 제목(name)을 최상단으로(기존 slug 가 최상단)
- 템플릿을 체크박스 → 기존 셀렉트 구조로 복원
  (없음[빈 저장소] / {템플릿slug} 템플릿, 가용 시에만 옵션 노출)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:36:48 +09:00
ttipo 58c0b9510d refactor: 프로젝트에서 공개 범위(visibility) 제거
불필요한 프로젝트 공개 범위 데이터를 백엔드·프론트에서 일괄 제거(저장소 Repo 의 visibility 는 Gitea 연동용이라 유지).

백엔드:
- Project 엔티티 visibility 컬럼·ProjectVisibility 타입 제거(dev synchronize 가 컬럼 DROP)
- create/update DTO, ProjectResponse, create/update/ensureUnassigned/toResponse 에서 제거
- ai.service 프로젝트 컨텍스트 문자열에서 공개범위 표기 제거
- activity 타입 project.visibility_changed 제거

프론트:
- types/project·project.store·mock Project 에서 visibility 제거
- ProjectCreatePage·ProjectSettingsPage 공개 범위 입력 폼+CSS 제거
- ProjectListPage 공개여부 세그먼트 필터+배지 제거, ProjectHeader 배지 제거
- activity 타입/스토어 project.visibility_changed 케이스 제거

런타임 검증: DB projects.visibility 컬럼 DROP, 생성/목록 응답에 visibility 없음, CRUD 정상.
백엔드 build/lint 0·14 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:39:48 +09:00
ttipo 9fdcecace8 feat: 이메일 인증 링크를 결과 페이지(창 닫기)로 분리 + 가입 대기 화면에 로그인 버튼
- 인증 링크(verify-email)의 리다이렉트 대상을 /login?verified → /email-verified?ok=1|0 으로 변경
- 신규 EmailVerifiedPage(/email-verified): 인증 완료/실패 표시 후 창 자동 닫기 시도.
  브라우저가 사용자가 연 탭의 close 를 막을 수 있어 안내 문구 + 수동 로그인/가입 링크 병행
- SignupPage 인증 대기 화면에 '인증을 완료했어요 · 로그인하러 가기' 기본 버튼 추가
  (다른 탭에서 인증을 마친 뒤 기존 화면에서 로그인 진입)

런타임 검증: 유효 토큰→/email-verified?ok=1·무효→ok=0, 인증 후 로그인 200, SPA 라우트 제공.
백엔드 build/lint 0, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:22:23 +09:00
ttipo 72b05f1b69 feat: 비밀번호 찾기 화면을 로그인 2단 레이아웃으로 변경
- 시안(비밀번호 찾기.html) 적용 — 좌측 브랜드 패널 + 우측 폼(로그인 화면과 동일 구조)
- 1단계 이메일 입력(뒤로가기 링크·재설정 링크 보내기) → 2단계 전송 완료
  (메일 아이콘·수신 이메일 칩·스팸함 안내 박스·다시 보내기·로그인 복귀)
- 다시 보내기 시 동일 이메일 재요청 + '전송됨 ✓' 일시 피드백
- 시안의 '30분 유효'는 실제 백엔드 토큰 TTL(1시간)에 맞춰 표기

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:15:22 +09:00
ttipo 52b70363d9 feat: 비밀번호 재설정 플로우 (이메일 링크 기반)
백엔드:
- User 엔티티에 passwordResetTokenHash/passwordResetExpiresAt(select:false) 추가
- UserService: setPasswordResetToken/findByPasswordResetTokenHash/updatePassword
- MailService: sendPasswordResetEmail (dev=로그 출력, prod=수신자만)
- AuthService: requestPasswordReset(존재 비노출·로컬계정만·1시간 토큰),
  resetPassword(토큰 해시 검증→새 비번 저장→전 세션 폐기)
- POST /auth/forgot-password(3/분)·/auth/reset-password(5/분), 가입과 동일 비번 규칙
- OAuth 콜백의 잔여 /repos 리다이렉트를 /projects 로 교정(리팩터 누락분)

프론트:
- useAuth: forgotPassword/resetPassword
- ForgotPasswordPage(/forgot-password), ResetPasswordPage(/reset-password, 토큰=쿼리)
- LoginPage '비밀번호 찾기' 링크 연결 + 재설정 완료(?reset=1) 배너

런타임 검증: 재설정→새 비번 로그인 200·구 비번 거부·토큰 재사용 거부·약한 비번 VAL_001·
없는 이메일 동일응답(비노출). 백엔드 build/lint 0·14 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:50:34 +09:00
ttipo cb6aa07ebc docs: 프로젝트 우선 리팩터 런타임 점검 완료 기록 (Phase 3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:12:19 +09:00
ttipo 9aa22f092d fix: 백엔드·프론트 헬스체크 오탐 수정 (런타임 점검 발견)
- 백엔드: 앱이 전역 프리픽스 /api 하위라 루트(/) 프로브가 404 → /api/health 프로브로 교정
- 공통: localhost 가 IPv6(::1)로 풀려 IPv4 전용 바인딩에 연결 실패하던 것을 127.0.0.1 고정
- 두 컨테이너가 정상 동작에도 unhealthy 로 표시되던 선존 버그(리팩터 무관)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:11:40 +09:00
ttipo 8a2e34d617 feat: 프로젝트 저장소 연동 UI + 화면 문구 정리 (Phase 2b)
- useRepo 복구(프로젝트 종속 /projects/:projectId/repos): list/meta/checkName/create/remove
- types/repo ApiRepo 를 새 RepoResponse 와 정합(projectId 추가, members/counts 제거)
- ProjectTabs 에 '저장소' 탭(repoCount) 추가, 4개 페이지 repo-count 전달
- ProjectReposPage 신설(/projects/:id/repos): 연동 저장소 목록 + 연동(생성) 폼
  (slug 가용성 디바운스 체크·템플릿 옵션) + Gitea 열기 + 연동 해제(admin)
- 화면 잔여 '저장소' 문구를 '프로젝트'로 정리(crumb/설정/멤버 모달/주석/not-found)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:52:23 +09:00
ttipo e028449312 feat: 프로젝트 카운트·멤버 활동/알림 배선 (Phase 1 마무리)
- ProjectService: 프로젝트별 연동 저장소 수·업무 완료/전체 수 집계
  (Repo/Task 엔티티 forFeature 등록, 그룹 쿼리로 N+1 회피, findOne 멤버 로딩)
- ProjectMemberService: 멤버 목록 taskCount(담당 업무 수) 집계
- ProjectMemberService: 초대/역할변경/제거 시 활동 적재 + 대상자 알림 발송
  (ActivityModule/NotificationModule 주입, member.* 페이로드 projectId/projectName/targetName)
- TaskService: 다른 모듈로 이관되어 미사용된 countsByProject/assigneeCounts 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:43:52 +09:00
ttipo 7cd6676ef9 feat: 프론트엔드 프로젝트 우선 구조 재구성 (Phase 2-a)
- 데이터 계층 신설: types/project·useProject·project.store (프로젝트 CRUD·페이지네이션)
- useTask/useMember/useActivity 경로를 /projects/:projectId/... 로 전환
- task·my-task·activity 타입/스토어 repoId/repoName → projectId/projectName
- 알림 store 라우팅·문구 프로젝트화, 활동 타입 repo.linked/unlinked·project.* 반영
- 라우팅 /projects/... 전면 교체, / → /projects 리다이렉트
- 페이지 rename Repo*→Project* (List/Create/Detail/Settings/Members/Activity)
- ProjectCreatePage: Gitea 저장소 폼 → 단순 프로젝트 생성 폼으로 재작성
- RepoHeader→ProjectHeader, RepoTabs→ProjectTabs, AppShell 네비 프로젝트화
- 죽은 파일 삭제: repo.store·useRepo·GiteaRepoBar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:37:51 +09:00
ttipo 6ba148314c refactor: 백엔드를 프로젝트 우선 구조로 이전 (업무·멤버·활동·저장소)
저장소=프로젝트 1:1 구조를 프로젝트 우선 + 프로젝트 1:N 저장소로 이전(2단계).

- Activity: repo FK → project FK, listForProject, /projects/:projectId/activity
- Repo: project FK 추가, RepoMember 폐지(멤버십은 프로젝트), /projects/:projectId/repos 로 이동
- Task: project 종속(seq=프로젝트 내 순번 @Unique[project,seq]), 권한=ProjectMember, /projects/:projectId/tasks
- RepoService: 프로젝트 종속(멤버·업무카운트·캐시 제거), 권한은 ProjectService 위임
- RepoSyncService: Gitea import → "미분류" 시스템 프로젝트로 연결, import-admin 로직 제거
- AiService: 컨텍스트·추천을 프로젝트 기준으로
- 삭제: repo-member 엔티티/멤버 서비스·컨트롤러/repo-cache
- 알림 payload repoId/repoName → projectId/projectName

검증: build/lint 0, 14 tests. 잔여(후속): ProjectResponse 카운트 집계·멤버 활동/알림 배선.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:03:09 +09:00
ttipo 2f66cdda8a feat: 프로젝트(Project) 모듈 기반 추가 — 프로젝트 우선 구조 1단계
저장소=프로젝트 1:1 구조를 프로젝트 우선 + 프로젝트 1:N 저장소로 전환하는 리팩터의 기반.
기존 코드는 건드리지 않고 추가만 함(기존 저장소 기반 동작 유지).

- Project / ProjectMember 엔티티 (isSystem '미분류' 버킷, admin/member/owner)
- ProjectService: CRUD + 권한 헬퍼(assertProjectMember/Admin) + 부팅 시 '미분류' 시스템 프로젝트 보장
- ProjectMemberService: 초대/역할/제거 (활동·알림 배선은 Activity 이관 단계 TODO)
- /projects, /projects/:id/members 라우트, app.module 등록
- docs/project-refactor-plan.md (리팩터 SSOT)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 18:41:58 +09:00
ttipo 9bcbc1564f revert: AI 코드 검토 기능 제거 (85ae82f 되돌림)
통짜 코드 전송 방식이 토큰 비효율·부정확해, 검색 기반(grep으로 좁힌 뒤 관련
스니펫만 판정) 방식으로 재설계하기 위해 일괄 제거. 추후 개선안으로 재작업 예정.

This reverts commit 85ae82f.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:56:43 +09:00
ttipo e72e6aee83 chore: 디자인 시안 HTML 파일 제거
Vue 이식이 완료된 초기 디자인 시안(Relay 화면 전체/*.html) 정리.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:20:13 +09:00
ttipo 85ae82f3ce feat: AI 코드 검토 — 연결된 Gitea 코드로 체크리스트 구현 여부 판정
- ChecklistItem.reviewVerdict('done'|'missing'|null) 컬럼 추가 + 응답/프론트 타입 전파
- GiteaService.fetchRepoCode: 기본 브랜치 텍스트 파일 스냅샷(의존성/빌드/바이너리 제외, ~120KB 예산)
- AiService.reviewChecklist: 코드+체크리스트를 Claude 에 보내 항목별 done/missing 판정 후 체크리스트에 반영
- POST /ai/review-checklist(throttle 6/분, Claude 타임아웃 60s), Gitea 미연동/코드없음/항목없음 400
- TaskService.getTaskForReview/applyReviewVerdicts
- 프론트: TaskDetailPage 승인 요청 카드(prog/changes)에 "AI 검토" 버튼 → 체크박스 ✓(초록)/✗(빨강), 요약 배너

베스트에포트 휴리스틱(코드 일부만 검토), Gitea 연동 저장소 한정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:19:01 +09:00
ttipo 57a88ff741 feat: 업무 생성 화면 첨부파일 활성화 (제출 시 업로드)
업로드 API 가 기존 taskId 종속이라 막혀 있던 생성 화면 첨부를 2단계 제출로 해결.
선택한 파일을 메모리에 보유하다 업무 생성/수정 후 그 taskId 로 순차 업로드(베스트에포트,
실패 개수 안내). 수정 모드는 기존 첨부 표시·즉시 삭제 + 새 파일 업로드. 25MB 사전 필터.
백엔드 변경 없음(기존 업로드 엔드포인트 재사용).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:18:35 +09:00
ttipo df97719e0f fix: 첨부 다운로드 링크를 백엔드 절대 URL 로 변환
백엔드가 내려준 루트 상대경로(/api/.../download)를 <a href> 가 프론트 오리진으로
해석해 404 페이지가 뜨던 문제 수정. useApi 에 toAbsoluteApiUrl 추가, 첨부 매핑에 적용.
(dev 프론트 5273 / 백엔드 3100 오리진 분리 환경)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:18:14 +09:00
ttipo 406e13da1c fix: 업무 상세 활동 탭 내용·시간 간격 축소
활동 항목의 내용과 아래 시간 텍스트 사이 간격(.act-time margin-top)을 0.375rem→0.1875rem 으로 줄임.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:17:44 +09:00
ttipo 0976a5057b feat: 업무 작성 AI 할 일 추천
- POST /ai/suggest-checklist — 저장소 표시제목/설명 + 업무 제목/내용을 Claude 에 보내
  카테고리별(필수/추가/보안) 할 일 항목을 JSON 으로 받아 파싱(코드펜스/잡텍스트 방어, 빈 결과 502)
- AiSuggestController + SuggestChecklistDto + AiService.suggestChecklist/parseSuggestions
- 프론트: TaskCreatePage 액션바 "취소 / AI 추천 / 지시 보내기" + 체크리스트 추천 패널
  (카테고리별 항목 클릭 추가·전체 추가·중복 스킵), useAi.suggestChecklist(silent)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 15:05:34 +09:00
ttipo 51b608efeb feat: AI 채팅 다듬기 — 저장소 컨텍스트·표시제목·마크다운 렌더·말투
- 컨텍스트에 저장소 목록 추가(RepoService.findAll), slug 빼고 표시 제목만 노출
- AI 응답 마크다운 렌더링: shared/utils/markdown.ts (escapeHtml 후 신뢰 태그만 조립, 의존성 0, XSS 안전 — 굵게/목록/코드/제목/링크)
- 시스템 프롬프트: 간결·이모지/사과 남발 금지·과도한 존칭 지양·마크다운 사용·저장소는 표시제목으로 안내

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 15:03:59 +09:00
ttipo b8e8095170 fix: 내 업무·AI 컨텍스트 orderBy 컬럼명 버그(databaseName) 수정
listAssigned/listIssued 가 to-many 조인 + 페이지네이션 정렬에서 DB 컬럼명
'task.due_date' 를 사용해, TypeORM distinct 경로가 컬럼 메타데이터를 못 찾아
"Cannot read properties of undefined (reading 'databaseName')" 로 크래시.
정렬 키를 엔티티 속성명 'task.dueDate' 로 변경 — 내 업무 화면 + AI 컨텍스트 동시 복구.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 14:59:45 +09:00
ttipo 2f7f28dd7b feat: AI 에이전트 주제 한정(가드레일) + 사용자 업무 컨텍스트 주입
- 시스템 프롬프트를 업무/Relay 한정으로 재작성 — 무관한 주제는 정중히 거절
- 매 요청 시 사용자 컨텍스트 주입: 오늘 날짜(KST) + 담당 업무 최대 30건(제목/상태/마감/체크리스트) + 지시 업무 수
- TaskService.listAssigned/listIssued 활용(AiModule→TaskModule 단방향), 조회 실패해도 채팅 진행
- callClaude(messages, system)로 시그니처 변경(매 메시지 최신 컨텍스트)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 09:03:45 +09:00
ttipo e432832c92 feat: AI 대화 DB 영속 + 다중 대화/기록 목록
- AiConversation·AiMessage 엔티티(user/conversation CASCADE), 제목 자동 생성
- 대화 스코프 API: GET/POST /ai/conversations, GET/:id, POST/:id/messages, DELETE/:id (전부 본인 소유 스코프 → IDOR 차단)
- 클라는 새 메시지 1건만 전송, 서버가 DB 이력으로 Claude 호출(무상태 /ai/chat 폐기)
- Claude 실패 시 사용자 메시지·빈 대화 롤백(고아 데이터 방지)
- 프론트: AgentChatPanel 채팅↔지난 대화 목록 뷰(열기/삭제), ai.store 재작성(목록·현재 대화)
- 다른 기기·재로그인·새로고침에도 기록 유지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 08:58:40 +09:00
ttipo 5da3ec8818 feat: AI 채팅 패널 (Claude API)
- 백엔드 modules/ai: POST /ai/chat — Anthropic Messages API 프록시(키 서버 env 한정, 30s 타임아웃, throttle 20/분, 미설정 503/외부오류 502)
- ChatDto 검증(role enum, content 8000자, 메시지 50개 상한), 모델 CLAUDE_MODEL(기본 claude-sonnet-4-6)
- 프론트 AgentChatPanel: 우측 슬라이드 채팅(말풍선/타이핑/에러/새 대화), plain text 렌더로 XSS 안전
- AppShell 헤더에 AI 버튼 상시 노출 — 모든 화면에서 열림
- 대화 이력은 stores/ai.store(싱글톤)에 보관 → 화면 이동에도 유지, '새 대화'로만 초기화
- TaskDetailPage 의 기존 목업 에이전트 패널 전부 제거(보안 L1 해소)
- env.example 에 CLAUDE_MODEL 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:04:48 +09:00