257 lines
6.1 KiB
JavaScript
257 lines
6.1 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const crypto = require('crypto');
|
|
const AdmZip = require('adm-zip');
|
|
const processManager = require('./processManager');
|
|
|
|
const DATA_DIR = path.join(__dirname, '../../data');
|
|
const PROJECTS_FILE = path.join(DATA_DIR, 'projects.json');
|
|
const LOGS_DIR = path.join(DATA_DIR, 'logs');
|
|
const DEPLOY_DIR = path.join(__dirname, '../../projects');
|
|
|
|
if (!fs.existsSync(DATA_DIR)) {
|
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
}
|
|
|
|
if (!fs.existsSync(LOGS_DIR)) {
|
|
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
}
|
|
|
|
if (!fs.existsSync(DEPLOY_DIR)) {
|
|
fs.mkdirSync(DEPLOY_DIR, { recursive: true });
|
|
}
|
|
|
|
const getProjects = () => {
|
|
if (!fs.existsSync(PROJECTS_FILE)) {
|
|
fs.writeFileSync(PROJECTS_FILE, JSON.stringify([]));
|
|
return [];
|
|
}
|
|
return JSON.parse(fs.readFileSync(PROJECTS_FILE, 'utf8'));
|
|
};
|
|
|
|
const saveProjects = (projects) => {
|
|
fs.writeFileSync(PROJECTS_FILE, JSON.stringify(projects, null, 2));
|
|
};
|
|
|
|
const addLog = (projectId, message, type = 'info') => {
|
|
const logFile = path.join(LOGS_DIR, `${projectId}.json`);
|
|
const logs = fs.existsSync(logFile)
|
|
? JSON.parse(fs.readFileSync(logFile, 'utf8'))
|
|
: [];
|
|
|
|
logs.push({
|
|
timestamp: new Date().toISOString(),
|
|
message,
|
|
type
|
|
});
|
|
|
|
fs.writeFileSync(logFile, JSON.stringify(logs, null, 2));
|
|
};
|
|
|
|
const calculateFileHash = (filePath) => {
|
|
const content = fs.readFileSync(filePath);
|
|
return crypto.createHash('md5').update(content).digest('hex');
|
|
};
|
|
|
|
const getAllProjects = () => {
|
|
return getProjects();
|
|
};
|
|
|
|
const getProjectById = (id) => {
|
|
const projects = getProjects();
|
|
return projects.find(p => p.id === id);
|
|
};
|
|
|
|
const createProject = ({ name, description, path: projectPath, files }) => {
|
|
const projects = getProjects();
|
|
const id = Date.now().toString();
|
|
|
|
const projectDir = path.join(DEPLOY_DIR, id);
|
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
|
|
const fileInfos = [];
|
|
|
|
files.forEach(file => {
|
|
const destPath = path.join(projectDir, file.originalname);
|
|
fs.copyFileSync(file.path, destPath);
|
|
fs.unlinkSync(file.path);
|
|
|
|
if (file.originalname.endsWith('.zip')) {
|
|
try {
|
|
const zip = new AdmZip(destPath);
|
|
zip.extractAllTo(projectDir, true);
|
|
fs.unlinkSync(destPath);
|
|
addLog(id, `Extracted ${file.originalname}`, 'success');
|
|
} catch (e) {
|
|
addLog(id, `Failed to extract ${file.originalname}: ${e.message}`, 'error');
|
|
}
|
|
} else {
|
|
fileInfos.push({
|
|
name: file.originalname,
|
|
size: file.size,
|
|
hash: calculateFileHash(destPath)
|
|
});
|
|
}
|
|
});
|
|
|
|
const allFiles = getAllFiles(projectDir);
|
|
|
|
const project = {
|
|
id,
|
|
name,
|
|
description,
|
|
path: projectPath || '',
|
|
files: allFiles.map(f => ({
|
|
name: path.relative(projectDir, f),
|
|
size: fs.statSync(f).size,
|
|
hash: calculateFileHash(f)
|
|
})),
|
|
status: 'stopped',
|
|
port: null,
|
|
url: null,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
lastDeployed: null
|
|
};
|
|
|
|
projects.push(project);
|
|
saveProjects(projects);
|
|
|
|
addLog(id, `Project "${name}" created with ${allFiles.length} file(s)`, 'success');
|
|
|
|
return project;
|
|
};
|
|
|
|
const getAllFiles = (dir) => {
|
|
const files = [];
|
|
const items = fs.readdirSync(dir);
|
|
|
|
for (const item of items) {
|
|
const fullPath = path.join(dir, item);
|
|
if (fs.statSync(fullPath).isDirectory()) {
|
|
files.push(...getAllFiles(fullPath));
|
|
} else {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
};
|
|
|
|
const updateProject = (id, updates) => {
|
|
const projects = getProjects();
|
|
const index = projects.findIndex(p => p.id === id);
|
|
|
|
if (index === -1) return null;
|
|
|
|
projects[index] = {
|
|
...projects[index],
|
|
...updates,
|
|
updatedAt: new Date().toISOString()
|
|
};
|
|
|
|
saveProjects(projects);
|
|
addLog(id, `Project updated: ${JSON.stringify(updates)}`);
|
|
|
|
return projects[index];
|
|
};
|
|
|
|
const deleteProject = (id) => {
|
|
const projects = getProjects();
|
|
const index = projects.findIndex(p => p.id === id);
|
|
|
|
if (index === -1) return false;
|
|
|
|
const project = projects[index];
|
|
|
|
if (project.status === 'running') {
|
|
processManager.stopProject(project);
|
|
}
|
|
|
|
const projectDir = path.join(DEPLOY_DIR, id);
|
|
if (fs.existsSync(projectDir)) {
|
|
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
}
|
|
|
|
const logFile = path.join(LOGS_DIR, `${id}.json`);
|
|
if (fs.existsSync(logFile)) {
|
|
fs.unlinkSync(logFile);
|
|
}
|
|
|
|
projects.splice(index, 1);
|
|
saveProjects(projects);
|
|
|
|
return true;
|
|
};
|
|
|
|
const addFilesToProject = (id, files) => {
|
|
const projects = getProjects();
|
|
const project = projects.find(p => p.id === id);
|
|
|
|
if (!project) return null;
|
|
|
|
const projectDir = path.join(DEPLOY_DIR, id);
|
|
|
|
const newFileInfos = files.map(file => {
|
|
const destPath = path.join(projectDir, file.originalname);
|
|
fs.copyFileSync(file.path, destPath);
|
|
fs.unlinkSync(file.path);
|
|
|
|
return {
|
|
name: file.originalname,
|
|
size: file.size,
|
|
hash: calculateFileHash(destPath)
|
|
};
|
|
});
|
|
|
|
project.files = [...project.files, ...newFileInfos];
|
|
project.updatedAt = new Date().toISOString();
|
|
|
|
saveProjects(projects);
|
|
addLog(id, `Added ${files.length} file(s) to project`);
|
|
|
|
return project;
|
|
};
|
|
|
|
const updateProjectStatus = (id, status, port = null, url = null) => {
|
|
const projects = getProjects();
|
|
const project = projects.find(p => p.id === id);
|
|
|
|
if (!project) return null;
|
|
|
|
project.status = status;
|
|
project.port = port;
|
|
project.url = url;
|
|
|
|
if (status === 'running') {
|
|
project.lastDeployed = new Date().toISOString();
|
|
addLog(id, `Project started on port ${port}`, 'success');
|
|
} else if (status === 'stopped') {
|
|
addLog(id, 'Project stopped');
|
|
}
|
|
|
|
saveProjects(projects);
|
|
|
|
return project;
|
|
};
|
|
|
|
const getProjectLogs = (id) => {
|
|
const logFile = path.join(LOGS_DIR, `${id}.json`);
|
|
|
|
if (!fs.existsSync(logFile)) {
|
|
return [];
|
|
}
|
|
|
|
return JSON.parse(fs.readFileSync(logFile, 'utf8'));
|
|
};
|
|
|
|
module.exports = {
|
|
getAllProjects,
|
|
getProjectById,
|
|
createProject,
|
|
updateProject,
|
|
deleteProject,
|
|
addFilesToProject,
|
|
updateProjectStatus,
|
|
getProjectLogs
|
|
}; |