package config import ( "cls/cls" Ihttp "cls/pkg/http" "cls/pkg/logger" "cls/pkg/sms" "cls/pkg/util/security" "cls/pkg/xorm_engine" "context" "fmt" _ "github.com/go-sql-driver/mysql" "net/http" "time" auth_middleware "cls/internal/infrastructure/middleware/auth" "github.com/ClickHouse/clickhouse-go/v2" "github.com/cenkalti/backoff/v4" "github.com/redis/go-redis/v9" "net" "strings" middleware "cls/pkg/web/middleware" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" "github.com/gin-gonic/gin" "go.uber.org/fx" "xorm.io/xorm" "xorm.io/xorm/names" ) func NewGinEngine(lc fx.Lifecycle, auth *auth_middleware.AuthMiddleware, appConfig *AppConfig) *gin.Engine { if strings.ToLower(appConfig.ServerConfig.Mode) == "prod" { gin.SetMode(gin.ReleaseMode) } handler := gin.New() handler.Use(gin.Recovery()) handler.Use(auth.Handle()) handler.Use(middleware.ServeRoot("/", "dist", &cls.RESOURCE)) handler.NoRoute(func(context *gin.Context) { if context.Request.RequestURI != "/" { context.Redirect(http.StatusTemporaryRedirect, "/") } }) srv := &http.Server{Addr: fmt.Sprintf(":%d", appConfig.ServerConfig.Port), Handler: handler} lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { ln, err := net.Listen("tcp", srv.Addr) if err != nil { return err } log.Infof("Web server starting on port %v", srv.Addr) log.Info("Press Ctrl+C to stop") go func() { if err0 := srv.Serve(ln); err0 != nil { log.Error(err0) } }() return nil }, OnStop: func(ctx context.Context) error { return srv.Shutdown(ctx) }, }) return handler } func NewXormEngine(appConfig *AppConfig, l logger.New) *xorm_engine.Engine { dbConfig := appConfig.MysqlConfig url := fmt.Sprintf("%s:%s@(%s)/%s", dbConfig.Username, dbConfig.Password, dbConfig.Address, dbConfig.Database) engine, err := xorm.NewEngineWithParams("mysql", url, dbConfig.Params) if err != nil { log.Fatal(err) } fmt.Println("done") engine.SetTableMapper(names.NewPrefixMapper(names.GonicMapper{}, "lc_")) engine.SetColumnMapper(names.GonicMapper{}) engine.SetLogger(logger.NewXormLogger(l("xorm"))) if dbConfig.ShowSql { engine.ShowSQL(true) } if err = backoff.Retry(engine.Ping, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } urlCls := fmt.Sprintf("%s:%s@(%s)/%s", dbConfig.Username, dbConfig.Password, dbConfig.Address, dbConfig.DatabaseCls) engineCls, err := xorm.NewEngineWithParams("mysql", urlCls, dbConfig.Params) if err != nil { log.Fatal(err) } if err = backoff.Retry(engineCls.Ping, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } return xorm_engine.NewEngine(engine, engineCls) } func NewClickhouseConn(appConfig *AppConfig) driver.Conn { dbConfig := appConfig.ClickhouseConfig conn, err := clickhouse.Open(&clickhouse.Options{ Addr: dbConfig.Address, Auth: clickhouse.Auth{ Database: dbConfig.Database, Username: dbConfig.Username, Password: dbConfig.Password, }, Settings: clickhouse.Settings{ "max_execution_time": 60, }, Compression: &clickhouse.Compression{ Method: clickhouse.CompressionLZ4, }, DialTimeout: time.Duration(10) * time.Second, MaxOpenConns: 5, MaxIdleConns: 5, ConnMaxLifetime: time.Duration(10) * time.Minute, ConnOpenStrategy: clickhouse.ConnOpenInOrder, BlockBufferSize: 10, }) if err != nil { log.Fatal(err) } if err = backoff.Retry(func() error { return conn.Ping(context.Background()) }, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } if v, err := conn.ServerVersion(); err != nil { log.Fatal(err) } else { log.Info(v) } return conn } func NewRedisClient(appConfig *AppConfig) redis.Cmdable { dbConfig := appConfig.RedisConfig var rdbCli redis.Cmdable switch dbConfig.Mode { case "sentinel": rdbCli = redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: dbConfig.MasterName, SentinelAddrs: dbConfig.SentinelAddrs, SentinelUsername: dbConfig.SentinelUsername, SentinelPassword: dbConfig.SentinelPassword, Username: dbConfig.Username, Password: dbConfig.Password, DB: 0, }) case "cluster": rdbCli = redis.NewClusterClient(&redis.ClusterOptions{ Addrs: dbConfig.Addrs, Username: dbConfig.Username, Password: dbConfig.Password, }) default: rdbCli = redis.NewClient(&redis.Options{ Addr: dbConfig.Addr, Username: dbConfig.Username, Password: dbConfig.Password, DB: dbConfig.DB, }) } if err := backoff.Retry(func() error { return rdbCli.Ping(context.Background()).Err() }, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } return rdbCli } func NewLoggerFactory(config *AppConfig) logger.New { return func(name string) logger.Logger { var options []logger.Option if v, ok := config.LogConfig.Loggers[strings.ToLower(name)]; ok { options = append(options, logger.Level(logger.LogLevel(v))) } return logger.NewLogger(name, options...) } } func NewPasswordEncoder() security.PasswordEncoder { return &security.BCryptPasswordEncoder{} } func NewSmsService() sms.IsmsService { return sms.NewSmsService() } func NewInternalClient() *Ihttp.Client { return Ihttp.NewClient() } func NewJWTAuthMiddleware(appConfig *AppConfig, log logger.New, redis redis.Cmdable) *auth_middleware.AuthMiddleware { return auth_middleware.NewAuthMiddleware(appConfig.JwtConfig.Secret, nil, redis, log) }