diff --git a/apps/scandic-web/app/api/web/warmup/route.ts b/apps/scandic-web/app/api/web/warmup/route.ts index ea6bfe219..c6933b4d1 100644 --- a/apps/scandic-web/app/api/web/warmup/route.ts +++ b/apps/scandic-web/app/api/web/warmup/route.ts @@ -16,6 +16,7 @@ export async function GET(req: NextRequest) { const key = url.searchParams.get("key") if (!isAuthroized(req)) { + logger.error("Unauthorized warmup request") return unauthorizedResponse() } @@ -25,7 +26,7 @@ export async function GET(req: NextRequest) { return invalidKeyResponse(key) } - logger.debug("Warming up:", key) + logger.info("Warming up:", key) const warmupResult = await warmup(key) const executionTime = performance.now() - executionStart @@ -61,7 +62,7 @@ function warmupCompletedResponse( key: string, executionTime: number ) { - logger.debug(`Warmup completed: ${key} in ${executionTime.toFixed(2)}ms`) + logger.info(`Warmup completed: ${key} in ${executionTime.toFixed(2)}ms`) return NextResponse.json( { ...warmupResult, @@ -77,7 +78,7 @@ function warmupSkippedResponse( key: string, executionTime: number ) { - logger.debug("Warmup skipped:", key) + logger.info("Warmup skipped:", key) return NextResponse.json( { diff --git a/apps/scandic-web/netlify/functions/warmup-background.mts b/apps/scandic-web/netlify/functions/warmup-background.mts index 64ca5688e..7d3014590 100644 --- a/apps/scandic-web/netlify/functions/warmup-background.mts +++ b/apps/scandic-web/netlify/functions/warmup-background.mts @@ -1,19 +1,50 @@ -import crypto from "crypto" +import crypto from "node:crypto" + +import Sentry from "@sentry/nextjs" import jwt from "jsonwebtoken" -import { createLogger } from "@scandic-hotels/common/logger/createLogger" -import { safeTry } from "@scandic-hotels/common/utils/safeTry" +import { type WarmupFunctionsKey } from "@/services/warmup/warmupKeys" -import { - type WarmupFunctionsKey, - warmupKeys, -} from "@/services/warmup/warmupKeys" -import { timeout } from "@/utils/timeout" +import { configureSentry } from "../utils/initSentry" +import { safeTry } from "../utils/safeTry" import type { Config, Context } from "@netlify/functions" -async function WarmupHandler(request: Request, context: Context) { - const warmupLogger = createLogger("warmup") +await configureSentry() + +export const config: Config = { + method: "POST", +} +const warmupLogger = { + info: (message: string, ...args: unknown[]) => { + // eslint-disable-next-line no-console + console.log(`[WARMUP] ${message}`, ...args) + Sentry.logger.info(`[WARMUP] ${message}`, { ...args }) + }, + warn: (message: string, ...args: unknown[]) => { + // eslint-disable-next-line no-console + console.warn(`[WARMUP] ${message}`, ...args) + Sentry.logger.warn(`[WARMUP] ${message}`, { ...args }) + }, + error: (message: string, ...args: unknown[]) => { + // eslint-disable-next-line no-console + console.error(`[WARMUP] ${message}`, ...args) + Sentry.logger.error(`[WARMUP] ${message}`, { ...args }) + }, +} + +const langs = ["en", "sv", "no", "fi", "da", "de"] as const +export const warmupKeys = [ + ...langs.map((lang) => `countries_${lang}` as const), + "hotelsByCountry", + ...langs.map((lang) => `hotelData_${lang}` as const), + ...langs.map((lang) => `autoComplete_${lang}` as const), +] satisfies WarmupFunctionsKey[] + +export default async function WarmupHandler( + request: Request, + context: Context +) { const [_, error] = await safeTry(validateRequest(request, context)) if (error) { @@ -42,13 +73,11 @@ async function validateRequest( request: Request, context: Context ): Promise { - const warmupLogger = createLogger("warmup") if (request.method !== "POST") { throw new Error(ErrorCodes.METHOD_NOT_ALLOWED) } const warmupEnabled = - true || process.env.WARMUP_ENABLED === "true" || Netlify.env.get("WARMUP_ENABLED") === "true" @@ -61,7 +90,6 @@ async function validateRequest( } const body = await request.text() - warmupLogger.info("Warmup body", body) const deployment = JSON.parse(body) as DeploymentInfo if (!deployment) { throw new Error(ErrorCodes.UNABLE_TO_PARSE_DEPLOYMENT_INFO) @@ -76,7 +104,6 @@ async function validateRequest( throw new Error(ErrorCodes.REQUEST_NOT_FOR_CURRENT_CONTEXT) } - warmupLogger.info("Warmup request", deployment) let signature: string try { const headerValue = request.headers.get("x-webhook-signature") @@ -103,7 +130,6 @@ async function validateRequest( } export async function performWarmup(context: Context) { - const warmupLogger = createLogger("warmup") for (const key of warmupKeys) { warmupLogger.info("Warming up cache", key) await callWarmup(key, context) @@ -112,16 +138,12 @@ export async function performWarmup(context: Context) { } } -async function callWarmup(key: WarmupFunctionsKey, context: Context) { - const warmupLogger = createLogger("warmup") +async function callWarmup(key: (typeof warmupKeys)[number], context: Context) { const baseUrl = context.url.origin - const url = new URL("/api/web/warmup", baseUrl) url.searchParams.set("key", key) - const warmupToken = process.env.WARMUP_TOKEN || Netlify.env.get("WARMUP_TOKEN") - try { const response = await fetch(url, { headers: { @@ -130,7 +152,6 @@ async function callWarmup(key: WarmupFunctionsKey, context: Context) { }, signal: AbortSignal.timeout(30_000), }) - if (!response.ok) { warmupLogger.error( `Warmup failed '${url.href}' with error: ${response.status}: ${response.statusText}` @@ -143,13 +164,7 @@ async function callWarmup(key: WarmupFunctionsKey, context: Context) { } } -export default WarmupHandler -export const config: Config = { - method: "POST", -} - async function validateSignature(token: string, buffer: string) { - const warmupLogger = createLogger("warmup") try { const secret = process.env.WARMUP_SIGNATURE_SECRET || @@ -218,14 +233,14 @@ type DeploymentInfo = { updated_at: string user_id: string error_message: string | null - required: any[] - required_functions: any[] + required: unknown[] + required_functions: unknown[] commit_ref: string review_id: string | null branch: string commit_url: string - skipped: any - locked: any + skipped: unknown + locked: unknown title: string commit_message: string | null review_url: string | null @@ -235,10 +250,10 @@ type DeploymentInfo = { available_functions: unknown[] screenshot_url: string | null committer: string - skipped_log: any + skipped_log: unknown manual_deploy: boolean plugin_state: string - lighthouse_plugin_scores: any + lighthouse_plugin_scores: unknown links: { permalink: string alias: string @@ -253,7 +268,7 @@ type DeploymentInfo = { }[] public_repo: boolean pending_review_reason: string | null - lighthouse: any + lighthouse: unknown edge_functions_present: boolean expires_at: string | null blobs_region: string @@ -273,3 +288,7 @@ const ErrorCodes = { WARMUP_DISABLED: "WARMUP IS DISABLED", } as const type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes] + +function timeout(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} diff --git a/apps/scandic-web/netlify/utils/hoteldata.ts b/apps/scandic-web/netlify/utils/hoteldata.ts deleted file mode 100644 index 8b04f6f44..000000000 --- a/apps/scandic-web/netlify/utils/hoteldata.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createLogger } from "@scandic-hotels/common/logger/createLogger" - -import type { Lang } from "@scandic-hotels/common/constants/language" - -export async function warmupHotelDataOnLang(lang: Lang) { - const warmupHotelDataOnLangLogger = createLogger("warmupHotelDataOnLang") - const PUBLIC_URL = Netlify.env.get("PUBLIC_URL") - - warmupHotelDataOnLangLogger.info( - `[WARMUP] Started warmup cache hoteldata for language ${lang} at: ${new Date().toISOString()}!` - ) - - try { - const hotelsResponse = await fetch( - `${PUBLIC_URL}/api/hoteldata?lang=${lang}`, - { - headers: { cache: "no-store" }, - signal: AbortSignal.timeout(30_000), - } - ) - - if (!hotelsResponse.ok) { - throw new Error( - `[WARMUP] Failed to warmup cache for hotels on language ${lang} with error: ${hotelsResponse.statusText}` - ) - } - - const hotels = await hotelsResponse.json() - warmupHotelDataOnLangLogger.info( - `[WARMUP] Retrieved ${hotels.length} hotels.` - ) - } catch (error) { - warmupHotelDataOnLangLogger.error( - `[WARMUP] Error warming cache with hoteldata on language ${lang} with error: ${error}` - ) - } -} diff --git a/apps/scandic-web/netlify/utils/initSentry.ts b/apps/scandic-web/netlify/utils/initSentry.ts new file mode 100644 index 000000000..b7a3bacd9 --- /dev/null +++ b/apps/scandic-web/netlify/utils/initSentry.ts @@ -0,0 +1,21 @@ +import Sentry from "@sentry/nextjs" + +export const denyUrls: (string | RegExp)[] = [ + // Ignore preview urls + /\/.{2}\/preview\//, +] + +export const onRequestError = Sentry.captureRequestError + +export async function configureSentry() { + const sentryEnvironment = Netlify.env.get("SENTRY_ENVIRONMENT") + const sampleRate = Number(Netlify.env.get("SENTRY_SERVER_SAMPLERATE") ?? 0.01) + Sentry.init({ + dsn: "https://fe39c070b4154e2f9cc35f0e5de0aedb@o4508102497206272.ingest.de.sentry.io/4508102500286544", + environment: sentryEnvironment, + enabled: sentryEnvironment !== "development", + tracesSampleRate: sampleRate, + denyUrls: denyUrls, + enableLogs: true, + }) +} diff --git a/apps/scandic-web/netlify/utils/safeTry.ts b/apps/scandic-web/netlify/utils/safeTry.ts new file mode 100644 index 000000000..5581fbe65 --- /dev/null +++ b/apps/scandic-web/netlify/utils/safeTry.ts @@ -0,0 +1,11 @@ +export type SafeTryResult = Promise< + [T, undefined] | [undefined, Error | unknown] +> + +export async function safeTry(func: Promise): SafeTryResult { + try { + return [await func, undefined] as const + } catch (err) { + return [undefined, err instanceof Error ? err : (err as unknown)] as const + } +}