- 修复内置模板验证问题(builtin-video-material等) - 添加模板验证工具 lib/template-validator.ts - 创建模板系统优化需求文档(30个详细需求) - 优化上传API的模板验证逻辑 - 改进错误提示信息的清晰度 - 添加字段池和分类管理相关需求 - 完善自定义字段的创建、验证、存储逻辑 - 清理过期的 .specstory 和 .trae 文件
69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
import type { NextRequest } from 'next/server'
|
||
// 尝试加载根目录的可移植部署变量文件(不会覆盖已存在的真实环境变量)
|
||
try {
|
||
// 动态引入,避免打包期间出错
|
||
const { loadPortableEnvIfPresent } = require('./configLoader')
|
||
if (typeof loadPortableEnvIfPresent === 'function') {
|
||
loadPortableEnvIfPresent()
|
||
}
|
||
} catch {}
|
||
|
||
export function resolveBaseUrl(req?: NextRequest): string {
|
||
const appPublicUrl = process.env.APP_PUBLIC_URL
|
||
const nextAuthUrl = process.env.NEXTAUTH_URL
|
||
const isLocal = (url?: string | null) => !!url && /localhost|127\.0\.0\.1/i.test(url)
|
||
|
||
if (appPublicUrl && !isLocal(appPublicUrl)) return appPublicUrl
|
||
|
||
if (req) {
|
||
const forwardedProto = req.headers.get('x-forwarded-proto')
|
||
const forwardedHost = req.headers.get('x-forwarded-host')
|
||
const originHeader = req.headers.get('origin')
|
||
const host = req.headers.get('host')
|
||
const headerBase =
|
||
(forwardedHost && `${forwardedProto || 'http'}://${forwardedHost}`) ||
|
||
(originHeader && !isLocal(originHeader) ? originHeader : null) ||
|
||
(host ? `http://${host}` : null)
|
||
if (headerBase) return headerBase
|
||
}
|
||
|
||
if (nextAuthUrl && !isLocal(nextAuthUrl)) return nextAuthUrl
|
||
return 'http://localhost:3000'
|
||
}
|
||
|
||
export const defaultLogos = {
|
||
// 后台默认使用内置静态资源;如需自定义,可通过环境变量覆盖
|
||
// logo1:浅色 Logo,适合深色背景(导航栏)
|
||
// logo2:深色 Logo,适合浅色背景(登录页)
|
||
header: process.env.DEFAULT_HEADER_LOGO_URL || '/logo/logo1.png',
|
||
login: process.env.DEFAULT_LOGIN_LOGO_URL || '/logo/logo2.png',
|
||
}
|
||
|
||
export const publicDefaults = {
|
||
// 前端默认 Logo,与后端 defaultLogos 保持一致,方便通过 env 统一覆盖
|
||
header: process.env.NEXT_PUBLIC_DEFAULT_HEADER_LOGO_URL || '/logo/logo1.png',
|
||
login: process.env.NEXT_PUBLIC_DEFAULT_LOGIN_LOGO_URL || '/logo/logo2.png',
|
||
intranetUrl: process.env.NEXT_PUBLIC_INTRANET_URL || '',
|
||
}
|
||
|
||
export function buildAbsoluteUrl(path: string, req?: NextRequest): string {
|
||
const base = resolveBaseUrl(req)
|
||
// 确保 base 不为空,避免返回 "http://"
|
||
if (!base || base === 'http://' || base === 'https://') {
|
||
return path.startsWith('http://') || path.startsWith('https://')
|
||
? path
|
||
: `http://localhost:3000${path.startsWith('/') ? path : `/${path}`}`
|
||
}
|
||
if (!path) return base
|
||
if (path.startsWith('http://') || path.startsWith('https://')) return path
|
||
return `${base}${path.startsWith('/') ? path : `/${path}`}`
|
||
}
|
||
|
||
// Feature flags (can be overridden via SystemSettings in future)
|
||
export const featureFlags = {
|
||
aiEditEnabled: (process.env.AI_EDIT_ENABLED || 'false').toLowerCase() === 'true',
|
||
tunnelEnabled: (process.env.TUNNEL_ENABLED || 'false').toLowerCase() === 'true',
|
||
}
|
||
|
||
|