feat: 저장소 소유자(조직)를 백엔드 설정값으로 표시

생성 폼/목록의 하드코딩된 'marketing-team' 을 실제 설정값으로 교체한다.
- 백엔드: GET /repos/owner ({ owner }) — GITEA_ORG(미연동 시 기본 상수) 반환
- 프론트: useRepo.owner() + repo.store owner/fetchOwner(캐시)
- RepoCreatePage 소유자 고정 표시, RepoListPage 배너를 실제 조직명으로

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 23:53:39 +09:00
parent efe7b29581
commit 6652e79768
6 changed files with 58 additions and 7 deletions
@@ -47,6 +47,14 @@ export class RepoController {
return this.repoService.create(dto, user.id);
}
// ':repoId' 보다 먼저 선언해야 'owner' 가 파라미터로 매칭되지 않음
@Get('owner')
@ApiOperation({ summary: '현재 조직(소유자) 조회 — 생성 폼 표시용' })
@ApiResponse({ status: 200, description: '조회 성공' })
getOwner(): { owner: string } {
return { owner: this.repoService.getOwner() };
}
@Get(':repoId')
@ApiOperation({ summary: '저장소 단건 조회' })
@ApiResponse({ status: 200, description: '조회 성공' })
+6
View File
@@ -57,6 +57,12 @@ export class RepoService {
private readonly gitea: GiteaService,
) {}
// 현재 저장소 소유자(조직) — Gitea 연동 시 GITEA_ORG, 아니면 기본 상수.
// 프론트 생성 폼의 "소유자 고정" 표시에 사용(저장소 생성 전 미리 노출).
getOwner(): string {
return this.gitea.enabled ? this.gitea.org : DEFAULT_OWNER;
}
// 내가 멤버인 저장소 목록
async findAllForUser(userId: string): Promise<RepoResponse[]> {
const memberships = await this.memberRepo.find({
+7 -1
View File
@@ -9,6 +9,12 @@ export function useRepo() {
return (await api.get('/repos')) as unknown as ApiRepo[]
}
// 현재 조직(소유자) — 생성 폼의 "소유자 고정" 표시용
async function owner(): Promise<string> {
const res = (await api.get('/repos/owner')) as unknown as { owner: string }
return res.owner
}
async function get(id: string): Promise<ApiRepo> {
return (await api.get(`/repos/${id}`)) as unknown as ApiRepo
}
@@ -25,5 +31,5 @@ export function useRepo() {
await api.delete(`/repos/${id}`)
}
return { list, get, create, update, remove }
return { list, owner, get, create, update, remove }
}
+12 -3
View File
@@ -1,14 +1,23 @@
<script setup lang="ts">
// 새 저장소 만들기 — 저장소 생성 폼
import { ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useRouter, RouterLink } from 'vue-router'
import AppShell from '@/layouts/AppShell.vue'
import { useRepoStore } from '@/stores/repo.store'
import { initialOf } from '@/shared/utils/format'
import type { Visibility } from '@/mock/relay.mock'
const router = useRouter()
const repoStore = useRepoStore()
// 현재 조직(소유자) — 백엔드 설정값(GITEA_ORG)을 받아 고정 표시
const { owner } = storeToRefs(repoStore)
const ownerInitial = computed(() => initialOf(owner.value || '?').toUpperCase())
onMounted(() => {
void repoStore.fetchOwner()
})
// 폼 상태
const repoName = ref('')
const displayTitle = ref('')
@@ -107,8 +116,8 @@ async function createRepo() {
class="owner-fixed"
title="소유자는 변경할 수 없습니다"
>
<span class="owner-avatar">M</span>
marketing-team
<span class="owner-avatar">{{ ownerInitial }}</span>
{{ owner || '조직 불러오는 중…' }}
<span class="lock">
<svg
width="13"
+3 -2
View File
@@ -13,10 +13,11 @@ const keyword = ref('')
const filter = ref<Filter>('all')
const repoStore = useRepoStore()
const { repos, loading } = storeToRefs(repoStore)
const { repos, loading, owner } = storeToRefs(repoStore)
onMounted(() => {
void repoStore.fetchList()
void repoStore.fetchOwner()
})
// 검색어 + 공개여부 필터 적용
@@ -59,7 +60,7 @@ const filteredRepos = computed(() =>
</span>
</div>
<p class="lede">
<b>marketing-team</b> 조직의 저장소입니다. 저장소를 열어 업무를 작성하고 담당자에게
<b>{{ owner || '내' }}</b> 조직의 저장소입니다. 저장소를 열어 업무를 작성하고 담당자에게
전달하세요.
</p>
+22 -1
View File
@@ -47,9 +47,19 @@ export const useRepoStore = defineStore('repo', () => {
const repos = ref<RepoView[]>([])
const current = ref<RepoView | null>(null)
const loading = ref(false)
// 현재 조직(소유자) — 백엔드 설정값(GITEA_ORG). 최초 1회 조회 후 캐시
const owner = ref('')
const repoApi = useRepo()
// 조직(소유자) 조회 — 이미 있으면 재사용
async function fetchOwner(): Promise<string> {
if (!owner.value) {
owner.value = await repoApi.owner()
}
return owner.value
}
// 목록 조회
async function fetchList(): Promise<void> {
loading.value = true
@@ -91,5 +101,16 @@ export const useRepoStore = defineStore('repo', () => {
if (current.value?.id === id) current.value = null
}
return { repos, current, loading, fetchList, fetchOne, create, update, remove }
return {
repos,
current,
loading,
owner,
fetchOwner,
fetchList,
fetchOne,
create,
update,
remove,
}
})