feat: 저장소 생성 시 Gitea 조직 연동 추가

외부 Gitea 인스턴스의 조직에 저장소를 자동 동기화한다.
- GiteaService: /api/v1 클라이언트(내장 fetch, 새 의존성 없음), enabled 게이트
- Repo 엔티티에 연동 컬럼(giteaRepoId/giteaFullName/cloneUrl/htmlUrl) 추가
- RepoService 생성/수정/삭제에 Gitea 동기화 와이어링(생성 우선·실패 시 롤백)
- GITEA_* env 문서화(.env.example), api-contract/진행현황 문서 갱신

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 22:17:33 +09:00
parent e1bc5a1dfc
commit 6027856cbb
10 changed files with 529 additions and 27 deletions
+21 -3
View File
@@ -85,15 +85,15 @@ status 매핑보다 우선해 그대로 표준 응답으로 내려보내고, 프
| PATCH | `/repos/:repoId` | `UpdateRepoDto` | `Repo` |
| DELETE | `/repos/:repoId` | — | `null` |
`CreateRepoDto`: `{ name, desc, visibility('private'|'public'), branch? }`
`CreateRepoDto`: `{ slug, name, visibility('private'|'public'), description?, branch? }`
`Repo` 응답(원시값 중심):
```jsonc
{
"id": "q3-launch-campaign", // slug 마지막 segment = 라우팅 id
"name": "3분기 신제품 런칭 캠페인",
"owner": "marketing-team",
"slug": "marketing-team/q3-launch-campaign",
"owner": "marketing-team", // Gitea 연동 시 Gitea 조직명
"slug": "marketing-team/q3-launch-campaign", // = Gitea full_name
"desc": "...",
"visibility": "private",
"branch": "main",
@@ -101,12 +101,30 @@ status 매핑보다 우선해 그대로 표준 응답으로 내려보내고, 프
"memberCount": 5,
"doneCount": 8,
"totalCount": 12,
"cloneUrl": "https://gitea.example.com/marketing-team/q3-launch-campaign.git", // 미연동 시 null
"htmlUrl": "https://gitea.example.com/marketing-team/q3-launch-campaign", // 미연동 시 null
"updatedAt": "2026-06-16T11:00:00Z"
}
```
> `moreCount`, `progressPct/Level/Label`, `updatedAgo` 는 프론트 계산.
> 매핑 페이지: RepoListPage, RepoCreatePage, RepoDetailPage(헤더), RepoSettingsPage.
#### Gitea 연동 (외부 인스턴스)
저장소 생명주기를 외부 Gitea 조직에 동기화한다. `GITEA_BASE_URL`/`GITEA_TOKEN`/`GITEA_ORG`
(backend `.env`) 가 모두 설정되면 활성화되고, 비어 있으면 Gitea 호출 없이 기존대로 동작한다.
| Relay 동작 | Gitea API | 매핑 |
|---|---|---|
| `POST /repos` | `POST /api/v1/orgs/{org}/repos` (auto_init) | `slug`→repo name, `visibility`→private, `branch`→default_branch, `name`+`desc`→description |
| `PATCH /repos/:id` | `PATCH /api/v1/repos/{org}/{slug}` | `name`/`desc` 변경→description, `visibility` 변경→private (slug 불변) |
| `DELETE /repos/:id` | `DELETE /api/v1/repos/{org}/{slug}` | — |
- 인증: 조직 권한 토큰 단일 사용(`Authorization: token <TOKEN>`). 사용자별 Gitea 계정 매핑은 추후.
- 생성은 Gitea 우선 → 성공 시 Relay 저장(연동 정보 `giteaRepoId`/`cloneUrl`/`htmlUrl` 보관). Relay 저장 실패 시 Gitea repo 롤백 삭제.
- Gitea 이름 충돌(409) → `BIZ_001` "이미 사용 중인 저장소 이름입니다.", 그 외 실패 → `BIZ_001`(502).
- 수정/삭제의 Gitea 동기화는 **베스트 에포트**(실패해도 Relay 상태는 반영, 에러 로그만 남김).
---
## 3. 멤버 (`/api/repos/:repoId/members`) — **3단계**