[object Object]

← back to Ventura Corridor

iter 152: /api/magazine/activity unified event feed across gen/publish/cover/feedback/inquiries

fa496556dd7094a373fd022b393680102d7f6244 · 2026-05-06 19:20:46 -0700 · SteveStudio2

Files touched

Diff

commit fa496556dd7094a373fd022b393680102d7f6244
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 6 19:20:46 2026 -0700

    iter 152: /api/magazine/activity unified event feed across gen/publish/cover/feedback/inquiries
---
 src/server/index.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/src/server/index.ts b/src/server/index.ts
index 27c8b10..15349eb 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1892,6 +1892,73 @@ app.get('/api/magazine/random', async (req, res) => {
   }
 });
 
+// Magazine activity feed — unified stream across feature gen/publish/cover + feedback + inquiries.
+// Powers a "what just happened" widget on /today.html and the daily-standup script.
+app.get('/api/magazine/activity', async (req, res) => {
+  try {
+    const limit = Math.min(parseInt(String(req.query.limit ?? '40'), 10) || 40, 200);
+    // UNION across event sources, tagged with a 'kind' for client-side rendering.
+    const r = await query(`
+      SELECT * FROM (
+        SELECT 'feature.generated' AS kind, mf.id AS ref_id,
+               coalesce(mf.headline, b.name) AS label, b.name AS biz_name,
+               mf.category_tag, NULL::text AS extra,
+               mf.generated_at AS at
+        FROM magazine_features mf JOIN businesses b ON b.id = mf.business_id
+        WHERE mf.generated_at > NOW() - INTERVAL '14 days'
+
+        UNION ALL
+        SELECT 'feature.published' AS kind, mf.id, coalesce(mf.headline, b.name), b.name,
+               mf.category_tag, NULL, mf.published_at
+        FROM magazine_features mf JOIN businesses b ON b.id = mf.business_id
+        WHERE mf.published_at > NOW() - INTERVAL '14 days'
+
+        UNION ALL
+        SELECT 'feature.reviewed' AS kind, mf.id, coalesce(mf.headline, b.name), b.name,
+               mf.category_tag, NULL, mf.reviewed_at
+        FROM magazine_features mf JOIN businesses b ON b.id = mf.business_id
+        WHERE mf.reviewed_at > NOW() - INTERVAL '14 days'
+
+        UNION ALL
+        SELECT 'cover.set' AS kind, i.cover_feature_id, mf.headline, b.name,
+               mf.category_tag, i.issue_month, i.cover_set_at
+        FROM magazine_issues i
+        LEFT JOIN magazine_features mf ON mf.id = i.cover_feature_id
+        LEFT JOIN businesses b ON b.id = mf.business_id
+        WHERE i.cover_set_at > NOW() - INTERVAL '30 days'
+
+        UNION ALL
+        SELECT ('feedback.' || rf.kind) AS kind, rf.feature_id, mf.headline, b.name,
+               mf.category_tag, rf.body, rf.received_at
+        FROM reader_feedback rf
+        LEFT JOIN magazine_features mf ON mf.id = rf.feature_id
+        LEFT JOIN businesses b ON b.id = mf.business_id
+        WHERE rf.received_at > NOW() - INTERVAL '14 days'
+
+        UNION ALL
+        SELECT 'inquiry.received' AS kind, si.feature_id, si.biz_name, si.biz_name,
+               si.tier, si.contact_email, si.received_at
+        FROM sponsor_inquiries si
+        WHERE si.received_at > NOW() - INTERVAL '30 days'
+      ) ev
+      WHERE ev.at IS NOT NULL
+      ORDER BY ev.at DESC
+      LIMIT $1
+    `, [limit]);
+
+    const stats = await query(`
+      SELECT
+        (SELECT count(*) FROM magazine_features WHERE generated_at > NOW() - INTERVAL '24 hours') AS gen_24h,
+        (SELECT count(*) FROM magazine_features WHERE published_at > NOW() - INTERVAL '24 hours') AS pub_24h,
+        (SELECT count(*) FROM reader_feedback WHERE received_at > NOW() - INTERVAL '24 hours') AS fb_24h,
+        (SELECT count(*) FROM sponsor_inquiries WHERE received_at > NOW() - INTERVAL '24 hours') AS inq_24h
+    `);
+    res.json({ count: r.rowCount, events: r.rows, stats: stats.rows[0] });
+  } catch (e: any) {
+    res.status(500).json({ error: e.message });
+  }
+});
+
 // Lottery — N random published features. Useful for "5 you may have missed" rails,
 // IG-autopost backup picks, and the fleet-tv rotator. Optional ?cat= filter; ?n= clamps to 1..50.
 app.get('/api/magazine/lottery', async (req, res) => {

← 0c9c392 iter 151: /quotes — typographic 3-col wall of every editoria  ·  back to Ventura Corridor  ·  iter 153: today.html ticker rail — '/api/magazine/activity' b1955e0 →