2b69686795
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引 - 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理 - 实现/article、/case、/product及/page动态路由兼容列表与详情展示 - 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置 - 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持 - 模板增强支持CMS单页内容加载及SEO信息动态设置 - 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
98 lines
2.9 KiB
TypeScript
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, '\\$&')
|
|
}
|