support paged launcher history queries
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-07 09:20:18 +09:00
parent b3dbcfd87f
commit 0951283c8b
6 changed files with 98 additions and 6 deletions
+12
View File
@@ -23,9 +23,21 @@ export class ListGiteaCommitsQuery {
@IsString() @IsString()
sha?: string; sha?: string;
@IsOptional()
@IsString()
page?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
limit?: string; limit?: string;
@IsOptional()
@IsString()
since?: string;
@IsOptional()
@IsString()
until?: string;
} }
export class ListGiteaTreeQuery { export class ListGiteaTreeQuery {
+9
View File
@@ -51,6 +51,15 @@ export class GiteaController {
return this.giteaService.listCommits(owner, repo, query); return this.giteaService.listCommits(owner, repo, query);
} }
@Get('repos/:owner/:repo/commits/:sha/diff')
getCommitDiff(
@Param('owner') owner: string,
@Param('repo') repo: string,
@Param('sha') sha: string,
) {
return this.giteaService.getCommitDiff(owner, repo, sha);
}
@Get('repos/:owner/:repo/releases') @Get('repos/:owner/:repo/releases')
listReleases(@Param('owner') owner: string, @Param('repo') repo: string) { listReleases(@Param('owner') owner: string, @Param('repo') repo: string) {
return this.giteaService.listReleases(owner, repo); return this.giteaService.listReleases(owner, repo);
+38
View File
@@ -97,9 +97,16 @@ export class GiteaService {
async listCommits(owner: string, repo: string, query: ListGiteaCommitsQuery) { async listCommits(owner: string, repo: string, query: ListGiteaCommitsQuery) {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set('limit', query.limit ?? '20'); params.set('limit', query.limit ?? '20');
params.set('page', query.page ?? '1');
if (query.sha) { if (query.sha) {
params.set('sha', query.sha); params.set('sha', query.sha);
} }
if (query.since) {
params.set('since', query.since);
}
if (query.until) {
params.set('until', query.until);
}
const commits = await this.request<unknown[]>( const commits = await this.request<unknown[]>(
`/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits?${params.toString()}`, `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits?${params.toString()}`,
@@ -108,6 +115,17 @@ export class GiteaService {
return { commits }; return { commits };
} }
async getCommitDiff(owner: string, repo: string, sha: string) {
const encodedOwner = encodeURIComponent(owner);
const encodedRepo = encodeURIComponent(repo);
const encodedSha = encodeURIComponent(sha);
const diff = await this.requestText(
`/${encodedOwner}/${encodedRepo}/commit/${encodedSha}.diff`,
);
return { diff };
}
async listReleases(owner: string, repo: string) { async listReleases(owner: string, repo: string) {
const releases = await this.request<unknown[]>( const releases = await this.request<unknown[]>(
`/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases`, `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases`,
@@ -255,6 +273,26 @@ export class GiteaService {
return (await response.json()) as T; return (await response.json()) as T;
} }
private async requestText(path: string): Promise<string> {
const response = await fetch(`${this.baseUrl}${path}`, {
headers: {
Accept: 'text/plain',
...(this.token ? { Authorization: `token ${this.token}` } : {}),
},
});
if (!response.ok) {
const message = await response.text();
throw new BadGatewayException({
message: 'Gitea API 요청이 실패했습니다.',
status: response.status,
detail: message,
});
}
return response.text();
}
private assertToken() { private assertToken() {
if (!this.token) { if (!this.token) {
throw new UnauthorizedException( throw new UnauthorizedException(
+18
View File
@@ -110,6 +110,24 @@ export class CreateLauncherBuildJobDto {
downloadUrl?: string; downloadUrl?: string;
} }
export class ListLauncherBuildJobsQuery {
@IsOptional()
@IsString()
target?: string;
@IsOptional()
@IsString()
page?: string;
@IsOptional()
@IsString()
limit?: string;
@IsOptional()
@IsString()
since?: string;
}
export class CreateLauncherAppLinkDto { export class CreateLauncherAppLinkDto {
@IsUUID() @IsUUID()
+4 -4
View File
@@ -1,6 +1,6 @@
import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
import { CreateLauncherAppLinkDto, CreateLauncherBuildJobDto, CreateLauncherCommonSettingDto, CreateLauncherVersionDto, UpdateLauncherSourceDto, LinkLauncherVersionSourceDto, MoveLauncherAppLinkDto, UpdateLauncherAppLinkDto, UpdateLauncherBuildEnvDto, UpdateLauncherCommonSettingDto, UpdateLauncherUserAppPermissionsDto, DeployLauncherBackendDto } from './dto'; import { CreateLauncherAppLinkDto, CreateLauncherBuildJobDto, CreateLauncherCommonSettingDto, CreateLauncherVersionDto, UpdateLauncherSourceDto, LinkLauncherVersionSourceDto, MoveLauncherAppLinkDto, UpdateLauncherAppLinkDto, UpdateLauncherBuildEnvDto, UpdateLauncherCommonSettingDto, UpdateLauncherUserAppPermissionsDto, DeployLauncherBackendDto, ListLauncherBuildJobsQuery } from './dto';
import { LauncherService } from './launcher.service'; import { LauncherService } from './launcher.service';
@Controller('launcher') @Controller('launcher')
@@ -49,8 +49,8 @@ export class LauncherController {
} }
@Get('builds') @Get('builds')
listBuildJobs() { listBuildJobs(@Query() query: ListLauncherBuildJobsQuery) {
return this.launcherService.listBuildJobs(); return this.launcherService.listBuildJobs(query);
} }
@Post('builds') @Post('builds')
+17 -2
View File
@@ -16,6 +16,7 @@ import {
CreateLauncherCommonSettingDto, CreateLauncherCommonSettingDto,
CreateLauncherVersionDto, CreateLauncherVersionDto,
DeployLauncherBackendDto, DeployLauncherBackendDto,
ListLauncherBuildJobsQuery,
UpdateLauncherSourceDto, UpdateLauncherSourceDto,
LinkLauncherVersionSourceDto, LinkLauncherVersionSourceDto,
UpdateLauncherAppLinkDto, UpdateLauncherAppLinkDto,
@@ -426,10 +427,19 @@ export class LauncherService {
}; };
} }
listBuildJobs() { listBuildJobs(query: ListLauncherBuildJobsQuery = {}) {
const page = toPositiveInt(query.page, 1);
const limit = Math.min(toPositiveInt(query.limit, 30), 100);
const since = query.since ? new Date(query.since) : null;
return this.prisma.launcherBuildJob.findMany({ return this.prisma.launcherBuildJob.findMany({
where: {
...(query.target ? { target: query.target } : {}),
...(since && Number.isFinite(since.getTime()) ? { createdAt: { gte: since } } : {}),
},
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: 'desc' },
take: 30, skip: (page - 1) * limit,
take: limit,
}); });
} }
@@ -1693,3 +1703,8 @@ echo "설치가 완료되었습니다."
function safeMetadataValue(value: string) { function safeMetadataValue(value: string) {
return value.replace(/[^\x20-\x7E]/g, '').slice(0, 512); return value.replace(/[^\x20-\x7E]/g, '').slice(0, 512);
} }
function toPositiveInt(value: string | undefined, fallback: number) {
const parsed = Number(value);
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}