package main import ( "context" "crypto/tls" "fmt" "io" "log" "net/http" "os" "os/exec" "sync" "github.com/gin-gonic/gin" "gorm.io/gorm" "src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel" "src.whiteboxsystems.nl/DECOZO/okapidemo/whiteboxservice/model" ) type UIService struct { srv *http.Server inited bool clientCert tls.Certificate data *gorm.DB } func (srv *UIService) LoadData(location string) error { var err error srv.data, err = model.GetDB(location) return err } func (srv *UIService) Addr() string { if srv.srv == nil { return "" } return srv.srv.Addr } func (srv *UIService) ListenAndServe() { if !srv.inited { srv.init() } log.Println("Listening on %v", srv.srv.Addr) srv.srv.ListenAndServe() } func (srv *UIService) Shutdown(ctx context.Context) error { return srv.srv.Shutdown(ctx) } func (srv *UIService) init() { r := srv.srv.Handler.(*gin.Engine) r.LoadHTMLGlob("templates/*") r.Static("/assets", "./assets") r.Use(srv.Authenticate) r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/ui") }) r.GET("/ui", srv.GetIndex) r.GET("/ui/*page", srv.GetIndex) r.GET("/api/connections", srv.GetConnections) r.GET("/api/connections/:connID", srv.GetConnection) r.GET("/api/connections/:connID/:serviceID", srv.GetSubscriptions) r.GET("/api/connections/:connID/:serviceID/:patientID", srv.GetPatient) r.GET("/api/registrations", srv.GetRegistrations) srv.inited = true } func (srv *UIService) GetIndex(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{}) } func (srv *UIService) GetConnection(c *gin.Context) { connID := c.Param("connID") connection := &sharedmodel.Connection{} srv.data.Where("id = ?", connID).Find(&connection) c.JSON(http.StatusOK, connection) } func (srv *UIService) GetSubscriptions(c *gin.Context) { connID := c.Param("connID") serviceID := c.Param("serviceID") serviceConfig := &sharedmodel.ServiceConfig{} srv.data.Preload("Service").Preload("Subscriptions").Where("connection_id = ? and id = ?", connID, serviceID).Find(&serviceConfig) c.JSON(http.StatusOK, serviceConfig) } func (srv *UIService) GetPatient(c *gin.Context) { connID := c.Param("connID") serviceID := c.Param("serviceID") patientID := c.Param("patientID") patient := &sharedmodel.Subscription{} serviceConfig := &sharedmodel.ServiceConfig{} if err := srv.data.Preload("FetchProtocol").Preload("FetchProtocol.AuthConfig").Preload("Service").Where("connection_id = ? and id = ?", connID, serviceID).Find(&serviceConfig).Error; err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } srv.data.Where("service_config_id = ? and id = ?", serviceID, patientID).Find(&patient) protoconfig := map[string]string{} protometa := map[string]string{} err := serviceConfig.FetchProtocol.UnmarshalConfig(&protoconfig) if err != nil { c.AbortWithStatus(http.StatusBadRequest) return } err = patient.GetProtocolMeta(&protometa) if err != nil { c.AbortWithStatus(http.StatusBadRequest) return } url := fmt.Sprintf("%v/%v/%v", protoconfig["url"], "patients", protometa["patientID"]) req, _ := http.NewRequest("GET", url, nil) client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, Certificates: []tls.Certificate{srv.clientCert}, }, }, } resp, err := client.Do(req) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if resp.StatusCode >= 400 { c.AbortWithError(resp.StatusCode, fmt.Errorf("%v", resp.Status)) return } cmd := exec.Command(binFolder+"/ediviewer", binFolder+"/medeur.json", binFolder+"/template.html", "/dev/stdin") sin, err := cmd.StdinPipe() if err != nil { log.Println("[ediviewer] Failed to open stdin pipe:", err) c.AbortWithError(http.StatusInternalServerError, err) return } defer sin.Close() serr, err := cmd.StderrPipe() if err != nil { log.Println("[ediviewer] Failed to open stderr pipe:", err) c.AbortWithError(http.StatusInternalServerError, err) return } defer serr.Close() sout, err := cmd.StdoutPipe() if err != nil { log.Println("[ediviewer] Failed to open stdout pipe:", err) c.AbortWithError(http.StatusInternalServerError, err) return } defer sout.Close() if err := cmd.Start(); err != nil { log.Println("[ediviewer] Failed to start:", err) c.AbortWithError(http.StatusInternalServerError, err) return } wg := sync.WaitGroup{} wg.Add(3) go func() { defer wg.Done() if _, err := io.Copy(sin, resp.Body); err != nil { log.Println("[ediviewer] Error reading EDIFACT:", err) c.AbortWithError(http.StatusInternalServerError, err) } }() go func() { defer wg.Done() if _, err := io.Copy(c.Writer, sout); err != nil { log.Println("[ediviewer] Error output EDIFACT:", err) c.AbortWithError(http.StatusInternalServerError, err) } }() go func() { defer wg.Done() if _, err := io.Copy(os.Stderr, serr); err != nil { log.Println("[ediviewer] Error stderr EDIFACT:", err) c.AbortWithError(http.StatusInternalServerError, err) } }() wg.Wait() if err := cmd.Wait(); err != nil { log.Println("[ediviewer] Failed:", err) c.AbortWithError(http.StatusInternalServerError, err) } } func (srv *UIService) Authenticate(c *gin.Context) { // Maybe authenticate user } func (srv *UIService) GetConnections(c *gin.Context) { connections := []*sharedmodel.Connection{} srv.data.Preload("Services").Preload("Services.Service").Preload("Services.Subscriptions").Find(&connections) c.JSON(http.StatusOK, connections) } func (srv *UIService) GetRegistrations(c *gin.Context) { registrations := []*sharedmodel.Registration{} srv.data.Where("status = ?", sharedmodel.RegistrationStatusPending).Find(®istrations) c.JSON(http.StatusOK, registrations) } func NewUIServer(addr string) *UIService { cert := loadCert() srv := &UIService{srv: &http.Server{ Addr: addr, Handler: gin.Default(), }, clientCert: *cert} return srv }