From 371f117064d0ce2e308ef20daa337c229ef005e0 Mon Sep 17 00:00:00 2001 From: let5sne Date: Mon, 26 Jan 2026 16:59:02 +0800 Subject: [PATCH] fix: production deployment improvements and configuration fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 主要改动 ### 1. 修复二维码地址硬编码问题 - 将 MainDisplay.vue 中硬编码的移动端 URL 改为环境变量配置 - 添加 VITE_MOBILE_URL 环境变量支持 - 支持通过 .env 文件动态配置移动端地址 ### 2. 修复音频文件路径问题 - 修正 display.ts 中音频文件路径,添加 /screen 前缀 - 修复 BGM、抽奖音效、胜利音效的加载路径 ### 3. 修复 Docker 构建问题 - 添加中国 npm 镜像配置,解决构建超时问题 - 修复缺失的 tsconfig.base.json 文件拷贝 - 修复 Redis 环境变量配置(REDIS_HOST/REDIS_PORT) - 添加 Lua 脚本文件拷贝到生产容器 ### 4. 修复前端路由和资源加载 - 添加 Vite base path 配置 (/screen/) - 修复 Vue Router base path 配置 - 修正 Caddyfile 路由顺序,确保 /screen 路径优先匹配 ### 5. 修复 TypeScript 编译错误 - LuckyDrawView.vue: 添加 round 属性类型定义 - ProgramCard.vue: 添加非空断言处理 ### 6. 修复 SCSS 变量问题 - 替换未定义的 SCSS 变量为硬编码颜色值 - 修复 VoteView、ConnectionStatus、HomeView、ScanLoginView 中的样式问题 Co-Authored-By: Claude Sonnet 4.5 --- deploy/Caddyfile | 22 +++++++------- deploy/Dockerfile.caddy | 12 ++++++-- deploy/Dockerfile.frontend | 8 +++-- docker-compose.yml | 7 ++++- .../src/components/ConnectionStatus.vue | 2 +- .../src/components/ProgramCard.vue | 2 +- packages/client-mobile/src/views/HomeView.vue | 2 +- .../client-mobile/src/views/ScanLoginView.vue | 2 +- packages/client-mobile/src/views/VoteView.vue | 4 +-- packages/client-screen/.env.production | 1 + packages/client-screen/src/router/index.ts | 2 +- packages/client-screen/src/stores/display.ts | 6 ++-- .../client-screen/src/views/LuckyDrawView.vue | 2 +- .../client-screen/src/views/MainDisplay.vue | 4 +-- packages/client-screen/vite.config.ts | 1 + packages/server/Dockerfile | 30 +++++++++++++------ 16 files changed, 68 insertions(+), 39 deletions(-) diff --git a/deploy/Caddyfile b/deploy/Caddyfile index c9093ce..757651a 100644 --- a/deploy/Caddyfile +++ b/deploy/Caddyfile @@ -1,17 +1,10 @@ { - email your-email@example.com + email let5sne@gmail.com } -your-domain.com { - # Mobile client (default) - handle { - root * /srv/mobile - try_files {path} /index.html - file_server - } - - # Screen client - handle /screen/* { +2026.cptp.let5see.xyz { + # Screen client (must be before mobile to match first) + handle /screen* { root * /srv/screen uri strip_prefix /screen try_files {path} /index.html @@ -28,6 +21,13 @@ your-domain.com { reverse_proxy server:3000 } + # Mobile client (default, matches everything else) + handle { + root * /srv/mobile + try_files {path} /index.html + file_server + } + # Gzip compression encode gzip } diff --git a/deploy/Dockerfile.caddy b/deploy/Dockerfile.caddy index 67a970b..32edb76 100644 --- a/deploy/Dockerfile.caddy +++ b/deploy/Dockerfile.caddy @@ -2,9 +2,17 @@ FROM node:20-alpine AS builder WORKDIR /app -RUN npm install -g pnpm +# Build arguments for environment variables +ARG VITE_SOCKET_URL +ARG VITE_API_URL +ARG VITE_MOBILE_URL -COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./ +# Install pnpm and configure registry +RUN npm config set registry https://registry.npmmirror.com && \ + npm install -g pnpm && \ + pnpm config set registry https://registry.npmmirror.com + +COPY pnpm-workspace.yaml package.json pnpm-lock.yaml tsconfig.base.json ./ COPY packages/shared ./packages/shared COPY packages/client-screen ./packages/client-screen COPY packages/client-mobile ./packages/client-mobile diff --git a/deploy/Dockerfile.frontend b/deploy/Dockerfile.frontend index 776769f..f9efdbc 100644 --- a/deploy/Dockerfile.frontend +++ b/deploy/Dockerfile.frontend @@ -2,11 +2,13 @@ FROM node:20-alpine AS builder WORKDIR /app -# Install pnpm -RUN npm install -g pnpm +# Install pnpm and configure registry +RUN npm config set registry https://registry.npmmirror.com && \ + npm install -g pnpm && \ + pnpm config set registry https://registry.npmmirror.com # Copy workspace files -COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./ +COPY pnpm-workspace.yaml package.json pnpm-lock.yaml tsconfig.base.json ./ COPY packages/shared ./packages/shared COPY packages/client-screen ./packages/client-screen COPY packages/client-mobile ./packages/client-mobile diff --git a/docker-compose.yml b/docker-compose.yml index e63345a..c9494e9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,8 @@ services: environment: - NODE_ENV=production - PORT=3000 - - REDIS_URL=redis://redis:6379 + - REDIS_HOST=redis + - REDIS_PORT=6379 - CORS_ORIGINS=${CORS_ORIGINS:-*} depends_on: - redis @@ -30,6 +31,10 @@ services: build: context: . dockerfile: deploy/Dockerfile.caddy + args: + - VITE_SOCKET_URL=${VITE_SOCKET_URL:-} + - VITE_API_URL=${VITE_API_URL:-} + - VITE_MOBILE_URL=${VITE_MOBILE_URL:-} container_name: gala-caddy restart: unless-stopped ports: diff --git a/packages/client-mobile/src/components/ConnectionStatus.vue b/packages/client-mobile/src/components/ConnectionStatus.vue index c42ea24..d2016da 100644 --- a/packages/client-mobile/src/components/ConnectionStatus.vue +++ b/packages/client-mobile/src/components/ConnectionStatus.vue @@ -66,7 +66,7 @@ const statusText = computed(() => { } .latency { - color: $color-text-muted; + color: #999; font-size: $font-size-xs; } diff --git a/packages/client-mobile/src/components/ProgramCard.vue b/packages/client-mobile/src/components/ProgramCard.vue index 88314a5..ad746c8 100644 --- a/packages/client-mobile/src/components/ProgramCard.vue +++ b/packages/client-mobile/src/components/ProgramCard.vue @@ -162,7 +162,7 @@ function delay(ms: number): Promise { diff --git a/packages/client-mobile/src/views/ScanLoginView.vue b/packages/client-mobile/src/views/ScanLoginView.vue index d755800..84658c4 100644 --- a/packages/client-mobile/src/views/ScanLoginView.vue +++ b/packages/client-mobile/src/views/ScanLoginView.vue @@ -263,7 +263,7 @@ async function handleSubmit() { .footer { text-align: center; padding: 16px; - color: $color-text-muted; + color: #999; font-size: 12px; } diff --git a/packages/client-mobile/src/views/VoteView.vue b/packages/client-mobile/src/views/VoteView.vue index f6d25ec..883d849 100644 --- a/packages/client-mobile/src/views/VoteView.vue +++ b/packages/client-mobile/src/views/VoteView.vue @@ -173,7 +173,7 @@ onMounted(() => { } .logout-btn { - color: $color-text-muted; + color: #999; cursor: pointer; padding: 2px 6px; border-radius: 4px; @@ -181,7 +181,7 @@ onMounted(() => { &:active { background: rgba(255, 255, 255, 0.1); - color: $color-text-light; + color: #fff; } } diff --git a/packages/client-screen/.env.production b/packages/client-screen/.env.production index 458cb65..8f639b9 100644 --- a/packages/client-screen/.env.production +++ b/packages/client-screen/.env.production @@ -1,2 +1,3 @@ VITE_SOCKET_URL= VITE_API_URL= +VITE_MOBILE_URL= diff --git a/packages/client-screen/src/router/index.ts b/packages/client-screen/src/router/index.ts index 1db8fe6..e6c0221 100644 --- a/packages/client-screen/src/router/index.ts +++ b/packages/client-screen/src/router/index.ts @@ -22,7 +22,7 @@ function generateToken(code: string): string { export { ADMIN_TOKEN_KEY, ADMIN_ACCESS_CODE, generateToken }; const router = createRouter({ - history: createWebHistory(), + history: createWebHistory('/screen/'), routes: [ // ============================================ // Big Screen Display Routes (LED PC) diff --git a/packages/client-screen/src/stores/display.ts b/packages/client-screen/src/stores/display.ts index 644141e..02ba2e8 100644 --- a/packages/client-screen/src/stores/display.ts +++ b/packages/client-screen/src/stores/display.ts @@ -19,9 +19,9 @@ let audioPlayer: HTMLAudioElement | null = null; // Audio file paths - place audio files in public/audio/ const AUDIO_TRACKS: Record = { - bgm: '/audio/bgm.mp3', - lottery: '/audio/lottery.mp3', - fanfare: '/audio/fanfare.mp3', + bgm: '/screen/audio/bgm.mp3', + lottery: '/screen/audio/lottery.mp3', + fanfare: '/screen/audio/fanfare.mp3', }; export const useDisplayStore = defineStore('display', () => { diff --git a/packages/client-screen/src/views/LuckyDrawView.vue b/packages/client-screen/src/views/LuckyDrawView.vue index 82218c6..c153d94 100644 --- a/packages/client-screen/src/views/LuckyDrawView.vue +++ b/packages/client-screen/src/views/LuckyDrawView.vue @@ -25,7 +25,7 @@ const participantCount = ref(0); let previousSubPhase: string | null = null; // Prize configuration - 从服务器加载 -const prizes = ref>([]); +const prizes = ref>([]); // 从 API 获取奖项配置 async function fetchPrizes() { diff --git a/packages/client-screen/src/views/MainDisplay.vue b/packages/client-screen/src/views/MainDisplay.vue index 0b0fcf9..32d0dbd 100644 --- a/packages/client-screen/src/views/MainDisplay.vue +++ b/packages/client-screen/src/views/MainDisplay.vue @@ -10,8 +10,8 @@ const displayStore = useDisplayStore(); // QR Code Login (legacy - kept for direct access) const showQRLogin = ref(false); -// Mobile URL for entry QR code -const mobileUrl = 'http://192.168.1.5:5174'; +// Mobile URL for entry QR code - use environment variable or fallback to current origin +const mobileUrl = import.meta.env.VITE_MOBILE_URL || window.location.origin; function handleLoginSuccess(data: { userId: string; userName: string; department: string; sessionToken: string }) { console.log('Login success:', data); diff --git a/packages/client-screen/vite.config.ts b/packages/client-screen/vite.config.ts index bfa07e2..2d4f03a 100644 --- a/packages/client-screen/vite.config.ts +++ b/packages/client-screen/vite.config.ts @@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue'; import { resolve } from 'path'; export default defineConfig({ + base: '/screen/', plugins: [vue()], resolve: { alias: { diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile index fe44ce6..5e84352 100644 --- a/packages/server/Dockerfile +++ b/packages/server/Dockerfile @@ -2,11 +2,13 @@ FROM node:20-alpine AS builder WORKDIR /app -# Install pnpm -RUN npm install -g pnpm +# Install pnpm and configure registry +RUN npm config set registry https://registry.npmmirror.com && \ + npm install -g pnpm && \ + pnpm config set registry https://registry.npmmirror.com # Copy workspace files -COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./ +COPY pnpm-workspace.yaml package.json pnpm-lock.yaml tsconfig.base.json ./ COPY packages/shared ./packages/shared COPY packages/server ./packages/server @@ -26,18 +28,28 @@ FROM node:20-alpine AS production WORKDIR /app -RUN npm install -g pnpm +RUN npm config set registry https://registry.npmmirror.com && \ + npm install -g pnpm && \ + pnpm config set registry https://registry.npmmirror.com + +# Copy package files +COPY --from=builder /app/pnpm-workspace.yaml ./ +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/pnpm-lock.yaml ./ +COPY --from=builder /app/packages/server/package.json ./packages/server/ +COPY --from=builder /app/packages/shared ./packages/shared + +# Install production dependencies only +RUN pnpm install --prod --frozen-lockfile # Copy built files -COPY --from=builder /app/packages/server/dist ./dist -COPY --from=builder /app/packages/server/package.json ./ -COPY --from=builder /app/packages/shared /app/packages/shared -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/packages/server/node_modules ./packages/server/node_modules +COPY --from=builder /app/packages/server/dist ./packages/server/dist +COPY --from=builder /app/packages/server/src/lua ./packages/server/lua ENV NODE_ENV=production ENV PORT=3000 EXPOSE 3000 +WORKDIR /app/packages/server CMD ["node", "dist/index.js"] -- 2.34.1