forked from wangziqi/gongxue-base
feat: scaffold supabase multi-tenant backend
This commit is contained in:
108
apps/api/src/features/tenant/routes.ts
Normal file
108
apps/api/src/features/tenant/routes.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { config } from '../../core/config.js';
|
||||
import { queryOne } from '../../core/db.js';
|
||||
import { getHeader, type RequestContext } from '../../core/http.js';
|
||||
|
||||
interface TenantResolveRow {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
status: string;
|
||||
mode: string;
|
||||
host: string | null;
|
||||
brand_name: string | null;
|
||||
short_name: string | null;
|
||||
slogan: string | null;
|
||||
logo_url: string | null;
|
||||
favicon_url: string | null;
|
||||
service_wechat: string | null;
|
||||
service_account_name: string | null;
|
||||
theme: Record<string, unknown>;
|
||||
feature_flags: Record<string, unknown>;
|
||||
admin_feature_flags: Record<string, unknown>;
|
||||
public_config: Record<string, unknown>;
|
||||
}
|
||||
|
||||
function normalizeHost(host: string) {
|
||||
return host.split(':')[0]?.trim().toLowerCase() || '';
|
||||
}
|
||||
|
||||
export async function resolveTenantRoute(ctx: RequestContext) {
|
||||
const hostParam = ctx.url.searchParams.get('host') || '';
|
||||
const tenantCode = ctx.url.searchParams.get('tenantCode') || getHeader(ctx.req, 'x-tenant-code');
|
||||
const requestHost = normalizeHost(hostParam || getHeader(ctx.req, 'x-forwarded-host') || getHeader(ctx.req, 'host'));
|
||||
|
||||
const row = tenantCode
|
||||
? await queryOne<TenantResolveRow>(
|
||||
`
|
||||
select t.id, t.slug, t.name, t.status, t.mode,
|
||||
null::text as host,
|
||||
b.brand_name, b.short_name, b.slogan, b.logo_url, b.favicon_url,
|
||||
b.service_wechat, b.service_account_name, coalesce(b.theme, '{}'::jsonb) as theme,
|
||||
coalesce(s.feature_flags, '{}'::jsonb) as feature_flags,
|
||||
coalesce(s.admin_feature_flags, '{}'::jsonb) as admin_feature_flags,
|
||||
coalesce(s.public_config, '{}'::jsonb) as public_config
|
||||
from public.tenants t
|
||||
left join public.tenant_branding b on b.tenant_id = t.id
|
||||
left join public.tenant_settings s on s.tenant_id = t.id
|
||||
where t.slug = $1 and t.status = 'active'
|
||||
limit 1
|
||||
`,
|
||||
[tenantCode],
|
||||
)
|
||||
: await queryOne<TenantResolveRow>(
|
||||
`
|
||||
select t.id, t.slug, t.name, t.status, t.mode,
|
||||
d.host::text,
|
||||
b.brand_name, b.short_name, b.slogan, b.logo_url, b.favicon_url,
|
||||
b.service_wechat, b.service_account_name, coalesce(b.theme, '{}'::jsonb) as theme,
|
||||
coalesce(s.feature_flags, '{}'::jsonb) as feature_flags,
|
||||
coalesce(s.admin_feature_flags, '{}'::jsonb) as admin_feature_flags,
|
||||
coalesce(s.public_config, '{}'::jsonb) as public_config
|
||||
from public.tenant_domains d
|
||||
join public.tenants t on t.id = d.tenant_id
|
||||
left join public.tenant_branding b on b.tenant_id = t.id
|
||||
left join public.tenant_settings s on s.tenant_id = t.id
|
||||
where d.host = $1 and d.status = 'active' and t.status = 'active'
|
||||
limit 1
|
||||
`,
|
||||
[requestHost || 'localhost'],
|
||||
);
|
||||
|
||||
if (!row && requestHost !== 'localhost') {
|
||||
ctx.url.searchParams.set('tenantCode', config.defaultTenantSlug);
|
||||
return resolveTenantRoute(ctx);
|
||||
}
|
||||
|
||||
if (!row) {
|
||||
return {
|
||||
found: false,
|
||||
message: 'Tenant not found',
|
||||
lookup: { host: requestHost, tenantCode: tenantCode || null },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
found: true,
|
||||
tenant: {
|
||||
id: row.id,
|
||||
slug: row.slug,
|
||||
name: row.name,
|
||||
status: row.status,
|
||||
mode: row.mode,
|
||||
host: row.host,
|
||||
},
|
||||
branding: {
|
||||
brandName: row.brand_name || row.name,
|
||||
shortName: row.short_name || row.name,
|
||||
slogan: row.slogan || '',
|
||||
logoUrl: row.logo_url || '',
|
||||
faviconUrl: row.favicon_url || '',
|
||||
serviceWechat: row.service_wechat || '',
|
||||
serviceAccountName: row.service_account_name || '',
|
||||
theme: row.theme || {},
|
||||
},
|
||||
features: row.feature_flags || {},
|
||||
adminFeatures: row.admin_feature_flags || {},
|
||||
publicConfig: row.public_config || {},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user