Update in progress
We’re making Synapsis better. We’ll be right back!
import http from 'node:http';
import { dirname, join } from 'node:path';
import { existsSync, readFileSync } from 'node:fs';
import { mkdir, rename, rm, writeFile } from 'node:fs/promises';
const host = process.env.MAINTENANCE_HOST || '127.0.0.1';
const port = Number.parseInt(process.env.PORT || '43821', 10);
const dataDir = process.env.MAINTENANCE_DATA_DIR
|| dirname(process.env.DATABASE_PATH || '/var/lib/synapsis/synapsis.db');
const brandingPath = join(dataDir, 'maintenance-branding.json');
const logoPath = join(dataDir, 'maintenance-logo');
function validAccent(value) {
return typeof value === 'string' && /^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(value)
? value
: '#9fdf5f';
}
async function captureBranding() {
const baseUrl = `http://127.0.0.1:${port}`;
await mkdir(dataDir, { recursive: true });
const nodeResponse = await fetch(`${baseUrl}/api/node`, { cache: 'no-store' });
if (!nodeResponse.ok) throw new Error(`Node branding returned HTTP ${nodeResponse.status}`);
const node = await nodeResponse.json();
const branding = {
accentColor: validAccent(node.accentColor),
externalLogoUrl: typeof node.logoUrl === 'string' && /^https?:\/\//i.test(node.logoUrl)
? node.logoUrl
: null,
logoContentType: null,
};
const logoResponse = await fetch(`${baseUrl}/api/node/logo`, { cache: 'no-store' });
if (logoResponse.ok) {
const contentType = logoResponse.headers.get('content-type')?.split(';')[0];
if (contentType?.startsWith('image/')) {
const logoTempPath = `${logoPath}.tmp`;
await writeFile(logoTempPath, Buffer.from(await logoResponse.arrayBuffer()));
await rename(logoTempPath, logoPath);
branding.logoContentType = contentType;
} else {
await rm(logoPath, { force: true });
}
} else {
await rm(logoPath, { force: true });
}
const brandingTempPath = `${brandingPath}.tmp`;
await writeFile(brandingTempPath, `${JSON.stringify(branding)}\n`, { mode: 0o640 });
await rename(brandingTempPath, brandingPath);
}
if (process.argv.includes('--capture-branding')) {
await captureBranding();
process.exit(0);
}
let branding = {};
try {
branding = JSON.parse(readFileSync(brandingPath, 'utf8'));
} catch {}
const accentColor = validAccent(branding.accentColor);
const logoContentType = typeof branding.logoContentType === 'string' && branding.logoContentType.startsWith('image/')
? branding.logoContentType
: 'application/octet-stream';
const externalLogoUrl = typeof branding.externalLogoUrl === 'string' && /^https?:\/\//i.test(branding.externalLogoUrl)
? branding.externalLogoUrl
: null;
const hasStoredLogo = existsSync(logoPath);
const logoMarkup = hasStoredLogo || externalLogoUrl
? ''
: '
We’re making Synapsis better. We’ll be right back!