修复打包

This commit is contained in:
小朱
2025-07-08 19:11:20 +08:00
parent 58e09fe048
commit 0f9b2a7b1f
21 changed files with 1149 additions and 95 deletions

View File

@@ -1,40 +1,70 @@
# GSM3 游戏服务管理面板环境配置
# GSM3 游戏服务管理系统配置
# 服务器配置
PORT=3000
HOST=localhost
# 服务器端口配置
# 后端API服务端口
SERVER_PORT=3001
# 前端开发服务端口(仅开发环境使用)
CLIENT_PORT=5173
# 环境配置
NODE_ENV=production
# 前端构建路径
CLIENT_BUILD_PATH=./public
# 数据存储路径
DATA_PATH=./data
# 日志配置
LOG_LEVEL=info
LOG_PATH=./logs
# JWT配置
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=24h
# 认证配置
MAX_LOGIN_ATTEMPTS=5
LOCKOUT_DURATION=900000
SESSION_TIMEOUT=86400000
# CORS配置
CORS_ORIGIN=http://localhost:3000
# 允许的前端访问地址,生产环境请修改为实际域名
CORS_ORIGIN=*
# Socket.IO配置
SOCKET_CORS_ORIGIN=*
# 数据目录
DATA_DIR=./data
# 日志目录
LOG_DIR=./logs
# PTY配置
PTY_TIMEOUT=1800000
PTY_MAX_SESSIONS=10
# 游戏服务器配置
GAME_DATA_PATH=./data/games
GAME_BACKUPS_PATH=./data/backups
GAME_MAX_INSTANCES=5
GAME_DATA_DIR=./data/games
# 文件上传配置
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=100MB
# 系统监控配置
SYSTEM_MONITOR_INTERVAL=5000
SYSTEM_STATS_HISTORY_SIZE=720
# 告警配置
ALERT_CPU_WARNING=70
ALERT_CPU_CRITICAL=90
ALERT_MEMORY_WARNING=80
ALERT_MEMORY_CRITICAL=95
ALERT_DISK_WARNING=85
ALERT_DISK_CRITICAL=95
# Java配置用于Minecraft服务器
JAVA_HOME=
JAVA_OPTS=-Xmx2G -Xms1G
# 安全配置
RATE_LIMIT_WINDOW=900000
RATE_LIMIT_MAX=100
SESSION_SECRET=your-secret-key-here
JWT_SECRET=your-jwt-secret-here
# 备份配置
BACKUP_ENABLED=true
BACKUP_INTERVAL=86400000
BACKUP_RETENTION=7
# 网络配置
MAX_UPLOAD_SIZE=100mb
REQUEST_TIMEOUT=30000
# 说明:
# 1. 修改 SERVER_PORT 可以更改后端服务端口
# 2. 生产环境部署时,请将 CORS_ORIGIN 和 SOCKET_CORS_ORIGIN 设置为实际的前端访问地址
# 3. 请务必修改 SESSION_SECRET 和 JWT_SECRET 为随机字符串
# 4. 根据服务器配置调整 JAVA_OPTS 中的内存设置

13
client/.env.example Normal file
View File

@@ -0,0 +1,13 @@
# 前端环境配置
# 后端服务端口(生产环境使用)
VITE_SERVER_PORT=3001
# 前端开发服务端口(仅开发环境使用)
VITE_CLIENT_PORT=5173
# 说明:
# 1. VITE_SERVER_PORT 用于配置前端连接的后端服务端口
# 2. VITE_CLIENT_PORT 用于配置前端开发服务器端口
# 3. 生产环境部署时,前端会自动根据当前页面地址和 VITE_SERVER_PORT 连接后端
# 4. 开发环境时Vite 会使用代理转发请求到后端服务

View File

