feat: 저장소 생성 이름 실시간 가용성 체크 + 소유자 아바타 제거

- GET /repos/name-available?slug= 추가(:repoId 앞 선언, Relay slug 중복만 검사)
- 생성 폼: 형식 즉시 검사 + 350ms 디바운스 서버 조회로 상태별(빈칸/형식위반/확인중/사용가능/중복) 색·아이콘 표기
- 항상 떠 있던 정적 "사용 가능한 이름입니다" 표시 제거
- 조직 이니셜 소유자 아바타 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 17:03:59 +09:00
parent 42db9b5d00
commit 7a2c70a260
4 changed files with 114 additions and 18 deletions
@@ -65,6 +65,14 @@ export class RepoController {
return this.repoService.getCreateMeta();
}
// ':repoId' 보다 먼저 선언 — 생성 폼의 이름(slug) 실시간 가용성 체크
@Get('name-available')
@ApiOperation({ summary: '저장소 이름(slug) 사용 가능 여부' })
@ApiResponse({ status: 200, description: '확인 성공' })
checkName(@Query('slug') slug: string): Promise<{ available: boolean }> {
return this.repoService.isNameAvailable(slug ?? '');
}
@Get(':repoId')
@ApiOperation({ summary: '저장소 단건 조회' })
@ApiResponse({ status: 200, description: '조회 성공' })
+11
View File
@@ -116,6 +116,17 @@ export class RepoService {
});
}
// 저장소 이름(slug) 사용 가능 여부 — 생성 폼의 실시간 체크용(Relay 중복만 검사).
// 빈 값/형식은 프론트가 거르고, Gitea 이름 충돌은 생성 시 최종 확인한다.
async isNameAvailable(slug: string): Promise<{ available: boolean }> {
const trimmed = (slug ?? '').trim();
if (!trimmed) return { available: false };
const existing = await this.repoRepo.findOne({
where: { slugName: trimmed },
});
return { available: !existing };
}
// 저장소 단건 — 조직 모델상 인증 사용자면 열람 가능(쓰기 권한은 별도 가드).
// 응답에 현재 사용자의 권한(canManage/isOwner)을 함께 내려, 프론트가 설정 탭 노출·진입을 결정한다.
async findOne(slugName: string, userId: string): Promise<RepoResponse> {