Migrate Phase 3 Webhook and Utilities

Extend createEdgeFunction usage to novu-webhook, seed-test-data, and sitemap by removing manual boilerplate (CORS, auth, tracking, error handling) and replacing logging with span-based tracing; wire in EdgeFunctionContext for supabase, user, span, and requestId; preserve core logic including webhook validation, data seeding utilities, and sitemap caching.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-11 21:17:32 +00:00
parent 96b7594738
commit 9ee84b31ff
3 changed files with 183 additions and 256 deletions

View File

@@ -1,5 +1,6 @@
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4';
import { edgeLogger } from '../_shared/logger.ts';
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createEdgeFunction, type EdgeFunctionContext } from '../_shared/edgeFunctionWrapper.ts';
import { addSpanEvent } from '../_shared/logger.ts';
import { formatEdgeError } from '../_shared/errorFormatter.ts';
const BASE_URL = 'https://dev.thrillwiki.com';
@@ -131,7 +132,7 @@ function generateSitemapXml(urls: SitemapUrl[]): string {
// SITEMAP GENERATION
// ============================================================================
async function generateSitemap(requestId: string): Promise<{
async function generateSitemap(requestId: string, span: any): Promise<{
xml: string;
stats: SitemapStats;
}> {
@@ -150,10 +151,12 @@ async function generateSitemap(requestId: string): Promise<{
generation_time_ms: 0,
};
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? ''
);
const supabaseUrl = Deno.env.get('SUPABASE_URL') ?? '';
const supabaseAnonKey = Deno.env.get('SUPABASE_ANON_KEY') ?? '';
// Dynamic import to avoid circular dependency issues
const { createClient } = await import('https://esm.sh/@supabase/supabase-js@2.57.4');
const supabase = createClient(supabaseUrl, supabaseAnonKey);
// Static pages
const now = new Date().toISOString();
@@ -265,8 +268,7 @@ async function generateSitemap(requestId: string): Promise<{
const xml = generateSitemapXml(urls);
edgeLogger.info('Sitemap generated', {
requestId,
addSpanEvent(span, 'sitemap_generated', {
stats,
sizeKB: (xml.length / 1024).toFixed(2),
});
@@ -293,76 +295,51 @@ function generateFallbackSitemap(): string {
// MAIN HANDLER
// ============================================================================
Deno.serve(async (req) => {
const requestId = crypto.randomUUID();
serve(createEdgeFunction({
name: 'sitemap',
requireAuth: false, // Public endpoint
corsHeaders: {},
}, async (req, { span, requestId }: EdgeFunctionContext) => {
const startTime = Date.now();
try {
// Return cached version if valid
if (isCacheValid()) {
const duration = Date.now() - startTime;
edgeLogger.info('Sitemap cache hit', {
requestId,
cacheAge: Date.now() - cacheTimestamp,
duration,
});
return new Response(cachedSitemap, {
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'X-Request-ID': requestId,
'X-Cache': 'HIT',
'X-Generation-Time': `${duration}ms`,
...cacheHeaders,
},
});
}
// Generate fresh sitemap
const sitemap = await generateSitemap(requestId);
// Update cache
cachedSitemap = sitemap.xml;
cacheTimestamp = Date.now();
// Return cached version if valid
if (isCacheValid()) {
const duration = Date.now() - startTime;
edgeLogger.info('Sitemap cache miss - generated', {
requestId,
addSpanEvent(span, 'sitemap_cache_hit', {
cacheAge: Date.now() - cacheTimestamp,
duration,
stats: sitemap.stats,
});
return new Response(sitemap.xml, {
return new Response(cachedSitemap, {
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'X-Request-ID': requestId,
'X-Cache': 'MISS',
'X-Cache': 'HIT',
'X-Generation-Time': `${duration}ms`,
...cacheHeaders,
},
});
} catch (error) {
const duration = Date.now() - startTime;
edgeLogger.error('Sitemap generation failed', {
requestId,
error: formatEdgeError(error),
duration,
});
// Return minimal valid sitemap on error (graceful degradation)
const fallbackSitemap = generateFallbackSitemap();
return new Response(fallbackSitemap, {
status: 200, // Still return 200 for SEO
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'X-Request-ID': requestId,
'X-Error': 'true',
'Cache-Control': 'public, max-age=300', // Cache errors for 5min only
},
});
}
});
// Generate fresh sitemap
const sitemap = await generateSitemap(requestId, span);
// Update cache
cachedSitemap = sitemap.xml;
cacheTimestamp = Date.now();
const duration = Date.now() - startTime;
addSpanEvent(span, 'sitemap_cache_miss', {
duration,
stats: sitemap.stats,
});
return new Response(sitemap.xml, {
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'X-Cache': 'MISS',
'X-Generation-Time': `${duration}ms`,
...cacheHeaders,
},
});
}));