From dceb20b26aae463d1d434f157d986c93a47fbe01 Mon Sep 17 00:00:00 2001 From: chyoo Date: Thu, 9 Jul 2026 11:01:39 +0900 Subject: [PATCH] improve common app pipeline history layout --- src/woodpecker/woodpecker.service.ts | 30 ++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/woodpecker/woodpecker.service.ts b/src/woodpecker/woodpecker.service.ts index 2616101..3ea3a1a 100644 --- a/src/woodpecker/woodpecker.service.ts +++ b/src/woodpecker/woodpecker.service.ts @@ -46,7 +46,7 @@ export class WoodpeckerService { const page = Math.max(1, input.page || 1); const limit = Math.min(50, Math.max(1, input.limit || 5)); - const body = await this.request( + const { body, headers } = await this.requestWithHeaders( `/api/repos/${encodeURIComponent(repoId)}/pipelines?page=${page}&limit=${limit + 1}`, ); const rows = Array.isArray(body) @@ -54,6 +54,7 @@ export class WoodpeckerService { : isRecord(body) && Array.isArray(body.pipelines) ? body.pipelines : []; + const total = getTotalCount(body, headers); const pipelines = rows .filter(isRecord) .slice(0, limit) @@ -62,7 +63,9 @@ export class WoodpeckerService { return { page, pageSize: limit, - hasNext: rows.length > limit, + total, + totalPages: total === null ? null : Math.max(1, Math.ceil(total / limit)), + hasNext: total === null ? rows.length > limit : page < Math.max(1, Math.ceil(total / limit)), pipelines, }; } @@ -88,6 +91,11 @@ export class WoodpeckerService { } private async request(path: string): Promise { + const { body } = await this.requestWithHeaders(path); + return body; + } + + private async requestWithHeaders(path: string): Promise<{ body: T; headers: Headers }> { const response = await fetch(`${this.baseUrl}${path}`, { headers: { Accept: 'application/json', @@ -104,7 +112,7 @@ export class WoodpeckerService { }); } - return (await response.json()) as T; + return { body: (await response.json()) as T, headers: response.headers }; } private assertToken() { @@ -193,6 +201,20 @@ function getPipelineTimestamp(value: unknown) { return 0; } +function getTotalCount(body: unknown, headers: Headers) { + const bodyTotal = isRecord(body) + ? getNumber(body.total) ?? getNumber(body.count) ?? getNumber(body.total_count) ?? getNumber(body.totalCount) + : null; + if (bodyTotal !== null) return bodyTotal; + + const headerTotal = headers.get('x-total-count') + ?? headers.get('x-total') + ?? headers.get('x-pagination-total') + ?? headers.get('x-page-total') + ?? headers.get('total'); + return getNumber(headerTotal); +} + function joinRepoName(repo: WoodpeckerRepo) { const owner = getString(repo.owner); const name = getString(repo.name); @@ -205,7 +227,7 @@ function getString(value: unknown) { function getNumber(value: unknown) { const number = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN; - return Number.isFinite(number) ? number : null; + return Number.isFinite(number) && number >= 0 ? number : null; } function isRecord(value: unknown): value is Record {