first commit

This commit is contained in:
meninjar
2026-04-14 01:21:54 +00:00
commit 35c101725f
443 changed files with 1245931 additions and 0 deletions
@@ -0,0 +1,123 @@
package servers
import (
"service/internal/infrastructure/cache"
"service/internal/infrastructure/config"
"service/internal/infrastructure/database"
"gorm.io/gorm"
"service/internal/auth"
antrol "service/internal/bpjs/antrol/reference"
aplicareBed "service/internal/bpjs/aplicare/bed"
apotekDpho "service/internal/bpjs/apotek/reference/dpho"
apotekPoli "service/internal/bpjs/apotek/reference/poli"
peserta "service/internal/bpjs/vclaim/peserta"
sep "service/internal/bpjs/vclaim/sep"
roleMaster "service/internal/master/role/master"
rolePages "service/internal/master/role/pages"
rolePermission "service/internal/master/role/permission"
satuSehatAuth "service/internal/satusehat/reference/auth"
satuSehatKfa "service/internal/satusehat/reference/kfa"
satuSehatLocation "service/internal/satusehat/reference/location"
satuSehatOrganization "service/internal/satusehat/reference/organization"
satuSehatPatient "service/internal/satusehat/reference/patient"
satuSehatPractitioner "service/internal/satusehat/reference/practitioner"
"service/internal/satusehat/usecase/allergyintolerance"
"service/internal/satusehat/usecase/careplan"
clinicalimpression "service/internal/satusehat/usecase/clinicalImpression"
"service/internal/satusehat/usecase/composition"
"service/internal/satusehat/usecase/condition"
"service/internal/satusehat/usecase/diagnosticreport"
"service/internal/satusehat/usecase/encounter"
"service/internal/satusehat/usecase/episodeofcare"
"service/internal/satusehat/usecase/imagingstudy"
"service/internal/satusehat/usecase/immunization"
"service/internal/satusehat/usecase/medication"
"service/internal/satusehat/usecase/medicationdispense"
"service/internal/satusehat/usecase/medicationrequest"
"service/internal/satusehat/usecase/medicationstatement"
"service/internal/satusehat/usecase/observation"
"service/internal/satusehat/usecase/procedure"
"service/internal/satusehat/usecase/questionnaireresponse"
"service/internal/satusehat/usecase/servicerequest"
"service/internal/satusehat/usecase/specimen"
)
// MasterServices menampung kumpulan service untuk domain Master Data
type MasterServices struct {
RolePages rolePages.Service
RolePermission rolePermission.Service
RoleMaster roleMaster.Service
}
// VClaimServices menampung kumpulan service untuk integrasi BPJS VClaim
type VClaimServices struct {
Sep sep.Service
Peserta peserta.Service
}
// AntrolServices menampung kumpulan service untuk integrasi BPJS Antrean RS
type AntrolServices struct {
Reference antrol.Service
}
// AplicaresServices menampung kumpulan service untuk integrasi BPJS Aplicares
type AplicaresServices struct {
Bed aplicareBed.Service
}
// ApotekServices menampung kumpulan service untuk integrasi BPJS Apotek
type ApotekServices struct {
DPHO apotekDpho.Service
Poli apotekPoli.Service
}
// SatuSehatServices menampung kumpulan service untuk integrasi Kemenkes Satu Sehat
type SatuSehatServices struct {
Auth satuSehatAuth.Service
Patient satuSehatPatient.Service
Practitioner satuSehatPractitioner.Service
Organization satuSehatOrganization.Service
Location satuSehatLocation.Service
KFA satuSehatKfa.Service
// Usecase Services
AllergyIntolerance allergyintolerance.Service
CarePlan careplan.Service
ClinicalImpression clinicalimpression.Service
Composition composition.Service
Condition condition.Service
DiagnosticReport diagnosticreport.Service
Encounter encounter.Service
EpisodeOfCare episodeofcare.Service
ImagingStudy imagingstudy.Service
Immunization immunization.Service
Medication medication.Service
MedicationDispense medicationdispense.Service
MedicationRequest medicationrequest.Service
MedicationStatement medicationstatement.Service
Observation observation.Service
Procedure procedure.Service
QuestionnaireResponse questionnaireresponse.Service
ServiceRequest servicerequest.Service
Specimen specimen.Service
}
// ServiceRegistry berfungsi sebagai Dependency Injection Container untuk transport HTTP.
// Struktur ini mencegah membengkaknya parameter pada saat inisialisasi API.
type ServiceRegistry struct {
Config *config.Config
DBManager database.Service
PrimaryDB *gorm.DB
CacheManager *cache.Manager
// Modul Aplikasi (Pisahkan berdasarkan Bounded Context)
AuthService auth.Service
Master *MasterServices
VClaim *VClaimServices
Antrol *AntrolServices
Aplicare *AplicaresServices
Apotek *ApotekServices
SatuSehat *SatuSehatServices
}
@@ -0,0 +1,266 @@
// internal/infrastructure/transport/http/servers/server.go
package servers
import (
"context" // PERBAIKAN: Tambahkan import untuk graceful shutdown
"fmt"
"log"
"net/http"
"time"
"service/internal/infrastructure/cache" // PERBAIKAN: Tambahkan import cache
"service/internal/infrastructure/config"
"service/internal/infrastructure/transport/http/middleware"
"service/internal/infrastructure/transport/http/routes"
antrolHttp "service/internal/infrastructure/transport/http/handlers/bpjs/antrol"
aplicareHttp "service/internal/infrastructure/transport/http/handlers/bpjs/aplicare"
apotekHttp "service/internal/infrastructure/transport/http/handlers/bpjs/apotek"
vclaimHttp "service/internal/infrastructure/transport/http/handlers/bpjs/vclaim"
authHttp "service/internal/infrastructure/transport/http/handlers/main/auth"
healthHttp "service/internal/infrastructure/transport/http/handlers/main/health"
roleHttp "service/internal/infrastructure/transport/http/handlers/main/master/roles"
satuSehatRefHttp "service/internal/infrastructure/transport/http/handlers/satusehat/reference"
satuSehatUsecaseHttp "service/internal/infrastructure/transport/http/handlers/satusehat/usecase"
// Inisialisasi pkg
"service/pkg/errors"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
type Server struct {
engine *gin.Engine
config *config.ServerRESTConfig
cacheConfig cache.CacheConfig // PERBAIKAN: Tambahkan cacheConfig
httpServer *http.Server // PERBAIKAN: Simpan instance http.Server untuk graceful shutdown
}
// NewHTTPServer membuat instance server HTTP baru
func NewHTTPServer(
restConfig *config.ServerRESTConfig,
registry *ServiceRegistry,
) *Server {
if !restConfig.Enabled {
log.Println("HTTP server is disabled in configuration.")
return &Server{
config: restConfig,
}
}
// Set mode Gin berdasarkan konfigurasi global
if registry.Config.Server.Mode == "release" {
gin.SetMode(gin.ReleaseMode)
}
engine := gin.New()
// Middleware untuk recovery dan penanganan error konsisten.
// Diletakkan paling awal untuk menangkap panic dari middleware/handler lain.
engine.Use(errors.CombinedMiddleware())
// Middleware lainnya
engine.Use(middleware.LoggingMiddleware())
engine.Use(middleware.SecurityMiddleware())
engine.Use(middleware.CORSMiddleware())
// PERBAIKAN: Berikan cacheManager ke RateLimitMiddleware
engine.Use(middleware.RateLimitMiddleware(registry.CacheManager))
// Inisialisasi ModuleHandlers secara dinamis berdasarkan fitur yang aktif
appHandlers := &routes.ModuleHandlers{}
if registry.AuthService != nil {
appHandlers.Auth = authHttp.NewAuthHandler(registry.AuthService)
}
// Inisialisasi Handlers Modul Master
if registry.Master != nil {
if registry.Master.RolePages != nil {
appHandlers.RolePages = roleHttp.NewRolPagesHandler(registry.Master.RolePages)
}
if registry.Master.RolePermission != nil {
appHandlers.RolePermission = roleHttp.NewRolPermissionHandler(registry.Master.RolePermission)
}
if registry.Master.RoleMaster != nil {
appHandlers.RoleMaster = roleHttp.NewRoleMasterHandler(registry.Master.RoleMaster)
}
}
// Inisialisasi Handlers Modul VClaim
if registry.VClaim != nil {
if registry.VClaim.Sep != nil {
appHandlers.Sep = vclaimHttp.NewSepHandler(registry.VClaim.Sep)
}
if registry.VClaim.Peserta != nil {
appHandlers.Peserta = vclaimHttp.NewPesertaHandler(registry.VClaim.Peserta)
}
}
// Inisialisasi Handlers Modul Antrol
if registry.Antrol != nil {
if registry.Antrol.Reference != nil {
appHandlers.Antrol = antrolHttp.NewAntrolHandler(registry.Antrol.Reference)
}
}
// Inisialisasi Handlers Modul Aplicares
if registry.Aplicare != nil {
if registry.Aplicare.Bed != nil {
appHandlers.AplicareBed = aplicareHttp.NewBedHandler(registry.Aplicare.Bed)
}
}
// Inisialisasi Handlers Modul Apotek
if registry.Apotek != nil {
if registry.Apotek.DPHO != nil && registry.Apotek.Poli != nil {
appHandlers.ApotekReference = apotekHttp.NewReferenceHandler(registry.Apotek.DPHO, registry.Apotek.Poli)
}
}
// Inisialisasi Handlers Modul Satu Sehat
if registry.SatuSehat != nil {
if registry.SatuSehat.Auth != nil {
appHandlers.SatuSehatAuth = satuSehatRefHttp.NewAuthHandler(registry.SatuSehat.Auth)
}
if registry.SatuSehat.Patient != nil {
appHandlers.SatuSehatPatient = satuSehatRefHttp.NewPatientHandler(registry.SatuSehat.Patient)
}
if registry.SatuSehat.Practitioner != nil {
appHandlers.SatuSehatPractitioner = satuSehatRefHttp.NewPractitionerHandler(registry.SatuSehat.Practitioner)
}
if registry.SatuSehat.Organization != nil {
appHandlers.SatuSehatOrganization = satuSehatRefHttp.NewOrganizationHandler(registry.SatuSehat.Organization)
}
if registry.SatuSehat.Location != nil {
appHandlers.SatuSehatLocation = satuSehatRefHttp.NewLocationHandler(registry.SatuSehat.Location)
}
if registry.SatuSehat.KFA != nil {
appHandlers.SatuSehatKFA = satuSehatRefHttp.NewKFAHandler(registry.SatuSehat.KFA)
}
// Inisialisasi Handlers Modul Satu Sehat Usecase
if registry.SatuSehat.AllergyIntolerance != nil {
appHandlers.SSAllergyIntolerance = satuSehatUsecaseHttp.NewAllergyIntoleranceHandler(registry.SatuSehat.AllergyIntolerance)
}
if registry.SatuSehat.CarePlan != nil {
appHandlers.SSCarePlan = satuSehatUsecaseHttp.NewCarePlanHandler(registry.SatuSehat.CarePlan)
}
if registry.SatuSehat.ClinicalImpression != nil {
appHandlers.SSClinicalImpression = satuSehatUsecaseHttp.NewClinicalImpressionHandler(registry.SatuSehat.ClinicalImpression)
}
if registry.SatuSehat.Composition != nil {
appHandlers.SSComposition = satuSehatUsecaseHttp.NewCompositionHandler(registry.SatuSehat.Composition)
}
if registry.SatuSehat.Condition != nil {
appHandlers.SSCondition = satuSehatUsecaseHttp.NewConditionHandler(registry.SatuSehat.Condition)
}
if registry.SatuSehat.DiagnosticReport != nil {
appHandlers.SSDiagnosticReport = satuSehatUsecaseHttp.NewDiagnosticReportHandler(registry.SatuSehat.DiagnosticReport)
}
if registry.SatuSehat.Encounter != nil {
appHandlers.SSEncounter = satuSehatUsecaseHttp.NewEncounterHandler(registry.SatuSehat.Encounter)
}
if registry.SatuSehat.EpisodeOfCare != nil {
appHandlers.SSEpisodeOfCare = satuSehatUsecaseHttp.NewEpisodeOfCareHandler(registry.SatuSehat.EpisodeOfCare)
}
if registry.SatuSehat.ImagingStudy != nil {
appHandlers.SSImagingStudy = satuSehatUsecaseHttp.NewImagingStudyHandler(registry.SatuSehat.ImagingStudy)
}
if registry.SatuSehat.Immunization != nil {
appHandlers.SSImmunization = satuSehatUsecaseHttp.NewImmunizationHandler(registry.SatuSehat.Immunization)
}
if registry.SatuSehat.Medication != nil {
appHandlers.SSMedication = satuSehatUsecaseHttp.NewMedicationHandler(registry.SatuSehat.Medication)
}
if registry.SatuSehat.MedicationDispense != nil {
appHandlers.SSMedicationDispense = satuSehatUsecaseHttp.NewMedicationDispenseHandler(registry.SatuSehat.MedicationDispense)
}
if registry.SatuSehat.MedicationRequest != nil {
appHandlers.SSMedicationRequest = satuSehatUsecaseHttp.NewMedicationRequestHandler(registry.SatuSehat.MedicationRequest)
}
if registry.SatuSehat.MedicationStatement != nil {
appHandlers.SSMedicationStatement = satuSehatUsecaseHttp.NewMedicationStatementHandler(registry.SatuSehat.MedicationStatement)
}
if registry.SatuSehat.Observation != nil {
appHandlers.SSObservation = satuSehatUsecaseHttp.NewObservationHandler(registry.SatuSehat.Observation)
}
if registry.SatuSehat.Procedure != nil {
appHandlers.SSProcedure = satuSehatUsecaseHttp.NewProcedureHandler(registry.SatuSehat.Procedure)
}
if registry.SatuSehat.QuestionnaireResponse != nil {
appHandlers.SSQuestionnaireResponse = satuSehatUsecaseHttp.NewQuestionnaireResponseHandler(registry.SatuSehat.QuestionnaireResponse)
}
if registry.SatuSehat.ServiceRequest != nil {
appHandlers.SSServiceRequest = satuSehatUsecaseHttp.NewServiceRequestHandler(registry.SatuSehat.ServiceRequest)
}
if registry.SatuSehat.Specimen != nil {
appHandlers.SSSpecimen = satuSehatUsecaseHttp.NewSpecimenHandler(registry.SatuSehat.Specimen)
}
}
// Inisialisasi Health Handler dengan dependensi yang benar
// Gunakan constructor baru yang menerima cache manager
healthHandler := healthHttp.NewHealthHandlerWithCache(registry.PrimaryDB, registry.CacheManager, registry.Config, registry.DBManager)
// Setup routes menggunakan handler yang sudah diinisialisasi
routes.SetupRoutes(
engine,
registry.Config,
registry.CacheManager,
healthHandler,
appHandlers,
)
return &Server{
engine: engine,
config: restConfig,
}
}
// RegisterSwagger mendaftarkan dokumentasi Swagger UI
func (s *Server) RegisterSwagger() {
if s.engine == nil {
log.Println("Cannot register Swagger: engine is nil (server disabled)")
return
}
s.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
// Start menjalankan server HTTP
func (s *Server) Start(globalConfig *config.ServerConfig) error {
if !s.config.Enabled {
log.Println("HTTP server is disabled, not starting.")
return nil
}
addr := fmt.Sprintf(":%d", s.config.Port)
log.Printf("Starting HTTP server on %s", addr)
// PERBAIKAN: Simpan instance server ke struct
s.httpServer = &http.Server{
Addr: addr,
Handler: s.engine,
ReadTimeout: time.Duration(globalConfig.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(globalConfig.WriteTimeout) * time.Second,
IdleTimeout: 60 * time.Second, // Good practice to have an idle timeout
}
return s.httpServer.ListenAndServe()
}
// PERBAIKAN: Tambahkan method untuk graceful shutdown
// Shutdown memberhentikan server HTTP dengan graceful shutdown
func (s *Server) Shutdown(ctx context.Context) error {
if s.httpServer == nil {
return nil
}
log.Println("Shutting down HTTP server...")
return s.httpServer.Shutdown(ctx)
}
// GetEngine mengembalikan instance Gin engine (terutama untuk testing)
func (s *Server) GetEngine() *gin.Engine {
return s.engine
}