Make the container deployable on Dokploy

domains.json was written next to server.js, i.e. into the image filesystem,
so every redeploy discarded the domain -> glossary_id mappings. Its path is
now overridable via DOMAINS_FILE and the image defaults it to /app/data,
a subdirectory so that mounting a volume there does not shadow server.js
and index.html in /app.

Also adds a GET /health endpoint plus a Docker HEALTHCHECK that uses it. The
probe deliberately does not call DeepL, so an upstream DeepL outage does not
get the container restarted.

.gitignore is new; .env holds a real API key and was previously untracked
only by convention.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-31 11:38:53 +02:00
parent 92f44a2916
commit 34ee2856b2
5 changed files with 127 additions and 4 deletions

View File

@@ -66,7 +66,10 @@ function baseUrlFor(apiKey) {
// the server disk, not in any user's browser, so every visitor to the site
// sees and edits the same shared glossaries.
const DOMAINS_FILE = path.join(__dirname, 'domains.json');
// The path is overridable via DOMAINS_FILE so a container deployment can point
// it at a mounted volume (e.g. /app/data/domains.json) instead of the image's
// own filesystem, which is recreated on every redeploy.
const DOMAINS_FILE = process.env.DOMAINS_FILE || path.join(__dirname, 'domains.json');
const VALID_DOMAINS = ['scheer-group', 'scheer-ids', 'scheer-pas', 'scheer-imc'];
const DOMAIN_LABELS = {
'scheer-group': 'Scheer Group',
@@ -137,6 +140,7 @@ function loadDomains() {
}
function saveDomains(data) {
fs.mkdirSync(path.dirname(DOMAINS_FILE), { recursive: true });
fs.writeFileSync(DOMAINS_FILE, JSON.stringify(data, null, 2));
}
@@ -448,7 +452,15 @@ function serveStatic(req, res) {
});
}
// Liveness probe for container platforms (Docker HEALTHCHECK, Dokploy).
// Deliberately does not call DeepL: it answers "is this process serving?",
// not "is the upstream API up", so a DeepL outage doesn't restart the app.
function handleHealth(req, res) {
sendJson(res, 200, { status: 'ok', apiKeyConfigured: Boolean(DEEPL_API_KEY) });
}
const routes = {
'GET /health': handleHealth,
'POST /api/translate': handleTranslate,
'POST /api/usage': handleUsage,
'POST /api/document/upload': handleDocumentUpload,