| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- import { existsSync } from 'node:fs'
- import { readFile } from 'node:fs/promises'
- import { resolve } from 'node:path'
- /** 生产构建只替换连接配置,保留运行时 JSON 中的 GIS/模型参数。 */
- export function productionRuntimeConfig(raw, env) {
- const keys = ['VITE_COCKPIT_DATA_SOURCE', 'VITE_COCKPIT_API_BASE_URL', 'VITE_COCKPIT_WS_URL']
- for (const key of keys) {
- if (typeof env[key] !== 'string' || !env[key].trim()) throw new Error('缺少生产环境变量:' + key)
- }
- const imageBase = typeof env.VITE_APP_MINIO_BASE_URL === 'string' && env.VITE_APP_MINIO_BASE_URL.trim()
- ? env.VITE_APP_MINIO_BASE_URL.trim() : '/minio/pipe/'
- try {
- const imageUrl = new URL(imageBase, 'http://runtime-image-config.invalid/')
- const publicPath = /^https?:\/\//i.test(imageBase) || (imageBase.startsWith('/') && !imageBase.startsWith('//'))
- if (!publicPath || !['http:', 'https:'].includes(imageUrl.protocol) || imageUrl.username || imageUrl.password || imageUrl.search || imageUrl.hash) throw new Error()
- } catch {
- throw new Error('生产图片访问基址必须是无凭据或签名参数的公开地址或同源路径')
- }
- return {
- ...raw,
- dataSource: env.VITE_COCKPIT_DATA_SOURCE,
- api: { ...raw.api, baseUrl: env.VITE_COCKPIT_API_BASE_URL },
- ws: { ...raw.ws, url: env.VITE_COCKPIT_WS_URL },
- images: { baseUrl: imageBase },
- }
- }
- /** 生产配置来自可复现的部署模板,绝不复制本机 public/cockpit/config.json。 */
- export default function cockpitEnvPlugin() {
- let config
- const readTemplate = async () => productionRuntimeConfig(
- JSON.parse(await readFile(resolve(config.root, 'deploy/cockpit.config.json'), 'utf8')),
- config.env,
- )
- return {
- name: 'cockpit-environment-config',
- configResolved(resolved) { config = resolved },
- configureServer(server) {
- // 干净检出也有完整地图配置;已有本机 JSON 时仍遵循原覆盖规则。
- server.middlewares.use('/cockpit/config.json', async (req, res, next) => {
- if (existsSync(resolve(config.publicDir, 'cockpit/config.json'))) return next()
- try {
- const raw = await readTemplate()
- res.setHeader('Content-Type', 'application/json; charset=utf-8')
- res.setHeader('Cache-Control', 'no-cache')
- res.end(JSON.stringify(raw))
- } catch (error) { next(error) }
- })
- },
- async generateBundle() {
- this.emitFile({
- type: 'asset',
- fileName: 'cockpit/config.json',
- source: JSON.stringify(await readTemplate(), null, 2) + '\n',
- })
- },
- }
- }
|