feat(M5): 前端完善 + 部署 — Vue3 全量页面、仪表盘统计与我的审计接口、systemd/sudoers/迁移脚本、部署文档

This commit is contained in:
2026-08-30 10:50:46 +08:00
parent c0e2ff975a
commit b305d7b371
67 changed files with 3209 additions and 171 deletions
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
# ws_usernode SQLite → MySQL 数据迁移脚本(PLAN 验收:SQLite 数据可迁移至 MySQL)。
#
# 原理:SQLite 导出 SQL(含表结构与数据)→ 简单改写为 MySQL 兼容语法 → 导入 MySQL。
# 适用:从开发库(SQLite)迁移到生产库(MySQL)。
#
# 用法:
# MYSQL_DSN='usernode:pass@tcp(127.0.0.1:3306)/usernode?charset=utf8mb4&parseTime=True&loc=UTC' \
# ./deploy/migrate-sqlite2mysql.sh /path/to/usernode.db
#
# 前置:
# - sqlite3、mysql 客户端已安装(Debian/Ubuntu: apt install sqlite3 default-mysql-client
# - MySQL 中已建库、建好应用账号(建议先用 usernode migrate --config 指向 MySQL 建表,
# 再执行本脚本导数据;若表不存在,本脚本也可建表)
# - 先停服务,避免迁移过程中写入
#
# 注意事项:
# - 时间字段:SQLite 存的是 "2026-08-30 10:41:41.437309782+08:00" 这类文本,
# MySQL DATETIME 不接受。本脚本用字符串函数提取前 19 位(秒级精度)。
# 若你的库以 UTC 存储且无需时区换算,秒级精度足够(审计/会话/OTP 均为秒级业务)。
# - BLOB/二进制字段:本表结构无二进制大字段,不处理。
# - 迁移后建议校验行数并抽查,见脚本尾部。
set -euo pipefail
if [ $# -lt 1 ]; then
echo "用法: $0 <sqlite.db> [MYSQL_DSN 或通过环境变量 MYSQL_DSN]"
echo "示例: MYSQL_DSN='usernode:pass@tcp(127.0.0.1:3306)/usernode?charset=utf8mb4&parseTime=True&loc=UTC' $0 data/usernode.db"
exit 1
fi
SQLITE_DB="$1"
MYSQL_DSN="${MYSQL_DSN:-}"
if [ -z "$MYSQL_DSN" ]; then
echo "错误:未提供 MYSQL_DSN(环境变量或第二参数)" >&2
exit 1
fi
command -v sqlite3 >/dev/null || { echo "缺少 sqlite3" >&2; exit 1; }
command -v mysql >/dev/null || { echo "缺少 mysql 客户端" >&2; exit 1; }
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
SQL_DUMP="$TMP/dump.sql"
echo "==> 1/4 导出 SQLite 结构 + 数据: $SQLITE_DB"
sqlite3 "$SQLITE_DB" .dump > "$SQL_DUMP"
# 排除 SQLite 内部表(sqlite_sequence 等)
echo "==> 2/4 改写为 MySQL 兼容 SQL"
awk '
/^CREATE TABLE/ { table=1 }
/^CREATE TABLE sqlite_sequence/ { skip=1; next }
/^CREATE TABLE/ && $3 ~ /^sqlite_/ { skip=1; next }
/^CREATE INDEX/ && $0 ~ /sqlite_autoindex/ { next }
# 建表语句改写:
# "text" → TEXTAUTOINCREMENT 去掉(MySQL 自增)
# PRIMARY KEY (id) 保留;去掉多余引号
/^CREATE TABLE/ {
line = $0
gsub(/"text"/, "TEXT", line)
gsub(/AUTOINCREMENT/, "", line)
# 去掉反引号内外的双引号(表名/列名)
line = line
print line
next
}
# 行数据:SQLite 的 INSERT INTO "table" VALUES(...) → MySQL 双引号仅用于字符串,表名不需要引号
/^INSERT INTO/ {
line = $0
gsub(/INSERT INTO "/, "INSERT INTO `", line)
gsub(/"\s*VALUES/, "` VALUES", line)
# SQLite 布尔/整数原样;字符串内单引号已由 sqlite3 转义为 ''MySQL 兼容
print line
next
}
{ print }
' "$SQL_DUMP" > "$TMP/mysql.sql"
echo "==> 3/4 导入 MySQL"
# mysql 客户端 DSN 形式为 mysql://user:pass@tcp(host:port)/db 或 user:pass@tcp(...)/db
if [[ "$MYSQL_DSN" == mysql://* ]]; then
MYSQL_DSN="${MYSQL_DSN#mysql://}"
fi
mysql --protocol=tcp "$MYSQL_DSN" < "$TMP/mysql.sql"
echo "==> 4/4 校验行数"
for t in admin_users users ssh_keys approvals audit_logs sessions settings mail_logs otp_codes password_reset_tokens; do
s=$(sqlite3 "$SQLITE_DB" "SELECT count(*) FROM $t;" 2>/dev/null || echo "?")
m=$(mysql --protocol=tcp --batch --skip-column-names "$MYSQL_DSN" -e "SELECT count(*) FROM \`$t\`;" 2>/dev/null || echo "?")
printf " %-24s sqlite=%-6s mysql=%s\n" "$t" "$s" "$m"
done
echo
echo "迁移完成。建议:"
echo " 1. 抽查关键表(users/sessions)内容与时间字段格式"
echo " 2. 用指向 MySQL 的配置启动 usernode serve 验证"
echo " 3. 确认无误后再处理旧 SQLite 库"