chore: 重组项目文件结构,文档和部署文件归档

文件结构优化:
- docs/ 归档所有项目文档(PROJECT.md, PROJECT_ISSUES.md 等)
- deploy/ 归档所有部署相关文件(docker-compose.prod.yml, nginx 配置等)
- scripts/ 归档脚本文件(deploy.sh, rollback.sh)
- tests/ 归档测试文件(test_data.php)

更新引用路径:
- CLAUDE.md 更新文档路径引用
- 部署文档更新配置文件路径引用

清理文件:
- 移除非必要的测试文件(TestController, 测试脚本等)
- 移除重复/错误的目录

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-02-27 18:01:17 +08:00
parent 1004aea725
commit 4ceb5eac7e
18 changed files with 4982 additions and 4 deletions
+51
View File
@@ -0,0 +1,51 @@
# X-Server Mobile 生产环境变量模板
# 使用方法: cp .env.production.template .env.production
# 然后填写实际值,不要将 .env.production 提交到代码仓库
# ============================================
# 数据库配置
# ============================================
DB_HOST=1Panel-mysql-xxxx
DB_PORT=3306
DB_DATABASE=xserver
DB_USERNAME=xserver
DB_PASSWORD=your_strong_password_here
# ============================================
# Redis 配置
# ============================================
REDIS_HOST=1Panel-redis-xxxx
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password_here
REDIS_DATABASE=0
# ============================================
# 飞书配置
# ============================================
FEISHU_APP_ID=cli_xxxxxxxxxxxxx
FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxxxxxxxx
FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxxxxxxxx
FEISHU_TENANT_KEY=14e7bc0fe5985740
# 飞书应用 URL(获取方式:飞书开放平台 -> 应用详情)
FEISHU_APP_URL=https://open.feishu.cn
# ============================================
# 应用配置
# ============================================
APP_ENV=production
APP_DEBUG=false
APP_URL=https://xsm.hnxinyouhe.com
# ============================================
# 日志配置
# ============================================
LOG_LEVEL=info
# ============================================
# 部署配置
# ============================================
DEPLOY_USER=root
DEPLOY_SERVER=your-server-ip
DEPLOY_PATH=/opt/x-server-mobile
+61
View File
@@ -0,0 +1,61 @@
# X-Server Mobile 生产环境 Dockerfile
# 基于官方 PHP 8.5 CLI 镜像
FROM php:8.5-cli
# 安装系统依赖
RUN apt-get update && apt-get install -y \
unzip \
git \
libzip-dev \
libpng-dev \
libonig-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# 安装 PHP 扩展
RUN docker-php-ext-install \
zip \
pdo \
pdo_mysql \
bcmath \
pcntl \
opcache \
mbstring
# 安装 Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
# 设置工作目录
WORKDIR /app
# 复制 composer 文件
COPY composer.json composer.lock* ./
# 安装依赖(生产环境)
RUN composer install \
--no-dev \
--optimize-autoloader \
--no-interaction \
--no-progress
# 复制应用文件
COPY . .
# 复制 PHP 生产环境配置
COPY php-prod.ini /usr/local/etc/php/conf.d/99-production.ini
# 创建运行时目录
RUN mkdir -p /app/runtime/logs \
&& mkdir -p /app/runtime/cache \
&& chmod -R 755 /app/runtime
# 暴露端口
EXPOSE 8787
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD php -r "exit(0);" || exit 1
# 启动 Webman(前台运行)
CMD ["php", "start.php", "start"]
+169
View File
@@ -0,0 +1,169 @@
#!/bin/bash
# X-Server Mobile 生产部署脚本
# 使用方法: ./deploy.sh [server]
set -e
# 配置
SERVER=${1:-"your-server.com"} # 默认服务器,可以通过参数指定
DEPLOY_USER="root"
DEPLOY_PATH="/opt/x-server-mobile"
FRONTEND_BUILD_DIR="./frontend/dist"
BACKEND_SOURCE_DIR="./backend"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查环境
check_env() {
log_info "检查部署环境..."
if ! command -v rsync &> /dev/null; then
log_error "rsync 未安装,请先安装: apt-get install rsync"
exit 1
fi
if [ ! -d "$FRONTEND_BUILD_DIR" ]; then
log_error "前端构建目录不存在: $FRONTEND_BUILD_DIR"
log_info "请先运行: cd frontend && npm install && npm run build"
exit 1
fi
log_info "环境检查通过"
}
# 备份远程服务器
backup_remote() {
log_info "备份远程服务器..."
BACKUP_NAME="$(date +%Y%m%d_%H%M%S)"
ssh ${DEPLOY_USER}@${SERVER} << EOF
mkdir -p ${DEPLOY_PATH}/backup/${BACKUP_NAME}
cp -r ${DEPLOY_PATH}/frontend ${DEPLOY_PATH}/backup/${BACKUP_NAME}/ 2>/dev/null || true
cp -r ${DEPLOY_PATH}/backend ${DEPLOY_PATH}/backup/${BACKUP_NAME}/ 2>/dev/null || true
echo "备份完成: ${BACKUP_NAME}"
EOF
log_info "备份完成: ${BACKUP_NAME}"
}
# 部署前端
deploy_frontend() {
log_info "部署前端..."
rsync -avz --delete \
--exclude='*.map' \
${FRONTEND_BUILD_DIR}/ \
${DEPLOY_USER}@${SERVER}:${DEPLOY_PATH}/frontend/
log_info "前端部署完成"
}
# 部署后端
deploy_backend() {
log_info "部署后端..."
rsync -avz \
--exclude='vendor/' \
--exclude='runtime/' \
--exclude='.git/' \
--exclude='*.log' \
${BACKEND_SOURCE_DIR}/ \
${DEPLOY_USER}@${SERVER}:${DEPLOY_PATH}/backend/
log_info "后端部署完成"
}
# 重启服务
restart_services() {
log_info "重启服务..."
ssh ${DEPLOY_USER}@${SERVER} << 'EOF'
cd /opt/x-server-mobile
# 重启后端容器
docker-compose -f docker-compose.prod.yml restart backend
# 等待后端启动
sleep 5
# 检查容器状态
docker ps | grep x-server-backend-prod
# 重载 OpenResty
openresty -s reload || nginx -s reload
echo "服务重启完成"
EOF
log_info "服务重启完成"
}
# 健康检查
health_check() {
log_info "健康检查..."
sleep 3
# 检查前端
if curl -s -o /dev/null -w "%{http_code}" http://${SERVER}/ | grep -q "200\|301\|302"; then
log_info "前端检查通过"
else
log_warn "前端检查失败"
fi
# 检查 API
if curl -s http://${SERVER}/api/v1/health 2>/dev/null | grep -q "ok\|success"; then
log_info "API 检查通过"
else
log_warn "API 检查失败(可能需要登录)"
fi
}
# 主流程
main() {
echo "========================================="
echo " X-Server Mobile 生产部署脚本"
echo "========================================="
echo "服务器: ${SERVER}"
echo "部署路径: ${DEPLOY_PATH}"
echo ""
# 确认部署
read -p "确认部署到 ${SERVER}? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "部署已取消"
exit 0
fi
check_env
backup_remote
deploy_frontend
deploy_backend
restart_services
health_check
echo ""
log_info "部署完成!"
echo "查看日志: ssh ${DEPLOY_USER}@${SERVER} 'docker logs -f x-server-backend-prod'"
}
# 执行主流程
main
+58
View File
@@ -0,0 +1,58 @@
# X-Server Mobile 生产环境 Docker Compose
# 使用方法: docker-compose -f docker-compose.prod.yml up -d
services:
# PHP 后端服务
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
container_name: x-server-backend-prod
restart: unless-stopped
ports:
- "8787:8787"
environment:
# 应用环境
- APP_ENV=production
- APP_DEBUG=false
# 数据库配置(使用 1Panel 的 MySQL)
# 注意:请替换为实际的容器名和密码
- DB_HOST=1Panel-mysql-409s
- DB_PORT=3306
- DB_DATABASE=xserver
- DB_USERNAME=xserver
- DB_PASSWORD=${DB_PASSWORD:-change_me_in_production}
# Redis 配置(使用 1Panel 的 Redis)
# 注意:请替换为实际的容器名和密码
- REDIS_HOST=1Panel-redis-xxxx
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-change_me_in_production}
# 飞书配置(请替换为实际值)
- FEISHU_APP_ID=${FEISHU_APP_ID}
- FEISHU_APP_SECRET=${FEISHU_APP_SECRET}
- FEISHU_ENCRYPT_KEY=${FEISHU_ENCRYPT_KEY}
- FEISHU_VERIFICATION_TOKEN=${FEISHU_VERIFICATION_TOKEN}
- FEISHU_TENANT_KEY=${FEISHU_TENANT_KEY:-14e7bc0fe5985740}
volumes:
- ./backend:/app
- ./logs/backend:/app/runtime/logs
networks:
- 1panel-network
healthcheck:
test: ["CMD", "php", "-r", "exit(0);"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
1panel-network:
external: true
+58
View File
@@ -0,0 +1,58 @@
; X-Server Mobile 生产环境 PHP 配置
; 文件位置: backend/php-prod.ini
; 用途: Docker 容器内的 PHP 生产环境配置
; ============================================
; OPcache 配置
; ============================================
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.save_comments=1
; ============================================
; 错误处理
; ============================================
display_errors=0
display_startup_errors=0
log_errors=1
error_log=/app/runtime/logs/php_errors.log
error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT
; ============================================
; 安全配置
; ============================================
expose_php=Off
allow_url_fopen=On
allow_url_include=Off
; 禁用危险函数(根据实际需求调整)
; disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
; ============================================
; 性能配置
; ============================================
max_execution_time=60
max_input_time=60
memory_limit=256M
post_max_size=50M
upload_max_filesize=50M
max_file_uploads=20
; ============================================
; 会话配置
; ============================================
session.cookie_httponly=1
session.cookie_secure=1
session.use_strict_mode=1
session.cookie_samesite="Lax"
; ============================================
; 其他配置
; ============================================
date.timezone=Asia/Shanghai
default_charset="UTF-8"
+159
View File
@@ -0,0 +1,159 @@
#!/bin/bash
# X-Server Mobile 回滚脚本
# 使用方法: ./rollback.sh [backup_name]
set -e
# 配置
SERVER="your-server.com"
DEPLOY_USER="root"
DEPLOY_PATH="/opt/x-server-mobile"
BACKUP_NAME=$1
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 列出可用备份
list_backups() {
log_info "可用备份列表:"
ssh ${DEPLOY_USER}@${SERVER} << 'EOF'
cd /opt/x-server-mobile/backup
ls -1t | head -10
EOF
}
# 确认备份存在
check_backup() {
log_info "检查备份 ${BACKUP_NAME}..."
EXISTS=$(ssh ${DEPLOY_USER}@${SERVER} "
if [ -d ${DEPLOY_PATH}/backup/${BACKUP_NAME} ]; then
echo 'exists'
else
echo 'not_found'
fi
")
if [ "$EXISTS" != "exists" ]; then
log_error "备份不存在: ${BACKUP_NAME}"
list_backups
exit 1
fi
log_info "备份检查通过"
}
# 回滚前端
rollback_frontend() {
log_info "回滚前端..."
ssh ${DEPLOY_USER}@${SERVER} << EOF
rm -rf ${DEPLOY_PATH}/frontend/*
cp -r ${DEPLOY_PATH}/backup/${BACKUP_NAME}/frontend/* ${DEPLOY_PATH}/frontend/
EOF
log_info "前端回滚完成"
}
# 回滚后端(可选)
rollback_backend() {
read -p "是否回滚后端代码? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "跳过后端回滚"
return
fi
log_info "回滚后端..."
ssh ${DEPLOY_USER}@${SERVER} << EOF
# 保留 vendor 目录
mv ${DEPLOY_PATH}/backend/vendor /tmp/backend_vendor_backup 2>/dev/null || true
rm -rf ${DEPLOY_PATH}/backend/*
cp -r ${DEPLOY_PATH}/backup/${BACKUP_NAME}/backend/* ${DEPLOY_PATH}/backend/
# 恢复 vendor 目录
mv /tmp/backend_vendor_backup ${DEPLOY_PATH}/backend/vendor 2>/dev/null || true
EOF
log_info "后端回滚完成"
}
# 重启服务
restart_services() {
log_info "重启服务..."
ssh ${DEPLOY_USER}@${SERVER} << 'EOF'
cd /opt/x-server-mobile
# 重启后端容器
docker-compose -f docker-compose.prod.yml restart backend
# 等待后端启动
sleep 5
# 重载 OpenResty
openresty -s reload || nginx -s reload
echo "服务重启完成"
EOF
log_info "服务重启完成"
}
# 主流程
main() {
echo "========================================="
echo " X-Server Mobile 回滚脚本"
echo "========================================="
echo "服务器: ${SERVER}"
if [ -z "$BACKUP_NAME" ]; then
log_error "请指定备份名称"
echo ""
list_backups
echo ""
echo "使用方法: ./rollback.sh <backup_name>"
exit 1
fi
echo "备份名称: ${BACKUP_NAME}"
echo ""
# 确认回滚
read -p "确认回滚到 ${BACKUP_NAME}? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "回滚已取消"
exit 0
fi
check_backup
rollback_frontend
rollback_backend
restart_services
echo ""
log_info "回滚完成!"
}
# 执行主流程
main
+183
View File
@@ -0,0 +1,183 @@
# X-Server Mobile 线上环境 OpenResty 配置
# 文件位置: /usr/local/openresty/nginx/conf/conf.d/x-server-mobile.conf
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name xsm.hnxinyouhe.com;
# Let's Encrypt ACME 挑战
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 强制 HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS 主服务
server {
listen 443 ssl http2;
server_name xsm.hnxinyouhe.com;
# ============================================
# SSL 证书配置
# ============================================
# Let's Encrypt 证书路径
ssl_certificate /etc/letsencrypt/live/xsm.hnxinyouhe.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xsm.hnxinyouhe.com/privkey.pem;
# 如果使用自签名证书,取消下面的注释并注释上面的 Let's Encrypt 配置
# ssl_certificate /etc/nginx/ssl/xsm.hnxinyouhe.com.crt;
# ssl_certificate_key /etc/nginx/ssl/xsm.hnxinyouhe.com.key;
# SSL 协议和加密套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/xsm.hnxinyouhe.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# ============================================
# 日志配置
# ============================================
access_log /opt/x-server-mobile/logs/nginx_access.log combined;
error_log /opt/x-server-mobile/logs/nginx_error.log warn;
# ============================================
# 安全头
# ============================================
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# ============================================
# Gzip 压缩
# ============================================
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/rss+xml
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
# ============================================
# 静态资源(长期缓存)
# ============================================
location ~* \.(js|css|png|jpg|jpeg|gif|webp|svg|ico|woff|woff2|ttf|eot)$ {
root /opt/x-server-mobile/frontend;
expires 7d;
add_header Cache-Control "public, immutable";
access_log off;
}
# ============================================
# 飞书回调路由(必须在 API 之前)
# ============================================
location /login {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
location /login/callback {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
location /login/qrcode {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
# 飞书卡片回调
location /feishu/card/callback {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
# ============================================
# API 代理
# ============================================
location /api/ {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
# 超时配置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 缓冲配置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}
# ============================================
# 前端 SPA(所有其他请求返回 index.html)
# ============================================
location / {
root /opt/x-server-mobile/frontend;
# SPA 路由支持
try_files $uri $uri/ /index.html;
# 禁用 HTML 缓存
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
}