package main import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" "primaya-api/cpone-middleware/internal/config" "primaya-api/cpone-middleware/internal/databaseconfig" apphttp "primaya-api/cpone-middleware/internal/http" ) func main() { cfg, err := config.Load(".env") if err != nil { log.Fatalf("load config: %v", err) } bootstrapCtx, bootstrapCancel := context.WithTimeout(context.Background(), 20*time.Second) registry, err := databaseconfig.NewRegistry(bootstrapCtx, databaseconfig.Setting{ Host: cfg.ManagementDBHost, Port: cfg.ManagementDBPort, Database: cfg.ManagementDBDatabase, Username: cfg.ManagementDBUsername, Password: cfg.ManagementDBPassword, }, cfg.DBDefaultRSCode, databaseconfig.Setting{ RSCode: cfg.DBDefaultRSCode, Name: cfg.DBDefaultRSName, Host: cfg.DBHost, Port: cfg.DBPort, Database: cfg.DBDatabase, Username: cfg.DBUsername, Password: cfg.DBPassword, }, databaseconfig.PoolConfig{ MaxOpenConns: cfg.DBMaxOpenConns, MaxIdleConns: cfg.DBMaxIdleConns, ConnMaxLifetime: cfg.DBConnMaxLifetime, }, cfg.DBCredentialSecret) bootstrapCancel() if err != nil { log.Fatalf("load database registry: %v", err) } defer registry.Close() defaultDBCtx, defaultDBCancel := context.WithTimeout(context.Background(), 10*time.Second) repo, err := registry.Resolve(defaultDBCtx, cfg.DBDefaultRSCode) defaultDBCancel() if err != nil { log.Fatalf("ping mysql: %v", err) } handler := apphttp.NewHandler(repo) settingsHandler := apphttp.NewDatabaseSettingsHandler(registry) protected := func(handler http.Handler) http.Handler { return apphttp.BearerAuth(cfg.BearerToken, apphttp.SelectDatabase(registry, handler)) } adminProtected := func(handler http.Handler) http.Handler { return apphttp.BearerAuth(cfg.DBSettingsToken, handler) } mux := http.NewServeMux() mux.Handle("GET /api/cpone/paket", protected(http.HandlerFunc(handler.ListPaket))) mux.Handle("GET /api/cpone/paket/{id}", protected(http.HandlerFunc(handler.GetPaket))) mux.Handle("GET /api/cpone/tarif-kerjasama", protected(http.HandlerFunc(handler.ListTarifKerjasama))) mux.Handle("GET /api/cpone/tarif-kerjasama/{id}", protected(http.HandlerFunc(handler.GetTarifKerjasama))) mux.Handle("GET /api/cpone/layanan-mapping-harga", protected(http.HandlerFunc(handler.MappingHarga))) mux.Handle("POST /api/cpone/patients/medrec", protected(http.HandlerFunc(handler.StoreMedrec))) mux.Handle("POST /api/cpone/patients/lab-registration", protected(http.HandlerFunc(handler.RegisterLab))) mux.Handle("POST /api/cpone/patients/lab-registration/{regId}/services", protected(http.HandlerFunc(handler.AddLabServices))) mux.Handle("POST /api/cpone/patients/lab-registration/{regId}/packages", protected(http.HandlerFunc(handler.AddLabPackage))) mux.Handle("GET /api/cpone/patients/lab-results/{labNumber}", protected(http.HandlerFunc(handler.GetLabResult))) mux.Handle("GET /api/cpone/database-settings", adminProtected(http.HandlerFunc(settingsHandler.List))) mux.Handle("GET /api/cpone/database-settings/{kodeRs}", adminProtected(http.HandlerFunc(settingsHandler.Get))) mux.Handle("POST /api/cpone/database-settings", adminProtected(http.HandlerFunc(settingsHandler.Store))) mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"success":true,"message":"ok","data":null}`)) }) server := &http.Server{ Addr: cfg.Addr(), Handler: mux, ReadHeaderTimeout: 10 * time.Second, } go func() { log.Printf("cpone middleware listening on %s", cfg.Addr()) if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("listen: %v", err) } }() stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) <-stop shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() if err := server.Shutdown(shutdownCtx); err != nil { log.Fatalf("shutdown: %v", err) } }