Compare commits
2 Commits
44bfe86c6c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cdf100206a | |||
| df89bcab7f |
@@ -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 "$@"
|
||||||
@@ -6,4 +6,5 @@ ALTER TABLE people ADD COLUMN deleted_at TEXT;
|
|||||||
ALTER TABLE stations ADD COLUMN deleted_at TEXT;
|
ALTER TABLE stations ADD COLUMN deleted_at TEXT;
|
||||||
ALTER TABLE work_records ADD COLUMN deleted_at TEXT;
|
ALTER TABLE work_records ADD COLUMN deleted_at TEXT;
|
||||||
ALTER TABLE work_records ADD COLUMN attachments TEXT;
|
ALTER TABLE work_records ADD COLUMN attachments TEXT;
|
||||||
ALTER TABLE work_records ADD COLUMN created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime'));
|
ALTER TABLE work_records ADD COLUMN created_at TEXT;
|
||||||
|
UPDATE work_records SET created_at = datetime('now', 'localtime') WHERE created_at IS NULL;
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ export function TopBar({ pageTitle, onMenuToggle }: TopBarProps) {
|
|||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
logout()
|
logout()
|
||||||
|
} else {
|
||||||
|
// 演示模式:清除 auth_role 退出到登录页
|
||||||
|
localStorage.removeItem('auth_role')
|
||||||
|
localStorage.removeItem('auth_name')
|
||||||
|
localStorage.removeItem('auth_station')
|
||||||
|
window.location.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,13 +139,15 @@ export function Dashboard({ records, onNavigate, onCreate, onSelect }: Dashboard
|
|||||||
<span>{role === 'reporter' ? '这是你的个人工作概况。' : role === 'station' ? '这是本站今日运行概况。' : '这里是全国记者站今日运行概况。'}</span>
|
<span>{role === 'reporter' ? '这是你的个人工作概况。' : role === 'station' ? '这是本站今日运行概况。' : '这里是全国记者站今日运行概况。'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{role !== 'headquarters' && (
|
</div>
|
||||||
|
{role !== 'headquarters' && (
|
||||||
|
<div style={{ marginBottom: 16 }}>
|
||||||
<button className="primary-button" onClick={onCreate}>
|
<button className="primary-button" onClick={onCreate}>
|
||||||
<Plus size={18} />
|
<Plus size={18} />
|
||||||
新建工作记录
|
新建工作记录
|
||||||
</button>
|
</button>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<div className="metric-grid">
|
<div className="metric-grid">
|
||||||
<div onClick={() => setDrillDown('pending')} style={{ cursor: 'pointer' }}>
|
<div onClick={() => setDrillDown('pending')} style={{ cursor: 'pointer' }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user