From 44af0ec9756d2b7763284997c8ec96d69575982a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 4 May 2025 21:03:31 -0600 Subject: [PATCH 1/2] Refactor Gitea repository fetching to handle pagination - Updated the `testGiteaConnection` and `getGiteaRepositories` functions to implement pagination when fetching repositories from the Gitea API, ensuring all repositories are retrieved. - Enhanced error handling for API responses and improved the structure of the returned repository data. - Removed deprecated code related to direct repository fetching, streamlining the overall logic. --- packages/server/src/utils/providers/gitea.ts | 121 +++++++++++++------ 1 file changed, 82 insertions(+), 39 deletions(-) diff --git a/packages/server/src/utils/providers/gitea.ts b/packages/server/src/utils/providers/gitea.ts index fc8e05a4..88144eb5 100644 --- a/packages/server/src/utils/providers/gitea.ts +++ b/packages/server/src/utils/providers/gitea.ts @@ -362,27 +362,48 @@ export const testGiteaConnection = async (input: { giteaId: string }) => { } const baseUrl = provider.giteaUrl.replace(/\/+$/, ""); - const url = `${baseUrl}/api/v1/user/repos`; + const limit = 30; + let allRepos = 0; + let nextUrl = `${baseUrl}/api/v1/repos/search?limit=${limit}`; - const response = await fetch(url, { - headers: { - Accept: "application/json", - Authorization: `token ${provider.accessToken}`, - }, - }); + while (nextUrl) { + const response = await fetch(nextUrl, { + headers: { + Accept: "application/json", + Authorization: `token ${provider.accessToken}`, + }, + }); - if (!response.ok) { - throw new Error( - `Failed to connect to Gitea API: ${response.status} ${response.statusText}`, - ); + if (!response.ok) { + throw new Error( + `Failed to connect to Gitea API: ${response.status} ${response.statusText}`, + ); + } + + const repos = await response.json(); + allRepos += repos.data.length; + + const linkHeader = response.headers.get("link"); + nextUrl = ""; + + if (linkHeader) { + const nextLink = linkHeader + .split(",") + .find((link) => link.includes('rel="next"')); + if (nextLink) { + const matches = nextLink.match(/<([^>]+)>/); + if (matches?.[1]) { + nextUrl = matches[1]; + } + } + } } - const repos = await response.json(); await updateGitea(giteaId, { lastAuthenticatedAt: Math.floor(Date.now() / 1000), }); - return repos.length; + return allRepos; } catch (error) { throw error; } @@ -394,38 +415,57 @@ export const getGiteaRepositories = async (giteaId?: string) => { } await refreshGiteaToken(giteaId); - const giteaProvider = await findGiteaById(giteaId); const baseUrl = giteaProvider.giteaUrl.replace(/\/+$/, ""); - const url = `${baseUrl}/api/v1/user/repos`; + const limit = 30; + let allRepositories: any[] = []; + let nextUrl = `${baseUrl}/api/v1/repos/search?limit=${limit}`; - const response = await fetch(url, { - headers: { - Accept: "application/json", - Authorization: `token ${giteaProvider.accessToken}`, - }, - }); - - if (!response.ok) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: `Failed to fetch repositories: ${response.statusText}`, + while (nextUrl) { + const response = await fetch(nextUrl, { + headers: { + Accept: "application/json", + Authorization: `token ${giteaProvider.accessToken}`, + }, }); + + if (!response.ok) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to fetch repositories: ${response.statusText}`, + }); + } + + const result = await response.json(); + allRepositories = [...allRepositories, ...result.data]; + + const linkHeader = response.headers.get("link"); + nextUrl = ""; + + if (linkHeader) { + const nextLink = linkHeader + .split(",") + .find((link) => link.includes('rel="next"')); + if (nextLink) { + const matches = nextLink.match(/<([^>]+)>/); + if (matches?.[1]) { + nextUrl = matches[1]; + } + } + } } - const repositories = await response.json(); - - const mappedRepositories = repositories.map((repo: any) => ({ - id: repo.id, - name: repo.name, - url: repo.full_name, - owner: { - username: repo.owner.login, - }, - })); - - return mappedRepositories; + return ( + allRepositories?.map((repo: any) => ({ + id: repo.id, + name: repo.name, + url: repo.full_name, + owner: { + username: repo.owner.login, + }, + })) || [] + ); }; export const getGiteaBranches = async (input: { @@ -457,7 +497,10 @@ export const getGiteaBranches = async (input: { const branches = await response.json(); - return branches.map((branch: any) => ({ + if (!branches) { + return []; + } + return branches?.map((branch: any) => ({ id: branch.name, name: branch.name, commit: { From 9fe2460b8869148f8e4359281a3f3611eb1b1106 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 4 May 2025 21:05:16 -0600 Subject: [PATCH 2/2] Add message for empty branches in Gitea provider - Introduced a conditional rendering for displaying a message when no branches are found in the Gitea provider component, enhancing user feedback during branch selection. - This update improves the overall user experience by clearly indicating the absence of branches. --- .../application/general/generic/save-gitea-provider.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx index 98d8cfd7..531ace12 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx @@ -381,6 +381,9 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => { No branch found. + {branches && branches.length === 0 && ( + No branches found. + )} {branches?.map((branch: GiteaBranch) => (