Fix stale featured event on homepage: revalidate cache when featured event changes #11
22
backend/src/lib/revalidate.ts
Normal file
22
backend/src/lib/revalidate.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Trigger frontend cache revalidation (fire-and-forget)
|
||||||
|
// Revalidates both the sitemap and the next-event data (homepage, llms.txt)
|
||||||
|
export function revalidateFrontendCache() {
|
||||||
|
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3002';
|
||||||
|
const secret = process.env.REVALIDATE_SECRET;
|
||||||
|
if (!secret) {
|
||||||
|
console.warn('REVALIDATE_SECRET not set, skipping frontend revalidation');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch(`${frontendUrl}/api/revalidate`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ secret, tag: ['events-sitemap', 'next-event'] }),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) console.error('Frontend revalidation failed:', res.status);
|
||||||
|
else console.log('Frontend revalidation triggered (sitemap + next-event)');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Frontend revalidation error:', err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { db, dbGet, dbAll, events, tickets, payments, eventPaymentOverrides, ema
|
|||||||
import { eq, desc, and, gte, sql } from 'drizzle-orm';
|
import { eq, desc, and, gte, sql } from 'drizzle-orm';
|
||||||
import { requireAuth, getAuthUser } from '../lib/auth.js';
|
import { requireAuth, getAuthUser } from '../lib/auth.js';
|
||||||
import { generateId, getNow, convertBooleansForDb, toDbDate, calculateAvailableSeats } from '../lib/utils.js';
|
import { generateId, getNow, convertBooleansForDb, toDbDate, calculateAvailableSeats } from '../lib/utils.js';
|
||||||
|
import { revalidateFrontendCache } from '../lib/revalidate.js';
|
||||||
|
|
||||||
interface UserContext {
|
interface UserContext {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -15,29 +16,6 @@ interface UserContext {
|
|||||||
|
|
||||||
const eventsRouter = new Hono<{ Variables: { user: UserContext } }>();
|
const eventsRouter = new Hono<{ Variables: { user: UserContext } }>();
|
||||||
|
|
||||||
// Trigger frontend cache revalidation (fire-and-forget)
|
|
||||||
// Revalidates both the sitemap and the next-event data (homepage, llms.txt)
|
|
||||||
function revalidateFrontendCache() {
|
|
||||||
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3002';
|
|
||||||
const secret = process.env.REVALIDATE_SECRET;
|
|
||||||
if (!secret) {
|
|
||||||
console.warn('REVALIDATE_SECRET not set, skipping frontend revalidation');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch(`${frontendUrl}/api/revalidate`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ secret, tag: ['events-sitemap', 'next-event'] }),
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (!res.ok) console.error('Frontend revalidation failed:', res.status);
|
|
||||||
else console.log('Frontend revalidation triggered (sitemap + next-event)');
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('Frontend revalidation error:', err.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to normalize event data for API response
|
// Helper to normalize event data for API response
|
||||||
// PostgreSQL decimal returns strings, booleans are stored as integers
|
// PostgreSQL decimal returns strings, booleans are stored as integers
|
||||||
function normalizeEvent(event: any) {
|
function normalizeEvent(event: any) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { db, dbGet, siteSettings, events } from '../db/index.js';
|
|||||||
import { eq, and, gte } from 'drizzle-orm';
|
import { eq, and, gte } from 'drizzle-orm';
|
||||||
import { requireAuth } from '../lib/auth.js';
|
import { requireAuth } from '../lib/auth.js';
|
||||||
import { generateId, getNow, toDbBool } from '../lib/utils.js';
|
import { generateId, getNow, toDbBool } from '../lib/utils.js';
|
||||||
|
import { revalidateFrontendCache } from '../lib/revalidate.js';
|
||||||
|
|
||||||
interface UserContext {
|
interface UserContext {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -172,6 +173,11 @@ siteSettingsRouter.put('/', requireAuth(['admin']), zValidator('json', updateSit
|
|||||||
(db as any).select().from(siteSettings).where(eq((siteSettings as any).id, existing.id))
|
(db as any).select().from(siteSettings).where(eq((siteSettings as any).id, existing.id))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Revalidate frontend cache if featured event changed
|
||||||
|
if (data.featuredEventId !== undefined) {
|
||||||
|
revalidateFrontendCache();
|
||||||
|
}
|
||||||
|
|
||||||
return c.json({ settings: updated, message: 'Settings updated successfully' });
|
return c.json({ settings: updated, message: 'Settings updated successfully' });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -216,6 +222,9 @@ siteSettingsRouter.put('/featured-event', requireAuth(['admin']), zValidator('js
|
|||||||
|
|
||||||
await (db as any).insert(siteSettings).values(newSettings);
|
await (db as any).insert(siteSettings).values(newSettings);
|
||||||
|
|
||||||
|
// Revalidate frontend cache so homepage shows the updated featured event
|
||||||
|
revalidateFrontendCache();
|
||||||
|
|
||||||
return c.json({ featuredEventId: eventId, message: eventId ? 'Event set as featured' : 'Featured event removed' });
|
return c.json({ featuredEventId: eventId, message: eventId ? 'Event set as featured' : 'Featured event removed' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +238,9 @@ siteSettingsRouter.put('/featured-event', requireAuth(['admin']), zValidator('js
|
|||||||
})
|
})
|
||||||
.where(eq((siteSettings as any).id, existing.id));
|
.where(eq((siteSettings as any).id, existing.id));
|
||||||
|
|
||||||
|
// Revalidate frontend cache so homepage shows the updated featured event
|
||||||
|
revalidateFrontendCache();
|
||||||
|
|
||||||
return c.json({ featuredEventId: eventId, message: eventId ? 'Event set as featured' : 'Featured event removed' });
|
return c.json({ featuredEventId: eventId, message: eventId ? 'Event set as featured' : 'Featured event removed' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user