diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..803c9b1 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ═══════════════════════════════════════════════════════════ +# 全国记者站管理系统 — 一键部署脚本 +# 用法: ./deploy.sh +# 功能: 拉取最新代码 → 安装依赖 → 数据库迁移 → 构建 → 重启服务 +# ═══════════════════════════════════════════════════════════ + +# ── 配置 ────────────────────────────────────────────────── +DEPLOY_DIR="/var/www/rsms" +GIT_REMOTE="origin" +BRANCH="main" +API_PORT="${API_PORT:-8787}" +PM2_NAME="rsms-server" +NGINX_SITE="r.all8ai.top" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log() { echo -e "${GREEN}[deploy]${NC} $1"; } +warn() { echo -e "${YELLOW}[warn]${NC} $1"; } +err() { echo -e "${RED}[error]${NC} $1"; exit 1; } + +# ── 前置检查 ────────────────────────────────────────────── +check_deps() { + log "检查依赖..." + command -v git >/dev/null || err "git 未安装" + command -v node >/dev/null || err "node 未安装" + command -v npm >/dev/null || err "npm 未安装" + command -v nginx >/dev/null || err "nginx 未安装" + command -v pm2 >/dev/null || err "pm2 未安装 (运行: npm install -g pm2)" + + NODE_MAJOR=$(node -v | sed 's/v\([0-9]*\).*/\1/') + [ "$NODE_MAJOR" -ge 22 ] || err "Node.js 版本需 >= 22 (当前: $(node -v))" + + log "依赖检查通过: node $(node -v), npm $(npm -v)" +} + +# ── 拉取最新代码 ────────────────────────────────────────── +pull_code() { + log "拉取最新代码..." + cd "$DEPLOY_DIR" + git fetch "$GIT_REMOTE" "$BRANCH" + git reset --hard "$GIT_REMOTE/$BRANCH" + log "代码已更新到 $(git log --oneline -1)" +} + +# ── 安装依赖 ────────────────────────────────────────────── +install_deps() { + log "安装依赖..." + npm install --production=false + log "依赖安装完成" +} + +# ── 数据库迁移 ──────────────────────────────────────────── +run_migrations() { + log "执行数据库迁移..." + npm run db:migrate + log "数据库迁移完成" +} + +# ── 构建前端 ────────────────────────────────────────────── +build_frontend() { + log "构建前端..." + npm run build + log "前端构建完成: $(ls dist/assets/*.js | wc -l) 个 JS 文件" +} + +# ── 重启后端服务 ────────────────────────────────────────── +restart_server() { + log "重启后端服务 (pm2: $PM2_NAME)..." + pm2 delete "$PM2_NAME" 2>/dev/null || true + pm2 start npm --name "$PM2_NAME" -- run server:start + pm2 save + sleep 2 + + # 健康检查 + local health + health=$(curl -s "http://127.0.0.1:${API_PORT}/api/health" 2>/dev/null || echo "FAIL") + if echo "$health" | grep -q '"status":"ok"'; then + log "后端服务健康检查通过" + else + warn "后端健康检查异常: $health" + warn "查看日志: pm2 logs $PM2_NAME --lines 20" + fi +} + +# ── 重载 nginx ──────────────────────────────────────────── +reload_nginx() { + log "检查 nginx 配置..." + nginx -t 2>&1 || err "nginx 配置检查失败" + nginx -s reload + log "nginx 已重载" +} + +# ── 验证站点 ────────────────────────────────────────────── +verify_site() { + log "验证站点..." + local http_code + http_code=$(curl -s -o /dev/null -w "%{http_code}" "http://${NGINX_SITE}/" 2>/dev/null || echo "000") + if [ "$http_code" = "200" ]; then + log "站点验证通过: http://${NGINX_SITE} → 200" + else + warn "站点返回: $http_code (可能需要检查 DNS 或 nginx 配置)" + fi + + # HTTPS 检查(如果证书存在) + if [ -d "/etc/letsencrypt/live/${NGINX_SITE}" ]; then + local https_code + https_code=$(curl -s -o /dev/null -w "%{http_code}" "https://${NGINX_SITE}/" 2>/dev/null || echo "000") + log "HTTPS 验证: https://${NGINX_SITE} → $https_code" + fi +} + +# ── 主流程 ──────────────────────────────────────────────── +main() { + echo "" + echo "══════════════════════════════════════════════════" + echo " 全国记者站管理系统 — 一键部署" + echo "══════════════════════════════════════════════════" + echo "" + + check_deps + pull_code + install_deps + run_migrations + build_frontend + restart_server + reload_nginx + verify_site + + echo "" + log "部署完成!" + echo " 站点: https://${NGINX_SITE}" + echo " API: https://${NGINX_SITE}/api/health" + echo " 日志: pm2 logs ${PM2_NAME}" + echo " 状态: pm2 list" + echo "" +} + +main "$@" diff --git a/src/components/layout/TopBar.tsx b/src/components/layout/TopBar.tsx index d74b443..a6c87df 100644 --- a/src/components/layout/TopBar.tsx +++ b/src/components/layout/TopBar.tsx @@ -19,6 +19,12 @@ export function TopBar({ pageTitle, onMenuToggle }: TopBarProps) { const handleLogout = () => { if (isAuthenticated) { logout() + } else { + // 演示模式:清除 auth_role 退出到登录页 + localStorage.removeItem('auth_role') + localStorage.removeItem('auth_name') + localStorage.removeItem('auth_station') + window.location.reload() } } diff --git a/src/pages/Dashboard/index.tsx b/src/pages/Dashboard/index.tsx index 661e947..c0de6c4 100644 --- a/src/pages/Dashboard/index.tsx +++ b/src/pages/Dashboard/index.tsx @@ -139,13 +139,15 @@ export function Dashboard({ records, onNavigate, onCreate, onSelect }: Dashboard {role === 'reporter' ? '这是你的个人工作概况。' : role === 'station' ? '这是本站今日运行概况。' : '这里是全国记者站今日运行概况。'} - {role !== 'headquarters' && ( + + {role !== 'headquarters' && ( +
- )} -
+ + )}
setDrillDown('pending')} style={{ cursor: 'pointer' }}>