Files
crawler-plugin/admin-frontend-vue/src/config/app.ts
T
huangzd1997 6f0357e41c task-2(壳层/路由): 冻结 /admin-vue/ base 与 History 路由契约为单一事实源
新增 src/config/app.ts(APP_BASE_PATH/ensureAppBasePath/joinAdminPath),
vite.config base、router createWebHistory、AdminLayout logo 路径统一消费常量,
禁止散落字面量;8 用例覆盖归一化/幂等/空段/单段/非法输入与字面量单源。
2026-09-05 14:43:52 +08:00

39 lines
1.7 KiB
TypeScript

/**
* /admin-vue/ 基准路径与 History 路由约定(任务 2 冻结的单一事实源)。
* Vite base、Vue Router history base、Nginx location 三者必须共用此常量。
*/
export const APP_BASE_PATH = '/admin-vue/'
/** 前端路由一律使用 History 模式,不引入 hash(旧 #tab 兼容不在目标契约内)。 */
export const APP_HISTORY_MODE = 'history' as const
function invalidBaseMessage(value: unknown): string {
return `管理后台 base 路径非法: ${JSON.stringify(value)},必须以 / 开头且形如 /xxx/,不允许空值或 ..`
}
/** 校验并归一化 base 路径:保证以 / 开头、无 ..、无内部连续斜杠、以单个 / 结尾。 */
export function ensureAppBasePath(value: string): string {
if (typeof value !== 'string' || value.length === 0) throw new Error(invalidBaseMessage(value))
if (!value.startsWith('/')) throw new Error(invalidBaseMessage(value))
if (value.includes('//')) throw new Error(invalidBaseMessage(value))
if (value.includes('..')) throw new Error(invalidBaseMessage(value))
return value.endsWith('/') ? value : `${value}/`
}
/** 把后台页面相对路径段拼成绝对 URL(不含 base 前缀的历史兼容子路径段由路由负责)。 */
export function joinAdminPath(...segments: string[]): string {
const parts: string[] = []
for (const segment of segments) {
for (const raw of segment.split('/')) {
const part = raw.trim()
if (!part || part === '.') continue
if (part === '..') {
throw new Error(`admin 路由段不允许出现 ..(越权跳转): ${segments.join('/')}`)
}
parts.push(part)
}
}
if (parts.length === 0) return APP_BASE_PATH
return `${APP_BASE_PATH}${parts.join('/')}`
}