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>
This commit is contained in:
2026-06-21 00:51:21 +09:00
parent 135cc0759f
commit 4d943e8d0d
6 changed files with 7 additions and 107 deletions
@@ -1,6 +1,5 @@
import {
IsBoolean,
IsIn,
IsNotEmpty,
IsOptional,
IsString,
@@ -8,7 +7,6 @@ import {
MaxLength,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import type { RepoVisibility } from '../entities/repo.entity';
// 저장소 생성 DTO
export class CreateRepoDto {
@@ -34,14 +32,6 @@ export class CreateRepoDto {
@MaxLength(40)
name!: string;
@ApiProperty({
description: '공개 범위',
enum: ['private', 'public'],
example: 'private',
})
@IsIn(['private', 'public'], { message: '공개 범위를 선택해 주세요.' })
visibility!: RepoVisibility;
@ApiProperty({
description: '프로젝트 설명(필수)',
example: '3분기 신제품 런칭을 위한 마케팅 캠페인 저장소',
@@ -1,8 +1,7 @@
import { IsIn, IsOptional, IsString, MaxLength } from 'class-validator';
import { IsOptional, IsString, MaxLength } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import type { RepoVisibility } from '../entities/repo.entity';
// 저장소 수정 DTO — 표시 제목/설명/공개범위/브랜치 (slug·owner 는 변경 불가)
// 저장소 수정 DTO — 표시 제목/설명/브랜치 (slug·owner 는 변경 불가, 공개범위는 Gitea 기준)
export class UpdateRepoDto {
@ApiProperty({ description: '표시 제목(한글)', required: false })
@IsOptional()
@@ -16,15 +15,6 @@ export class UpdateRepoDto {
@MaxLength(300)
description?: string;
@ApiProperty({
description: '공개 범위',
enum: ['private', 'public'],
required: false,
})
@IsOptional()
@IsIn(['private', 'public'], { message: '공개 범위를 선택해 주세요.' })
visibility?: RepoVisibility;
@ApiProperty({ description: '메인 브랜치', required: false })
@IsOptional()
@IsString()
+4 -11
View File
@@ -37,7 +37,6 @@ export interface RepoResponse {
owner: string;
slug: string; // owner/slugName
desc: string | null;
visibility: RepoVisibility;
branch: string;
cloneUrl: string | null;
htmlUrl: string | null;
@@ -139,7 +138,8 @@ export class RepoService {
}
const description = dto.description;
const visibility = dto.visibility;
// 공개 범위는 사용자 입력에서 제거됨 — 새 저장소는 공개(public)로 생성
const visibility: RepoVisibility = 'public';
const requestedBranch = dto.branch?.trim() || 'main';
const owner = this.gitea.enabled ? this.gitea.org : DEFAULT_OWNER;
@@ -206,37 +206,31 @@ export class RepoService {
const prevName = repo.name;
const prevDescription = repo.description ?? '';
const prevVisibility = repo.visibility;
const prevBranch = repo.branch;
if (dto.name !== undefined) repo.name = dto.name;
if (dto.description !== undefined) repo.description = dto.description;
if (dto.visibility !== undefined) repo.visibility = dto.visibility;
if (dto.branch !== undefined && dto.branch.trim())
repo.branch = dto.branch.trim();
const nameChanged = repo.name !== prevName;
const descriptionChanged = (repo.description ?? '') !== prevDescription;
const visibilityChanged = repo.visibility !== prevVisibility;
const branchChanged = repo.branch !== prevBranch;
await this.repoRepo.save(repo);
// Gitea 메타데이터 동기화 (실제로 바뀐 경우에만)
// Gitea 메타데이터 동기화 (실제로 바뀐 경우에만). 공개 범위는 Gitea 기준이라 푸시하지 않음.
const giteaDescChanged = nameChanged || descriptionChanged;
if (
this.gitea.enabled &&
repo.giteaRepoId !== null &&
(giteaDescChanged || visibilityChanged || branchChanged)
(giteaDescChanged || branchChanged)
) {
await this.gitea
.updateRepo(repo.slugName, {
description: giteaDescChanged
? this.buildGiteaDescription(repo.name, repo.description)
: undefined,
private: visibilityChanged
? repo.visibility === 'private'
: undefined,
defaultBranch: branchChanged ? repo.branch : undefined,
})
.catch((e: unknown) => {
@@ -376,7 +370,6 @@ export class RepoService {
owner: repo.owner,
slug: `${repo.owner}/${repo.slugName}`,
desc: repo.description,
visibility: repo.visibility,
branch: repo.branch,
cloneUrl: repo.cloneUrl,
htmlUrl: repo.htmlUrl,