| |
|
|
| import axios, { AxiosResponse } from 'axios'; |
| import fs from 'fs'; |
| import path from 'path'; |
| import simpleGit, { SimpleGit } from 'simple-git'; |
| import { Request, Response, Application } from 'express'; |
| import { WebSocketServer } from 'ws'; |
| import { Server as HttpServer } from 'http'; |
|
|
| const TEMPLATE_URL = 'https://raw.githubusercontent.com/ChoruOfficial/global-exocore/main/template.json'; |
| const configPath = path.resolve(__dirname, '../config.json'); |
| const git: SimpleGit = simpleGit(); |
|
|
| interface Template { |
| id: string; |
| name: string; |
| description: string; |
| image: string; |
| git: string; |
| } |
|
|
| interface ConfigData { |
| project?: string; |
| [key: string]: any; |
| } |
|
|
| interface CreateProjectOptions { |
| templateId?: string; |
| gitUrl?: string; |
| } |
|
|
| async function pathExists(p: string): Promise<boolean> { |
| try { |
| await fs.promises.access(p); |
| return true; |
| } catch { |
| return false; |
| } |
| } |
|
|
| const api = { |
| async getTemplates(): Promise<Template[]> { |
| try { |
| const res: AxiosResponse<Template[]> = await axios.get(TEMPLATE_URL); |
| if (Array.isArray(res.data)) return res.data; |
| console.warn('Fetched template.json is not an array.'); |
| return []; |
| } catch (err: unknown) { |
| console.error('Failed to fetch templates:', err); |
| return []; |
| } |
| }, |
|
|
| async updateConfig(projectName: string): Promise<void> { |
| let config: ConfigData = {}; |
| try { |
| if (await pathExists(configPath)) { |
| const file = await fs.promises.readFile(configPath, 'utf-8'); |
| config = file.trim() ? JSON.parse(file) : {}; |
| } |
| } catch (err) { |
| console.error('Error reading config:', err); |
| } |
|
|
| config.project = `../${projectName}`; |
| try { |
| await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2)); |
| console.log(`Config updated: ${configPath}`); |
| } catch (err) { |
| console.error('Error writing config:', err); |
| } |
| }, |
|
|
| async cloneTemplate(gitUrl: string, targetPath: string): Promise<void> { |
| try { |
| await git.clone(gitUrl, targetPath); |
| console.log('Cloned template successfully.'); |
| } catch (err) { |
| console.error('Git clone failed:', err); |
| throw new Error('Git clone failed'); |
| } |
| }, |
|
|
| async checkProjectStatus(): Promise<{ exists: boolean }> { |
| if (!await pathExists(configPath)) return { exists: false }; |
| try { |
| const raw = await fs.promises.readFile(configPath, 'utf-8'); |
| const config: ConfigData = JSON.parse(raw); |
| if (!config.project) return { exists: false }; |
|
|
| const folder = path.resolve(path.dirname(configPath), config.project); |
| return { exists: await pathExists(folder) }; |
| } catch (err) { |
| console.error('Error checking project status:', err); |
| return { exists: false }; |
| } |
| }, |
|
|
| async createProject(projectName: string, { templateId, gitUrl }: CreateProjectOptions): Promise<string> { |
| if (!projectName.trim()) throw new Error('Project name is required.'); |
|
|
| let cloneUrl: string; |
|
|
| if (gitUrl && templateId) throw new Error('Provide either a template ID or a Git URL, not both.'); |
| if (gitUrl) { |
| cloneUrl = gitUrl; |
| } else if (templateId) { |
| const templates = await api.getTemplates(); |
| const template = templates.find(t => t.id === templateId); |
| if (!template) throw new Error(`Template "${templateId}" not found.`); |
| cloneUrl = template.git; |
| } else { |
| throw new Error('You must provide a template ID or Git URL.'); |
| } |
|
|
| const base = path.resolve(__dirname, '../..'); |
| const target = path.join(base, projectName); |
|
|
| if (await pathExists(target)) { |
| throw new Error(`Project "${projectName}" already exists.`); |
| } |
|
|
| await api.cloneTemplate(cloneUrl, target); |
| await api.updateConfig(projectName); |
| return `Project "${projectName}" created at ${target}`; |
| }, |
| }; |
|
|
| interface ProjectRouteParams { |
| req: Request; |
| res: Response; |
| app?: Application; |
| wss?: WebSocketServer; |
| wssConsole?: WebSocketServer; |
| Shellwss?: WebSocketServer; |
| server?: HttpServer; |
| } |
|
|
| interface ProjectExpressRouteModule { |
| method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head' | 'all'; |
| path: string; |
| install: (params: any) => Promise<void> | void; |
| } |
|
|
| interface CreateProjectRequestBody { |
| name: string; |
| template?: string; |
| gitUrl?: string; |
| } |
|
|
| export const modules: ProjectExpressRouteModule[] = [ |
| { |
| method: 'post', |
| path: '/templates', |
| install: async ({ res }: Pick<ProjectRouteParams, 'res'>) => { |
| try { |
| const templates = await api.getTemplates(); |
| res.json(templates); |
| } catch (err) { |
| console.error('Error in /templates:', err); |
| res.status(500).json({ error: 'Failed to load templates.' }); |
| } |
| } |
| }, |
| { |
| method: 'post', |
| path: '/project', |
| install: async ({ req, res }: Pick<ProjectRouteParams, 'req' | 'res'>) => { |
| const { name, template, gitUrl } = req.body as CreateProjectRequestBody; |
| try { |
| const msg = await api.createProject(name, { templateId: template, gitUrl }); |
| res.status(201).json({ success: true, message: msg, projectPath: name }); |
| } catch (err) { |
| console.error('Project creation failed:', err); |
| res.status(400).json({ success: false, error: (err as Error).message }); |
| } |
| } |
| }, |
| { |
| method: 'post', |
| path: '/project/status', |
| install: async ({ res }: Pick<ProjectRouteParams, 'res'>) => { |
| try { |
| const status = await api.checkProjectStatus(); |
| res.json(status); |
| } catch (err) { |
| console.error('Error checking status:', err); |
| res.status(500).json({ error: 'Unable to check project status.' }); |
| } |
| } |
| } |
| ]; |
|
|