mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
fix(templates): add fetch timeout and handle network errors gracefully
Add 10s AbortSignal timeout to all template fetch calls so they fail cleanly instead of hanging indefinitely when templates.dokploy.com is unreachable. Add try/catch to getTags endpoint which was missing error handling, causing a 500 instead of returning an empty list. Closes #4282
This commit is contained in:
@@ -700,11 +700,14 @@ export const composeRouter = createTRPCRouter({
|
|||||||
getTags: protectedProcedure
|
getTags: protectedProcedure
|
||||||
.input(z.object({ baseUrl: z.string().optional() }))
|
.input(z.object({ baseUrl: z.string().optional() }))
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const githubTemplates = await fetchTemplatesList(input.baseUrl);
|
try {
|
||||||
|
const githubTemplates = await fetchTemplatesList(input.baseUrl);
|
||||||
const allTags = githubTemplates.flatMap((template) => template.tags);
|
const allTags = githubTemplates.flatMap((template) => template.tags);
|
||||||
const uniqueTags = _.uniq(allTags);
|
return _.uniq(allTags);
|
||||||
return uniqueTags;
|
} catch (error) {
|
||||||
|
console.warn("Failed to fetch template tags:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
disconnectGitProvider: protectedProcedure
|
disconnectGitProvider: protectedProcedure
|
||||||
.input(apiFindCompose)
|
.input(apiFindCompose)
|
||||||
|
|||||||
@@ -55,25 +55,22 @@ interface TemplateMetadata {
|
|||||||
export async function fetchTemplatesList(
|
export async function fetchTemplatesList(
|
||||||
baseUrl = "https://templates.dokploy.com",
|
baseUrl = "https://templates.dokploy.com",
|
||||||
): Promise<TemplateMetadata[]> {
|
): Promise<TemplateMetadata[]> {
|
||||||
try {
|
const response = await fetch(`${baseUrl}/meta.json`, {
|
||||||
const response = await fetch(`${baseUrl}/meta.json`);
|
signal: AbortSignal.timeout(10000),
|
||||||
if (!response.ok) {
|
});
|
||||||
throw new Error(`Failed to fetch templates: ${response.statusText}`);
|
if (!response.ok) {
|
||||||
}
|
throw new Error(`Failed to fetch templates: ${response.statusText}`);
|
||||||
const templates = (await response.json()) as TemplateMetadata[];
|
|
||||||
return templates.map((template) => ({
|
|
||||||
id: template.id,
|
|
||||||
name: template.name,
|
|
||||||
description: template.description,
|
|
||||||
version: template.version,
|
|
||||||
logo: template.logo,
|
|
||||||
links: template.links,
|
|
||||||
tags: template.tags,
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching templates list:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
const templates = (await response.json()) as TemplateMetadata[];
|
||||||
|
return templates.map((template) => ({
|
||||||
|
id: template.id,
|
||||||
|
name: template.name,
|
||||||
|
description: template.description,
|
||||||
|
version: template.version,
|
||||||
|
logo: template.logo,
|
||||||
|
links: template.links,
|
||||||
|
tags: template.tags,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,27 +80,26 @@ export async function fetchTemplateFiles(
|
|||||||
templateId: string,
|
templateId: string,
|
||||||
baseUrl = "https://templates.dokploy.com",
|
baseUrl = "https://templates.dokploy.com",
|
||||||
): Promise<{ config: CompleteTemplate; dockerCompose: string }> {
|
): Promise<{ config: CompleteTemplate; dockerCompose: string }> {
|
||||||
try {
|
const timeout = AbortSignal.timeout(10000);
|
||||||
// Fetch both files in parallel
|
const [templateYmlResponse, dockerComposeResponse] = await Promise.all([
|
||||||
const [templateYmlResponse, dockerComposeResponse] = await Promise.all([
|
fetch(`${baseUrl}/blueprints/${templateId}/template.toml`, {
|
||||||
fetch(`${baseUrl}/blueprints/${templateId}/template.toml`),
|
signal: timeout,
|
||||||
fetch(`${baseUrl}/blueprints/${templateId}/docker-compose.yml`),
|
}),
|
||||||
]);
|
fetch(`${baseUrl}/blueprints/${templateId}/docker-compose.yml`, {
|
||||||
|
signal: timeout,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!templateYmlResponse.ok || !dockerComposeResponse.ok) {
|
if (!templateYmlResponse.ok || !dockerComposeResponse.ok) {
|
||||||
throw new Error("Template files not found");
|
throw new Error("Template files not found");
|
||||||
}
|
|
||||||
|
|
||||||
const [templateYml, dockerCompose] = await Promise.all([
|
|
||||||
templateYmlResponse.text(),
|
|
||||||
dockerComposeResponse.text(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const config = parse(templateYml) as CompleteTemplate;
|
|
||||||
|
|
||||||
return { config, dockerCompose };
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error fetching template ${templateId}:`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [templateYml, dockerCompose] = await Promise.all([
|
||||||
|
templateYmlResponse.text(),
|
||||||
|
dockerComposeResponse.text(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const config = parse(templateYml) as CompleteTemplate;
|
||||||
|
|
||||||
|
return { config, dockerCompose };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user