Fix static data volume mount and add global hover-to-translate mode
This commit is contained in:
1 parent
f35864a03e
commit
ffe25e782a
7 files changed
+569
-80
No files matched your search
+41
-10
@@ -11,29 +11,30 @@ const app = express();
|
||||
const PORT = process.env.PORT || 80;
|
||||
const DATA_DIR = process.env.DATA_DIR || path.resolve(__dirname, '../data_store');
|
||||
const STATE_FILE = path.join(DATA_DIR, 'wortkraft_state.json');
|
||||
const TRANSLATE_CACHE_FILE = path.join(DATA_DIR, 'translations_cache.json');
|
||||
|
||||
if (!fs.existsSync(DATA_DIR)) {
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
function readState() {
|
||||
function readJson(filePath, fallback) {
|
||||
try {
|
||||
if (fs.existsSync(STATE_FILE)) {
|
||||
const raw = fs.readFileSync(STATE_FILE, 'utf8');
|
||||
if (fs.existsSync(filePath)) {
|
||||
const raw = fs.readFileSync(filePath, 'utf8');
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[WORTKRAFT] Error reading state file:', err);
|
||||
console.error(`[WORTKRAFT] Error reading ${filePath}:`, err);
|
||||
}
|
||||
return null;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function writeState(stateObj) {
|
||||
function writeJson(filePath, obj) {
|
||||
try {
|
||||
fs.writeFileSync(STATE_FILE, JSON.stringify(stateObj, null, 2), 'utf8');
|
||||
fs.writeFileSync(filePath, JSON.stringify(obj, null, 2), 'utf8');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('[WORTKRAFT] Error writing state file:', err);
|
||||
console.error(`[WORTKRAFT] Error writing ${filePath}:`, err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -53,7 +54,7 @@ app.get('/api/health', (_req, res) => {
|
||||
|
||||
// Get persisted learning state (SRS deck, streak, custom words & stories)
|
||||
app.get('/api/state', (_req, res) => {
|
||||
const state = readState();
|
||||
const state = readJson(STATE_FILE, null);
|
||||
res.json({ exists: Boolean(state), state });
|
||||
});
|
||||
|
||||
@@ -64,10 +65,40 @@ app.post('/api/state', (req, res) => {
|
||||
return res.status(400).json({ error: 'Invalid state payload' });
|
||||
}
|
||||
payload.updatedAt = new Date().toISOString();
|
||||
const ok = writeState(payload);
|
||||
const ok = writeJson(STATE_FILE, payload);
|
||||
res.json({ success: ok, updatedAt: payload.updatedAt });
|
||||
});
|
||||
|
||||
// On-the-fly German -> English word translation fallback for custom texts
|
||||
app.get('/api/translate', async (req, res) => {
|
||||
const word = String(req.query.word || '').trim();
|
||||
if (!word) {
|
||||
return res.status(400).json({ error: 'Missing word parameter' });
|
||||
}
|
||||
const key = word.toLowerCase();
|
||||
const cache = readJson(TRANSLATE_CACHE_FILE, {});
|
||||
if (cache[key]) {
|
||||
return res.json({ word, english: cache[key], cached: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(word)}&langpair=de|en`;
|
||||
const response = await fetch(url);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const translated = data?.responseData?.translatedText;
|
||||
if (translated && translated.toLowerCase() !== key) {
|
||||
cache[key] = translated;
|
||||
writeJson(TRANSLATE_CACHE_FILE, cache);
|
||||
return res.json({ word, english: translated, cached: false });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[WORTKRAFT] Translation fallback error:', err.message);
|
||||
}
|
||||
res.json({ word, english: null });
|
||||
});
|
||||
|
||||
// Serve static frontend files from project root
|
||||
const PUBLIC_DIR = path.resolve(__dirname, '..');
|
||||
app.use('/data', express.static(path.join(PUBLIC_DIR, 'data')));
|
||||
|
||||
Reference in new issue
Block a user