feat: run.sh四模式(dev/docker/build/binary) + CLI flags + Redka内嵌Redis + Agent复制修复 + 服务器编辑IP字段 + 安装Agent按钮
This commit is contained in:
1 parent
d0b94d39c0
commit
2354959811
14 files changed
+1355
-575
No files matched your search
+189
-13
@@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -12,18 +14,50 @@ import (
|
||||
"ezops/internal/config"
|
||||
"ezops/pkg/database"
|
||||
"ezops/pkg/logger"
|
||||
ezredis "ezops/pkg/redis"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 版本信息(通过编译时 ldflags 注入)
|
||||
// 版本信息(通过编译时 ldflags 注入)// Logan.Li
|
||||
var (
|
||||
Version = "dev"
|
||||
BuildTime = "unknown"
|
||||
GitCommit = "unknown"
|
||||
)
|
||||
|
||||
// CLI 参数定义 // Logan.Li
|
||||
var (
|
||||
flagPort = flag.String("port", "", "服务端口 (默认: 8080)")
|
||||
flagConfig = flag.String("config", "", "配置文件路径 (默认: config.yaml)")
|
||||
flagMode = flag.String("mode", "", "运行模式: development, production, release")
|
||||
flagLogLevel = flag.String("log-level", "", "日志级别: debug, info, warn, error")
|
||||
|
||||
// 数据库参数
|
||||
flagDBDriver = flag.String("db-driver", "", "数据库驱动: sqlite, mysql, postgres (默认: sqlite)")
|
||||
flagDBHost = flag.String("db-host", "", "数据库主机")
|
||||
flagDBPort = flag.Int("db-port", 0, "数据库端口")
|
||||
flagDBUser = flag.String("db-user", "", "数据库用户名")
|
||||
flagDBPassword = flag.String("db-password", "", "数据库密码")
|
||||
flagDBName = flag.String("db-name", "", "数据库名 / SQLite 文件路径")
|
||||
flagDBCharset = flag.String("db-charset", "", "数据库字符集")
|
||||
|
||||
// Redis 参数
|
||||
flagRedisHost = flag.String("redis-host", "", "Redis 主机")
|
||||
flagRedisPort = flag.Int("redis-port", 0, "Redis 端口")
|
||||
flagRedisPassword = flag.String("redis-password", "", "Redis 密码")
|
||||
flagRedisDB = flag.Int("redis-db", 0, "Redis 数据库编号")
|
||||
|
||||
// JWT 参数
|
||||
flagJWTSecret = flag.String("jwt-secret", "", "JWT 密钥")
|
||||
flagJWTExpire = flag.Int("jwt-expire", 0, "JWT 过期时间 (小时)")
|
||||
|
||||
// 显示版本
|
||||
flagVersion = flag.Bool("version", false, "显示版本信息")
|
||||
)
|
||||
|
||||
// @title AIOps运维平台 API
|
||||
// @version 1.0
|
||||
// @description AIOps智能运维平台后端API文档
|
||||
@@ -45,32 +79,67 @@ var (
|
||||
// @description Type "Bearer" followed by a space and JWT token.
|
||||
|
||||
func main() {
|
||||
// 加载配置
|
||||
cfg := config.Load()
|
||||
// 解析 CLI 参数 // Logan.Li
|
||||
flag.Parse()
|
||||
|
||||
// 显示版本信息 // Logan.Li
|
||||
if *flagVersion {
|
||||
fmt.Printf("AIOps 智能运维平台\n")
|
||||
fmt.Printf(" 版本: %s\n", Version)
|
||||
fmt.Printf(" 构建: %s\n", BuildTime)
|
||||
fmt.Printf(" 提交: %s\n", GitCommit)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// 加载配置 // Logan.Li
|
||||
cfg := loadConfig()
|
||||
|
||||
// 初始化日志
|
||||
log := logger.New(cfg.LogLevel)
|
||||
defer log.Sync()
|
||||
|
||||
log.Info("AIOps 启动中",
|
||||
zap.String("version", Version),
|
||||
zap.String("mode", cfg.Mode),
|
||||
zap.String("port", cfg.Port),
|
||||
)
|
||||
|
||||
// 设置Gin模式
|
||||
if cfg.Mode == "production" {
|
||||
if cfg.Mode == "production" || cfg.Mode == "release" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
// 初始化数据库
|
||||
db, err := database.New(cfg.Database)
|
||||
if err != nil {
|
||||
log.Fatal("failed to connect database", zap.Error(err))
|
||||
log.Fatal("数据库连接失败", zap.Error(err))
|
||||
}
|
||||
|
||||
// 自动迁移
|
||||
if err := database.AutoMigrate(db); err != nil {
|
||||
log.Fatal("failed to migrate database", zap.Error(err))
|
||||
log.Fatal("数据库迁移失败", zap.Error(err))
|
||||
}
|
||||
|
||||
// 初始化种子数据
|
||||
seedData(db)
|
||||
|
||||
// 初始化缓存(Redka 内嵌 / Redis 外部)// Logan.Li
|
||||
cache, err := ezredis.New(ezredis.Config{
|
||||
Driver: "redka", // 默认内嵌 Redka,零依赖
|
||||
Host: cfg.Redis.Host,
|
||||
Port: cfg.Redis.Port,
|
||||
Password: cfg.Redis.Password,
|
||||
DB: cfg.Redis.DB,
|
||||
DataDir: "data/redka",
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn("缓存初始化失败,继续启动", zap.Error(err))
|
||||
cache = nil
|
||||
} else {
|
||||
log.Info("缓存已就绪", zap.String("driver", "redka"))
|
||||
defer cache.Close()
|
||||
}
|
||||
|
||||
// 创建Gin实例
|
||||
r := gin.New()
|
||||
|
||||
@@ -85,11 +154,13 @@ func main() {
|
||||
"version": Version,
|
||||
"build_time": BuildTime,
|
||||
"git_commit": GitCommit,
|
||||
"mode": cfg.Mode,
|
||||
"db_driver": cfg.Database.Driver,
|
||||
})
|
||||
})
|
||||
|
||||
// 注册路由
|
||||
routes.Register(r, db, log, cfg)
|
||||
routes.Register(r, db, log, cfg, cache)
|
||||
|
||||
// 创建HTTP服务器
|
||||
srv := &http.Server{
|
||||
@@ -99,28 +170,133 @@ func main() {
|
||||
|
||||
// 优雅关闭
|
||||
go func() {
|
||||
log.Info("服务已启动", zap.String("addr", srv.Addr))
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal("failed to start server", zap.Error(err))
|
||||
log.Fatal("服务启动失败", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
log.Info("server started", zap.String("port", cfg.Port))
|
||||
|
||||
// 等待中断信号
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
log.Info("shutting down server...")
|
||||
log.Info("正在关闭服务...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
log.Fatal("server forced to shutdown", zap.Error(err))
|
||||
log.Fatal("强制关闭服务", zap.Error(err))
|
||||
}
|
||||
|
||||
log.Info("server exited")
|
||||
log.Info("服务已退出")
|
||||
}
|
||||
|
||||
// loadConfig 加载配置(CLI flags 优先于 config.yaml)// Logan.Li
|
||||
func loadConfig() *config.Config {
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath("./configs")
|
||||
viper.AddConfigPath(".")
|
||||
|
||||
// 如果指定了配置文件路径,使用它 // Logan.Li
|
||||
if *flagConfig != "" {
|
||||
viper.SetConfigFile(*flagConfig)
|
||||
}
|
||||
|
||||
// 默认值(独立二进制模式:SQLite + 无 Redis)// Logan.Li
|
||||
viper.SetDefault("mode", "release")
|
||||
viper.SetDefault("port", "8080")
|
||||
viper.SetDefault("log_level", "info")
|
||||
|
||||
viper.SetDefault("database.driver", "sqlite")
|
||||
viper.SetDefault("database.host", "localhost")
|
||||
viper.SetDefault("database.port", 3306)
|
||||
viper.SetDefault("database.user", "")
|
||||
viper.SetDefault("database.password", "")
|
||||
viper.SetDefault("database.dbname", "data/aiops.db")
|
||||
viper.SetDefault("database.charset", "utf8mb4")
|
||||
viper.SetDefault("database.max_conn", 100)
|
||||
viper.SetDefault("database.max_idle", 10)
|
||||
|
||||
viper.SetDefault("redis.host", "localhost")
|
||||
viper.SetDefault("redis.port", 6379)
|
||||
viper.SetDefault("redis.password", "")
|
||||
viper.SetDefault("redis.db", 0)
|
||||
|
||||
viper.SetDefault("jwt.secret", "aiops-jwt-secret-change-in-production")
|
||||
viper.SetDefault("jwt.expire_hour", 24)
|
||||
|
||||
viper.SetDefault("websocket.read_buffer_size", 1024)
|
||||
viper.SetDefault("websocket.write_buffer_size", 1024)
|
||||
|
||||
// 读取配置文件(不存在也不报错)// Logan.Li
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
// 配置文件不存在是正常的(独立二进制模式)
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
fmt.Fprintf(os.Stderr, "警告: 读取配置文件失败: %v,使用默认配置\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 环境变量覆盖 // Logan.Li
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// CLI flags 覆盖(最高优先级)// Logan.Li
|
||||
applyFlags()
|
||||
|
||||
// 解析配置 // Logan.Li
|
||||
var cfg config.Config
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "配置解析失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
||||
// applyFlags 将 CLI 参数映射到 viper(覆盖 config.yaml 和默认值)// Logan.Li
|
||||
func applyFlags() {
|
||||
// 检查是否有任何 flag 被显式设置 // Logan.Li
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
switch f.Name {
|
||||
case "port":
|
||||
viper.Set("port", *flagPort)
|
||||
case "mode":
|
||||
viper.Set("mode", *flagMode)
|
||||
case "log-level":
|
||||
viper.Set("log_level", *flagLogLevel)
|
||||
|
||||
case "db-driver":
|
||||
viper.Set("database.driver", *flagDBDriver)
|
||||
case "db-host":
|
||||
viper.Set("database.host", *flagDBHost)
|
||||
case "db-port":
|
||||
viper.Set("database.port", *flagDBPort)
|
||||
case "db-user":
|
||||
viper.Set("database.user", *flagDBUser)
|
||||
case "db-password":
|
||||
viper.Set("database.password", *flagDBPassword)
|
||||
case "db-name":
|
||||
viper.Set("database.dbname", *flagDBName)
|
||||
case "db-charset":
|
||||
viper.Set("database.charset", *flagDBCharset)
|
||||
|
||||
case "redis-host":
|
||||
viper.Set("redis.host", *flagRedisHost)
|
||||
case "redis-port":
|
||||
viper.Set("redis.port", *flagRedisPort)
|
||||
case "redis-password":
|
||||
viper.Set("redis.password", *flagRedisPassword)
|
||||
case "redis-db":
|
||||
viper.Set("redis.db", *flagRedisDB)
|
||||
|
||||
case "jwt-secret":
|
||||
viper.Set("jwt.secret", *flagJWTSecret)
|
||||
case "jwt-expire":
|
||||
viper.Set("jwt.expire_hour", *flagJWTExpire)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// CORS 跨域中间件
|
||||
|
||||
Reference in new issue
Block a user