@@ -0,0 +1,45 @@
// 前端配置文件
// 获取后端服务端口
function getServerPort(): string {
// 优先从环境变量读取Vite会自动注入VITE_开头的环境变量
if (import.meta.env.VITE_SERVER_PORT) {
return import.meta.env.VITE_SERVER_PORT
}
// 从URL参数读取
const urlParams = new URLSearchParams(window.location.search)
const portParam = urlParams.get('server_port')
if (portParam) {
return portParam
}
// 默认端口
return '3001'
}
// 获取完整的服务器URL
function getServerUrl(): string {
const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:'
const hostname = window.location.hostname
const port = getServerPort()
return `${protocol}//${hostname}:${port}`
}
// 获取API基础URL
function getApiBaseUrl(): string {
return `${getServerUrl()}/api`
}
// 导出配置
export const config = {
serverPort: getServerPort(),
serverUrl: getServerUrl(),
apiBaseUrl: getApiBaseUrl(),
// 其他配置
requestTimeout: 30000,
socketTimeout: 20000,
}
export default config

View File

@@ -1,5 +1,6 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { LoginRequest, LoginResponse, ApiResponse, User } from '@/types'
import config from '@/config'
class ApiClient {
private client: AxiosInstance
@@ -7,8 +8,8 @@ class ApiClient {
constructor() {
this.client = axios.create({
baseURL: 'http://localhost:3001/api',
timeout: 30000,
baseURL: config.apiBaseUrl,
timeout: config.requestTimeout,
headers: {
'Content-Type': 'application/json',
},

View File

@@ -1,5 +1,6 @@
import { io, Socket } from 'socket.io-client'
import { SocketEvents } from '@/types'
import config from '@/config'
class SocketClient {
private socket: Socket | null = null
@@ -15,12 +16,12 @@ class SocketClient {
private connect() {
const token = localStorage.getItem('gsm3_token')
this.socket = io('http://localhost:3001', {
this.socket = io(config.serverUrl, {
auth: {
token,
},
transports: ['websocket', 'polling'],
timeout: 20000,
timeout: config.socketTimeout,
forceNew: true,
})

11
client/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_SERVER_PORT?: string
readonly VITE_CLIENT_PORT?: string
// 更多环境变量可以在这里添加
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

View File

@@ -15,10 +15,10 @@
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"strict": false,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": false,
/* Path mapping */
"baseUrl": ".",

View File

@@ -2,6 +2,19 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
// 获取后端服务端口
const getServerPort = () => {
return process.env.VITE_SERVER_PORT || process.env.SERVER_PORT || '3001'
}
// 获取前端开发端口
const getClientPort = () => {
return parseInt(process.env.VITE_CLIENT_PORT || process.env.CLIENT_PORT || '5173', 10)
}
const serverPort = getServerPort()
const clientPort = getClientPort()
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
@@ -11,14 +24,14 @@ export default defineConfig({
},
},
server: {
port: 3000,
port: clientPort,
proxy: {
'/api': {
target: 'http://localhost:3001',
target: `http://localhost:${serverPort}`,
changeOrigin: true,
},
'/socket.io': {
target: 'http://localhost:3001',
target: `http://localhost:${serverPort}`,
changeOrigin: true,
ws: true,
},

905
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,30 @@ async function createPackage() {
path.join(packageDir, 'server', 'PTY')
)
// 复制环境变量配置文件
await fs.copy(
path.join(__dirname, '..', 'server', '.env.example'),
path.join(packageDir, 'server', '.env.example')
)
await fs.copy(
path.join(__dirname, '..', '.env.example'),
path.join(packageDir, '.env.example')
)
console.log('📥 安装服务端生产依赖...')
// 在打包的服务端目录中安装生产依赖
try {
execSync('npm install --production --omit=dev', {
cwd: path.join(packageDir, 'server'),
stdio: 'inherit'
})
console.log('✅ 服务端依赖安装完成')
} catch (error) {
console.error('❌ 服务端依赖安装失败:', error)
throw error
}
console.log('🎨 复制前端文件...')
// 复制前端构建文件
await fs.copy(
@@ -48,7 +72,7 @@ async function createPackage() {
const startScript = `@echo off
echo 正在启动GSM3管理面板...
cd server
node index.js
node_app.exe index.js
pause`
await fs.writeFile(
@@ -60,7 +84,7 @@ pause`
const startShScript = `#!/bin/bash
echo "正在启动GSM3管理面板..."
cd server
node index.js`
/opt/node-v22.17.0-linux-x64/bin/node index.js`
await fs.writeFile(
path.join(packageDir, 'start.sh'),
@@ -81,20 +105,29 @@ node index.js`
## 安装说明
1. 确保已安装 Node.js (版本 >= 18)
2. 进入 server 目录
3. 运行 \`npm install --production\` 安装依赖
4. 复制 .env.example 为 .env 并配置相关参数
5. 运行启动脚本:
2. 解压缩包到目标目录
3. (可选) 配置端口和其他参数:
- 复制 .env.example 为 .env 并修改 SERVER_PORT 等配置
- 复制 server/.env.example 为 server/.env 并配置详细参数
4. 运行启动脚本:
- Windows: 双击 start.bat
- Linux/Mac: 运行 ./start.sh
## 默认访问地址
http://localhost:3000
http://localhost:3001
## 端口配置
- 修改根目录 .env 文件中的 SERVER_PORT 可以更改服务端口
- 修改后需要重启服务才能生效
- 确保防火墙允许新端口访问
## 注意事项
- 首次运行需要创建管理员账户
- 依赖已预装,无需手动安装
- 首次运行会自动创建默认管理员账户 (admin/admin123)
- 请立即登录并修改默认密码
- 确保防火墙允许相关端口访问
- 建议在生产环境中使用 PM2 等进程管理工具

View File

@@ -8,22 +8,24 @@ import path from 'path'
import { fileURLToPath } from 'url'
import winston from 'winston'
import { TerminalManager } from './modules/terminal/TerminalManager'
import { GameManager } from './modules/game/GameManager'
import { SystemManager } from './modules/system/SystemManager'
import { ConfigManager } from './modules/config/ConfigManager'
import { AuthManager } from './modules/auth/AuthManager'
import { setupTerminalRoutes } from './routes/terminal'
import { setupGameRoutes } from './routes/games'
import { setupSystemRoutes } from './routes/system'
import { setupAuthRoutes } from './routes/auth'
import { setAuthManager } from './middleware/auth'
import { TerminalManager } from './modules/terminal/TerminalManager.js'
import { GameManager } from './modules/game/GameManager.js'
import { SystemManager } from './modules/system/SystemManager.js'
import { ConfigManager } from './modules/config/ConfigManager.js'
import { AuthManager } from './modules/auth/AuthManager.js'
import { setupTerminalRoutes } from './routes/terminal.js'
import { setupGameRoutes } from './routes/games.js'
import { setupSystemRoutes } from './routes/system.js'
import { setupAuthRoutes } from './routes/auth.js'
import { setAuthManager } from './middleware/auth.js'
// 获取当前文件目录
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 加载环境变量
// 首先尝试加载根目录的.env文件然后加载server目录的.env文件
dotenv.config({ path: path.join(__dirname, '../../.env') })
dotenv.config()
// 配置日志
@@ -73,6 +75,7 @@ app.use(express.urlencoded({ extended: true, limit: '10mb' }))
// 静态文件服务
app.use('/static', express.static(path.join(__dirname, '../public')))
app.use(express.static(path.join(__dirname, '../public')))
// 管理器变量声明
let configManager: ConfigManager
@@ -175,12 +178,18 @@ async function startServer() {
app.use('/api/game', setupGameRoutes(gameManager))
app.use('/api/system', setupSystemRoutes(systemManager))
// 404处理必须在所有路由之后
app.use('*', (req, res) => {
res.status(404).json({
error: '接口不存在',
path: req.originalUrl
})
// 前端路由处理SPA支持
app.get('*', (req, res) => {
// 如果是API请求返回404
if (req.path.startsWith('/api/')) {
res.status(404).json({
error: '接口不存在',
path: req.originalUrl
})
} else {
// 其他请求返回前端页面
res.sendFile(path.join(__dirname, '../public/index.html'))
}
})
// Socket.IO 连接处理
@@ -250,7 +259,7 @@ async function startServer() {
})
})
const PORT = parseInt(process.env.PORT || '3001', 10)
const PORT = parseInt(process.env.SERVER_PORT || process.env.PORT || '3001', 10)
const HOST = process.env.HOST || '0.0.0.0'
server.listen(PORT, HOST, () => {

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from 'express'
import { AuthManager } from '../modules/auth/AuthManager'
import logger from '../utils/logger'
import { AuthManager } from '../modules/auth/AuthManager.js'
import logger from '../utils/logger.js'
export interface AuthenticatedRequest extends Request {
user?: {

View File

@@ -3,7 +3,7 @@ import jwt from 'jsonwebtoken'
import fs from 'fs/promises'
import path from 'path'
import winston from 'winston'
import { ConfigManager } from '../config/ConfigManager'
import { ConfigManager } from '../config/ConfigManager.js'
export interface User {
id: string
@@ -223,7 +223,7 @@ export class AuthManager {
role: user.role
},
jwtConfig.secret,
{ expiresIn: jwtConfig.expiresIn }
{ expiresIn: jwtConfig.expiresIn as `${number}h` }
)
this.logger.info(`用户 ${username} 登录成功`)

View File

@@ -52,9 +52,9 @@ export class TerminalManager {
// 根据操作系统选择PTY程序路径
const platform = os.platform()
if (platform === 'win32') {
this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_win32_x64.exe')
this.ptyPath = path.resolve(__dirname, '../../PTY/pty_win32_x64.exe')
} else {
this.ptyPath = path.resolve(__dirname, '../../../PTY/pty_linux_x64')
this.ptyPath = path.resolve(__dirname, '../../PTY/pty_linux_x64')
}
this.logger.info(`终端管理器初始化完成PTY路径: ${this.ptyPath}`)

View File

@@ -1,8 +1,8 @@
import { Router, Request, Response } from 'express'
import rateLimit from 'express-rate-limit'
import { AuthManager } from '../modules/auth/AuthManager'
import { authenticateToken, AuthenticatedRequest, requireAdmin } from '../middleware/auth'
import logger from '../utils/logger'
import { AuthManager } from '../modules/auth/AuthManager.js'
import { authenticateToken, AuthenticatedRequest, requireAdmin } from '../middleware/auth.js'
import logger from '../utils/logger.js'
import Joi from 'joi'
const router = Router()

View File

@@ -1,6 +1,6 @@
import { Router, Request, Response } from 'express'
import { GameManager } from '../modules/game/GameManager'
import logger from '../utils/logger'
import { GameManager } from '../modules/game/GameManager.js'
import logger from '../utils/logger.js'
import Joi from 'joi'
const router = Router()

View File

@@ -1,8 +1,8 @@
import { Router } from 'express'
import terminalRoutes from './terminal'
import gameRoutes from './games'
import systemRoutes from './system'
import fileRoutes from './files'
import terminalRoutes from './terminal.js'
import gameRoutes from './games.js'
import systemRoutes from './system.js'
import fileRoutes from './files.js'
const router = Router()

View File

@@ -1,6 +1,6 @@
import { Router, Request, Response } from 'express'
import { SystemManager } from '../modules/system/SystemManager'
import logger from '../utils/logger'
import { SystemManager } from '../modules/system/SystemManager.js'
import logger from '../utils/logger.js'
import os from 'os'
import fs from 'fs/promises'
import path from 'path'

View File

@@ -1,6 +1,6 @@
import { Router, Request, Response } from 'express'
import { TerminalManager } from '../modules/terminal/TerminalManager'
import logger from '../utils/logger'
import { TerminalManager } from '../modules/terminal/TerminalManager.js'
import logger from '../utils/logger.js'
const router = Router()

View File

@@ -8,20 +8,21 @@
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"allowJs": true,
"strict": true,
"strict": false,
"noEmit": false,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"removeComments": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": false,
"strictNullChecks": false,
"strictFunctionTypes": false,
"noImplicitThis": false,
"noImplicitReturns": false,
"noFallthroughCasesInSwitch": false,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": false,
"noImplicitAny": false
},
"include": [
"src/**/*"

View File