- Validação cross-tenant no login e rotas protegidas
- File serving via /api/files/{bucket}/{path} (eliminação DNS)
- Mensagens de erro humanizadas inline (sem pop-ups)
- Middleware tenant detection via headers customizados
- Upload de logos retorna URLs via API
- README atualizado com changelog v1.4 completo
80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
/**
|
|
* Server-side API functions
|
|
* Estas funções são executadas APENAS no servidor (não no cliente)
|
|
*/
|
|
|
|
import { cookies, headers } from 'next/headers';
|
|
|
|
const API_BASE_URL = process.env.API_INTERNAL_URL || 'http://backend:8080';
|
|
|
|
interface AgencyBrandingData {
|
|
logo_url?: string;
|
|
primary_color?: string;
|
|
secondary_color?: string;
|
|
name?: string;
|
|
}
|
|
|
|
/**
|
|
* Busca os dados de branding da agência no servidor
|
|
* Usa o subdomínio do request para identificar a agência
|
|
*/
|
|
export async function getAgencyBranding(): Promise<AgencyBrandingData | null> {
|
|
try {
|
|
// Pegar o hostname do request
|
|
const headersList = await headers();
|
|
const hostname = headersList.get('host') || '';
|
|
const subdomain = hostname.split('.')[0];
|
|
|
|
if (!subdomain || subdomain === 'localhost' || subdomain === 'www') {
|
|
return null;
|
|
}
|
|
|
|
// Buscar dados da agência pela API
|
|
const url = `${API_BASE_URL}/api/tenant/config?subdomain=${subdomain}`;
|
|
console.log(`[ServerAPI] Fetching agency config from: ${url}`);
|
|
|
|
const response = await fetch(url, {
|
|
cache: 'no-store', // Sempre buscar dados atualizados
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error(`[ServerAPI] Failed to fetch agency branding for ${subdomain}: ${response.status}`);
|
|
return null;
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log(`[ServerAPI] Agency branding data for ${subdomain}:`, JSON.stringify(data));
|
|
return data as AgencyBrandingData;
|
|
} catch (error) {
|
|
console.error('[ServerAPI] Error fetching agency branding:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Busca apenas o logo da agência (para metadata)
|
|
*/
|
|
export async function getAgencyLogo(): Promise<string | null> {
|
|
const branding = await getAgencyBranding();
|
|
return branding?.logo_url || null;
|
|
}
|
|
|
|
/**
|
|
* Busca as cores da agência (para passar ao client component)
|
|
*/
|
|
export async function getAgencyColors(): Promise<{ primary: string; secondary: string } | null> {
|
|
const branding = await getAgencyBranding();
|
|
|
|
if (branding?.primary_color && branding?.secondary_color) {
|
|
return {
|
|
primary: branding.primary_color,
|
|
secondary: branding.secondary_color,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|