Files
id/legacy-nextjs/lib/branding.ts
2026-05-25 15:14:35 -07:00

839 lines
22 KiB
TypeScript

/**
* Branding configuration fetched from IAM backend based on domain
*
* Each organization can customize:
* - Logo (URL or base64)
* - Colors (primary, secondary, background, text)
* - Login page content (quotes, testimonials)
* - Links (terms, privacy, support)
* - Features (which auth methods to show)
*/
export interface BrandingConfig {
// Organization info
orgId: string
orgName: string
domain: string
// Visual branding
logo: string
logoAlt?: string
favicon?: string
// Color scheme
colors: {
primary: string // Button color, accents
primaryText: string // Text on primary color
background: string // Page background
surface: string // Card/form background
text: string // Primary text
textMuted: string // Secondary text
border: string // Borders
error: string // Error states
}
// Login page content
content: {
title?: string // Main heading
subtitle?: string // Subheading
tagline?: string // Marketing tagline
quotes?: Quote[] // Testimonials/quotes
features?: Feature[] // Feature highlights
}
// Links
links: {
terms?: string
privacy?: string
support?: string
docs?: string
home?: string
}
// Auth features
auth: {
passwordEnabled: boolean
codeEnabled: boolean // Email/SMS code
webauthnEnabled: boolean // Passkeys
faceIdEnabled: boolean
socialProviders: string[] // google, github, etc
}
}
export interface Quote {
text: string
author: string
role?: string
company?: string
avatar?: string
}
export interface Feature {
title: string
description: string
icon?: string
}
// Default Hanzo branding (fallback)
export const defaultBranding: BrandingConfig = {
orgId: 'hanzo',
orgName: 'Hanzo',
domain: 'hanzo.id',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7', // Zinc-200 (monochrome white)
primaryText: '#09090b', // Zinc-950 (dark text on light buttons)
background: '#000000', // Pure black
surface: '#0a0a0a', // Near black
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Start building in seconds',
subtitle: 'Describe your idea and watch AI bring it to life instantly',
tagline: 'AI-powered development',
quotes: [
{
text: 'Hanzo is amazing! It\'s revolutionizing how we build and deploy applications.',
author: 'Developer',
role: 'Software Engineer',
}
],
},
links: {
terms: 'https://hanzo.ai/terms',
privacy: 'https://hanzo.ai/privacy',
support: 'https://hanzo.ai/support',
docs: 'https://docs.hanzo.ai',
home: 'https://hanzo.ai',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
}
// Fetch branding from IAM backend based on domain
export async function getBranding(domain: string): Promise<BrandingConfig> {
const iamUrl = process.env.HANZO_IAM_URL || 'https://api.hanzo.id'
try {
const res = await fetch(`${iamUrl}/api/branding?domain=${encodeURIComponent(domain)}`, {
next: { revalidate: 300 }, // Cache for 5 minutes
})
if (!res.ok) {
console.warn(`Failed to fetch branding for ${domain}, using defaults`)
return defaultBranding
}
const data = await res.json()
return { ...defaultBranding, ...data }
} catch (error) {
console.error(`Error fetching branding for ${domain}:`, error)
return defaultBranding
}
}
// Static branding configs for known domains (can be overridden by IAM)
// Supports both {org}.id format (e.g. lux.id) and id.{domain} format (e.g. id.ad.nexus)
export const staticBranding: Record<string, Partial<BrandingConfig>> = {
'hanzo.id': {
orgId: 'hanzo',
orgName: 'Hanzo',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7', // Zinc-200 (monochrome white)
primaryText: '#09090b', // Zinc-950 (dark text on light buttons)
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
},
'id.hanzo.ai': {
orgId: 'hanzo',
orgName: 'Hanzo',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7', // Zinc-200 (monochrome white)
primaryText: '#09090b', // Zinc-950 (dark text on light buttons)
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
},
'pars.id': {
orgId: 'pars',
orgName: 'Pars Network',
logo: '/logos/pars.svg',
colors: {
primary: '#3b82f6', // Blue
primaryText: '#ffffff',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Welcome to Pars',
subtitle: 'The decentralized network for the next generation',
},
links: {
terms: 'https://pars.network/terms',
privacy: 'https://pars.network/privacy',
support: 'https://pars.network/support',
docs: 'https://pars.network/docs',
home: 'https://pars.network',
},
},
'id.pars.network': {
orgId: 'pars',
orgName: 'Pars Network',
logo: '/logos/pars.svg',
colors: {
primary: '#3b82f6', // Blue
primaryText: '#ffffff',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Welcome to Pars',
subtitle: 'The decentralized network for the next generation',
},
links: {
terms: 'https://pars.network/terms',
privacy: 'https://pars.network/privacy',
support: 'https://pars.network/support',
docs: 'https://pars.network/docs',
home: 'https://pars.network',
},
},
'lux.id': {
orgId: 'lux',
orgName: 'Lux Network',
logo: '/logos/lux.svg',
colors: {
primary: '#e4e4e7', // Zinc-200 (clean white)
primaryText: '#09090b', // Zinc-950 (dark text on light buttons)
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Start deploying in seconds',
subtitle: 'High-performance blockchain infrastructure for the Lux ecosystem',
tagline: 'Lux-powered infrastructure',
quotes: [
{
text: "Lux is fast. We deploy chains in minutes, not weeks.",
author: 'Validator',
role: 'Node Operator',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://lux.network/terms',
privacy: 'https://lux.network/privacy',
support: 'https://lux.network/support',
docs: 'https://docs.lux.network',
home: 'https://lux.network',
},
},
'id.lux.cloud': {
orgId: 'lux',
orgName: 'Lux Network',
logo: '/logos/lux.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Start deploying in seconds',
subtitle: 'High-performance blockchain infrastructure for the Lux ecosystem',
tagline: 'Lux-powered infrastructure',
quotes: [
{
text: "Lux is fast. We deploy chains in minutes, not weeks.",
author: 'Validator',
role: 'Node Operator',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://lux.network/terms',
privacy: 'https://lux.network/privacy',
support: 'https://lux.network/support',
docs: 'https://docs.lux.network',
home: 'https://lux.network',
},
},
'id.lux.network': {
orgId: 'lux',
orgName: 'Lux Network',
logo: '/logos/lux.svg',
colors: {
primary: '#e4e4e7', // Zinc-200 (clean white)
primaryText: '#09090b', // Zinc-950 (dark text on light buttons)
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Start deploying in seconds',
subtitle: 'High-performance blockchain infrastructure for the Lux ecosystem',
tagline: 'Lux-powered infrastructure',
quotes: [
{
text: "Lux is fast. We deploy chains in minutes, not weeks.",
author: 'Validator',
role: 'Node Operator',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://lux.network/terms',
privacy: 'https://lux.network/privacy',
support: 'https://lux.network/support',
docs: 'https://docs.lux.network',
home: 'https://lux.network',
},
},
'id.lux-dev.network': {
orgId: 'lux',
orgName: 'Lux Network (Devnet)',
logo: '/logos/lux.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Lux Devnet',
subtitle: 'Development network — unstable, reset frequently',
tagline: 'Lux devnet infrastructure',
quotes: [
{
text: "Break things fast. Devnet resets nightly.",
author: 'Engineer',
role: 'Infra',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://lux.network/terms',
privacy: 'https://lux.network/privacy',
support: 'https://lux.network/support',
docs: 'https://docs.lux.network',
home: 'https://lux-dev.network',
},
},
'id.lux-test.network': {
orgId: 'lux',
orgName: 'Lux Network (Testnet)',
logo: '/logos/lux.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Lux Testnet',
subtitle: 'Test the full stack before mainnet deploy',
tagline: 'Lux testnet infrastructure',
quotes: [
{
text: "Validator stability tested here for 48h before mainnet promotion.",
author: 'Validator',
role: 'Operator',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://lux.network/terms',
privacy: 'https://lux.network/privacy',
support: 'https://lux.network/support',
docs: 'https://docs.lux.network',
home: 'https://lux-test.network',
},
},
'id-dev.hanzo.ai': {
orgId: 'hanzo',
orgName: 'Hanzo (Dev)',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Hanzo Dev',
subtitle: 'Development environment',
tagline: 'AI-powered development',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
},
'id-test.hanzo.ai': {
orgId: 'hanzo',
orgName: 'Hanzo (Test)',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Hanzo Test',
subtitle: 'Test environment',
tagline: 'AI-powered development',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
},
'zoolabs.id': {
orgId: 'zoo',
orgName: 'Zoo Labs',
logo: '/logos/zoo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Build the future of DeAI',
subtitle: 'Open AI research + decentralized science for everyone',
tagline: 'Open AI research network',
quotes: [
{
text: "Zoo is where bleeding-edge DeAI experiments actually ship.",
author: 'Researcher',
role: 'ML Engineer',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://zoo.ngo/terms',
privacy: 'https://zoo.ngo/privacy',
support: 'https://zoo.ngo/support',
docs: 'https://zoo.ngo/docs',
home: 'https://zoo.ngo',
},
},
'id.zoo.network': {
orgId: 'zoo',
orgName: 'Zoo Labs',
logo: '/logos/zoo.svg',
colors: {
primary: '#22c55e', // Green
primaryText: '#ffffff',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Build the future of DeAI',
subtitle: 'Open AI research + decentralized science for everyone',
tagline: 'Open AI research network',
quotes: [
{
text: "Zoo is where bleeding-edge DeAI experiments actually ship.",
author: 'Researcher',
role: 'ML Engineer',
}
],
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
terms: 'https://zoo.ngo/terms',
privacy: 'https://zoo.ngo/privacy',
support: 'https://zoo.ngo/support',
docs: 'https://zoo.ngo/docs',
home: 'https://zoo.ngo',
},
},
'id.hanzo.network': {
orgId: 'hanzo',
orgName: 'Hanzo (Network)',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Welcome to Hanzo Network',
subtitle: 'Hanzo network identity',
tagline: 'AI-powered infrastructure',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
home: 'https://hanzo.network',
},
},
'id.hanzo-dev.network': {
orgId: 'hanzo',
orgName: 'Hanzo (Devnet)',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Hanzo Devnet',
subtitle: 'Hanzo development network — resets nightly',
tagline: 'Development environment',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
home: 'https://hanzo-dev.network',
},
},
'id.hanzo-test.network': {
orgId: 'hanzo',
orgName: 'Hanzo (Testnet)',
logo: '/logos/hanzo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Hanzo Testnet',
subtitle: 'Hanzo test network — staging before mainnet',
tagline: 'Test environment',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
home: 'https://hanzo-test.network',
},
},
'id.zoo-dev.network': {
orgId: 'zoo',
orgName: 'Zoo Labs (Devnet)',
logo: '/logos/zoo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Zoo Devnet',
subtitle: 'Zoo development network',
tagline: 'Zoo devnet',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
home: 'https://zoo-dev.network',
},
},
'id.zoo-test.network': {
orgId: 'zoo',
orgName: 'Zoo Labs (Testnet)',
logo: '/logos/zoo.svg',
colors: {
primary: '#e4e4e7',
primaryText: '#09090b',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Zoo Testnet',
subtitle: 'Zoo test network',
tagline: 'Zoo testnet',
},
auth: {
passwordEnabled: true,
codeEnabled: true,
webauthnEnabled: true,
faceIdEnabled: true,
socialProviders: ['metamask', 'google', 'github'],
},
links: {
home: 'https://zoo-test.network',
},
},
'zen.id': {
orgId: 'zen',
orgName: 'Zen LM',
logo: '/logos/zen.svg',
colors: {
primary: '#a855f7', // Purple (Zen violet)
primaryText: '#ffffff',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Welcome to Zen',
subtitle: 'Frontier AI models for everyone',
},
links: {
terms: 'https://zenlm.org/terms',
privacy: 'https://zenlm.org/privacy',
support: 'https://zenlm.org/support',
docs: 'https://zenlm.org/docs',
home: 'https://zenlm.org',
},
},
'id.ad.nexus': {
orgId: 'adnexus',
orgName: 'Ad Nexus',
logo: '/logos/adnexus.svg',
logoAlt: 'Ad Nexus',
colors: {
primary: '#8b5cf6', // Purple
primaryText: '#ffffff',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Welcome to Ad Nexus',
subtitle: 'Programmatic advertising platform',
},
links: {
terms: 'https://ad.nexus/terms',
privacy: 'https://ad.nexus/privacy',
support: 'https://ad.nexus/support',
home: 'https://ad.nexus',
},
},
'ad.nexus': {
orgId: 'adnexus',
orgName: 'Ad Nexus',
logo: '/logos/adnexus.svg',
logoAlt: 'Ad Nexus',
colors: {
primary: '#8b5cf6', // Purple
primaryText: '#ffffff',
background: '#000000',
surface: '#0a0a0a',
text: '#ffffff',
textMuted: '#a1a1aa',
border: '#27272a',
error: '#dc2626',
},
content: {
title: 'Welcome to Ad Nexus',
subtitle: 'Programmatic advertising platform',
},
links: {
terms: 'https://ad.nexus/terms',
privacy: 'https://ad.nexus/privacy',
support: 'https://ad.nexus/support',
home: 'https://ad.nexus',
},
},
}
// Resolve domain to branding key
// Handles: exact match, id.{domain} → {domain}, {sub}.{domain} patterns
// Runtime-extensible tenants: deployments can ship additional tenant branding via
// TENANT_BRANDING_JSON env var (a JSON object: { domain: BrandingConfig, ... }).
// Downstream tenants and other white-label deployments can override/add tenants
// without modifying this source.
const ENV_TENANTS: Record<string, Partial<BrandingConfig>> = (() => {
try {
const raw = process.env.TENANT_BRANDING_JSON
if (!raw) return {}
const parsed = JSON.parse(raw)
return typeof parsed === 'object' && parsed !== null ? parsed : {}
} catch {
return {}
}
})()
// Merge static (compile-time) + env (runtime) tenants. Env wins.
Object.assign(staticBranding, ENV_TENANTS)
export function resolveBrandingDomain(host: string): string {
const domain = host.split(':')[0]
// Exact match first
if (staticBranding[domain]) return domain
// Try stripping 'id.' prefix: id.ad.nexus → ad.nexus
if (domain.startsWith('id.')) {
const stripped = domain.slice(3)
if (staticBranding[stripped]) return stripped
}
return domain
}