Files
edict/edict.sh
cft0808 74d8130391 feat: Week 0-4 optimizations - event bus, state machine, dispatch, outbox relay
- EventBus: Redis Streams pub/sub for decoupled service communication
- State machine: strict lifecycle transitions with audit logging
- Dispatch worker: parallel execution, retry with backoff, resource locking
- Orchestrator: DAG-based task decomposition and dependency resolution
- Outbox relay: transactional outbox pattern for reliable event delivery
- Auth: dashboard authentication module
- Agent groups: sansheng/liubu agent configuration
- CI/CD: Docker publish workflow, systemd service, start script
- Frontend: dashboard build assets
- Tests: state machine consistency tests
2026-04-04 12:16:32 +08:00

236 lines
7.6 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ══════════════════════════════════════════════════════════════
# 三省六部 · 统一服务管理脚本
# 用法: ./edict.sh {start|stop|status|restart|logs}
# ══════════════════════════════════════════════════════════════
set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PIDDIR="$REPO_DIR/.pids"
LOGDIR="$REPO_DIR/logs"
SERVER_PIDFILE="$PIDDIR/server.pid"
LOOP_PIDFILE="$PIDDIR/loop.pid"
SERVER_LOG="$LOGDIR/server.log"
LOOP_LOG="$LOGDIR/loop.log"
# 可通过环境变量覆盖的配置
DASHBOARD_HOST="${EDICT_DASHBOARD_HOST:-127.0.0.1}"
DASHBOARD_PORT="${EDICT_DASHBOARD_PORT:-7891}"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
# ── 工具函数 ──
_ensure_dirs() {
mkdir -p "$PIDDIR" "$LOGDIR" "$REPO_DIR/data"
# 初始化必需的数据文件
for f in live_status.json agent_config.json model_change_log.json sync_status.json; do
[ ! -f "$REPO_DIR/data/$f" ] && echo '{}' > "$REPO_DIR/data/$f"
done
[ ! -f "$REPO_DIR/data/pending_model_changes.json" ] && echo '[]' > "$REPO_DIR/data/pending_model_changes.json"
[ ! -f "$REPO_DIR/data/tasks_source.json" ] && echo '[]' > "$REPO_DIR/data/tasks_source.json"
[ ! -f "$REPO_DIR/data/tasks.json" ] && echo '[]' > "$REPO_DIR/data/tasks.json"
[ ! -f "$REPO_DIR/data/officials.json" ] && echo '[]' > "$REPO_DIR/data/officials.json"
[ ! -f "$REPO_DIR/data/officials_stats.json" ] && echo '{}' > "$REPO_DIR/data/officials_stats.json"
}
_is_running() {
local pidfile="$1"
if [[ -f "$pidfile" ]]; then
local pid
pid=$(cat "$pidfile" 2>/dev/null)
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
return 0
fi
# PID 文件存在但进程已死,清理
rm -f "$pidfile"
fi
return 1
}
_get_pid() {
local pidfile="$1"
if [[ -f "$pidfile" ]]; then
cat "$pidfile" 2>/dev/null
fi
}
# ── 启动 ──
do_start() {
_ensure_dirs
if ! command -v python3 &>/dev/null; then
echo -e "${RED}❌ 未找到 python3请先安装 Python 3.9+${NC}"
exit 1
fi
echo -e "${BLUE}╔══════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ 🏛️ 三省六部 · 服务启动中 ║${NC}"
echo -e "${BLUE}╚══════════════════════════════════════════╝${NC}"
echo ""
# 检查是否已在运行
local already=0
if _is_running "$SERVER_PIDFILE"; then
echo -e "${YELLOW}⚠️ 看板服务器已在运行 (PID=$(_get_pid "$SERVER_PIDFILE"))${NC}"
already=$((already+1))
fi
if _is_running "$LOOP_PIDFILE"; then
echo -e "${YELLOW}⚠️ 数据刷新循环已在运行 (PID=$(_get_pid "$LOOP_PIDFILE"))${NC}"
already=$((already+1))
fi
if [[ $already -eq 2 ]]; then
echo -e "${YELLOW}所有服务均已运行,如需重启请用: $0 restart${NC}"
return 0
fi
# 启动数据刷新循环(后台)
if ! _is_running "$LOOP_PIDFILE"; then
if command -v openclaw &>/dev/null; then
echo -e "${GREEN}▶ 启动数据刷新循环...${NC}"
nohup bash "$REPO_DIR/scripts/run_loop.sh" >> "$LOOP_LOG" 2>&1 &
echo $! > "$LOOP_PIDFILE"
echo -e " PID=$(_get_pid "$LOOP_PIDFILE") 日志: ${BLUE}$LOOP_LOG${NC}"
else
echo -e "${YELLOW}⚠️ 未检测到 OpenClaw CLI跳过数据刷新循环${NC}"
echo -e "${YELLOW} 看板将以只读模式运行(使用已有数据)${NC}"
fi
fi
# 启动看板服务器(后台)
if ! _is_running "$SERVER_PIDFILE"; then
echo -e "${GREEN}▶ 启动看板服务器...${NC}"
nohup python3 "$REPO_DIR/dashboard/server.py" \
--host "$DASHBOARD_HOST" --port "$DASHBOARD_PORT" \
>> "$SERVER_LOG" 2>&1 &
echo $! > "$SERVER_PIDFILE"
echo -e " PID=$(_get_pid "$SERVER_PIDFILE") 日志: ${BLUE}$SERVER_LOG${NC}"
fi
sleep 1
echo ""
if _is_running "$SERVER_PIDFILE"; then
echo -e "${GREEN}✅ 服务已启动!${NC}"
echo -e " 看板地址: ${BLUE}http://${DASHBOARD_HOST}:${DASHBOARD_PORT}${NC}"
else
echo -e "${RED}❌ 看板服务器启动失败,请查看日志: $SERVER_LOG${NC}"
exit 1
fi
}
# ── 停止 ──
do_stop() {
echo -e "${YELLOW}正在关闭服务...${NC}"
local stopped=0
for label_pid in "看板服务器:$SERVER_PIDFILE" "数据刷新循环:$LOOP_PIDFILE"; do
local label="${label_pid%%:*}"
local pidfile="${label_pid#*:}"
if _is_running "$pidfile"; then
local pid
pid=$(_get_pid "$pidfile")
kill "$pid" 2>/dev/null
# 等待最多 5 秒
for _ in $(seq 1 10); do
kill -0 "$pid" 2>/dev/null || break
sleep 0.5
done
# 如果还在运行,强制 kill
if kill -0 "$pid" 2>/dev/null; then
kill -9 "$pid" 2>/dev/null
fi
rm -f "$pidfile"
echo -e "${label} (PID=$pid) 已停止"
stopped=$((stopped+1))
fi
done
if [[ $stopped -eq 0 ]]; then
echo -e "${YELLOW} 没有正在运行的服务${NC}"
else
echo -e "${GREEN}✅ 所有服务已关闭${NC}"
fi
}
# ── 状态 ──
do_status() {
echo -e "${BLUE}🏛️ 三省六部 · 服务状态${NC}"
echo ""
for label_pid in "看板服务器:$SERVER_PIDFILE" "数据刷新循环:$LOOP_PIDFILE"; do
local label="${label_pid%%:*}"
local pidfile="${label_pid#*:}"
if _is_running "$pidfile"; then
local pid
pid=$(_get_pid "$pidfile")
echo -e " ${GREEN}${NC} ${label} PID=$pid ${GREEN}运行中${NC}"
else
echo -e " ${RED}${NC} ${label} ${RED}未运行${NC}"
fi
done
echo ""
# 如果看板在运行,尝试 healthz
if _is_running "$SERVER_PIDFILE"; then
local health
if health=$(python3 -c "
import urllib.request, json, sys
try:
r = urllib.request.urlopen('http://${DASHBOARD_HOST}:${DASHBOARD_PORT}/healthz', timeout=3)
d = json.loads(r.read())
print('healthy' if d.get('status')=='ok' else 'unhealthy')
except Exception:
print('unreachable')
" 2>/dev/null); then
case "$health" in
healthy) echo -e " 健康检查: ${GREEN}✅ 正常${NC}" ;;
unhealthy) echo -e " 健康检查: ${YELLOW}⚠️ 异常${NC}" ;;
*) echo -e " 健康检查: ${RED}❌ 无法连接${NC}" ;;
esac
fi
echo -e " 看板地址: ${BLUE}http://${DASHBOARD_HOST}:${DASHBOARD_PORT}${NC}"
fi
}
# ── 日志 ──
do_logs() {
local target="${1:-all}"
case "$target" in
server) tail -f "$SERVER_LOG" ;;
loop) tail -f "$LOOP_LOG" ;;
all) tail -f "$SERVER_LOG" "$LOOP_LOG" ;;
*) echo "用法: $0 logs [server|loop|all]"; exit 1 ;;
esac
}
# ── 主入口 ──
case "${1:-}" in
start) do_start ;;
stop) do_stop ;;
restart) do_stop; sleep 1; do_start ;;
status) do_status ;;
logs) do_logs "${2:-all}" ;;
*)
echo "用法: $0 {start|stop|restart|status|logs}"
echo ""
echo "命令:"
echo " start 启动所有服务(看板 + 数据刷新)"
echo " stop 停止所有服务"
echo " restart 重启所有服务"
echo " status 查看运行状态"
echo " logs 查看日志 (logs [server|loop|all])"
echo ""
echo "环境变量:"
echo " EDICT_DASHBOARD_HOST 监听地址 (默认: 127.0.0.1)"
echo " EDICT_DASHBOARD_PORT 监听端口 (默认: 7891)"
exit 1
;;
esac