Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { createClient } from '@/lib/supabase/server';
import Header from "@/components/header";
import Footer from "@/components/footer";

// Force dynamic rendering to prevent stale data
export const dynamic = 'force-dynamic';
export const revalidate = 0;
export const fetchCache = 'force-no-store';

interface UsernamePageProps {
params: Promise<{
username: string;
Expand Down
16 changes: 3 additions & 13 deletions app/api/companies/[slug]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,7 @@ export async function GET(
try {
const { slug } = await params

// Try to get from cache first
const cacheKey = `company:${slug}`
const cached = await UnifiedCache.get(cacheKey)

if (cached) {
return UnifiedCache.createResponse(cached, 'API_STANDARD')
}

// Fetch company
// Fetch company - no caching to prevent stale data
const company = await companyService.getCompanyBySlug(slug)

if (!company) {
Expand Down Expand Up @@ -110,10 +102,8 @@ export async function GET(
total_participants: eventParticipants + hackathonParticipants,
}

// Cache the result
await UnifiedCache.set(cacheKey, { company: enrichedCompany }, 'API_STANDARD')

return UnifiedCache.createResponse({ company: enrichedCompany }, 'API_STANDARD')
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse({ company: enrichedCompany }, 'USER_PRIVATE')
} catch (error) {
console.error('Error in GET /api/companies/[slug]:', error)

Expand Down
16 changes: 3 additions & 13 deletions app/api/companies/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,11 @@ export async function GET(request: NextRequest) {
offset: parseInt(searchParams.get('offset') || '0'),
}

// Try to get from cache first
const cacheKey = `companies:list:${JSON.stringify(filters)}`
const cached = await UnifiedCache.get(cacheKey)

if (cached) {
return UnifiedCache.createResponse(cached, 'API_STANDARD')
}

// Fetch from database
// Fetch from database - no caching to prevent stale data
const result = await companyService.listCompanies(filters)

// Cache the result
await UnifiedCache.set(cacheKey, result, 'API_STANDARD')

return UnifiedCache.createResponse(result, 'API_STANDARD')
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse(result, 'USER_PRIVATE')
} catch (error) {
console.error('Error in GET /api/companies:', error)

Expand Down
16 changes: 3 additions & 13 deletions app/api/events/[slug]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,7 @@ export async function GET(
try {
const { slug } = await params;

// Try to get from cache first
const cacheKey = `event:${slug}`;
const cached = await UnifiedCache.get(cacheKey);

if (cached) {
return UnifiedCache.createResponse(cached, 'API_STANDARD');
}

// Fetch from database
// Fetch from database - no caching to prevent stale data
const event = await eventsService.getEventBySlug(slug);

if (!event) {
Expand All @@ -33,10 +25,8 @@ export async function GET(
);
}

// Cache the result
await UnifiedCache.set(cacheKey, event, 'API_STANDARD');

return UnifiedCache.createResponse(event, 'API_STANDARD');
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse(event, 'USER_PRIVATE');

} catch (error) {
console.error('Error in GET /api/events/[slug]:', error);
Expand Down
16 changes: 3 additions & 13 deletions app/api/events/featured/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,11 @@ export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const limit = parseInt(searchParams.get('limit') || '5');

// Try to get from cache first
const cacheKey = `featured_events:${limit}`;
const cached = await UnifiedCache.get(cacheKey);

if (cached) {
return UnifiedCache.createResponse({ events: cached }, 'API_STANDARD');
}

// Fetch from database
// Fetch from database - no caching to prevent stale data
const events = await eventsService.getFeaturedEvents(limit);

// Cache the result
await UnifiedCache.set(cacheKey, events, 'API_STANDARD');

return UnifiedCache.createResponse({ events }, 'API_STANDARD');
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse({ events }, 'USER_PRIVATE');

} catch (error) {
console.error('Error in GET /api/events/featured:', error);
Expand Down
16 changes: 3 additions & 13 deletions app/api/events/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,11 @@ export async function GET(request: NextRequest) {
offset: parseInt(searchParams.get('offset') || '0')
};

// Try to get from cache first
const cacheKey = `events:${JSON.stringify(filters)}`;
const cached = await UnifiedCache.get(cacheKey);

if (cached) {
return UnifiedCache.createResponse(cached, 'API_STANDARD');
}

// Fetch from database
// Fetch from database - no caching to prevent stale data
const result = await eventsService.getEvents(filters);

// Cache the result
await UnifiedCache.set(cacheKey, result, 'API_STANDARD');

return UnifiedCache.createResponse(result, 'API_STANDARD');
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse(result, 'USER_PRIVATE');

} catch (error) {
console.error('Error in GET /api/events:', error);
Expand Down
28 changes: 10 additions & 18 deletions app/api/hackathons/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,17 @@ export async function GET(request: NextRequest) {
offset: searchParams.get('offset') ? parseInt(searchParams.get('offset')!) : undefined,
};

// Create cache key based on filters
const cacheKey = `hackathons-${JSON.stringify(filters)}`;

// Use unified cache system with DYNAMIC_CONTENT strategy for fast updates
const result = await UnifiedCache.cachedQuery(
cacheKey,
async () => {
// Add timeout to prevent hanging requests
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), 8000);
});

const resultPromise = hackathonsService.getHackathons(filters);
return await Promise.race([resultPromise, timeoutPromise]);
},
'DYNAMIC_CONTENT' // Use dynamic content strategy for hackathons
);
// Fetch from database - no caching to prevent stale data
// Add timeout to prevent hanging requests
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), 8000);
});

const resultPromise = hackathonsService.getHackathons(filters);
const result = await Promise.race([resultPromise, timeoutPromise]);

