Adds a self-service editor at admin.larpaso.de so texts, images, links, Impressum/Datenschutz, fonts, font sizes and colors can be changed without a developer. All homepage/legal content moves into content/site.json and a new content/theme.json drives every CSS color/font/size token; the Astro pages read from these at build time. The admin/ service (Node/Express) provides bcrypt+session login with rate limiting, a schema-driven form UI (including a hex/RGB/alpha color picker), image upload, and a publish action that runs the Astro build and republishes into a volume shared with the nginx website container - so edits go live without redeploying that container. See DEPLOYMENT_ADMIN.md for the server-side setup steps.
62 lines
1.9 KiB
JavaScript
62 lines
1.9 KiB
JavaScript
const { spawn } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const PROJECT_DIR = process.env.PROJECT_DIR || path.resolve(__dirname, '..', '..');
|
|
const DIST_TARGET_DIR = process.env.DIST_TARGET_DIR || '';
|
|
|
|
function copyDirRecursive(src, dest) {
|
|
fs.mkdirSync(dest, { recursive: true });
|
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
const srcPath = path.join(src, entry.name);
|
|
const destPath = path.join(dest, entry.name);
|
|
if (entry.isDirectory()) {
|
|
copyDirRecursive(srcPath, destPath);
|
|
} else {
|
|
fs.copyFileSync(srcPath, destPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Runs `npm run build` in the Astro project and, if configured, publishes
|
|
* the resulting dist/ into the shared volume nginx serves from. */
|
|
function runBuild() {
|
|
return new Promise((resolve) => {
|
|
let child;
|
|
try {
|
|
child = spawn('npm', ['run', 'build'], { cwd: PROJECT_DIR, shell: true });
|
|
} catch (err) {
|
|
resolve({ ok: false, output: '', error: `Build konnte nicht gestartet werden: ${err.message}` });
|
|
return;
|
|
}
|
|
|
|
let output = '';
|
|
child.stdout.on('data', (chunk) => (output += chunk.toString()));
|
|
child.stderr.on('data', (chunk) => (output += chunk.toString()));
|
|
|
|
child.on('error', (err) => {
|
|
resolve({ ok: false, output, error: `Build konnte nicht gestartet werden: ${err.message}` });
|
|
});
|
|
|
|
child.on('close', (code) => {
|
|
if (code !== 0) {
|
|
resolve({ ok: false, output, error: `Build fehlgeschlagen (Exit-Code ${code}).` });
|
|
return;
|
|
}
|
|
|
|
if (DIST_TARGET_DIR) {
|
|
try {
|
|
const distDir = path.join(PROJECT_DIR, 'dist');
|
|
copyDirRecursive(distDir, DIST_TARGET_DIR);
|
|
} catch (err) {
|
|
resolve({ ok: false, output, error: `Build ok, aber Kopieren fehlgeschlagen: ${err.message}` });
|
|
return;
|
|
}
|
|
}
|
|
|
|
resolve({ ok: true, output });
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = { runBuild };
|