package main import ( "context" "expvar" "log" "runtime" "time" "github.com/FernandoVideira/LK_API_Temp/internal/auth" "github.com/FernandoVideira/LK_API_Temp/internal/db" "github.com/FernandoVideira/LK_API_Temp/internal/env" "github.com/FernandoVideira/LK_API_Temp/internal/mailer" "github.com/FernandoVideira/LK_API_Temp/internal/ratelimiter" "github.com/FernandoVideira/LK_API_Temp/internal/store" "github.com/FernandoVideira/LK_API_Temp/internal/store/cache" "github.com/go-redis/redis/v8" "go.uber.org/zap" ) const version = "1.0.2" // @title LK API Template // @description This is a template for building APIs with Go, Chi, and Swagger. // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @BasePath /v1 // // @securityDefinitions.apikey ApiKeyAuth // @in header // @name Authorization // @description func main() { cfg := config{ addr: env.GetString("ADDR", ":8080"), apiURL: env.GetString("EXTERNAL_URL", "localhost:8080"), frontEndURL: env.GetString("FRONTEND_URL", "http://localhost:5173"), db: dbConfig{ addr: env.GetString("DB_ADDR", "postgres://postgres:postgres@localhost:5432/lk_tmpl?sslmode=disable"), maxOpenConns: env.GetInt("DB_MAX_OPEN_CONNS", 30), maxIdleConns: env.GetInt("DB_MAX_IDLE_CONNS", 30), maxIdleTime: env.GetDuration("DB_MAX_IDLE_TIME", "15m"), }, redisCfg: redisConfig{ addr: env.GetString("REDIS_ADDR", "localhost:6379"), pw: env.GetString("REDIS_PASSWORD", ""), db: env.GetInt("REDIS_DB", 0), enabled: env.GetBool("REDIS_ENABLED", false), }, env: env.GetString("ENV", "development"), mail: mailConfig{ inviteExp: time.Hour * 24 * 3, passwordRefreshExp: time.Hour * 24 * 7, fromEmail: env.GetString("FROM_EMAIL", ""), sendGrid: sendGridConfig{ apiKey: env.GetString("SENDGRID_API_KEY", ""), }, }, auth: authConfig{ basic: basicConfig{ username: env.GetString("BASIC_AUTH_USERNAME", "admin"), password: env.GetString("BASIC_AUTH_PASSWORD", "admin"), }, token: tokenConfig{ secret: env.GetString("AUTH_TOKEN_SECRET", "secret"), exp: env.GetDuration("AUTH_TOKEN_EXP", "24h"), refreshExp: time.Hour * 24 * 7, iss: env.GetString("AUTH_TOKEN_ISS", "lkapi"), aud: env.GetString("AUTH_TOKEN_AUD", "lkapi"), }, }, rateLimiter: ratelimiter.Config{ RequestsPerTimeFrame: env.GetInt("RATE_LIMITER_REQS_COUNT", 20), TimeFrame: env.GetDuration("RATE_LIMITER_TIME_FRAME", "1m"), Enabled: env.GetBool("RATE_LIMITER_ENABLED", true), }, } // Logger logger := zap.Must(zap.NewProduction()).Sugar() defer logger.Sync() // Database db, err := db.New( cfg.db.addr, cfg.db.maxOpenConns, cfg.db.maxIdleConns, cfg.db.maxIdleTime, ) if err != nil { logger.Fatal(err) } defer db.Close() logger.Info("database connection established") // Cache var redisDb *redis.Client if cfg.redisCfg.enabled { redisDb = cache.NewRedisClient(cfg.redisCfg.addr, cfg.redisCfg.pw, cfg.redisCfg.db) logger.Info("redis connection established") } // Rate Limiter rateLimiter := ratelimiter.NewFixedWindowLimiter( cfg.rateLimiter.RequestsPerTimeFrame, cfg.rateLimiter.TimeFrame, ) store, err := store.NewStorage(db) if err != nil { logger.Fatal(err) } cacheStorage := cache.NewRedisStorage(redisDb) mailer := mailer.NewSendGrid(cfg.mail.sendGrid.apiKey, cfg.mail.fromEmail) jwtAuthenticator := auth.NewJWTAuthenticator( cfg.auth.token.secret, cfg.auth.token.aud, cfg.auth.token.iss, ) api := api{ config: cfg, store: store, cacheStore: cacheStorage, logger: logger, mailer: mailer, authenticator: jwtAuthenticator, rateLimiter: rateLimiter, } // Metrics Collected expvar.NewString("version").Set(version) expvar.Publish("database", expvar.Func(func() any { return db.Stats() })) expvar.Publish("redis", expvar.Func(func() any { return redisDb.Info(context.Background(), "keyspace") })) expvar.Publish("goroutines", expvar.Func(func() any { return runtime.NumGoroutine() })) mux := api.mount() log.Fatal(api.run(mux)) }