diff --git a/apps/www/.env.example b/apps/www/.env.example index 5fff5e8..66f18bf 100644 --- a/apps/www/.env.example +++ b/apps/www/.env.example @@ -1,6 +1,2 @@ NEXT_PUBLIC_SITE_URL=https://ui.trophy.so NEXT_PUBLIC_GA_MEASUREMENT_ID= - -PROFOUND_CUSTOM_LOGGING_ENABLED=false -PROFOUND_API_KEY= -PROFOUND_LOG_QUERY_PARAM_ALLOWLIST=utm_source,utm_medium,utm_campaign,utm_term,utm_content,utm_id,utm_source_platform,utm_creative_format,utm_marketing_tactic diff --git a/apps/www/app/(app)/(root)/page.tsx b/apps/www/app/(app)/(root)/page.tsx index 009776e..e41cd7e 100644 --- a/apps/www/app/(app)/(root)/page.tsx +++ b/apps/www/app/(app)/(root)/page.tsx @@ -135,7 +135,7 @@ export default function IndexPage() { /> - + {/* Gamification UI Kit */} diff --git a/apps/www/lib/fonts.ts b/apps/www/lib/fonts.ts index 2893e37..5ab28de 100644 --- a/apps/www/lib/fonts.ts +++ b/apps/www/lib/fonts.ts @@ -1,4 +1,4 @@ -import { Geist, Geist_Mono, Montserrat } from "next/font/google" +import { Geist, Geist_Mono, Plus_Jakarta_Sans } from "next/font/google" import { cn } from "@/lib/utils" @@ -13,13 +13,13 @@ const fontMono = Geist_Mono({ weight: ["400"], }) -const fontMontserrat = Montserrat({ +const fontJakartaSans = Plus_Jakarta_Sans({ subsets: ["latin"], - variable: "--font-montserrat", + variable: "--font-jakarta-sans", }) export const fontVariables = cn( fontSans.variable, fontMono.variable, - fontMontserrat.variable + fontJakartaSans.variable ) diff --git a/apps/www/lib/profound/customLogs.ts b/apps/www/lib/profound/customLogs.ts deleted file mode 100644 index 363de52..0000000 --- a/apps/www/lib/profound/customLogs.ts +++ /dev/null @@ -1,180 +0,0 @@ -const PROFOUND_CUSTOM_LOGS_ENDPOINT = - "https://artemis.api.tryprofound.com/v1/logs/custom" -const MAX_BATCH_SIZE = 1000 - -type QueryParams = Record - -export type ProFoundCustomLogEntry = { - timestamp: string | number - method: string - host: string - path: string - status_code: number - ip: string - user_agent: string - query_params?: QueryParams - referer?: string - bytes_sent?: number - duration_ms?: number -} - -type ProfoundSendOptions = { - apiKey?: string - enabled?: boolean - timeoutMs?: number -} - -const DEFAULT_TIMEOUT_MS = 1200 - -type ProcessEnv = Record - -function getProcessEnv(): ProcessEnv | undefined { - const globalWithProcess = globalThis as typeof globalThis & { - process?: { env?: ProcessEnv } - } - return globalWithProcess.process?.env -} - -function truncate(value: string, maxLength: number): string { - if (value.length <= maxLength) return value - return value.slice(0, maxLength) -} - -function toIsoTimestamp(value: string | number): string | number | null { - if (typeof value === "number") { - if (!Number.isFinite(value)) return null - return value - } - - const date = new Date(value) - if (Number.isNaN(date.getTime())) return null - return date.toISOString() -} - -function sanitizeQueryParams( - value: ProFoundCustomLogEntry["query_params"] -): QueryParams | undefined { - if (!value || typeof value !== "object") return undefined - - const entries = Object.entries(value) - .slice(0, 50) - .map(([key, val]) => [ - truncate(String(key), 100), - truncate(String(val), 1000), - ]) - - if (entries.length === 0) return undefined - return Object.fromEntries(entries) -} - -export function sanitizeCustomLogEntry( - entry: ProFoundCustomLogEntry -): ProFoundCustomLogEntry | null { - const timestamp = toIsoTimestamp(entry.timestamp) - if (!timestamp) return null - - const statusCode = Number(entry.status_code) - if (!Number.isInteger(statusCode) || statusCode < 100 || statusCode > 599) { - return null - } - - const method = truncate(String(entry.method || ""), 10) - const host = truncate(String(entry.host || ""), 255) - const path = truncate(String(entry.path || ""), 2048) - const ip = truncate(String(entry.ip || ""), 45) - const userAgent = truncate(String(entry.user_agent || ""), 1024) - - if (!method || !host || !path || !ip || !userAgent) return null - - const sanitized: ProFoundCustomLogEntry = { - timestamp, - method, - host, - path, - status_code: statusCode, - ip, - user_agent: userAgent, - } - - const queryParams = sanitizeQueryParams(entry.query_params) - if (queryParams) sanitized.query_params = queryParams - - if (typeof entry.referer === "string" && entry.referer) { - sanitized.referer = truncate(entry.referer, 2048) - } - - if (typeof entry.bytes_sent === "number" && entry.bytes_sent >= 0) { - sanitized.bytes_sent = entry.bytes_sent - } - - if (typeof entry.duration_ms === "number" && entry.duration_ms >= 0) { - sanitized.duration_ms = entry.duration_ms - } - - return sanitized -} - -function chunkLogs( - entries: ProFoundCustomLogEntry[], - chunkSize: number -): ProFoundCustomLogEntry[][] { - const chunks: ProFoundCustomLogEntry[][] = [] - - for (let index = 0; index < entries.length; index += chunkSize) { - chunks.push(entries.slice(index, index + chunkSize)) - } - - return chunks -} - -export async function sendProfoundCustomLogs( - payload: ProFoundCustomLogEntry | ProFoundCustomLogEntry[], - options: ProfoundSendOptions = {} -): Promise { - const env = getProcessEnv() - const enabled = - options.enabled ?? env?.PROFOUND_CUSTOM_LOGGING_ENABLED === "true" - const apiKey = options.apiKey ?? env?.PROFOUND_API_KEY - - if (!enabled || !apiKey) return - - const entries = Array.isArray(payload) ? payload : [payload] - const sanitizedEntries = entries - .map((entry) => sanitizeCustomLogEntry(entry)) - .filter((entry): entry is ProFoundCustomLogEntry => entry !== null) - - if (sanitizedEntries.length === 0) return - - const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS - const batches = chunkLogs(sanitizedEntries, MAX_BATCH_SIZE) - - await Promise.all( - batches.map(async (batch) => { - const controller = new AbortController() - const timeout = setTimeout(() => controller.abort(), timeoutMs) - - try { - const response = await fetch(PROFOUND_CUSTOM_LOGS_ENDPOINT, { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-api-key": apiKey, - }, - body: JSON.stringify(batch), - signal: controller.signal, - }) - - if (!response.ok) { - console.error("Profound custom logs request failed", { - status: response.status, - statusText: response.statusText, - }) - } - } catch (error) { - console.error("Profound custom logs request error", error) - } finally { - clearTimeout(timeout) - } - }) - ) -} diff --git a/apps/www/proxy.ts b/apps/www/proxy.ts deleted file mode 100644 index 12561a6..0000000 --- a/apps/www/proxy.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { NextRequest, NextResponse, type NextFetchEvent } from "next/server" - -import { sendProfoundCustomLogs } from "@/lib/profound/customLogs" - -const PROFOUND_LOG_HOST = "ui.trophy.so"; - -const SAFE_QUERY_PARAMS = new Set( - (process.env.PROFOUND_LOG_QUERY_PARAM_ALLOWLIST ?? "") - .split(",") - .map((value) => value.trim().toLowerCase()) - .filter(Boolean) -) - -function isSafeQueryParam(key: string): boolean { - return SAFE_QUERY_PARAMS.has(key.toLowerCase()) -} - -function getRequestIp(request: NextRequest): string { - const forwardedFor = request.headers.get("x-forwarded-for") - if (forwardedFor) { - const firstIp = forwardedFor.split(",")[0]?.trim() - if (firstIp) return firstIp - } - - const realIp = request.headers.get("x-real-ip") - if (realIp) return realIp - - return "0.0.0.0" -} - -function getSafeQueryParams(url: URL): Record | undefined { - const safeEntries = Array.from(url.searchParams.entries()).filter(([key]) => - isSafeQueryParam(key) - ) - - if (safeEntries.length === 0) return undefined - return Object.fromEntries(safeEntries) -} - -export function proxy(request: NextRequest, event: NextFetchEvent) { - const response = NextResponse.next() - - const referer = request.headers.get("referer") || undefined - const queryParams = getSafeQueryParams(request.nextUrl) - - event.waitUntil( - sendProfoundCustomLogs({ - timestamp: new Date().toISOString(), - method: request.method, - host: PROFOUND_LOG_HOST, - path: request.nextUrl.pathname, - status_code: response.status, - ip: getRequestIp(request), - user_agent: request.headers.get("user-agent") || "unknown-user-agent", - referer, - query_params: queryParams, - }) - ) - - return response -} - -export const config = { - matcher: [ - "/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js|map)$).*)", - ], -} diff --git a/apps/www/public/PROFOUND-FF985355BC66F445.txt b/apps/www/public/PROFOUND-FF985355BC66F445.txt deleted file mode 100644 index 5145fd7..0000000 --- a/apps/www/public/PROFOUND-FF985355BC66F445.txt +++ /dev/null @@ -1 +0,0 @@ -PROFOUND-FF985355BC66F445 \ No newline at end of file diff --git a/apps/www/public/og.png b/apps/www/public/og.png index d236e61..3cca0c0 100644 Binary files a/apps/www/public/og.png and b/apps/www/public/og.png differ diff --git a/apps/www/styles/globals.css b/apps/www/styles/globals.css index 1b3e863..de861e8 100644 --- a/apps/www/styles/globals.css +++ b/apps/www/styles/globals.css @@ -26,7 +26,7 @@ --breakpoint-4xl: 2000px; --font-sans: var(--font-sans); --font-mono: var(--font-mono); - --font-montserrat: var(--font-montserrat); + --font-jakarta-sans: var(--font-jakarta-sans); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius);