import { execSync } from "node:child_process"; import { randomBytes } from "node:crypto"; import { existsSync } from "node:fs"; import { mkdir, readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { Octokit } from "@octokit/rest"; import * as esbuild from "esbuild"; import { load } from "js-yaml"; import { templateConfig } from "../config"; import type { Template } from "./index"; import { generateBase64, generateHash, generatePassword, generateRandomDomain, } from "./index"; // GitHub API client const octokit = new Octokit({ auth: templateConfig.token, }); /** * Interface for template metadata */ export interface TemplateMetadata { id: string; name: string; version: string; description: string; logo: string; links: { github?: string; website?: string; docs?: string; }; tags: string[]; } /** * Fetches the list of available templates from GitHub */ export async function fetchTemplatesList( owner = templateConfig.owner, repo = templateConfig.repo, branch = templateConfig.branch, ): Promise { try { // Fetch templates directory content const { data: dirContent } = await octokit.repos.getContent({ owner, repo, path: "templates", ref: branch, }); console.log("DIR CONTENT", dirContent); if (!Array.isArray(dirContent)) { throw new Error("Templates directory not found or is not a directory"); } // Filter for directories only (each directory is a template) const templateDirs = dirContent.filter((item) => item.type === "dir"); // Fetch metadata for each template const templates = await Promise.all( templateDirs.map(async (dir) => { try { // Try to fetch metadata.json for each template const { data: metadataFile } = await octokit.repos.getContent({ owner, repo, path: `templates/${dir.name}/metadata.json`, ref: branch, }); if ("content" in metadataFile && metadataFile.encoding === "base64") { const content = Buffer.from( metadataFile.content, "base64", ).toString(); return JSON.parse(content) as TemplateMetadata; } } catch (error) { // If metadata.json doesn't exist, create a basic metadata object return { id: dir.name, name: dir.name.charAt(0).toUpperCase() + dir.name.slice(1), version: "latest", description: `${dir.name} template`, logo: "default.svg", links: {}, tags: [], }; } return null; }), ); return templates.filter(Boolean) as TemplateMetadata[]; } catch (error) { console.error("Error fetching templates list:", error); throw error; } } /** * Fetches a specific template's files from GitHub */ export async function fetchTemplateFiles( templateId: string, owner = templateConfig.owner, repo = templateConfig.repo, branch = templateConfig.branch, ): Promise<{ indexTs: string; dockerCompose: string }> { try { // Fetch index.ts const { data: indexFile } = await octokit.repos.getContent({ owner, repo, path: `templates/${templateId}/index.ts`, ref: branch, }); // Fetch docker-compose.yml const { data: composeFile } = await octokit.repos.getContent({ owner, repo, path: `templates/${templateId}/docker-compose.yml`, ref: branch, }); if (!("content" in indexFile) || !("content" in composeFile)) { throw new Error("Template files not found"); } const indexTs = Buffer.from(indexFile.content, "base64").toString(); const dockerCompose = Buffer.from(composeFile.content, "base64").toString(); return { indexTs, dockerCompose }; } catch (error) { console.error(`Error fetching template ${templateId}:`, error); throw error; } } /** * Executes the template's index.ts code dynamically * Uses a template-based approach that's safer and more efficient */ export async function executeTemplateCode( indexTsCode: string, schema: { serverIp: string; projectName: string }, ): Promise