mirror of
https://github.com/Dokploy/website.git
synced 2026-06-15 20:25:25 +02:00
Enhance documentation generation and template handling
- Added a new script to generate templates from external sources, improving the documentation process. - Integrated new components (Tabs and Tab) into the MDX documentation for better content organization. - Updated the Next.js configuration to allow images from the templates URL. - Implemented batch processing for fetching and generating template files, enhancing performance and error handling. These changes streamline the documentation generation workflow and improve the user experience when accessing template information.
This commit is contained in:
@@ -14,6 +14,8 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --filter=./apps/d
|
||||
# Generate OpenAPI documentation from apps/docs/public/openapi.json
|
||||
RUN pnpm --filter=./apps/docs run build:docs
|
||||
|
||||
# Generate templates
|
||||
RUN pnpm --filter=./apps/docs run generate-templates
|
||||
# Deploy only the dokploy app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { APIPage } from "@/lib/source";
|
||||
import { Callout } from "fumadocs-ui/components/callout";
|
||||
import { ImageZoom } from "fumadocs-ui/components/image-zoom";
|
||||
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
|
||||
import defaultMdxComponents from "fumadocs-ui/mdx";
|
||||
import type { MDXComponents } from "mdx/types";
|
||||
|
||||
@@ -9,6 +10,8 @@ export function getMDXComponents(components?: MDXComponents): MDXComponents {
|
||||
...defaultMdxComponents,
|
||||
ImageZoom,
|
||||
Callout,
|
||||
Tab,
|
||||
Tabs,
|
||||
APIPage,
|
||||
...components,
|
||||
p: ({ children }) => (
|
||||
|
||||
@@ -5,6 +5,14 @@ const withMDX = createMDX();
|
||||
/** @type {import('next').NextConfig} */
|
||||
const config = {
|
||||
reactStrictMode: true,
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "templates.dokploy.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default withMDX(config);
|
||||
|
||||
@@ -2,29 +2,135 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const TEMPLATES_URL = 'https://templates.dokploy.com/meta.json';
|
||||
const BASE_BLUEPRINT_URL = 'https://templates.dokploy.com/blueprints';
|
||||
const OUTPUT_DIR = './content/docs/templates';
|
||||
|
||||
async function fetchWithTimeout(url, options = {}, timeout = 10000) {
|
||||
const controller = new AbortController();
|
||||
const id = setTimeout(() => controller.abort(), timeout);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal
|
||||
});
|
||||
clearTimeout(id);
|
||||
return response;
|
||||
} catch (e) {
|
||||
clearTimeout(id);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function getTemplateFile(id, fileName) {
|
||||
try {
|
||||
const response = await fetchWithTimeout(`${BASE_BLUEPRINT_URL}/${id}/${fileName}`);
|
||||
if (!response.ok) return null;
|
||||
return await response.text();
|
||||
} catch (error) {
|
||||
console.error(`Error fetching ${fileName} for ${id}:`, error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Normalize and indent code so it renders correctly inside MDX code blocks (preserves YAML/TOML formatting). */
|
||||
function formatCodeForMdx(code) {
|
||||
if (!code || !code.trim()) return '# Not available';
|
||||
return code
|
||||
.replace(/\r\n/g, '\n')
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((line) => line.trimEnd())
|
||||
.join('\n')
|
||||
.split('\n')
|
||||
.map(line => ` ${line}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/** Build Base64 payload for Dokploy import (same format as UI: compose + config as JSON, then base64). */
|
||||
function templateToBase64(dockerCompose, config) {
|
||||
const configObj = {
|
||||
compose: dockerCompose || '',
|
||||
config: config || '',
|
||||
};
|
||||
const jsonString = JSON.stringify(configObj, null, 2);
|
||||
return Buffer.from(jsonString, 'utf-8').toString('base64');
|
||||
}
|
||||
|
||||
async function generateTemplates() {
|
||||
try {
|
||||
console.log('Fetching templates...');
|
||||
console.log('Fetching templates metadata...');
|
||||
const response = await fetch(TEMPLATES_URL);
|
||||
const templates = await response.json();
|
||||
|
||||
console.log(`Found ${templates.length} templates.`);
|
||||
console.log(`Found ${templates.length} templates. Starting data collection...`);
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Generate individual MDX files
|
||||
for (const template of templates) {
|
||||
const safeDescription = template.description.replace(/"/g, '\\"');
|
||||
const safeName = template.name.replace(/"/g, '\\"');
|
||||
const mdxContent = `---
|
||||
const batchSize = 10;
|
||||
for (let i = 0; i < templates.length; i += batchSize) {
|
||||
const batch = templates.slice(i, i + batchSize);
|
||||
console.log(`Processing batch ${i / batchSize + 1}/${Math.ceil(templates.length / batchSize)}...`);
|
||||
|
||||
await Promise.all(batch.map(async (template) => {
|
||||
const composeYaml = await getTemplateFile(template.id, 'docker-compose.yml');
|
||||
const templateToml = await getTemplateFile(template.id, 'template.toml');
|
||||
const instructionsRaw = await getTemplateFile(template.id, 'instructions.md');
|
||||
const hasRealInstructions =
|
||||
instructionsRaw &&
|
||||
instructionsRaw.trim().length > 0 &&
|
||||
!/^\s*<!doctype/i.test(instructionsRaw.trim()) &&
|
||||
!/^\s*<html\b/i.test(instructionsRaw.trim());
|
||||
const instructionsSafe = hasRealInstructions
|
||||
? instructionsRaw.trim().replace(/\$\{/g, '\\${')
|
||||
: '';
|
||||
const safeDescription = template.description.replace(/"/g, '\\"');
|
||||
const safeName = template.name.replace(/"/g, '\\"');
|
||||
const logoUrl = `${BASE_BLUEPRINT_URL}/${template.id}/${template.logo}`;
|
||||
|
||||
const mdxContent = `---
|
||||
title: "${safeName}"
|
||||
description: "${safeDescription}"
|
||||
---
|
||||
|
||||
<ImageZoom
|
||||
src="${logoUrl}"
|
||||
alt="${template.name} logo"
|
||||
width={100}
|
||||
height={100}
|
||||
className="my-8 rounded-xl"
|
||||
/>
|
||||
|
||||
## Configuration
|
||||
|
||||
<Tabs items={["docker-compose.yml", "template.toml"]}>
|
||||
<Tab value="docker-compose.yml">
|
||||
\`\`\`yaml
|
||||
${formatCodeForMdx(composeYaml)}
|
||||
\`\`\`
|
||||
</Tab>
|
||||
<Tab value="template.toml">
|
||||
\`\`\`toml
|
||||
${formatCodeForMdx(templateToml)}
|
||||
\`\`\`
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Base64
|
||||
|
||||
To import this template in Dokploy: create a **Compose** service → **Advanced** → **Base64 import** and paste the content below:
|
||||
|
||||
\`\`\`text
|
||||
${templateToBase64(composeYaml, templateToml)}
|
||||
\`\`\`
|
||||
${instructionsSafe ? `
|
||||
|
||||
## Instructions
|
||||
|
||||
${instructionsSafe}
|
||||
` : ''}
|
||||
|
||||
## Links
|
||||
${template.links.website ? `- [Website](${template.links.website})` : ''}
|
||||
${template.links.github ? `- [Github](${template.links.github})` : ''}
|
||||
@@ -37,7 +143,8 @@ ${template.tags.map(tag => `\`${tag}\``).join(', ')}
|
||||
|
||||
Version: \`${template.version}\`
|
||||
`;
|
||||
fs.writeFileSync(path.join(OUTPUT_DIR, `${template.id}.mdx`), mdxContent);
|
||||
fs.writeFileSync(path.join(OUTPUT_DIR, `${template.id}.mdx`), mdxContent);
|
||||
}));
|
||||
}
|
||||
|
||||
// Generate index.mdx
|
||||
|
||||
Reference in New Issue
Block a user