Files
hjc-web/server/utils/tenant.ts
T
gxwebsoft 2b69686795 feat(app): 添加多模板关于我们页面及相关路由和404页面
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
2026-09-08 12:13:44 +08:00

98 lines
2.9 KiB
TypeScript

import type { TenantContext } from '~/app/types'
type RuntimeConfig = ReturnType<typeof useRuntimeConfig>
/**
* 从 Host 解析租户上下文
*
* 策略:
* 1. 如果 host 匹配 `{prefix}-{tenantId}.{baseDomain}` 模式(前缀走白名单、
* 主域走配置清单),从子域名提取 tenantId
* - 白名单前缀:site / shop / store / mp / app / oa(由 subdomainPrefixes 配置)
* - 主域清单:shoplnk.cn / sitelink.cn / wsdns.cn(由 baseDomains 配置)
* 2. 否则返回环境变量默认值(后续可由域名绑定表查询覆盖)
*
* 注意:子域名解析仅为「兜底」来源(优先级低于 appDomain 绑定表与
* app_product.domain),因此后台绑定的合法自定义域名天然优先,不会被子域名覆盖。
*/
export function resolveTenantFromHost(
host: string,
baseDomains: string[] | string,
prefixes: string[] | string,
defaultTenantId: string,
defaultAppId: string,
defaultTemplateId: string
): TenantContext {
const hostname = host.split(':')[0]
// 防御性类型保障:runtimeConfig 序列化可能将数组退化为字符串
const safeDomains = Array.isArray(baseDomains)
? baseDomains
: String(baseDomains || '').split(',').map((s: string) => s.trim()).filter(Boolean)
const safePrefixes = Array.isArray(prefixes)
? prefixes
: String(prefixes || '').split(',').map((s: string) => s.trim()).filter(Boolean)
const domains = (safeDomains && safeDomains.length
? safeDomains
: ['shoplnk.cn']
).map(d => escapeRegex(d.trim())).filter(Boolean).join('|')
const prefixList = (safePrefixes && safePrefixes.length
? safePrefixes
: ['site']
).map(p => escapeRegex(p.trim())).filter(Boolean).join('|')
// 匹配 {site|shop|store|mp|app|oa}-{tenantId}.{baseDomain}
const subdomainPattern = new RegExp(
`^(?:${prefixList})-(\\d+)\\.(?:${domains})$`,
'i'
)
const match = hostname.match(subdomainPattern)
if (match && match[1]) {
return {
tenantId: match[1],
appId: defaultAppId,
templateId: defaultTemplateId,
source: 'subdomain',
host
}
}
// 默认使用环境变量
return {
tenantId: defaultTenantId,
appId: defaultAppId,
templateId: defaultTemplateId,
source: 'env',
host
}
}
/**
* 从 event.context 获取租户上下文
* 如果不存在则回退到运行时配置默认值
*/
export function getTenantFromContext(
event: Parameters<Parameters<typeof defineEventHandler>[0]>[0],
config: RuntimeConfig
): TenantContext {
const ctx = event.context?.tenant as TenantContext | undefined
if (ctx?.tenantId) {
return ctx
}
return {
tenantId: config.public.tenantId as string,
appId: config.public.appId as string,
templateId: config.public.templateId as string,
source: 'env'
}
}
function escapeRegex(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}