2025-10-03 03:39:24 +08:00
|
|
|
import { createServerFileRoute } from '@tanstack/react-start/server';
|
|
|
|
import { chromium } from 'playwright';
|
|
|
|
import { z } from 'zod';
|
2025-10-03 05:14:07 +08:00
|
|
|
import { getMetric } from '@/api/buster_rest/metrics';
|
|
|
|
import { getSupabaseServerClient } from '@/integrations/supabase/server';
|
2025-10-03 03:53:39 +08:00
|
|
|
import { Route as MetricContentRoute } from './_content/metrics.$metricId.content';
|
2025-10-03 03:39:24 +08:00
|
|
|
|
2025-10-03 05:14:07 +08:00
|
|
|
const isDev = import.meta.env.DEV;
|
|
|
|
|
2025-10-03 03:50:26 +08:00
|
|
|
export const GetMetricScreenshotParamsSchema = z.object({
|
2025-10-03 03:39:24 +08:00
|
|
|
metricId: z.string(),
|
|
|
|
});
|
|
|
|
|
2025-10-03 03:50:26 +08:00
|
|
|
export const GetMetricScreenshotQuerySchema = z.object({
|
2025-10-03 03:39:24 +08:00
|
|
|
version_number: z.coerce.number().min(1).optional(),
|
|
|
|
width: z.coerce.number().min(100).max(3840).default(800),
|
|
|
|
height: z.coerce.number().min(100).max(2160).default(450),
|
2025-10-03 03:50:26 +08:00
|
|
|
type: z.enum(['png', 'jpeg']).default('png'),
|
2025-10-03 03:39:24 +08:00
|
|
|
});
|
|
|
|
|
2025-10-03 03:50:26 +08:00
|
|
|
export const ServerRoute = createServerFileRoute('/screenshots/metrics/$metricId/').methods({
|
2025-10-03 03:39:24 +08:00
|
|
|
GET: async ({ request, params }) => {
|
2025-10-03 05:14:07 +08:00
|
|
|
const bearerToken = request.headers.get('Authorization') || '';
|
|
|
|
const accessToken = bearerToken.replace('Bearer ', '');
|
|
|
|
const supabase = getSupabaseServerClient();
|
|
|
|
const {
|
|
|
|
data: { user },
|
|
|
|
} = await supabase.auth.getUser(accessToken);
|
|
|
|
|
|
|
|
if (!user || user.is_anonymous) {
|
|
|
|
return new Response('Unauthorized', { status: 401 });
|
|
|
|
}
|
|
|
|
|
2025-10-03 03:39:24 +08:00
|
|
|
console.time('capture screenshot');
|
|
|
|
const { metricId } = GetMetricScreenshotParamsSchema.parse(params);
|
2025-10-03 03:50:26 +08:00
|
|
|
const { version_number, type, width, height } = GetMetricScreenshotQuerySchema.parse(
|
2025-10-03 03:39:24 +08:00
|
|
|
Object.fromEntries(new URL(request.url).searchParams)
|
|
|
|
);
|
|
|
|
const origin = new URL(request.url).origin;
|
2025-10-03 05:14:07 +08:00
|
|
|
|
2025-10-03 03:39:24 +08:00
|
|
|
console.timeLog('capture screenshot', 'params parsed');
|
|
|
|
const browser = await chromium.launch();
|
|
|
|
console.timeLog('capture screenshot', 'browser launched');
|
|
|
|
try {
|
2025-10-03 05:14:07 +08:00
|
|
|
// Create a session object for Supabase
|
|
|
|
|
|
|
|
// Create browser context with authentication cookies
|
|
|
|
const context = await browser.newContext({
|
2025-10-03 03:39:24 +08:00
|
|
|
viewport: { width, height },
|
|
|
|
});
|
2025-10-03 05:14:07 +08:00
|
|
|
|
|
|
|
const page = await context.newPage();
|
|
|
|
console.timeLog('capture screenshot', 'page created with auth cookie');
|
2025-10-03 03:39:24 +08:00
|
|
|
|
2025-10-03 03:50:26 +08:00
|
|
|
const fullPath = `${origin}${MetricContentRoute.fullPath}`;
|
2025-10-03 03:39:24 +08:00
|
|
|
await page.goto(fullPath, { waitUntil: 'networkidle' });
|
|
|
|
console.timeLog('capture screenshot', 'page navigated');
|
|
|
|
const screenshotBuffer = await page.screenshot({
|
2025-10-03 03:50:26 +08:00
|
|
|
type,
|
2025-10-03 03:39:24 +08:00
|
|
|
});
|
|
|
|
console.timeLog('capture screenshot', 'screenshot taken');
|
|
|
|
console.timeEnd('capture screenshot');
|
2025-10-03 05:14:07 +08:00
|
|
|
|
|
|
|
if (!isDev) {
|
|
|
|
return new Response(
|
|
|
|
JSON.stringify({
|
|
|
|
success: true,
|
|
|
|
}),
|
|
|
|
{
|
|
|
|
status: 200,
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-10-03 03:39:24 +08:00
|
|
|
return new Response(new Uint8Array(screenshotBuffer), {
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'image/png',
|
|
|
|
'Content-Length': screenshotBuffer.length.toString(),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
// console.error('Error capturing metric screenshot', error);
|
|
|
|
return new Response(
|
|
|
|
JSON.stringify({
|
|
|
|
message: 'Failed to capture screenshot',
|
|
|
|
}),
|
|
|
|
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
|
|
|
);
|
|
|
|
} finally {
|
|
|
|
await browser.close();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|