feat: M4-06 — project center with Gitea API integration
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 41s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 41s
- GiteaService: query repos, milestones, issues, releases, runners - ProjectCenterController: 5 AAPI endpoints - Replace iframe-only GiteaEmbed with full ProjectCenter page (repos table, issues, milestones cards, releases, runners, Gitea embed tab) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b188988e82
commit
8abf94872a
@ -54,6 +54,7 @@ import { CacheModule } from './common/cache/cache.module';
|
||||
import { AdminCacheModule } from './modules/admin-cache/admin-cache.module';
|
||||
import { BackupModule } from './modules/backup/backup.module';
|
||||
import { ReportingModule } from './modules/reporting/reporting.module';
|
||||
import { ProjectCenterModule } from './modules/project-center/project-center.module';
|
||||
|
||||
import { JwtAuthGuard } from './common/guards/jwt-auth.guard';
|
||||
import { RolesGuard } from './common/guards/roles.guard';
|
||||
@ -153,6 +154,7 @@ import appleConfig from './config/apple.config';
|
||||
AdminCacheModule,
|
||||
BackupModule,
|
||||
ReportingModule,
|
||||
ProjectCenterModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: APP_GUARD, useClass: RateLimitGuard },
|
||||
|
||||
58
src/modules/project-center/gitea.service.ts
Normal file
58
src/modules/project-center/gitea.service.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
const GITEA_BASE = 'https://git.admin.longde.cloud/api/v1';
|
||||
const GITEA_TOKEN = process.env.GITEA_TOKEN || '0b190ab69b751c6f4ce3f92cbd3ed762e5cd5bfa';
|
||||
|
||||
@Injectable()
|
||||
export class GiteaService {
|
||||
private readonly logger = new Logger(GiteaService.name);
|
||||
|
||||
private async giteaGet<T>(path: string): Promise<T | null> {
|
||||
try {
|
||||
const res = await fetch(`${GITEA_BASE}${path}`, {
|
||||
headers: { Authorization: `token ${GITEA_TOKEN}`, Accept: 'application/json' },
|
||||
});
|
||||
if (!res.ok) { this.logger.warn(`Gitea GET ${path}: ${res.status}`); return null; }
|
||||
return await res.json() as T;
|
||||
} catch (err: any) {
|
||||
this.logger.warn(`Gitea GET ${path} failed: ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async getRepos() {
|
||||
const repos = await this.giteaGet<any[]>('/repos/search?limit=50');
|
||||
if (!repos) return [];
|
||||
return Promise.all(repos.map(async (r: any) => {
|
||||
const [issues, pulls, milestones] = await Promise.all([
|
||||
this.giteaGet<any[]>(`/repos/${r.full_name}/issues?state=all&limit=1`).then(d => d?.length ?? 0).catch(() => 0),
|
||||
this.giteaGet<any[]>(`/repos/${r.full_name}/pulls?state=all&limit=1`).then(d => d?.length ?? 0).catch(() => 0),
|
||||
this.giteaGet<any[]>(`/repos/${r.full_name}/milestones?state=open`).catch(() => []),
|
||||
]);
|
||||
return {
|
||||
id: r.id, name: r.name, fullName: r.full_name, description: r.description,
|
||||
owner: r.owner?.login, stars: r.stars_count, forks: r.forks_count,
|
||||
openIssues: r.open_issues_count, openPulls: pulls, milestones: milestones?.length ?? 0,
|
||||
defaultBranch: r.default_branch, updatedAt: r.updated_at,
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
async getMilestones(owner: string, repo: string) {
|
||||
return this.giteaGet<any[]>(`/repos/${owner}/${repo}/milestones?state=all`) ?? [];
|
||||
}
|
||||
|
||||
async getIssues(owner: string, repo: string, milestone?: string, state = 'open') {
|
||||
let path = `/repos/${owner}/${repo}/issues?state=${state}&limit=50`;
|
||||
if (milestone) path += `&milestone=${milestone}`;
|
||||
return this.giteaGet<any[]>(path) ?? [];
|
||||
}
|
||||
|
||||
async getRunners() {
|
||||
return this.giteaGet<any[]>('/admin/runners') ?? [];
|
||||
}
|
||||
|
||||
async getReleases(owner: string, repo: string) {
|
||||
return this.giteaGet<any[]>(`/repos/${owner}/${repo}/releases?limit=20`) ?? [];
|
||||
}
|
||||
}
|
||||
48
src/modules/project-center/project-center.controller.ts
Normal file
48
src/modules/project-center/project-center.controller.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery } from '@nestjs/swagger';
|
||||
import { GiteaService } from './gitea.service';
|
||||
import { AdminAuthGuard } from '../../common/guards/admin-auth.guard';
|
||||
import { AdminRolesGuard } from '../../common/guards/admin-roles.guard';
|
||||
|
||||
@ApiTags('admin-project-center')
|
||||
@ApiBearerAuth()
|
||||
@Controller('admin-api/projects')
|
||||
@UseGuards(AdminAuthGuard, AdminRolesGuard)
|
||||
export class ProjectCenterController {
|
||||
constructor(private readonly gitea: GiteaService) {}
|
||||
|
||||
@Get('repos')
|
||||
@ApiOperation({ summary: 'Gitea 仓库列表' })
|
||||
async getRepos() {
|
||||
return this.gitea.getRepos();
|
||||
}
|
||||
|
||||
@Get('repos/:owner/:repo/milestones')
|
||||
@ApiOperation({ summary: '仓库里程碑' })
|
||||
async getMilestones(@Param('owner') owner: string, @Param('repo') repo: string) {
|
||||
return this.gitea.getMilestones(owner, repo);
|
||||
}
|
||||
|
||||
@Get('repos/:owner/:repo/issues')
|
||||
@ApiOperation({ summary: '仓库 Issue 列表' })
|
||||
@ApiQuery({ name: 'milestone', required: false })
|
||||
@ApiQuery({ name: 'state', required: false })
|
||||
async getIssues(
|
||||
@Param('owner') owner: string, @Param('repo') repo: string,
|
||||
@Query('milestone') milestone?: string, @Query('state') state?: string,
|
||||
) {
|
||||
return this.gitea.getIssues(owner, repo, milestone, state);
|
||||
}
|
||||
|
||||
@Get('repos/:owner/:repo/releases')
|
||||
@ApiOperation({ summary: '仓库 Release 列表' })
|
||||
async getReleases(@Param('owner') owner: string, @Param('repo') repo: string) {
|
||||
return this.gitea.getReleases(owner, repo);
|
||||
}
|
||||
|
||||
@Get('runners')
|
||||
@ApiOperation({ summary: 'Gitea Runner 状态' })
|
||||
async getRunners() {
|
||||
return this.gitea.getRunners();
|
||||
}
|
||||
}
|
||||
10
src/modules/project-center/project-center.module.ts
Normal file
10
src/modules/project-center/project-center.module.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ProjectCenterController } from './project-center.controller';
|
||||
import { GiteaService } from './gitea.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ProjectCenterController],
|
||||
providers: [GiteaService],
|
||||
exports: [GiteaService],
|
||||
})
|
||||
export class ProjectCenterModule {}
|
||||
Loading…
x
Reference in New Issue
Block a user