feat: Gitea 조직 저장소 역방향 동기화 (기동 시·매시간 import)
- GiteaService.listOrgRepos(): 조직 저장소 페이지네이션 조회 - RepoSyncService: OnApplicationBootstrap(비차단) + @Cron(매시간) 동기화 - 추가형 import(멤버 0명), 연동정보 backfill, Gitea 누락 시 giteaMissing 표기 - Repo.giteaMissing 컬럼 + RepoResponse/프론트 ApiRepo 노출 - GET /repos 를 '조직 저장소 전체'(findAll)로, 단건은 인증 사용자 열람 허용 - docs(api-contract·integration-progress) 2.6단계 반영 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-1
@@ -79,7 +79,7 @@ status 매핑보다 우선해 그대로 표준 응답으로 내려보내고, 프
|
||||
|
||||
| 메서드 | 경로 | 요청 | 응답 |
|
||||
|---|---|---|---|
|
||||
| GET | `/repos` | `?page&size` | `Repo[]`(요약) — 내가 멤버인 저장소 |
|
||||
| GET | `/repos` | `?page&size` | `Repo[]`(요약) — **조직 저장소 전체**(생성 출처 무관) |
|
||||
| GET | `/repos/meta` | — | `{ owner, template }` — 생성 폼 메타(`:repoId` 보다 먼저 매칭) |
|
||||
| POST | `/repos` | `CreateRepoDto` | `Repo` |
|
||||
| GET | `/repos/:repoId` | — | `Repo`(상세) |
|
||||
@@ -108,10 +108,12 @@ status 매핑보다 우선해 그대로 표준 응답으로 내려보내고, 프
|
||||
"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
|
||||
"giteaMissing": false, // Gitea 조직에서 사라진 저장소 표시(동기화로 갱신)
|
||||
"updatedAt": "2026-06-16T11:00:00Z"
|
||||
}
|
||||
```
|
||||
> `moreCount`, `progressPct/Level/Label`, `updatedAgo` 는 프론트 계산.
|
||||
> `GET /repos` 는 단일 조직 모델상 **조직의 모든 저장소**를 반환한다(멤버십은 역할/권한에만 사용). Gitea-동기화로 import 된 저장소는 멤버 0명으로 노출되며, `giteaMissing:true` 면 프론트에서 '원격 없음' 배지로 표기한다.
|
||||
> 매핑 페이지: RepoListPage, RepoCreatePage, RepoDetailPage(헤더), RepoSettingsPage.
|
||||
|
||||
#### Gitea 연동 (외부 인스턴스)
|
||||
@@ -132,6 +134,15 @@ status 매핑보다 우선해 그대로 표준 응답으로 내려보내고, 프
|
||||
- Gitea 이름 충돌(409) → `BIZ_001` "이미 사용 중인 저장소 이름입니다.", 그 외 실패 → `BIZ_001`(502).
|
||||
- 수정/삭제의 Gitea 동기화는 **베스트 에포트**(실패해도 Relay 상태는 반영, 에러 로그만 남김).
|
||||
|
||||
##### Gitea → Relay 역방향 동기화 (주기적 import)
|
||||
|
||||
조직에 직접 만든 저장소도 Relay 에 보이도록, **서버 기동 직후 1회 + 매시간 정각**에 조직 저장소 목록(`GET /api/v1/orgs/{org}/repos`)을 받아 Relay 와 맞춘다(`RepoSyncService`).
|
||||
|
||||
- **추가형(additive)**: Gitea ID → slug 순으로 매칭. Relay 에 없으면 import(멤버 0명), 양쪽에 있으면 연동정보(`giteaRepoId`/`cloneUrl`/`htmlUrl`)만 backfill. 표시명/설명/공개범위는 import 이후 **Relay 가 SSOT**(재동기화로 덮어쓰지 않음).
|
||||
- **import 표시명**: Gitea description 의 `"표시제목 — 설명"` 규칙을 파싱(구분자 없으면 slug 를 표시명, 전체를 설명으로).
|
||||
- **누락 표기**: Gitea 에서 사라진(연동 origin) 저장소는 **삭제하지 않고** `giteaMissing:true` 로만 표기. 다시 나타나면 false 로 복구.
|
||||
- Gitea 미연동(`GITEA_*` 미설정) 시 동기화는 건너뛴다. 중복 실행 방지 가드 포함.
|
||||
|
||||
---
|
||||
|
||||
## 3. 멤버 (`/api/repos/:repoId/members`) — **3단계**
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
> 프론트 UI(목업)를 NestJS 백엔드에 단계적으로 연동하는 작업의 진행 기록.
|
||||
> 계약/스키마 상세는 [`api-contract.md`](./api-contract.md) 참조.
|
||||
> 최종 갱신: 2026-06-17 · 현재 위치: **0~3단계 완료(+ Gitea 연동·템플릿 복제·생성폼 보강), 4단계(업무) 대기**
|
||||
> 남은 프론트 연동: RepoMembersPage(3단계), cloneUrl/htmlUrl 표시(2.5단계). 그 외 4~8단계 미착수.
|
||||
> 최종 갱신: 2026-06-17 · 현재 위치: **0~3단계 완료(+ Gitea 양방향 연동·템플릿 복제·생성폼 보강), 4단계(업무) 대기**
|
||||
> 남은 프론트 연동: RepoMembersPage(3단계), cloneUrl/htmlUrl 표시·giteaMissing 배지(2.5단계). 그 외 4~8단계 미착수.
|
||||
|
||||
---
|
||||
|
||||
@@ -78,6 +78,19 @@
|
||||
|
||||
> **TODO(프론트)**: RepoSettingsPage/RepoDetailPage 에 `cloneUrl`/`htmlUrl` 표시(클론 주소 복사, Gitea 열기 버튼). 백엔드 응답에는 이미 포함.
|
||||
|
||||
### 2.6단계 — Gitea → Relay 역방향 동기화 ✅ (백엔드)
|
||||
|
||||
**백엔드** `backend/src/modules/gitea/`, `modules/repo/repo-sync.service.ts`
|
||||
- 문제: 동기화가 단방향(Relay→Gitea)이라 **Gitea 조직에 직접 만든 저장소가 Relay 목록에 안 떴음**.
|
||||
- `GiteaService.listOrgRepos()`: 조직 저장소 페이지네이션 순회(`GET /orgs/{org}/repos`). 결과에 `name`/`description`/`private` 추가.
|
||||
- `RepoSyncService`: **서버 기동 직후 1회(`OnApplicationBootstrap`, 비차단) + 매시간(`@Cron(EVERY_HOUR)`)** 동기화. `ScheduleModule.forRoot()` 는 `app.module` 에 이미 등록됨.
|
||||
- **추가형**: Gitea ID→slug 매칭. 없으면 import(멤버 0명), 있으면 연동정보만 backfill. 표시명/설명/공개범위는 import 후 Relay 가 SSOT.
|
||||
- **누락 표기**: Gitea 에서 사라진 연동 origin 저장소는 삭제하지 않고 `giteaMissing:true` 로만 표기(재출현 시 복구). `Repo.giteaMissing` 컬럼 추가, `RepoResponse`/프론트 `ApiRepo` 에 노출.
|
||||
- 미연동 시 건너뜀 + 중복 실행 가드.
|
||||
- **목록 모델 변경**: `GET /repos` 가 '내 멤버 저장소' → **'조직 저장소 전체'**(`findAll`). 단건(`findOne`)도 조직 모델상 인증 사용자 열람 허용(쓰기 권한은 기존 admin/owner 유지). 멤버십은 역할/권한 전용.
|
||||
|
||||
> **TODO(프론트)**: RepoListPage 에 `giteaMissing` '원격 없음' 배지 표시. import 된 멤버 0명 저장소의 빈 아바타/0% 진행률은 정상.
|
||||
|
||||
### 3단계 — 멤버 관리 ✅ (백엔드)
|
||||
|
||||
**백엔드** `backend/src/modules/repo/`
|
||||
|
||||
Reference in New Issue
Block a user