Einstellungen-Seite: .env-Werte in DB (app_state), keine .env mehr
Neue /einstellungen-Seite (Zahnrad im Header) mit allen bisherigen .env-Werten (Ollama, E-Mail, CalDAV, REST-API), gespeichert in SQLite (app_state, cfg:-Prefix). Libs lesen per lib/config.js zur Laufzeit statt beim Start -> Aenderungen wirken sofort, kein Neustart. Bestehende .env wird beim ersten Start einmalig migriert. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,7 @@ const { buildOpenApiSpec } = require('./lib/openapi');
|
||||
const blacklist = require('./lib/blacklist');
|
||||
const caldav = require('./lib/caldav');
|
||||
const { LABEL_OPTIONS, parseLabels, serializeLabels } = require('./lib/labels');
|
||||
const config = require('./lib/config');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
@@ -1065,9 +1066,14 @@ function initializeDatabase() {
|
||||
}
|
||||
|
||||
// Initialize and start server
|
||||
initializeDatabase().then(() => {
|
||||
initializeDatabase().then(async () => {
|
||||
console.log('Database initialized successfully');
|
||||
|
||||
|
||||
// Load configuration from the DB (migrates any still-present .env values
|
||||
// once). Must run before the boot checks below (mailer/caldav configured?) and
|
||||
// before any route that reads config — values live in the DB now, not in .env.
|
||||
await config.init({ dbAll, dbRun });
|
||||
|
||||
// Routes
|
||||
app.get('/', async (req, res) => {
|
||||
try {
|
||||
@@ -1976,7 +1982,7 @@ initializeDatabase().then(() => {
|
||||
hasSignatur: Boolean(currentSignaturFile()),
|
||||
hasFoto: Boolean(currentFotoFile()),
|
||||
basisTypOptions: BASIS_TYP_OPTIONS,
|
||||
hasApiKey: Boolean(process.env.OLLAMA_API_KEY),
|
||||
hasApiKey: Boolean(config.get('OLLAMA_API_KEY')),
|
||||
hideSettings: true,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -2653,6 +2659,34 @@ initializeDatabase().then(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Einstellungen (früher .env — jetzt in der Datenbank) -----
|
||||
// Alle Konfigurationswerte (Ollama, E-Mail, CalDAV, REST-API) liegen in der
|
||||
// app_state-Tabelle und sind hier editierbar. Speichern wirkt sofort, ein
|
||||
// Neustart ist nicht nötig (die Libs lesen per config.get() zur Laufzeit).
|
||||
app.get('/einstellungen', (req, res) => {
|
||||
res.render('settings', {
|
||||
felder: config.FIELDS,
|
||||
werte: config.getAll(),
|
||||
hideSettings: true,
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/einstellungen', async (req, res) => {
|
||||
try {
|
||||
const werte = {};
|
||||
for (const sektion of config.FIELDS) {
|
||||
for (const f of sektion.items) {
|
||||
werte[f.key] = req.body[f.key] != null ? String(req.body[f.key]) : '';
|
||||
}
|
||||
}
|
||||
await config.saveAll(werte);
|
||||
res.redirect('/einstellungen');
|
||||
} catch (error) {
|
||||
console.error('Error saving settings:', error);
|
||||
res.status(500).send('Serverfehler');
|
||||
}
|
||||
});
|
||||
|
||||
// ----- Conversational KI-Chat (Ollama, streaming) -----
|
||||
// Gated behind OLLAMA_API_KEY. Threads + messages persist in SQLite; the
|
||||
// assistant answer is streamed back via Server-Sent Events.
|
||||
@@ -2804,7 +2838,7 @@ initializeDatabase().then(() => {
|
||||
|
||||
// Chat page: list threads + render the active thread (or a fresh empty one).
|
||||
app.get('/chat', async (req, res) => {
|
||||
if (!chat.isConfigured()) return res.status(503).send('KI-Chat deaktiviert – OLLAMA_API_KEY fehlt.');
|
||||
if (!chat.isConfigured()) return res.status(503).send('KI-Chat deaktiviert – kein Ollama-API-Schlüssel konfiguriert (unter „Einstellungen“ eintragen).');
|
||||
try {
|
||||
const threads = await dbAll(
|
||||
'SELECT id, titel, updated_at FROM chat_threads ORDER BY updated_at DESC'
|
||||
@@ -2965,8 +2999,9 @@ initializeDatabase().then(() => {
|
||||
|
||||
// ----- Third-party REST API (/api/v1) + OpenAPI/Swagger -----
|
||||
// API key for third-party software. When unset, the API responds 503 on
|
||||
// every endpoint except /health — it never silently exposes data.
|
||||
const apiToken = process.env.API_TOKEN || '';
|
||||
// every endpoint except /health — it never silently exposes data. Read from
|
||||
// the DB via config so an edit on /einstellungen is picked up live.
|
||||
const apiToken = () => config.get('API_TOKEN') || '';
|
||||
app.use('/api/v1', createExternalApi({
|
||||
dbGet,
|
||||
dbAll,
|
||||
@@ -3036,7 +3071,7 @@ initializeDatabase().then(() => {
|
||||
// Start server
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server läuft auf http://localhost:${PORT}`);
|
||||
if (apiToken) console.log('REST-API (/api/v1) aktiv – Swagger unter /swagger');
|
||||
if (apiToken()) console.log('REST-API (/api/v1) aktiv – Swagger unter /swagger');
|
||||
else console.log('REST-API deaktiviert – API_TOKEN fehlt (Swagger unter /swagger weiterhin verfügbar)');
|
||||
});
|
||||
|
||||
@@ -3045,7 +3080,7 @@ initializeDatabase().then(() => {
|
||||
mailer.verify()
|
||||
.then(() => console.log(`E-Mail aktiv: Versand über ${mailer.config().host} als ${mailer.fromAddress()}`))
|
||||
.catch((e) => console.warn('E-Mail SMTP-Verbindung nicht verifizierbar:', e.message));
|
||||
const pollMs = Math.max(60000, Number(process.env.MAIL_POLL_MS) || 180000);
|
||||
const pollMs = Math.max(60000, Number(config.get('MAIL_POLL_MS')) || 180000);
|
||||
setTimeout(() => { pollInbox().catch(() => {}); }, 8000); // initial fetch after boot
|
||||
setInterval(() => { pollInbox().catch(() => {}); }, pollMs); // periodic fetch
|
||||
} else {
|
||||
@@ -3055,7 +3090,7 @@ initializeDatabase().then(() => {
|
||||
// Calendar: reconcile our appointments with the SOGo CalDAV calendar.
|
||||
if (caldav.isConfigured()) {
|
||||
console.log(`Kalender aktiv: CalDAV ${caldav.collectionUrl()}`);
|
||||
const calPoll = Math.max(60000, Number(process.env.CALDAV_POLL_MS) || 300000);
|
||||
const calPoll = Math.max(60000, Number(config.get('CALDAV_POLL_MS')) || 300000);
|
||||
setTimeout(() => { refreshCaldav().catch(() => {}); }, 10000); // initial sync after boot
|
||||
setInterval(() => { refreshCaldav().catch(() => {}); }, calPoll); // periodic reconcile
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user