Compare commits

...

3 Commits

Author SHA1 Message Date
2b34d14e96 Merge pull request #1
fix: production deployment improvements and configuration fixes
2026-01-26 17:21:38 +08:00
1e1563c5f5 Merge branch 'main' into fix/production-deployment-improvements 2026-01-26 17:17:51 +08:00
371f117064 fix: production deployment improvements and configuration fixes
## 主要改动

### 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 <noreply@anthropic.com>
2026-01-26 16:59:02 +08:00
16 changed files with 68 additions and 39 deletions

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -66,7 +66,7 @@ const statusText = computed(() => {
}
.latency {
color: $color-text-muted;
color: #999;
font-size: $font-size-xs;
}

View File

@@ -162,7 +162,7 @@ function delay(ms: number): Promise<void> {
<Postmark
v-if="hasStamp && stampInfo"
:award-name="stampInfo.name"
:award-icon-key="stampedWith"
:award-icon-key="stampedWith!"
:user-name="connectionStore.userName || ''"
color="gold"
class="applied-stamp"

View File

@@ -289,7 +289,7 @@ async function handleEnter() {
.footer {
text-align: center;
padding: $spacing-md;
color: $color-text-muted;
color: #999;
font-size: $font-size-xs;
}
</style>

View File

@@ -263,7 +263,7 @@ async function handleSubmit() {
.footer {
text-align: center;
padding: 16px;
color: $color-text-muted;
color: #999;
font-size: 12px;
}
</style>

View File

@@ -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;
}
}

View File

@@ -1,2 +1,3 @@
VITE_SOCKET_URL=
VITE_API_URL=
VITE_MOBILE_URL=

View File

@@ -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)

View File

@@ -19,9 +19,9 @@ let audioPlayer: HTMLAudioElement | null = null;
// Audio file paths - place audio files in public/audio/
const AUDIO_TRACKS: Record<string, string> = {
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', () => {

View File

@@ -25,7 +25,7 @@ const participantCount = ref(0);
let previousSubPhase: string | null = null;
// Prize configuration - 从服务器加载
const prizes = ref<Array<{ level: string; name: string; winnerCount: number; poolTag?: string }>>([]);
const prizes = ref<Array<{ level: string; name: string; winnerCount: number; poolTag?: string; round?: number }>>([]);
// 从 API 获取奖项配置
async function fetchPrizes() {

View File

@@ -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);

View File

@@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
export default defineConfig({
base: '/screen/',
plugins: [vue()],
resolve: {
alias: {

View File

@@ -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"]