package main import ( "context" "database/sql" "encoding/json" "fmt" "kemendagri/sipd/services/sipd_auth/controller" "kemendagri/sipd/services/sipd_auth/handler/http" "kemendagri/sipd/services/sipd_auth/handler/http/configs" "kemendagri/sipd/services/sipd_auth/handler/http/http_util" _deliveryMiddleware "kemendagri/sipd/services/sipd_auth/handler/http/middleware" db2 "kemendagri/sipd/services/sipd_auth/model/db" "kemendagri/sipd/services/sipd_auth/utils" "kemendagri/sipd/services/sipd_auth/utils/captcha_store" "os" "path" "strconv" "strings" "time" swagger "github.com/arsmn/fiber-swagger/v2" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "github.com/gofiber/storage/redis/v3" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database" _ "github.com/golang-migrate/migrate/v4/source/file" "github.com/jackc/pgx/v5/pgxpool" _ "github.com/lib/pq" "github.com/minio/minio-go/v7" "github.com/sirupsen/logrus" swagDoc "kemendagri/sipd/services/sipd_auth/docs" // load API Docs files (Swagger) ) var serverName, serverUrl, serverReadTimeout, dbServerUrl, dbServerUrlPegawai, dbServerUrlMstData, dbServerUrlTransaksi, jwtSecertKey, jwtExpiredMinutes, refreshTokenExpiredHour, alwOrg, redisHost, redisUsername, redisPassword, urlScheme, baseUrl, basePath, avYear string var usedProvArr, avYearArr []string var redisPort int var db *sql.DB var pgxConn, pgxConnPegawai, pgxConnMstData *pgxpool.Pool var dbConnMapsAnggaran map[string]*pgxpool.Pool var driver database.Driver var migration *migrate.Migrate var jwtMgr *utils.JWTManager var vld *validator.Validate var minioClient *minio.Client var redisCl *redis.Storage var logger *logrus.Logger var err error func init() { // Server Env serverName = os.Getenv("SERVER_NAME") if serverName == "" { exitf("SERVER_NAME env is required") } serverUrl = os.Getenv("SERVER_URL") if serverUrl == "" { exitf("SERVER_URL env is required") } serverReadTimeout = os.Getenv("SERVER_READ_TIMEOUT") if serverReadTimeout == "" { exitf("SERVER_READ_TIMEOUT env is required") } // JWT Env jwtSecertKey = os.Getenv("JWT_SECRET_KEY") if jwtSecertKey == "" { exitf("JWT_SECRET_KEY env is required") } jwtExpiredMinutes = os.Getenv("JWT_EXPIRED_MINUTES") if jwtExpiredMinutes == "" { exitf("JWT_EXPIRED_MINUTES env is required") } refreshTokenExpiredHour = os.Getenv("REFRESH_TOKEN_EXPIRED_HOUR") if refreshTokenExpiredHour == "" { exitf("REFRESH_TOKEN_EXPIRED_HOUR env is required") } // CORS alwOrg = os.Getenv("SIPD_CORS_WHITELISTS") if alwOrg == "" { exitf("SIPD_CORS_WHITELISTS config is required") } urlScheme = os.Getenv("URL_SCHEME") if urlScheme == "" { exitf("URL_SCHEME config is required") } baseUrl = os.Getenv("BASE_URL") if baseUrl == "" { exitf("BASE_URL config is required") } /*basePath = os.Getenv("BASE_PATH") if basePath == "" { exitf("BASE_PATH config is required") }*/ avYear = os.Getenv("AVAILABLE_YEAR") if avYear == "" { exitf("AVAILABLE_YEAR config is required") } avYearArr = strings.Split(avYear, ",") // Databse Env dbServerUrl = os.Getenv("DB_SIPD_AUTH") if dbServerUrl == "" { exitf("DB_SIPD_AUTH config is required") } dbServerUrlPegawai = os.Getenv("DB_SIPD_PEGAWAI") if dbServerUrlPegawai == "" { exitf("DB_SIPD_PEGAWAI config is required") } dbServerUrlMstData = os.Getenv("DB_SIPD_MST_DATA") if dbServerUrlMstData == "" { exitf("DB_SIPD_MST_DATA config is required") } dbServerUrlTransaksi = os.Getenv("DB_SIPD_TRANSAKSI") if dbServerUrlTransaksi == "" { exitf("DB_SIPD_TRANSAKSI config is required") } var dbConnObj db2.DatabaseConnProv err = json.Unmarshal([]byte(dbServerUrlTransaksi), &dbConnObj) if err != nil { exitf("gagal encode db connection object: %v\n", err) } for _, y := range avYearArr { yPrefix := "_" + y for _, p := range dbConnObj { usedProvArr = append(usedProvArr, p.DdnProv+yPrefix) // penganggaran err = os.Setenv( "DB_SIPD_TRANSAKSI_"+p.DdnProv+yPrefix, fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s%s sslmode=%s application_name=%s", p.Host, p.Port, p.User, p.Password, p.Dbname, yPrefix, p.SslMode, serverName, ), ) if err != nil { exitf("gagal set env (%s) : %v\n", p.DdnProv+yPrefix, err) } } } } func dbConnection() { var maxConnLifetime, maxConnIdleTime time.Duration var maxPoolConn int32 maxConnLifetime = 5 * time.Minute maxConnIdleTime = 2 * time.Minute maxPoolConn = 1000 var cfg, cfgMstData, cfgPegawai *pgxpool.Config // auth cfg, err = pgxpool.ParseConfig(dbServerUrl + " application_name=" + serverName) if err != nil { exitf("Unable to create db pool config auth %v\n", err) } cfg.MaxConns = maxPoolConn // Maximum total connections in the pool cfg.MaxConnLifetime = maxConnLifetime // Maximum lifetime of a connection cfg.MaxConnIdleTime = maxConnIdleTime // Maximum time a connection can be idle pgxConn, err = pgxpool.NewWithConfig(context.Background(), cfg) if err != nil { exitf("Unable to connect to database auth: %v\n", err) } // pegawai cfgPegawai, err = pgxpool.ParseConfig(dbServerUrlPegawai + " application_name=" + serverName) if err != nil { exitf("Unable to create db pool config pegawai %v\n", err) } cfgPegawai.MaxConns = maxPoolConn // Maximum total connections in the pool cfgPegawai.MaxConnLifetime = maxConnLifetime // Maximum lifetime of a connection cfgPegawai.MaxConnIdleTime = maxConnIdleTime // Maximum time a connection can be idle pgxConnPegawai, err = pgxpool.NewWithConfig(context.Background(), cfgPegawai) if err != nil { exitf("Unable to connect to database pegawai: %v\n", err) } // mst_data cfgMstData, err = pgxpool.ParseConfig(dbServerUrlMstData + " application_name=" + serverName) if err != nil { exitf("Unable to create db pool config mst_data %v\n", err) } cfgMstData.MaxConns = maxPoolConn // Maximum total connections in the pool cfgMstData.MaxConnLifetime = maxConnLifetime // Maximum lifetime of a connection cfgMstData.MaxConnIdleTime = maxConnIdleTime // Maximum time a connection can be idle pgxConnMstData, err = pgxpool.NewWithConfig(context.Background(), cfgMstData) if err != nil { exitf("Unable to connect to database mst_data: %v\n", err) } dbConnMapsAnggaran = map[string]*pgxpool.Pool{} for _, kodeProv := range usedProvArr { // penganggaran cfg, err = pgxpool.ParseConfig(os.Getenv("DB_SIPD_TRANSAKSI_"+kodeProv) + " application_name=" + serverName) if err != nil { exitf("Unable to create db pool config referensi %v\n", err) } cfg.MaxConns = maxPoolConn // Maximum total connections in the pool cfg.MaxConnLifetime = maxConnLifetime // Maximum lifetime of a connection cfg.MaxConnIdleTime = maxConnIdleTime // Maximum time a connection can be idle dbConnMapsAnggaran[kodeProv], err = pgxpool.NewWithConfig(context.Background(), cfg) if err != nil { exitf("Unable to connect to database referensi %s: %v\n", kodeProv, err) } } } // @title SIPD Service Auth // @version 1.0 // @description SIPD Service Auth Rest API. // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.email lifelinejar@mail.com // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @securityDefinitions.apikey ApiKeyAuth // @in header // @name Authorization // @BasePath /auth/ func main() { dbConnection() defer func() { pgxConn.Close() pgxConnMstData.Close() }() serverReadTimeoutInt, err := strconv.Atoi(serverReadTimeout) if err != nil { exitf("Failed casting timeout context: ", err) } timeoutContext := time.Duration(serverReadTimeoutInt) * time.Second // Define a validator vld = utils.NewValidator() // jwt manager jwtMgr = utils.NewJWTManager(jwtSecertKey, serverName) swagDoc.SwaggerInfo.Host = "http://localhost" swagDoc.SwaggerInfo.BasePath = "/" // Define Fiber config. config := configs.FiberConfig() app := fiber.New(config) app.Static("/assets", "./assets") // Swagger handler //app.Get("/swagger/*", swagger.HandlerDefault) swagDoc.SwaggerInfo.Host = baseUrl swagDoc.SwaggerInfo.BasePath = basePath app.Get("/swagger/*", swagger.New(swagger.Config{ // URL: urlScheme + baseUrl + basePath + "swagger/doc.json", // default search box URL: urlScheme + baseUrl + path.Join(basePath, "swagger/doc.json"), })) middL := _deliveryMiddleware.InitMiddleware(app, redisCl, logger) //app.Use(middL.RateLimiter()) app.Use(middL.CORS()) app.Use(middL.LOGGER()) //app.Use(middL.RateLimiter()) app.Use(func(c *fiber.Ctx) error { // Middleware to check for whitelisted domains if alwOrg == "*" { // Continue to the next middleware/handler return c.Next() } // Use "X-Forwarded-Host" to simulate the Host header in Postman origin := c.Get("Origin") // log.Println("Origin: ", origin) alwOrgArr := strings.Split(alwOrg, ",") // log.Println("alwOrgArr: ", alwOrgArr) var originMatch bool for _, alo := range alwOrgArr { if origin == alo { originMatch = true break } else { /*host := c.Hostname() // log.Println("Host: ", host) if "https://"+host == alo || "http://"+host == alo { originMatch = true break }*/ } } if !originMatch { // log.Println("not match") return c.Status(fiber.StatusForbidden).SendString("403 - AU: origin not allowed") } // Continue to the next middleware/handler return c.Next() }) http.NewSiteHandler(app, controller.NewSiteController(pgxConn, timeoutContext), vld) captchaStore := captcha_store.NewPostgreSQLStore(pgxConn) http.NewCaptchaHandler(app, controller.NewCaptchaController(captchaStore, timeoutContext, vld)) authController := controller.NewAuthController( pgxConn, pgxConnPegawai, pgxConnMstData, dbConnMapsAnggaran, timeoutContext, jwtMgr, vld, ) http.NewAuthHandler(app, authController, vld) // private router rStrict := app.Group("/strict", middL.JWT()) // router for api private access userController := controller.NewUserController(pgxConn, pgxConnPegawai, pgxConnMstData, dbConnMapsAnggaran, minioClient, timeoutContext, redisCl) http.NewUserHandler(rStrict, vld, userController) // end router http_util.StartServer(app) } func exitf(s string, args ...interface{}) { errorf(s, args...) os.Exit(1) } func errorf(s string, args ...interface{}) { fmt.Fprintf(os.Stderr, s+"\n", args...) }