From cb8519d3f99e9401f8e34226fa3f4157d99bc69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E5=85=AE?= Date: Fri, 12 Jun 2026 04:38:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B1=95=E7=A4=BA=E9=A1=B9=E7=9B=AE=20?= =?UTF-8?q?Gitea=20=E4=BB=93=E5=BA=93=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - devopsApi: 接入项目 Gitea 诊断接口 - platform store: 回填仓库可达性和默认分支 commit - ProjectsView: 展示 Gitea 状态、分支 tag 数量和 commit 摘要 --- src/services/devopsApi.ts | 68 +++++++++++++++++++++++++++++++ src/stores/platform.ts | 82 ++++++++++++++++++++++++++++++++++++++ src/types/devops.ts | 31 ++++++++++++++ src/views/ProjectsView.vue | 78 ++++++++++++++++++++++++++++++++---- 4 files changed, 252 insertions(+), 7 deletions(-) diff --git a/src/services/devopsApi.ts b/src/services/devopsApi.ts index 9dd9c26..3e41633 100644 --- a/src/services/devopsApi.ts +++ b/src/services/devopsApi.ts @@ -29,6 +29,8 @@ import type { PipelineStep, PlatformMessageSummary, ProjectConfig, + ProjectGiteaDiagnosticsSummary, + ProjectGiteaRepositoryDiagnostic, ProjectJenkinsDiagnosticsSummary, ProjectJenkinsJobDiagnostic, ProjectRefs, @@ -359,6 +361,14 @@ export const devopsApi = { return sanitizeProjectJenkinsDiagnostics(data) }, + async getProjectGiteaDiagnostics(): Promise { + const { data } = await http.get( + '/projects/gitea-diagnostics', + ) + lastDataSource = 'backend' + return sanitizeProjectGiteaDiagnostics(data) + }, + async getProjectRefs(projectKey: string): Promise { const { data } = await http.get(`/projects/${projectKey}/refs`) lastDataSource = 'backend' @@ -786,6 +796,8 @@ function toProjectConfigs(backendProjects: BackendProject[]): ProjectConfig[] { serviceType: serviceTypeLabel(project.key), defaultBranch: project.defaultBranch, tagPolicy: tagPolicyLabel(project.environments), + giteaStatus: 'warning', + giteaMessage: '尚未执行 Gitea 诊断', environments: project.environments.map((environment) => { return { name: environment.name, @@ -812,6 +824,62 @@ function toProjectConfigs(backendProjects: BackendProject[]): ProjectConfig[] { }) } +function sanitizeProjectGiteaDiagnostics( + value: ProjectGiteaDiagnosticsSummary, +): ProjectGiteaDiagnosticsSummary { + return { + checkedAt: safeText(value.checkedAt, new Date().toISOString()), + repositories: Array.isArray(value.repositories) + ? value.repositories.map(sanitizeProjectGiteaRepositoryDiagnostic) + : [], + } +} + +function sanitizeProjectGiteaRepositoryDiagnostic( + value: ProjectGiteaRepositoryDiagnostic, +): ProjectGiteaRepositoryDiagnostic { + return { + projectKey: safeText(value.projectKey, 'unknown-project'), + repositoryUrl: safeText(value.repositoryUrl, ''), + owner: optionalSafeText(value.owner), + repo: optionalSafeText(value.repo), + status: toProjectGiteaDiagnosticStatus(value.status), + message: optionalSafeText(value.message), + defaultBranch: optionalSafeText(value.defaultBranch), + webUrl: optionalSafeText(value.webUrl), + cloneUrl: optionalSafeText(value.cloneUrl), + archived: typeof value.archived === 'boolean' ? value.archived : undefined, + private: typeof value.private === 'boolean' ? value.private : undefined, + empty: typeof value.empty === 'boolean' ? value.empty : undefined, + branchCount: Number.isFinite(value.branchCount) ? value.branchCount : undefined, + tagCount: Number.isFinite(value.tagCount) ? value.tagCount : undefined, + latestCommit: value.latestCommit + ? { + sha: safeText(value.latestCommit.sha, ''), + message: safeText(value.latestCommit.message, ''), + authorName: optionalSafeText(value.latestCommit.authorName), + authoredAt: optionalSafeText(value.latestCommit.authoredAt), + } + : undefined, + updatedAt: optionalSafeText(value.updatedAt), + } +} + +function toProjectGiteaDiagnosticStatus( + value: ProjectGiteaRepositoryDiagnostic['status'], +): ProjectGiteaRepositoryDiagnostic['status'] { + if ( + value === 'ok' || + value === 'missing' || + value === 'not_configured' || + value === 'unavailable' + ) { + return value + } + + return 'unavailable' +} + function sanitizeProjectJenkinsDiagnostics( value: ProjectJenkinsDiagnosticsSummary, ): ProjectJenkinsDiagnosticsSummary { diff --git a/src/stores/platform.ts b/src/stores/platform.ts index 6d136e3..126409d 100644 --- a/src/stores/platform.ts +++ b/src/stores/platform.ts @@ -12,6 +12,7 @@ import type { IntegrationStatus, IntegrationConfigStatus, ProjectConfig, + ProjectGiteaDiagnosticsSummary, ProjectJenkinsDiagnosticsSummary, ProjectRefs, JenkinsSyncSummary, @@ -34,6 +35,7 @@ export const usePlatformStore = defineStore('platform', () => { const auditLogs = ref([]) const notificationOutbox = ref([]) const projectJenkinsDiagnostics = ref(null) + const projectGiteaDiagnostics = ref(null) const integrationConfig = ref({ checkedAt: '', integrations: [], @@ -75,6 +77,7 @@ export const usePlatformStore = defineStore('platform', () => { releaseProcessXml.value = processXml auditLogs.value = recentAuditLogs notificationOutbox.value = recentNotificationOutbox + applyProjectGiteaDiagnostics() applyProjectJenkinsDiagnostics() dataSource.value = devopsApi.getLastDataSource() } finally { @@ -135,6 +138,14 @@ export const usePlatformStore = defineStore('platform', () => { return diagnostics } + async function loadProjectGiteaDiagnostics(): Promise { + const diagnostics = await devopsApi.getProjectGiteaDiagnostics() + projectGiteaDiagnostics.value = diagnostics + applyProjectGiteaDiagnostics() + dataSource.value = devopsApi.getLastDataSource() + return diagnostics + } + function applyProjectJenkinsDiagnostics() { const diagnostics = projectJenkinsDiagnostics.value @@ -177,6 +188,43 @@ export const usePlatformStore = defineStore('platform', () => { }) } + function applyProjectGiteaDiagnostics() { + const diagnostics = projectGiteaDiagnostics.value + + if (!diagnostics) { + return + } + + const repositoryByProject = new Map( + diagnostics.repositories.map((repository) => [repository.projectKey, repository]), + ) + + projects.value = projects.value.map((project) => { + const diagnostic = repositoryByProject.get(project.key) + + if (!diagnostic) { + return project + } + + const giteaStatus = giteaDiagnosticState(diagnostic.status) + + return { + ...project, + giteaStatus, + giteaMessage: diagnostic.message || giteaDiagnosticMessage(diagnostic.status), + giteaWebUrl: diagnostic.webUrl, + giteaDefaultBranch: diagnostic.defaultBranch, + giteaBranchCount: diagnostic.branchCount, + giteaTagCount: diagnostic.tagCount, + giteaLatestCommit: diagnostic.latestCommit, + integrations: { + ...project.integrations, + gitea: giteaStatus, + }, + } + }) + } + async function loadAuditLogs() { auditLogs.value = await devopsApi.getAuditLogs() dataSource.value = devopsApi.getLastDataSource() @@ -257,6 +305,7 @@ export const usePlatformStore = defineStore('platform', () => { auditLogs, notificationOutbox, projectJenkinsDiagnostics, + projectGiteaDiagnostics, integrationConfig, releaseProcessXml, projectRefs, @@ -270,6 +319,7 @@ export const usePlatformStore = defineStore('platform', () => { syncJenkinsRuns, loadProjectRefs, loadProjectJenkinsDiagnostics, + loadProjectGiteaDiagnostics, loadAuditLogs, loadNotificationOutbox, retryNotificationOutboxMessage, @@ -308,6 +358,38 @@ function jenkinsDiagnosticMessage( return 'Jenkins job 不可用' } +function giteaDiagnosticState( + status: ProjectGiteaDiagnosticsSummary['repositories'][number]['status'], +) { + if (status === 'ok') { + return 'healthy' as const + } + + if (status === 'not_configured') { + return 'warning' as const + } + + return 'error' as const +} + +function giteaDiagnosticMessage( + status: ProjectGiteaDiagnosticsSummary['repositories'][number]['status'], +): string { + if (status === 'ok') { + return 'Gitea 仓库可达' + } + + if (status === 'missing') { + return 'Gitea 仓库不存在或无权访问' + } + + if (status === 'not_configured') { + return 'Gitea 集成未配置' + } + + return 'Gitea 仓库不可用' +} + function projectEnvironmentKey( projectKey: string, environment: ReleaseRun['environment'], diff --git a/src/types/devops.ts b/src/types/devops.ts index 77b076f..7ccd12f 100644 --- a/src/types/devops.ts +++ b/src/types/devops.ts @@ -208,6 +208,13 @@ export interface ProjectConfig { serviceType: string defaultBranch: string tagPolicy: string + giteaStatus: HealthState + giteaMessage?: string + giteaWebUrl?: string + giteaDefaultBranch?: string + giteaBranchCount?: number + giteaTagCount?: number + giteaLatestCommit?: ProjectRefCommit environments: EnvironmentStatus[] integrations: { jenkins: HealthState @@ -251,6 +258,30 @@ export interface ProjectRefCommit { authoredAt?: string } +export interface ProjectGiteaRepositoryDiagnostic { + projectKey: string + repositoryUrl: string + owner?: string + repo?: string + status: 'ok' | 'missing' | 'not_configured' | 'unavailable' + message?: string + defaultBranch?: string + webUrl?: string + cloneUrl?: string + archived?: boolean + private?: boolean + empty?: boolean + branchCount?: number + tagCount?: number + latestCommit?: ProjectRefCommit + updatedAt?: string +} + +export interface ProjectGiteaDiagnosticsSummary { + checkedAt: string + repositories: ProjectGiteaRepositoryDiagnostic[] +} + export interface ProjectRefPullRequest { number: number title: string diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 682dc4d..2e7b252 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -1,24 +1,27 @@