← back to Ventura Corridor
iter 157: /api/magazine/feature-of-the-month — manual cover wins, falls back to auto-pick heuristic
8fee7065de2045ded8dd6a92f7b5b29a67e35eaf · 2026-05-06 19:28:03 -0700 · SteveStudio2
Files touched
Diff
commit 8fee7065de2045ded8dd6a92f7b5b29a67e35eaf
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 6 19:28:03 2026 -0700
iter 157: /api/magazine/feature-of-the-month — manual cover wins, falls back to auto-pick heuristic
---
src/server/index.ts | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/src/server/index.ts b/src/server/index.ts
index 65c5072..279494f 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1833,6 +1833,41 @@ app.get('/api/magazine/feature-of-the-day', async (_req, res) => {
}
});
+// Feature of the month — returns the cover-pick if set; otherwise the same heuristic as auto-cover.
+app.get('/api/magazine/feature-of-the-month', async (_req, res) => {
+ try {
+ const month = new Date().toISOString().slice(0, 7);
+ // Manual cover pick wins.
+ let r = await query(`
+ SELECT mf.id, mf.headline, mf.subhead, mf.editorial, mf.pull_quote, mf.category_tag,
+ i.cover_caption, i.cover_kicker, i.cover_image_path,
+ b.name AS biz_name, b.address AS biz_address, b.city
+ FROM magazine_issues i
+ JOIN magazine_features mf ON mf.id = i.cover_feature_id
+ JOIN businesses b ON b.id = mf.business_id
+ WHERE i.issue_month = $1 AND mf.status = 'published'
+ `, [month]);
+ if (r.rowCount > 0) return res.json({ feature: r.rows[0], source: 'cover_pick', month });
+
+ // Fallback: top-tap × 3 + views, this month, same heuristic as auto_cover.ts.
+ r = await query(`
+ SELECT mf.id, mf.headline, mf.subhead, mf.editorial, mf.pull_quote, mf.category_tag,
+ b.name AS biz_name, b.address AS biz_address, b.city
+ FROM magazine_features mf
+ JOIN businesses b ON b.id = mf.business_id
+ WHERE mf.status = 'published' AND length(mf.editorial) > 240
+ AND to_char(mf.published_at AT TIME ZONE 'America/Los_Angeles', 'YYYY-MM') = $1
+ ORDER BY (COALESCE(mf.taps,0) * 3 + COALESCE(mf.views,0)) DESC, mf.published_at DESC
+ LIMIT 1
+ `, [month]);
+ if (r.rowCount > 0) return res.json({ feature: r.rows[0], source: 'auto_pick', month });
+
+ return res.json({ feature: null, source: 'none', month });
+ } catch (e: any) {
+ res.status(500).json({ error: e.message });
+ }
+});
+
// Unified daily payload — what an IG-autopost / morning-email / standup-audio job needs in one call.
app.get('/api/magazine/today', async (_req, res) => {
try {
← 37c543b iter 156: /api/magazine/quality-flags audit + nav-pill on to
·
back to Ventura Corridor
·
iter 158: coverage_snapshots table + nightly 11:55 PM job + 52e164c →