feat(journal): live AI status updates in entry cards via Postgres NOTIFY trigger + SSE ai_status events
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user