feat(caddy): single owner via CADDY_MANAGER env flag
One Caddy serves the whole container and POST /load replaces the entire config, so two instances pushing would clobber each other. Now only the instance with CADDY_MANAGER=true (production) pushes, seeds routes from the Caddyfile, and accepts route mutations (others get 403). /api/auth/config exposes caddyManaged so the non-owner Settings UI shows the Caddy section read-only. The installer sets the flag on the production .env only.
This commit is contained in:
14
server.ts
14
server.ts
@ -15,6 +15,11 @@ const uid = (prefix: string) => `${prefix}-${Date.now()}-${Math.random().toStrin
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'ghostgrid-dev-secret-change-in-production';
|
||||
const JWT_EXPIRY = '24h';
|
||||
|
||||
// One Caddy serves the whole container; only the instance with CADDY_MANAGER=true
|
||||
// owns it (pushes config, seeds routes, accepts route edits). The other instance
|
||||
// must never push — POST /load replaces the entire config and would clobber it.
|
||||
const IS_CADDY_MANAGER = process.env.CADDY_MANAGER === 'true';
|
||||
|
||||
interface JwtPayload {
|
||||
userId: string;
|
||||
email: string;
|
||||
@ -90,7 +95,7 @@ function buildCaddyfile(): string {
|
||||
lines.push(' header_up X-Real-IP {remote_host}');
|
||||
lines.push(' header_up Host {host}');
|
||||
if (/^https:\/\//i.test(route.upstream)) {
|
||||
// HTTPS upstream (e.g. Semaphore) — connect over TLS and skip certificate
|
||||
// HTTPS upstream - connect over TLS and skip certificate
|
||||
// verification, since such backends typically use a self-signed cert.
|
||||
lines.push(' transport http {');
|
||||
lines.push(' tls_insecure_skip_verify');
|
||||
@ -142,6 +147,7 @@ function importCaddyfileRoutes(userId?: string): void {
|
||||
}
|
||||
|
||||
async function pushCaddyConfig(): Promise<void> {
|
||||
if (!IS_CADDY_MANAGER) return;
|
||||
if (getSetting('caddy_enabled') !== 'true') return;
|
||||
const adminUrl = getSetting('caddy_admin_url') || 'http://localhost:2019';
|
||||
const body = buildCaddyfile();
|
||||
@ -168,7 +174,7 @@ async function startServer() {
|
||||
console.log('[Init] Default admin user created — login: admin@ghostgrid.local / admin');
|
||||
}
|
||||
|
||||
if (getSetting('caddy_enabled') === 'true' && getCaddyRoutes().length === 0) {
|
||||
if (IS_CADDY_MANAGER && getSetting('caddy_enabled') === 'true' && getCaddyRoutes().length === 0) {
|
||||
importCaddyfileRoutes();
|
||||
}
|
||||
|
||||
@ -265,6 +271,7 @@ async function startServer() {
|
||||
effectiveRedirectUri,
|
||||
checkmkEnabled: getSetting('checkmk_enabled') === 'true',
|
||||
checkmkBaseUrl: cmkApiUrl.replace(/\/api\/.*$/, ''),
|
||||
caddyManaged: IS_CADDY_MANAGER,
|
||||
});
|
||||
});
|
||||
|
||||
@ -1192,6 +1199,7 @@ async function startServer() {
|
||||
|
||||
app.post('/api/caddy/routes', requireAuth, async (req, res) => {
|
||||
try {
|
||||
if (!IS_CADDY_MANAGER) return res.status(403).json({ error: 'Caddy is managed by another instance.' });
|
||||
const { hostname, upstream, tls, compress } = req.body as {
|
||||
hostname?: string; upstream?: string; tls?: boolean; compress?: boolean;
|
||||
};
|
||||
@ -1211,6 +1219,7 @@ async function startServer() {
|
||||
|
||||
app.put('/api/caddy/routes/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
if (!IS_CADDY_MANAGER) return res.status(403).json({ error: 'Caddy is managed by another instance.' });
|
||||
const id = Number(req.params.id);
|
||||
if (!id) return res.status(400).json({ error: 'Invalid route id.' });
|
||||
const { hostname, upstream, tls, compress } = req.body as {
|
||||
@ -1230,6 +1239,7 @@ async function startServer() {
|
||||
|
||||
app.delete('/api/caddy/routes/:id', requireAuth, (req, res) => {
|
||||
try {
|
||||
if (!IS_CADDY_MANAGER) return res.status(403).json({ error: 'Caddy is managed by another instance.' });
|
||||
const id = Number(req.params.id);
|
||||
if (!id) return res.status(400).json({ error: 'Invalid route id.' });
|
||||
const existing = getCaddyRouteById(id);
|
||||
|
||||
Reference in New Issue
Block a user