72 lines
2.1 KiB
TypeScript
72 lines
2.1 KiB
TypeScript
import "server-only";
|
|
import { prisma } from "@/lib/db";
|
|
import { getEffectivePlan } from "@/lib/billing/subscription";
|
|
|
|
export interface ActiveBranding {
|
|
brandName: string | null;
|
|
logoUrl: string | null;
|
|
primaryColor: string | null; // hex (#rrggbb)
|
|
removePoweredBy: boolean;
|
|
}
|
|
|
|
/**
|
|
* Resolve the white-label branding that should apply to the current request.
|
|
* Only returns branding when an organization is active AND its plan includes
|
|
* the `custom_branding` feature AND a branding row exists. Otherwise null
|
|
* (the default Podcast Distribution AI look).
|
|
*/
|
|
export async function getActiveBranding(
|
|
userId: string,
|
|
activeOrgId?: string | null
|
|
): Promise<ActiveBranding | null> {
|
|
if (!activeOrgId) return null;
|
|
const { plan } = await getEffectivePlan(userId, activeOrgId);
|
|
if (!plan.features.includes("custom_branding")) return null;
|
|
|
|
const b = await prisma.orgBranding.findUnique({ where: { organizationId: activeOrgId } });
|
|
if (!b) return null;
|
|
return {
|
|
brandName: b.brandName,
|
|
logoUrl: b.logoUrl,
|
|
primaryColor: b.primaryColor,
|
|
removePoweredBy: b.removePoweredBy,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Convert a #rrggbb hex color to an `"H S% L%"` triplet for the `--brand` CSS
|
|
* variable (the app's accent is consumed as `hsl(var(--brand))`). Returns null
|
|
* for anything that isn't a valid 6-digit hex.
|
|
*/
|
|
export function hexToHslTriplet(hex: string | null | undefined): string | null {
|
|
if (!hex) return null;
|
|
const m = /^#?([0-9a-fA-F]{6})$/.exec(hex.trim());
|
|
if (!m) return null;
|
|
const int = parseInt(m[1], 16);
|
|
const r = ((int >> 16) & 255) / 255;
|
|
const g = ((int >> 8) & 255) / 255;
|
|
const b = (int & 255) / 255;
|
|
|
|
const max = Math.max(r, g, b);
|
|
const min = Math.min(r, g, b);
|
|
const l = (max + min) / 2;
|
|
let h = 0;
|
|
let s = 0;
|
|
if (max !== min) {
|
|
const d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
switch (max) {
|
|
case r:
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
break;
|
|
case g:
|
|
h = (b - r) / d + 2;
|
|
break;
|
|
default:
|
|
h = (r - g) / d + 4;
|
|
}
|
|
h /= 6;
|
|
}
|
|
return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
|
|
}
|