sipd-auth/main.go
2025-09-16 08:32:11 +07:00

360 lines
10 KiB
Go

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...)
}