Scheer Enterprise Translation Tool
Node HTTP server that proxies the DeepL API and serves a single-page UI, plus per-domain glossary management for the four Scheer entities. Runs on Node's built-ins only; no dependencies. Containerized for Dokploy: the domain -> glossary_id map is written to DOMAINS_FILE (default /app/data/domains.json) so it can live on a mounted volume and survive redeploys. See DEPLOY.md. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
103
cleanup-glossaries.js
Normal file
103
cleanup-glossaries.js
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* One-off maintenance script: lists every glossary that currently exists on
|
||||
* your DeepL account and compares it against the glossaries this app
|
||||
* actually tracks in domains.json. Run this on the machine where server.js
|
||||
* runs (it needs the same DEEPL_API_KEY and network access to DeepL).
|
||||
*
|
||||
* Why you might need this: DeepL accounts have a cap on total glossary
|
||||
* count. If you ever hit a "Too many glossaries" (456) error when saving
|
||||
* terms, it means glossaries exist on your DeepL account that this app
|
||||
* isn't tracking anymore (e.g. left over from earlier testing/debugging, or
|
||||
* from a save that got interrupted). This script finds and removes those
|
||||
* orphans so you have room to save again.
|
||||
*
|
||||
* Usage:
|
||||
* node cleanup-glossaries.js # dry run - lists orphans only
|
||||
* node cleanup-glossaries.js --delete # actually deletes the orphans
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function loadEnv() {
|
||||
const envPath = path.join(__dirname, '.env');
|
||||
if (!fs.existsSync(envPath)) return;
|
||||
for (const line of fs.readFileSync(envPath, 'utf8').split('\n')) {
|
||||
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/i);
|
||||
if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^["']|["']$/g, '');
|
||||
}
|
||||
}
|
||||
loadEnv();
|
||||
|
||||
const DEEPL_API_KEY = process.env.DEEPL_API_KEY;
|
||||
if (!DEEPL_API_KEY) {
|
||||
console.error('DEEPL_API_KEY not found (checked .env and environment).');
|
||||
process.exit(1);
|
||||
}
|
||||
const base = DEEPL_API_KEY.endsWith(':fx') ? 'https://api-free.deepl.com' : 'https://api.deepl.com';
|
||||
|
||||
function loadDomainsJson() {
|
||||
const p = path.join(__dirname, 'domains.json');
|
||||
if (!fs.existsSync(p)) return {};
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const domains = loadDomainsJson();
|
||||
const trackedIds = new Set();
|
||||
for (const d of Object.values(domains)) {
|
||||
if (!d || !d.glossaries) continue;
|
||||
for (const g of Object.values(d.glossaries)) {
|
||||
if (g && g.id) trackedIds.add(g.id);
|
||||
}
|
||||
}
|
||||
|
||||
const resp = await fetch(`${base}/v2/glossaries`, {
|
||||
headers: { Authorization: `DeepL-Auth-Key ${DEEPL_API_KEY}` },
|
||||
});
|
||||
if (!resp.ok) {
|
||||
console.error(`Failed to list glossaries: ${resp.status} ${await resp.text()}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const { glossaries } = await resp.json();
|
||||
|
||||
console.log(`Found ${glossaries.length} glossary/glossaries on the DeepL account. This app tracks ${trackedIds.size} of them in domains.json.\n`);
|
||||
|
||||
const orphans = glossaries.filter((g) => !trackedIds.has(g.glossary_id));
|
||||
|
||||
if (orphans.length === 0) {
|
||||
console.log('No orphaned glossaries found. Nothing to clean up.');
|
||||
console.log('(If you are still getting "Too many glossaries", the account limit may just need to be raised with DeepL, or every glossary on the account is legitimately in use.)');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Orphaned glossaries (exist on DeepL, not referenced by domains.json):\n`);
|
||||
for (const g of orphans) {
|
||||
console.log(` - ${g.glossary_id} "${g.name}" ${g.source_lang}->${g.target_lang} (${g.entry_count} entries, created ${g.creation_time})`);
|
||||
}
|
||||
|
||||
const shouldDelete = process.argv.includes('--delete');
|
||||
if (!shouldDelete) {
|
||||
console.log(`\nDry run only - nothing was deleted. Re-run with --delete to remove the ${orphans.length} orphan(s) above.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\nDeleting orphans...');
|
||||
for (const g of orphans) {
|
||||
const delResp = await fetch(`${base}/v2/glossaries/${g.glossary_id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { Authorization: `DeepL-Auth-Key ${DEEPL_API_KEY}` },
|
||||
});
|
||||
console.log(` - ${g.glossary_id}: ${delResp.ok ? 'deleted' : `failed (${delResp.status})`}`);
|
||||
}
|
||||
console.log('Done.');
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user