return UnifiedCache.createResponse(result, 'DYNAMIC_CONTENT');
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse(result, 'USER_PRIVATE');

} catch (error) {
console.error('Error in GET /api/hackathons:', error);
Expand Down
16 changes: 4 additions & 12 deletions app/api/leaderboard/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,11 @@ export async function GET(request: NextRequest) {
const timeRange = searchParams.get('timeRange') || 'all';
const badge = searchParams.get('badge') || null;

// Create cache key based on parameters
const cacheKey = `leaderboard-${page}-${limit}-${timeRange}-${badge || 'all'}`;

// Use unified cache system
const data = await UnifiedCache.cachedQuery(
cacheKey,
async () => {
return await fetchLeaderboardData(page, limit, timeRange, badge);
},
'API_STANDARD'
);
// Fetch from database - no caching to prevent stale data
const data = await fetchLeaderboardData(page, limit, timeRange, badge);

return UnifiedCache.createResponse(data, 'API_STANDARD');
// Return with no-cache headers to prevent stale data
return UnifiedCache.createResponse(data, 'USER_PRIVATE');

} catch (error) {
console.error('Leaderboard API error:', error);
Expand Down
112 changes: 14 additions & 98 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,6 @@ const nextConfig: NextConfig = {
key: 'Vary',
value: 'Accept-Encoding',
},
{
key: 'Cache-Tag',
value: 'static',
},
],
},

Expand All @@ -152,86 +148,32 @@ const nextConfig: NextConfig = {
key: 'CDN-Cache-Control',
value: isProd ? 'public, max-age=2592000, immutable' : 'no-cache',
},
{
key: 'Cache-Tag',
value: 'media',
},
],
},

// HOMEPAGE: No caching to ensure auth state is always fresh
// ALL DYNAMIC PAGES: No caching to prevent stale data
{
source: '/',
source: '/((?!_next|api).*)',
headers: [
{
key: 'Cache-Control',
value: 'no-cache, no-store, must-revalidate',
value: 'no-cache, no-store, must-revalidate, max-age=0',
},
{
key: 'CDN-Cache-Control',
value: 'no-cache',
value: 'no-cache, no-store, must-revalidate',
},
{
key: 'Pragma',
value: 'no-cache',
},
{
key: 'Vary',
value: 'Cookie, Accept-Encoding',
},
{
key: 'X-Build-ID',
value: buildId,
},
],
},

// DYNAMIC CONTENT: Dynamic pages (events, hackathons, etc.)
{
source: '/(hackathons|events|leaderboard|opportunities)/:path*',
headers: [
{
key: 'Cache-Control',
value: isDev
? 'no-cache, no-store, must-revalidate'
: 'public, max-age=60, stale-while-revalidate=300', // 1min cache, 5min SWR
},
{
key: 'CDN-Cache-Control',
value: isProd ? 'public, max-age=60, stale-while-revalidate=300' : 'no-cache',
},
{
key: 'X-Build-ID',
value: buildId,
},
{
key: 'Cache-Tag',
value: 'content',
key: 'Expires',
value: '0',
},
{
key: 'Vary',
value: 'Cookie, Accept-Encoding',
},
],
},

// DATABASE QUERIES: API routes that query database
{
source: '/api/(hackathons|leaderboard|tests|verify-certificate)/:path*',
headers: [
{
key: 'Cache-Control',
value: isDev
? 'no-cache, no-store, must-revalidate'
: 'public, max-age=300, stale-while-revalidate=600', // 5min cache, 10min SWR
},
{
key: 'CDN-Cache-Control',
value: isProd ? 'public, max-age=300, stale-while-revalidate=600' : 'no-cache',
},
{
key: 'Cache-Tag',
value: 'api',
value: 'Cookie, Accept-Encoding, Authorization',
},
{
key: 'X-Build-ID',
Expand All @@ -240,56 +182,30 @@ const nextConfig: NextConfig = {
],
},

// USER PRIVATE: Auth and user-specific routes
// API ROUTES: No caching for dynamic data
{
source: '/(protected|admin|profile|dashboard|auth)/:path*',
source: '/api/:path*',
headers: [
{
key: 'Cache-Control',
value: 'private, no-cache, no-store, must-revalidate',
value: 'no-cache, no-store, must-revalidate, max-age=0',
},
{
key: 'CDN-Cache-Control',
value: 'no-cache',
value: 'no-cache, no-store, must-revalidate',
},
{
key: 'Pragma',
value: 'no-cache',
},
],
},

// API STANDARD: General API routes and public pages
{
source: '/((?!_next|protected|admin|profile|dashboard|auth).*)',
headers: [
{
key: 'Cache-Control',
value: isDev
? 'no-cache, no-store, must-revalidate'
: 'public, max-age=0, must-revalidate', // Always revalidate HTML
},
{
key: 'CDN-Cache-Control',
value: isProd ? 'public, max-age=60, stale-while-revalidate=300' : 'no-cache',
},
{
key: 'Cache-Tag',
value: 'pages',
},
{
key: 'X-Build-ID',
value: buildId,
key: 'Expires',
value: '0',
},
{
key: 'Vary',
value: 'Cookie, Accept-Encoding',
value: 'Cookie, Accept-Encoding, Authorization',
},
// Security headers
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '1; mode=block' },
{ key: 'Referrer-Policy', value: 'origin-when-cross-origin' },
],
},
]
Expand Down
11 changes: 1 addition & 10 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,7 @@
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=2592000, immutable"
}
]
},
{
"source": "/cache-manifest.json",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=0, must-revalidate"
"value": "public, max-age=31536000, immutable"
}
]
},
Expand Down
Loading