Initial commit: DashCaddy v1.0

Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.
This commit is contained in:
2026-03-05 02:26:12 -08:00
commit f61e85d9a7
337 changed files with 75282 additions and 0 deletions

View File

@@ -0,0 +1,230 @@
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const copyFile = promisify(fs.copyFile);
const mkdir = promisify(fs.mkdir);
const readdir = promisify(fs.readdir);
/**
* FileDeployer - Handles copying dashboard and API files to installation directory
*
* In dev mode: reads from sibling directories (../../../status, ../../../dashcaddy-api)
* In packaged exe: reads from extraResources (process.resourcesPath/status, .../dashcaddy-api)
*/
class FileDeployer {
constructor() {
this._resolveSourcePaths();
}
_resolveSourcePaths() {
let app;
try {
app = require('electron').app;
} catch {
// Not in Electron context
}
const isPackaged = app?.isPackaged || false;
if (isPackaged) {
// Packaged exe: files are in extraResources
this.dashboardSource = path.join(process.resourcesPath, 'status');
this.apiSource = path.join(process.resourcesPath, 'dashcaddy-api');
} else {
// Dev mode: sibling directories in the project tree
this.dashboardSource = path.join(__dirname, '../../../status');
this.apiSource = path.join(__dirname, '../../../dashcaddy-api');
}
}
/**
* Copy directory recursively, skipping node_modules, .git, and test files
*/
async copyDirectory(src, dest, progressCallback = null) {
try {
await mkdir(dest, { recursive: true });
const entries = await readdir(src, { withFileTypes: true });
let copiedCount = 0;
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
// Skip unnecessary directories and files
if (['node_modules', '.git', '__tests__', 'test', '.nyc_output', 'coverage'].includes(entry.name)) {
continue;
}
// Skip test files
if (entry.name.endsWith('.test.js') || entry.name.endsWith('.spec.js')) {
continue;
}
if (entry.isDirectory()) {
const subResult = await this.copyDirectory(srcPath, destPath, progressCallback);
if (subResult.success) copiedCount += subResult.filesCopied;
} else {
await copyFile(srcPath, destPath);
copiedCount++;
if (progressCallback) {
progressCallback({
type: 'file',
source: srcPath,
destination: destPath,
count: copiedCount
});
}
}
}
return { success: true, filesCopied: copiedCount };
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* Deploy dashboard files to installation directory
*/
async deployDashboard(installPath, progressCallback = null) {
try {
const dashboardDest = path.join(installPath, 'sites', 'status');
if (progressCallback) {
progressCallback({
type: 'start',
message: 'Deploying dashboard files...',
source: this.dashboardSource,
destination: dashboardDest
});
}
// Verify source exists
if (!fs.existsSync(this.dashboardSource)) {
throw new Error(`Dashboard source not found at: ${this.dashboardSource}`);
}
const result = await this.copyDirectory(
this.dashboardSource,
dashboardDest,
progressCallback
);
if (!result.success) {
throw new Error(result.error);
}
if (progressCallback) {
progressCallback({
type: 'complete',
message: `Dashboard deployed (${result.filesCopied} files)`,
filesCopied: result.filesCopied
});
}
return {
success: true,
path: dashboardDest,
filesCopied: result.filesCopied
};
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* Deploy API server files to installation directory
*/
async deployAPI(installPath, progressCallback = null) {
try {
const apiDest = path.join(installPath, 'sites', 'dashcaddy-api');
if (progressCallback) {
progressCallback({
type: 'start',
message: 'Deploying API server files...',
source: this.apiSource,
destination: apiDest
});
}
if (!fs.existsSync(this.apiSource)) {
throw new Error(`API source not found at: ${this.apiSource}`);
}
const result = await this.copyDirectory(
this.apiSource,
apiDest,
progressCallback
);
if (!result.success) {
throw new Error(result.error);
}
if (progressCallback) {
progressCallback({
type: 'complete',
message: `API server deployed (${result.filesCopied} files)`,
filesCopied: result.filesCopied
});
}
return {
success: true,
path: apiDest,
filesCopied: result.filesCopied
};
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* Deploy both dashboard and API files
*/
async deployComplete(installPath, progressCallback = null) {
try {
const dashboardResult = await this.deployDashboard(installPath, progressCallback);
if (!dashboardResult.success) {
throw new Error(`Dashboard deployment failed: ${dashboardResult.error}`);
}
const apiResult = await this.deployAPI(installPath, progressCallback);
if (!apiResult.success) {
throw new Error(`API deployment failed: ${apiResult.error}`);
}
return {
success: true,
dashboard: dashboardResult,
api: apiResult
};
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* Check if source directories exist
*/
async validateSources() {
const checks = {
dashboard: fs.existsSync(this.dashboardSource),
api: fs.existsSync(this.apiSource)
};
return {
valid: checks.dashboard && checks.api,
checks,
paths: {
dashboard: this.dashboardSource,
api: this.apiSource
}
};
}
}
module.exports = FileDeployer;