feat(caddy): auto-import Caddyfile on first enable; seed default admin user

When Caddy is enabled for the first time (caddy routes table empty),
importCaddyfileRoutes() reads /etc/caddy/Caddyfile and seeds all
hostname/upstream blocks as custom routes — no manual entry needed after deploy.

On first startup with an empty users table, a default admin user is created
(admin@ghostgrid.local / admin) so the system is immediately usable.
This commit is contained in:
Brückner
2026-06-08 10:09:26 +02:00
parent 47e7b65613
commit 00cf5dd02d
2 changed files with 68 additions and 1 deletions

View File

@ -89,6 +89,37 @@ function buildCaddyfile(): string {
return lines.join('\n');
}
function importCaddyfileRoutes(): void {
if (getCaddyRoutes().length > 0) return;
const caddyfilePath = '/etc/caddy/Caddyfile';
if (!fs.existsSync(caddyfilePath)) return;
const lines = fs.readFileSync(caddyfilePath, 'utf-8').split('\n');
let i = 0;
while (i < lines.length) {
const line = lines[i].trim();
const headerMatch = line.match(/^(\S+)\s*\{$/);
if (headerMatch && headerMatch[1] !== '{') {
const hostname = headerMatch[1];
const blockLines: string[] = [];
i++;
while (i < lines.length && lines[i].trim() !== '}') {
blockLines.push(lines[i]);
i++;
}
const block = blockLines.join('\n');
const upstreamMatch = block.match(/reverse_proxy\s+(\S+)/);
if (upstreamMatch) {
const upstream = upstreamMatch[1];
const tls = /tls\s+internal/.test(block);
const compress = /encode/.test(block);
addCaddyRoute(hostname, upstream, tls, compress);
console.log(`[Caddy] Imported route from Caddyfile: ${hostname}${upstream}`);
}
}
i++;
}
}
async function pushCaddyConfig(): Promise<void> {
if (getSetting('caddy_enabled') !== 'true') return;
const adminUrl = getSetting('caddy_admin_url') || 'http://localhost:2019';
@ -108,6 +139,14 @@ async function startServer() {
const app = express();
const PORT = Number(process.env.PORT) || 3000;
const { cnt } = db.prepare('SELECT COUNT(*) as cnt FROM users').get() as { cnt: number };
if (cnt === 0) {
const passwordHash = bcrypt.hashSync('admin', 10);
db.prepare('INSERT INTO users (id, name, role, email, password_hash) VALUES (?, ?, ?, ?, ?)')
.run(uid('u'), 'Admin', 'Admin', 'admin@ghostgrid.local', passwordHash);
console.log('[Init] Default admin user created — login: admin@ghostgrid.local / admin');
}
app.use(express.json());
// -------------------------------------------------------------
@ -285,11 +324,15 @@ async function startServer() {
'semaphore_enabled', 'semaphore_api_url', 'semaphore_api_token', 'semaphore_project_id',
'caddy_enabled', 'caddy_admin_url'];
const updates = req.body as Record<string, string>;
const caddyWasEnabled = getSetting('caddy_enabled') === 'true';
for (const key of allowed) {
if (key in updates && updates[key] !== '__SET__') {
setSetting(key, String(updates[key]));
}
}
if (!caddyWasEnabled && updates.caddy_enabled === 'true') {
importCaddyfileRoutes();
}
pushCaddyConfig().catch(err => console.warn('[Caddy] Could not push config after settings save:', err.message));
res.json(maskSettings(getAllSettings()));
} catch (err: any) {