import { eq } from "drizzle-orm" import { db } from "@/lib/db" import { profiles } from "@/lib/db/schema" import { PLAN_LIMITS } from "@/lib/stripe/plans" import type { Plan } from "@/types" export type Branding = { brandName: string | null logoUrl: string | null color: string | null hidePoweredBy: boolean /** True only when the owner's plan includes white-label (landlord/lifetime). */ enabled: boolean } const DEFAULT_BRANDING: Branding = { brandName: null, logoUrl: null, color: null, hidePoweredBy: false, enabled: false, } /** * Loads a landlord's white-label branding for a given owner user id. * * Branding only applies when that owner's plan includes white-label * (landlord/lifetime via PLAN_LIMITS[plan].hasWhiteLabel). A downgraded user's * stored branding is preserved in the DB but `enabled` returns false, so callers * fall back to the default (unbranded) look. Fails safe to unbranded on error. */ export async function getBranding(ownerUserId: string): Promise { if (!ownerUserId) return DEFAULT_BRANDING try { const profile = await db.query.profiles.findFirst({ where: eq(profiles.id, ownerUserId), columns: { plan: true, brand_name: true, brand_logo_url: true, brand_color: true, hide_powered_by: true, }, }) if (!profile) return DEFAULT_BRANDING const plan = (profile.plan ?? "starter") as Plan const enabled = PLAN_LIMITS[plan]?.hasWhiteLabel === true if (!enabled) return DEFAULT_BRANDING return { brandName: profile.brand_name?.trim() || null, logoUrl: profile.brand_logo_url?.trim() || null, color: profile.brand_color?.trim() || null, hidePoweredBy: profile.hide_powered_by === true, enabled: true, } } catch { return DEFAULT_BRANDING } } /** Matches a 6-digit hex color like #RRGGBB (case-insensitive). */ export const HEX_COLOR_RE = /^#[0-9a-fA-F]{6}$/ /** Returns a normalized #RRGGBB hex string, or null if invalid/empty. */ export function normalizeHexColor(input: string | null | undefined): string | null { if (!input) return null const value = input.trim() if (!value) return null return HEX_COLOR_RE.test(value) ? value.toLowerCase() : null }