feat(journal): live AI status updates in entry cards via Postgres NOTIFY trigger + SSE ai_status events

This commit is contained in:
dev
2026-09-17 11:14:27 +03:00
parent cc9a9e6c2d
commit bbfde902a5
4 changed files with 102 additions and 4 deletions
+40
View File
@@ -17,6 +17,39 @@ types.setTypeParser(1082, v => v);
const app = express();
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const pgClient = require('pg').Client;
const lister = new pgClient({ connectionString: process.env.DATABASE_URL });
let listerConnected = false;
function connectLister() {
if (listerConnected) return;
listerConnected = true;
lister.connect()
.then(() => lister.query('LISTEN entries_changed'))
.catch(e => {
listerConnected = false;
console.error('LISTEN entries_changed failed:', e.message);
setTimeout(connectLister, 5000);
});
}
connectLister();
lister.on('error', (e) => {
console.error('LISTEN connection error:', e.message);
});
lister.on('end', () => {
listerConnected = false;
setTimeout(connectLister, 3000);
});
lister.on('notification', (msg) => {
let payload = null;
try { payload = JSON.parse(msg.payload || '{}'); } catch (e) { payload = null; }
const type = payload && payload.type ? payload.type : 'entry_created';
if (type === 'ai_status') {
broadcastAiStatus(payload.id, payload.status, payload.error);
} else {
broadcastEntryChanged();
}
});
const cacheStore = new Map();
const SETTINGS_TTL_MS = 30 * 1000;
const PUBLIC_TTL_MS = 60 * 1000;
@@ -72,6 +105,13 @@ function broadcastEntryChanged() {
}
}
function broadcastAiStatus(entryId, status, error) {
const frame = `event: ai_status\ndata: ${JSON.stringify({ id: entryId, ai_status: status, ai_error: error || null, ts: Date.now() })}\n\n`;
for (const client of sseClients) {
try { client.write(frame); } catch (e) { sseClients.delete(client); }
}
}
app.get('/api/events', async (req, res) => {
try {
const token = req.headers['x-auth-token'] || req.query.token;