Files
wehub-resource-sync 1a390b2815
Scorecard supply-chain security / Scorecard analysis (push) Failing after 1s
/ test (push) Failing after 0s
secretlint / Run secretlint to diff files (push) Failing after 1s
UI-TARS E2E Test / E2E (macos-13) (push) Has been cancelled
UI-TARS E2E Test / E2E (macos-latest) (push) Has been cancelled
UI-TARS E2E Test / E2E (windows-latest) (push) Has been cancelled
CI Test, Typecheck / Test & Typecheck (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:08:49 +08:00

162 lines
4.4 KiB
TypeScript

import { NextResponse } from 'next/server';
import Browserbase from '@browserbasehq/sdk';
type BrowserbaseRegion =
| 'us-west-2'
| 'us-east-1'
| 'eu-central-1'
| 'ap-southeast-1';
// Exact timezone matches for east coast cities
const exactTimezoneMap: Record<string, BrowserbaseRegion> = {
'America/New_York': 'us-east-1',
'America/Detroit': 'us-east-1',
'America/Toronto': 'us-east-1',
'America/Montreal': 'us-east-1',
'America/Boston': 'us-east-1',
'America/Chicago': 'us-east-1',
};
// Prefix-based region mapping
const prefixToRegion: Record<string, BrowserbaseRegion> = {
America: 'us-west-2',
US: 'us-west-2',
Canada: 'us-west-2',
Europe: 'eu-central-1',
Africa: 'eu-central-1',
Asia: 'ap-southeast-1',
Australia: 'ap-southeast-1',
Pacific: 'ap-southeast-1',
};
// Offset ranges to regions (inclusive bounds)
const offsetRanges: {
min: number;
max: number;
region: BrowserbaseRegion;
}[] = [
{ min: -24, max: -4, region: 'us-west-2' }, // UTC-24 to UTC-4
{ min: -3, max: 4, region: 'eu-central-1' }, // UTC-3 to UTC+4
{ min: 5, max: 24, region: 'ap-southeast-1' }, // UTC+5 to UTC+24
];
function getClosestRegion(timezone?: string): BrowserbaseRegion {
try {
if (!timezone) {
return 'us-west-2'; // Default if no timezone provided
}
// Check exact matches first
if (timezone in exactTimezoneMap) {
return exactTimezoneMap[timezone];
}
// Check prefix matches
const prefix = timezone.split('/')[0];
if (prefix in prefixToRegion) {
return prefixToRegion[prefix];
}
// Use offset-based fallback
const date = new Date();
// Create a date formatter for the given timezone
const formatter = new Intl.DateTimeFormat('en-US', { timeZone: timezone });
// Get the timezone offset in minutes
const timeString = formatter.format(date);
const testDate = new Date(timeString);
const hourOffset = (testDate.getTime() - date.getTime()) / (1000 * 60 * 60);
const matchingRange = offsetRanges.find(
(range) => hourOffset >= range.min && hourOffset <= range.max,
);
return matchingRange?.region ?? 'us-west-2';
} catch {
return 'us-west-2';
}
}
async function createSession(timezone?: string, contextId?: string) {
const bb = new Browserbase({
apiKey: process.env.BROWSERBASE_API_KEY!,
});
const browserSettings: { context?: { id: string; persist: boolean } } = {};
if (contextId) {
browserSettings.context = {
id: contextId,
persist: true,
};
} else {
const context = await bb.contexts.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!,
});
browserSettings.context = {
id: context.id,
persist: true,
};
}
console.log('timezone ', timezone);
console.log('getClosestRegion(timezone)', getClosestRegion(timezone));
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!,
browserSettings,
keepAlive: true,
region: getClosestRegion(timezone),
});
return {
session,
contextId: browserSettings.context?.id,
};
}
async function endSession(sessionId: string) {
const bb = new Browserbase({
apiKey: process.env.BROWSERBASE_API_KEY!,
});
await bb.sessions.update(sessionId, {
projectId: process.env.BROWSERBASE_PROJECT_ID!,
status: 'REQUEST_RELEASE',
});
}
async function getDebugUrl(sessionId: string) {
const bb = new Browserbase({
apiKey: process.env.BROWSERBASE_API_KEY!,
});
const session = await bb.sessions.debug(sessionId);
return session.debuggerFullscreenUrl;
}
export async function POST(request: Request) {
try {
const body = await request.json();
const timezone = body.timezone as string;
const providedContextId = body.contextId as string;
const { session, contextId } = await createSession(
timezone,
providedContextId,
);
const liveUrl = await getDebugUrl(session.id);
return NextResponse.json({
success: true,
sessionId: session.id,
sessionUrl: liveUrl,
contextId,
});
} catch (error) {
console.error('Error creating session:', error);
return NextResponse.json(
{ success: false, error: 'Failed to create session' },
{ status: 500 },
);
}
}
export async function DELETE(request: Request) {
const body = await request.json();
const sessionId = body.sessionId as string;
await endSession(sessionId);
return NextResponse.json({ success: true });
}