448 lines
16 KiB
TypeScript
448 lines
16 KiB
TypeScript
import wasm from 'vite-plugin-wasm'
|
|
import glsl from 'vite-plugin-glsl'
|
|
import { fileURLToPath } from 'url'
|
|
import { index, uniqueIndex } from 'drizzle-orm'
|
|
|
|
const customSeoConfig: Record<string, unknown> | null = (() => {
|
|
const raw = process.env.NUXT_PUBLIC_SEO_CONFIG
|
|
if (!raw) return null
|
|
try {
|
|
const parsed = JSON.parse(raw)
|
|
if (parsed && typeof parsed === 'object') return parsed as Record<string, unknown>
|
|
return null
|
|
} catch {
|
|
return null
|
|
}
|
|
})()
|
|
|
|
function readNumberEnv(name: string, defaultValue: number): number {
|
|
const raw = process.env[name]
|
|
if (raw === undefined || raw === '') return defaultValue
|
|
const num = Number(raw)
|
|
return Number.isFinite(num) ? num : defaultValue
|
|
}
|
|
|
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
export default defineNuxtConfig({
|
|
compatibilityDate: '2026-01-30',
|
|
future: {
|
|
compatibilityVersion: 4
|
|
},
|
|
srcDir: 'app',
|
|
serverDir: fileURLToPath(new URL('./server', import.meta.url)),
|
|
dir: {
|
|
public: fileURLToPath(new URL('./public', import.meta.url))
|
|
},
|
|
devtools: { enabled: process.env.NODE_ENV === 'development' },
|
|
devServer: {
|
|
host: '0.0.0.0',
|
|
port: 3000
|
|
},
|
|
modules: [
|
|
'@nuxtjs/tailwindcss',
|
|
...((process.env.NODE_ENV === 'development' || process.env.npm_lifecycle_event?.includes('lint'))
|
|
? ['@nuxt/eslint']
|
|
: []),
|
|
...((process.env.PWA_ENABLED || 'false') === 'true'
|
|
? ['@vite-pwa/nuxt']
|
|
: [])
|
|
],
|
|
|
|
pwa: (process.env.PWA_ENABLED || 'false') === 'true'
|
|
? {
|
|
registerType: 'autoUpdate',
|
|
manifest: {
|
|
name: process.env.NUXT_PUBLIC_SITE_TITLE || '鏍″洯骞挎挱绔欑偣姝岀郴缁?,
|
|
short_name: process.env.NUXT_PUBLIC_SITE_TITLE || 'VoiceHub',
|
|
description: process.env.NUXT_PUBLIC_SITE_DESCRIPTION || '鏍″洯骞挎挱绔欑偣姝岀郴缁?,
|
|
display: (process.env.PWA_DISPLAY as any) || 'standalone',
|
|
background_color: process.env.PWA_BACKGROUND_COLOR || '#000000',
|
|
theme_color: process.env.PWA_THEME_COLOR || '#111111',
|
|
icons: [
|
|
{
|
|
src: '/images/logo.png',
|
|
sizes: '192x192',
|
|
type: 'image/png'
|
|
},
|
|
{
|
|
src: '/images/logo.png',
|
|
sizes: '512x512',
|
|
type: 'image/png'
|
|
}
|
|
]
|
|
},
|
|
workbox: {
|
|
navigateFallback: '/',
|
|
globPatterns: ['**/*.{js,css,html,png,jpg,jpeg,svg,ico,webp,woff2}']
|
|
},
|
|
client: {
|
|
installPrompt: true,
|
|
periodicSyncForUpdates: 3600
|
|
},
|
|
devOptions: {
|
|
enabled: process.env.NODE_ENV === 'development'
|
|
}
|
|
}
|
|
: {},
|
|
|
|
// 寮曞叆鍏ㄥ眬CSS
|
|
css: [
|
|
'~/assets/css/variables.css',
|
|
'~/assets/css/components.css',
|
|
'~/assets/css/main.css',
|
|
'~/assets/css/transitions.css',
|
|
'~/assets/css/mobile-admin.css',
|
|
'~/assets/css/print-fix.css',
|
|
'~/assets/css/sf-pro-icons.css'
|
|
],
|
|
|
|
build: {
|
|
transpile: []
|
|
},
|
|
|
|
// 瀹為獙鎬т紭鍖栧姛鑳? experimental: {
|
|
payloadExtraction: true,
|
|
renderJsonPayloads: true,
|
|
viewTransition: true
|
|
},
|
|
|
|
// 閰嶇疆杩愯鏃堕厤缃? runtimeConfig: {
|
|
// 鏈嶅姟鍣ㄧ鏈夐敭锛堜笉浼氭毚闇插埌瀹㈡埛绔級
|
|
jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
|
|
// Redis閰嶇疆锛堝彲閫夛級
|
|
redisUrl: process.env.REDIS_URL || '',
|
|
redisKeyPrefix: process.env.REDIS_KEY_PREFIX || '',
|
|
// Sentry 鏈嶅姟绔厤缃紙涓嶄細鏆撮湶鍒板鎴风锛? sentry: {
|
|
serverDsn: process.env.SENTRY_SERVER_DSN || process.env.SENTRY_DSN || '',
|
|
dsn: process.env.SENTRY_DSN || '',
|
|
errorSampleRate: readNumberEnv('SENTRY_ERROR_SAMPLE_RATE', 1.0),
|
|
tracesSampleRate: readNumberEnv('SENTRY_TRACES_SAMPLE_RATE', 1.0),
|
|
replaySessionSampleRate: readNumberEnv('SENTRY_REPLAY_SESSION_SAMPLE_RATE', 0),
|
|
replayOnErrorSampleRate: readNumberEnv('SENTRY_REPLAY_ON_ERROR_SAMPLE_RATE', 0),
|
|
release: process.env.SENTRY_RELEASE || '',
|
|
environment: process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || 'production'
|
|
},
|
|
// 鍏叡閿紙浼氭毚闇插埌瀹㈡埛绔級
|
|
public: {
|
|
host: process.env.NUXT_PUBLIC_HOST || '',
|
|
apiBase: '/api',
|
|
oauth: {
|
|
github: !!process.env.GITHUB_CLIENT_ID,
|
|
casdoor: !!process.env.CASDOOR_CLIENT_ID,
|
|
google: !!process.env.GOOGLE_CLIENT_ID,
|
|
aggregate: !!process.env.AGGREGATE_OAUTH_APP_ID
|
|
},
|
|
siteTitle: process.env.NUXT_PUBLIC_SITE_TITLE || '鏍″洯骞挎挱绔欑偣姝岀郴缁?,
|
|
siteLogo: process.env.NUXT_PUBLIC_SITE_LOGO || '',
|
|
siteDescription:
|
|
process.env.NUXT_PUBLIC_SITE_DESCRIPTION || '鏍″洯骞挎挱绔欑偣姝岀郴缁?- 璁╀綘鐨勫0闊宠鍚',
|
|
isNetlify: process.env.NETLIFY === 'true',
|
|
// SEO 鑷畾涔夐厤缃紙閫氳繃 NUXT_PUBLIC_SEO_CONFIG 娉ㄥ叆 JSON 瀛楃涓诧級
|
|
seoConfig: customSeoConfig ?? {},
|
|
// 鍥惧舰楠岃瘉鐮佺浉鍏? captcha: {
|
|
enabled: (process.env.CAPTCHA_ENABLED || 'false') === 'true',
|
|
provider: process.env.CAPTCHA_PROVIDER || 'turnstile',
|
|
siteKey: process.env.CAPTCHA_SITE_KEY || ''
|
|
},
|
|
// PWA 鐩稿叧缁撴瀯锛堟湰鍦颁笉鍚敤妯″潡锛屼絾淇濈暀閰嶇疆椤逛互渚夸笂灞傚簲鐢ㄤ娇鐢級
|
|
pwa: {
|
|
enabled: (process.env.PWA_ENABLED || 'false') === 'true',
|
|
display: process.env.PWA_DISPLAY || 'standalone',
|
|
backgroundColor: process.env.PWA_BACKGROUND_COLOR || '#000000',
|
|
themeColor: process.env.PWA_THEME_COLOR || '#111111'
|
|
},
|
|
// 涓婚閰嶇疆
|
|
defaultTheme: process.env.NUXT_PUBLIC_DEFAULT_THEME || 'ClassicLight',
|
|
enabledThemes: (process.env.NUXT_PUBLIC_ENABLED_THEMES || 'ClassicLight,ModernLight')
|
|
.split(',')
|
|
.map((s) => s.trim())
|
|
.filter(Boolean),
|
|
// Sentry 瀹㈡埛绔厤缃紙浠呭叕寮€ DSN 涓庨噰鏍风巼锛? sentry: {
|
|
dsn: process.env.SENTRY_DSN || '',
|
|
tracesSampleRate: readNumberEnv('SENTRY_TRACES_SAMPLE_RATE', 1.0),
|
|
replaySessionSampleRate: readNumberEnv('SENTRY_REPLAY_SESSION_SAMPLE_RATE', 0),
|
|
replayOnErrorSampleRate: readNumberEnv('SENTRY_REPLAY_ON_ERROR_SAMPLE_RATE', 0)
|
|
}
|
|
}
|
|
},
|
|
|
|
// 閰嶇疆鐜鍙橀噺
|
|
app: {
|
|
head: {
|
|
title: process.env.NUXT_PUBLIC_SITE_TITLE || '鏍″洯骞挎挱绔欑偣姝岀郴缁?,
|
|
htmlAttrs: { lang: 'zh-CN' },
|
|
meta: [
|
|
{ charset: 'utf-8' },
|
|
{
|
|
name: 'viewport',
|
|
content:
|
|
'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover'
|
|
},
|
|
{
|
|
name: 'description',
|
|
content:
|
|
process.env.NUXT_PUBLIC_SITE_DESCRIPTION || '鏍″洯骞挎挱绔欑偣姝岀郴缁?- 璁╀綘鐨勫0闊宠鍚'
|
|
},
|
|
// 绉诲姩绔紭鍖? { name: 'theme-color', content: '#111111' },
|
|
{
|
|
name: 'x-default-theme',
|
|
content: process.env.NUXT_PUBLIC_DEFAULT_THEME || 'ClassicLight'
|
|
},
|
|
{
|
|
name: 'x-enabled-themes',
|
|
content: process.env.NUXT_PUBLIC_ENABLED_THEMES || 'ClassicLight,ModernLight'
|
|
},
|
|
{ name: 'apple-mobile-web-app-capable', content: 'yes' },
|
|
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
|
|
{ name: 'apple-mobile-web-app-title', content: 'VoiceHub绠$悊' },
|
|
{ name: 'mobile-web-app-capable', content: 'yes' },
|
|
{ name: 'format-detection', content: 'telephone=no' }
|
|
],
|
|
script: [
|
|
{
|
|
type: 'text/javascript',
|
|
tagPosition: 'head',
|
|
children: [
|
|
`(function(){try{`,
|
|
` var k='voicehub:theme', d=document, r=d.documentElement, s=null, eff=null, def=null, list=[];`,
|
|
` try{`,
|
|
` var mt=d.querySelector('meta[name="x-enabled-themes"]');`,
|
|
` if(mt && mt.content){list=String(mt.content).split(',').map(function(x){return x.replace(/^\\s+|\\s+$/g,'')}).filter(Boolean)}`,
|
|
` }catch(e){list=[]}`,
|
|
` try{`,
|
|
` var md=d.querySelector('meta[name="x-default-theme"]');`,
|
|
` if(md && md.content){def=md.content}`,
|
|
` }catch(e){def=null}`,
|
|
` if(!def){def='ClassicLight'}`,
|
|
` if(list.length===0){list=['ClassicLight','ModernLight']}`,
|
|
` function valid(t){`,
|
|
` if(list.indexOf(t)!==-1){return t}`,
|
|
` if(list.indexOf(def)!==-1){return def}`,
|
|
` return list[0]`,
|
|
` }`,
|
|
` try{s=localStorage.getItem(k)}catch(e){s=null}`,
|
|
` if(!s){s=def}`,
|
|
` else{s=valid(s)}`,
|
|
` if(typeof s==='string' && (s==='System'||s==='system')){eff=window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}`,
|
|
` else if(typeof s==='string'){eff=s.endsWith('Dark')||s==='dark'?'dark':(s.endsWith('Light')||s==='light'?'light':s)}`,
|
|
` else{eff='light'}`,
|
|
` r.setAttribute('data-theme', (typeof s==='string'?s:eff));`,
|
|
` if(eff==='dark'){r.classList.add('dark'); r.classList.remove('light'); r.style.colorScheme='dark'}`,
|
|
` else if(eff==='light'){r.classList.add('light'); r.classList.remove('dark'); r.style.colorScheme='light'}`,
|
|
` else{r.classList.remove('dark','light'); r.style.colorScheme='light dark'}`,
|
|
` try{`,
|
|
` var tm=d.querySelector('meta[name="theme-color"]');`,
|
|
` if(tm){tm.content=(eff==='light'?'#f8fafc':'#111111')}`,
|
|
` }catch(e){}`,
|
|
`}catch(e){}})();`
|
|
].join('')
|
|
}
|
|
],
|
|
link: [
|
|
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
|
{ rel: 'preload', as: 'image', href: '/images/logo.png' },
|
|
{ rel: 'preload', as: 'image', href: '/images/site-logo.png' },
|
|
{ rel: 'preload', as: 'image', href: '/images/school-logo-white.png' },
|
|
// 浼樺厛鍔犺浇甯歌瀛椾綋锛岀‘淇濋〉闈㈠揩閫熸樉绀? {
|
|
rel: 'preload',
|
|
as: 'style',
|
|
href: 'https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Regular.min.css'
|
|
},
|
|
{
|
|
rel: 'stylesheet',
|
|
href: 'https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Regular.min.css'
|
|
},
|
|
// 寤惰繜鍔犺浇鍏朵粬瀛楅噸锛岄伩鍏嶉樆濉為〉闈㈡覆鏌? {
|
|
rel: 'preload',
|
|
as: 'style',
|
|
href: 'https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Medium.min.css',
|
|
onload: "this.onload=null;this.rel='stylesheet'"
|
|
},
|
|
{
|
|
rel: 'preload',
|
|
as: 'style',
|
|
href: 'https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Semibold.min.css',
|
|
onload: "this.onload=null;this.rel='stylesheet'"
|
|
},
|
|
{
|
|
rel: 'preload',
|
|
as: 'style',
|
|
href: 'https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Bold.min.css',
|
|
onload: "this.onload=null;this.rel='stylesheet'"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
|
|
features: {
|
|
inlineStyles: false
|
|
},
|
|
|
|
// TypeScript閰嶇疆
|
|
typescript: {
|
|
strict: true
|
|
},
|
|
|
|
// 鏈嶅姟鍣ㄧ閰嶇疆
|
|
nitro: {
|
|
compressPublicAssets: true,
|
|
preset: process.env.VERCEL
|
|
? 'vercel'
|
|
: process.env.NETLIFY
|
|
? 'netlify'
|
|
: process.env.NITRO_PRESET || 'node-server',
|
|
// 澧炲己閿欒澶勭悊鍜岀ǔ瀹氭€? experimental: {
|
|
wasm: true,
|
|
asyncContext: true
|
|
},
|
|
timing: true,
|
|
// 澧炲姞璇锋眰瓒呮椂鏃堕棿
|
|
routeRules: {
|
|
// 瀹屽叏绂佺敤鎵€鏈堿PI璺敱鐨勭紦瀛橈紝纭繚姣忔閮借姹傛暟鎹簱
|
|
'/api/**': {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0',
|
|
Connection: 'keep-alive'
|
|
}
|
|
},
|
|
// 闈欐€佽祫婧愭枃浠剁紦瀛橀厤缃? '/_nuxt/**': {
|
|
headers: {
|
|
'Cache-Control': 'public, max-age=31536000, immutable'
|
|
}
|
|
},
|
|
'/assets/**': {
|
|
headers: {
|
|
'Cache-Control': 'public, max-age=31536000, immutable'
|
|
}
|
|
},
|
|
'/favicon.ico': {
|
|
headers: {
|
|
'Cache-Control': 'public, max-age=86400'
|
|
}
|
|
},
|
|
// 鍥剧墖銆丆SS銆丣S绛夐潤鎬佽祫婧愮紦瀛? '/**/*.{png,jpg,jpeg,gif,webp,svg,ico}': {
|
|
headers: {
|
|
'Cache-Control': 'public, max-age=31536000, immutable'
|
|
}
|
|
},
|
|
'/**/*.{css,js}': {
|
|
headers: {
|
|
'Cache-Control': 'public, max-age=31536000, immutable'
|
|
}
|
|
},
|
|
// 璁よ瘉鐩稿叧椤甸潰涓嶇紦瀛? '/login': {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0'
|
|
}
|
|
},
|
|
'/dashboard': {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0'
|
|
}
|
|
},
|
|
'/change-password': {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0'
|
|
}
|
|
},
|
|
'/auth/**': {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0'
|
|
}
|
|
},
|
|
'/notification-settings': {
|
|
headers: {
|
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
Pragma: 'no-cache',
|
|
Expires: '0'
|
|
}
|
|
}
|
|
},
|
|
// 鏍规嵁閮ㄧ讲鐜璋冩暣閰嶇疆
|
|
...(process.env.VERCEL
|
|
? {
|
|
// Vercel 鐜锛氫娇鐢ㄦ爣鍑嗛厤缃? }
|
|
: process.env.NETLIFY
|
|
? {
|
|
// Netlify 鐜锛氱‘淇?Drizzle 姝g‘鎵撳寘
|
|
experimental: {
|
|
wasm: true
|
|
}
|
|
}
|
|
: {
|
|
// 鍏朵粬鐜锛氫娇鐢ㄦ爣鍑嗛厤缃? })
|
|
},
|
|
|
|
// Vite 閰嶇疆
|
|
vite: {
|
|
build: {
|
|
chunkSizeWarningLimit: 2000,
|
|
cssCodeSplit: true,
|
|
rollupOptions: {
|
|
maxParallelFileOps: 2,
|
|
output: {
|
|
manualChunks(id) {
|
|
if (id.includes('node_modules/lucide-vue-next')) {
|
|
return 'lucide'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
resolve: {
|
|
alias: [
|
|
{
|
|
find: '@applemusic-like-lyrics/core/style.css',
|
|
replacement: fileURLToPath(
|
|
new URL('./vendor/amll-core/src/styles/index.css', import.meta.url)
|
|
)
|
|
},
|
|
{
|
|
find: '@applemusic-like-lyrics/core',
|
|
replacement: fileURLToPath(new URL('./vendor/amll-core/src/index.ts', import.meta.url))
|
|
}
|
|
]
|
|
},
|
|
plugins: [wasm(), glsl()],
|
|
optimizeDeps: {
|
|
include: ['drizzle-orm'],
|
|
exclude: ['@applemusic-like-lyrics/vue', '@applemusic-like-lyrics/lyric']
|
|
},
|
|
build: {
|
|
sourcemap: false,
|
|
rollupOptions: {
|
|
output: {
|
|
manualChunks(id) {
|
|
if (!id.includes('node_modules')) return
|
|
if (id.includes('lucide-vue-next')) return 'icons'
|
|
if (id.includes('@pixi')) return 'pixi'
|
|
if (id.includes('@applemusic-like-lyrics')) return 'lyric-engine'
|
|
if (id.includes('drizzle-orm') || id.includes('postgres')) return 'database'
|
|
if (id.includes('xlsx') || id.includes('jspdf') || id.includes('jszip')) return 'office'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
// 娣诲姞 WASM 鏀寔閰嶇疆
|
|
assetsInclude: ['**/*.wasm'],
|
|
// SSR閰嶇疆
|
|
ssr: {
|
|
noExternal: process.env.NETLIFY
|
|
? ['drizzle-orm', 'postgres']
|
|
: process.env.VERCEL
|
|
? []
|
|
: ['drizzle-orm', 'postgres']
|
|
}
|
|
}
|
|
})
|