| import { clsx, type ClassValue } from "clsx"; |
| import { twMerge } from "tailwind-merge"; |
|
|
| export function cn(...inputs: ClassValue[]) { |
| return twMerge(clsx(inputs)); |
| } |
|
|
| export function formatDate(date: string | Date | null): string { |
| if (!date) return "β"; |
| const d = typeof date === "string" ? new Date(date) : date; |
| return d.toLocaleString("en-US", { |
| month: "short", |
| day: "numeric", |
| year: "numeric", |
| hour: "2-digit", |
| minute: "2-digit", |
| }); |
| } |
|
|
| export function formatBytes(bytes: number): string { |
| if (bytes === 0) return "0 B"; |
| const k = 1024; |
| const sizes = ["B", "KB", "MB", "GB"]; |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; |
| } |
|
|
| export function truncate(text: string, max: number): string { |
| if (text.length <= max) return text; |
| return text.slice(0, max) + "..."; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function normalizeOrigin(origin: string): string { |
| const envBase = process.env.NEXT_PUBLIC_OAUTH_REDIRECT_BASE; |
| const base = typeof envBase === "string" && envBase.trim() ? envBase : origin; |
| return base.replace(/127\.0\.0\.1/, "localhost").replace(/\/$/, ""); |
| } |
|
|
| |
| |
| |
| |
| export function getRequestOrigin(request: { headers: Headers; nextUrl: URL }): string { |
| const xfHost = request.headers.get("x-forwarded-host"); |
| const xfProto = request.headers.get("x-forwarded-proto") || "https"; |
| const host = xfHost || request.headers.get("host") || request.nextUrl.host; |
| return normalizeOrigin(`${xfProto}://${host}`); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function safeJson<T = any>(text: string | null | undefined): T | null { |
| if (!text) return null; |
| const trimmed = text.trim(); |
| if (!trimmed) return null; |
| |
| if (trimmed[0] === "<") return null; |
| try { |
| return JSON.parse(trimmed) as T; |
| } catch { |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| |
| export async function safeJsonResponse<T = any>(res: Response): Promise<T | null> { |
| const text = await res.text().catch(() => ""); |
| return safeJson<T>(text); |
| } |
|
|