feat(cli): 添加命令行敏感操作门禁和数据库管理功能
- 添加 migrate、seed、reset-all 命令行子命令 - 实现管理员密码门禁机制保护敏感操作 - 添加数据库表结构迁移和种子数据初始化功能 - 实现清空并重建全部表的 reset-all 功能 - 重构种子数据只包含系统必需基础数据,移除演示数据 - 添加 RBAC 权限角色初始化的命令行支持 - 配置命令行密码验证和操作日志记录功能
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/subtle"
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
@@ -13,6 +16,8 @@ import (
|
||||
"time"
|
||||
|
||||
"bj_power_wms/internal/config"
|
||||
"bj_power_wms/internal/db"
|
||||
"bj_power_wms/internal/eventlog"
|
||||
"bj_power_wms/internal/handler"
|
||||
"bj_power_wms/internal/logdaily"
|
||||
"bj_power_wms/internal/svc"
|
||||
@@ -69,6 +74,102 @@ func openBrowser(url string) {
|
||||
_ = cmd.Start()
|
||||
}
|
||||
|
||||
// promptPassword 从终端读取一行输入(不隐藏回显,用于运维终端操作)
|
||||
func promptPassword(msg string) string {
|
||||
fmt.Print(msg)
|
||||
line, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
return strings.TrimRight(line, "\r\n")
|
||||
}
|
||||
|
||||
// adminGate 管理员密码门禁:敏感命令行操作(migrate/seed/reset-all)必须输入与配置
|
||||
// Cli.AdminPassword 一致的密码才放行;校验失败记录日志并终止,绝不继续执行(对齐 MES)。
|
||||
func adminGate(c config.Config) bool {
|
||||
pwd := promptPassword("请输入管理员密码以执行 [" + flag.Arg(0) + "]: ")
|
||||
if subtle.ConstantTimeCompare([]byte(pwd), []byte(c.Cli.AdminPassword)) != 1 {
|
||||
logx.Errorf("敏感命令 [%s] 因管理员密码校验失败被终止", flag.Arg(0))
|
||||
fmt.Println("管理员密码不正确,已终止该操作。")
|
||||
return false
|
||||
}
|
||||
logx.Infof("敏感命令 [%s] 已通过管理员密码校验", flag.Arg(0))
|
||||
return true
|
||||
}
|
||||
|
||||
// dbConf 从服务配置构造数据库连接配置
|
||||
func dbConf(c config.Config) db.DatabaseConf {
|
||||
return db.DatabaseConf{
|
||||
Host: c.Postgres.Host,
|
||||
Port: c.Postgres.Port,
|
||||
User: c.Postgres.User,
|
||||
Password: c.Postgres.Password,
|
||||
Dbname: c.Postgres.DBName,
|
||||
}
|
||||
}
|
||||
|
||||
// runMigrate 仅建表/补列(只增,不删任何数据)
|
||||
func runMigrate(c config.Config) {
|
||||
dbc := dbConf(c)
|
||||
if err := db.EnsureDB(dbc); err != nil {
|
||||
logx.Errorf("ensure database failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
client := db.MustNewDB(dbc)
|
||||
defer client.Close()
|
||||
if err := db.AutoMigrate(client); err != nil {
|
||||
logx.Errorf("migrate failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("migrate: 数据库结构迁移成功(只增/补列,未删除任何数据)。")
|
||||
}
|
||||
|
||||
// runSeed 对齐基础主数据 + 权限角色(不删数据;表不存在则先建)
|
||||
func runSeed(c config.Config) {
|
||||
dbc := dbConf(c)
|
||||
if err := db.EnsureDB(dbc); err != nil {
|
||||
logx.Errorf("ensure database failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
client := db.MustNewDB(dbc)
|
||||
defer client.Close()
|
||||
if err := db.AutoMigrate(client); err != nil {
|
||||
logx.Errorf("seed migrate failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := db.SeedIfEmpty(client); err != nil {
|
||||
logx.Errorf("seed failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
svcCtx := &svc.ServiceContext{EntClient: client, EventLog: eventlog.NewWriter(client)}
|
||||
p, r, b := handler.SeedRBAC(svcCtx, "system")
|
||||
fmt.Printf("seed: 基础数据与权限已对齐(新增权限 %d、角色 %d、绑定用户 %d)。\n", p, r, b)
|
||||
}
|
||||
|
||||
// runResetAll 清空并重建全部表 + 重灌全部种子(基础主数据 + 权限角色)。此操作不可恢复!
|
||||
func runResetAll(c config.Config) {
|
||||
dbc := dbConf(c)
|
||||
if err := db.EnsureDB(dbc); err != nil {
|
||||
logx.Errorf("ensure database failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := db.DropAllTables(dbc); err != nil {
|
||||
logx.Errorf("reset-all drop failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
client := db.MustNewDB(dbc)
|
||||
defer client.Close()
|
||||
if err := db.AutoMigrate(client); err != nil {
|
||||
logx.Errorf("reset-all migrate failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := db.SeedIfEmpty(client); err != nil {
|
||||
logx.Errorf("reset-all seed failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
svcCtx := &svc.ServiceContext{EntClient: client, EventLog: eventlog.NewWriter(client)}
|
||||
p, r, b := handler.SeedRBAC(svcCtx, "system")
|
||||
logx.Infof("reset-all 完成:新增权限 %d、角色 %d、绑定用户 %d", p, r, b)
|
||||
fmt.Println("reset-all: 已清空并重建全部表,写入基础主数据与权限(管理员 admin/123456)。")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
@@ -83,6 +184,35 @@ func main() {
|
||||
logx.SetWriter(logx.NewWriter(w))
|
||||
}
|
||||
|
||||
// 命令行子命令:DDL/删库/种子等敏感操作一律显式触发 + 管理员密码门禁,
|
||||
// 绝不随服务启动自动执行(对齐 MES:migrate / seed / reset-all / serve)。
|
||||
switch cmd := flag.Arg(0); cmd {
|
||||
case "reset-all":
|
||||
fmt.Println("reset-all: 即将清空并重建 WMS 全部表,此操作不可恢复!")
|
||||
if !adminGate(c) {
|
||||
os.Exit(1)
|
||||
}
|
||||
runResetAll(c)
|
||||
return
|
||||
case "seed":
|
||||
if !adminGate(c) {
|
||||
os.Exit(1)
|
||||
}
|
||||
runSeed(c)
|
||||
return
|
||||
case "migrate":
|
||||
if !adminGate(c) {
|
||||
os.Exit(1)
|
||||
}
|
||||
runMigrate(c)
|
||||
return
|
||||
case "", "serve":
|
||||
// 正常启动服务:落到下方启动逻辑
|
||||
default:
|
||||
fmt.Println("未知命令: " + cmd + ";可用命令: migrate / seed / reset-all / serve")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
staticHandler := newStaticHandler(webFS)
|
||||
|
||||
// 兜底路由:已注册的 /api/* 走正常匹配;未注册的非 /api 路径走嵌入式静态资源
|
||||
|
||||
Reference in New Issue
Block a user