You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
okapidemo/his/srv.go

420 lines
9.5 KiB

package main
import (
"context"
"io"
"log"
"net/http"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"whiteboxsystems.nl/openkvpoc/his/model"
"whiteboxsystems.nl/openkvpoc/openkv"
"whiteboxsystems.nl/openkvpoc/sharedmodel"
)
type HISServer struct {
srv *http.Server
inited bool
data *gorm.DB
stopTasks chan struct{}
}
func (srv *HISServer) LoadData(location string) error {
var err error
srv.data, err = model.GetDB(location)
return err
}
func (srv *HISServer) Addr() string {
if srv.srv == nil {
return ""
}
return srv.srv.Addr
}
func (srv *HISServer) ListenAndServe() {
if !srv.inited {
srv.init()
}
log.Println("Listening on %v", srv.srv.Addr)
srv.srv.ListenAndServe()
}
func (srv *HISServer) Shutdown(ctx context.Context) error {
return srv.srv.Shutdown(ctx)
}
func (srv *HISServer) 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(301, "/ui")
})
r.GET("/ui", srv.GetIndex)
r.GET("/ui/*page", srv.GetIndex)
r.GET("/api/patients", srv.GetPatients)
r.POST("/api/patients/:id/consent", srv.UpdateConsent)
r.POST("/api/patients/:id/consent/:consentID", srv.DeleteConsent)
r.GET("/api/connections", srv.GetConnections)
r.POST("/api/connections", srv.NewConnection)
r.POST("/api/connections/:id/activate", srv.ActivateConnection)
r.POST("/api/connections/:id/services", srv.ModService)
r.GET("/api/connections/:id", srv.GetConnection)
r.GET("/api/services", srv.GetServices)
r.POST("/api/services/:id/subscriptions", srv.UpdateSubscription)
r.Use(srv.Authenticate)
r.GET("/external/api/patients/:id", srv.GetPatient)
r.GET("/external/fhir/Patient", srv.GetFHIRPatient)
srv.inited = true
ticker := time.NewTicker(30 * time.Second)
srv.stopTasks = make(chan struct{})
srv.TaskOptOut()
go func() {
for {
select {
case <-ticker.C:
srv.TaskOptOut()
case <-srv.stopTasks:
ticker.Stop()
return
}
}
}()
}
func (srv *HISServer) GetIndex(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{})
}
func (srv *HISServer) TaskOptOut() {
services := []model.Service{}
srv.data.Where("subscription_policy = ?", openkv.SubscriptionPolicy_optout).Preload("Connection").Preload("Connection.AuthConfig").Preload("Subscriptions").Find(&services)
patients := []model.Patient{}
srv.data.Find(&patients)
for _, service := range services {
activePatients := []model.Patient{}
for _, p := range patients {
include := true
for _, sub := range service.Subscriptions {
if sub.ID == p.ID {
include = false
}
}
if include {
activePatients = append(activePatients, p)
}
}
_, err := srv.subscribePatients(service.Connection, &service, true, activePatients)
if err != nil {
log.Println("active patients for optout (%v) err: %v", service.Name, err)
} else {
log.Printf("sync patients for %v: %v patients subscribed", service.Name, len(activePatients))
}
inactivePatients := service.Subscriptions
_, err = srv.subscribePatients(service.Connection, &service, false, inactivePatients)
if err != nil {
log.Println("inactive patients for optout (%v) err: %v", service.Name, err)
} else {
log.Printf("sync patients for %v: %v patients unsubscribed", service.Name, len(inactivePatients))
}
}
}
func (srv *HISServer) Authenticate(c *gin.Context) {
if !strings.HasPrefix(c.Request.RequestURI, "/external/") {
return
}
authHeader := c.Request.Header.Get("Authorization")
log.Printf("authHeader: %v", authHeader)
authConfig := &sharedmodel.AuthConfig{}
if err := srv.data.Where("raw = ?", authHeader).First(authConfig).Error; err != nil {
c.Status(401)
c.Abort()
return
}
if strings.HasPrefix(c.Request.RequestURI, "/external/api") {
if srv.data.Where("auth_config_id = ? and service_id like ?", authConfig.ID, "wbx:%").First(&model.Service{}).Error == nil {
return
}
}
if strings.HasPrefix(c.Request.RequestURI, "/external/fhir") {
if srv.data.Where("auth_config_id = ? and service_id like ?", authConfig.ID, "%:dvza").First(&model.Service{}).Error == nil {
return
}
}
c.Status(401)
c.Abort()
}
func (srv *HISServer) GetPatients(c *gin.Context) {
patients := []model.Patient{}
if err := srv.data.Preload("Consent").Find(&patients).Error; err != nil {
c.AbortWithError(500, err)
return
}
c.JSON(200, patients)
}
func (srv *HISServer) GetConnections(c *gin.Context) {
connections := []model.Connection{}
if err := srv.data.Preload("AuthConfig").Preload("Services").Find(&connections).Error; err != nil {
c.AbortWithError(500, err)
return
}
c.JSON(200, connections)
}
func (srv *HISServer) GetServices(c *gin.Context) {
services := []model.Service{}
if err := srv.data.Preload("Connection").Preload("Subscriptions").Find(&services).Error; err != nil {
c.AbortWithError(500, err)
return
}
c.JSON(200, services)
}
func (srv *HISServer) GetConnection(c *gin.Context) {
connID := c.Param("id")
connection := &model.Connection{}
if err := srv.data.Where("id = ?", connID).Preload("AuthConfig").Preload("Services").Find(connection).Error; err != nil {
c.AbortWithError(404, err)
return
}
serviceMeta, err := srv.listMeta(connection)
if err != nil {
c.AbortWithError(400, err)
return
}
c.JSON(200, map[string]interface{}{"connection": connection, "meta": serviceMeta.Services})
}
func (srv *HISServer) ModService(c *gin.Context) {
var payload struct {
Active bool `json:"active"`
Service string `json:"service"`
}
c.BindJSON(&payload)
connID := c.Param("id")
connection := &model.Connection{}
if err := srv.data.Where("id = ?", connID).Preload("AuthConfig").Preload("Services").Find(connection).Error; err != nil {
c.AbortWithError(404, err)
return
}
err := srv.enableService(connection, payload.Service, payload.Active)
if err != nil {
log.Println("err: %v", err)
c.AbortWithError(400, err)
return
}
c.Status(200)
}
func (srv *HISServer) applyPolicy(sub bool, service *model.Service) bool {
if service.SubscriptionPolicy == openkv.SubscriptionPolicy_optout {
return !sub
}
return sub
}
func (srv *HISServer) UpdateConsent(c *gin.Context) {
consent := &model.Consent{}
c.BindJSON(consent)
pId, _ := strconv.ParseUint(c.Param("id"), 10, 64)
consent.PatientID = uint(pId)
if consent.ID != 0 {
srv.data.Save(consent)
c.Status(200)
} else {
srv.data.Create(consent)
c.Status(201)
}
}
func (srv *HISServer) DeleteConsent(c *gin.Context) {
consent := &model.Consent{}
srv.data.Unscoped().Where("id = ? and patient_id = ?", c.Param("consentID"), c.Param("id")).Delete(consent)
c.Status(200)
}
func (srv *HISServer) UpdateSubscription(c *gin.Context) {
var payload struct {
Active bool `json:"active"`
Patient uint `json:"patient"`
}
c.BindJSON(&payload)
serviceID := c.Param("id")
service := &model.Service{}
if err := srv.data.Where("id = ?", serviceID).Preload("Connection").Preload("Connection.AuthConfig").Find(service).Error; err != nil {
c.AbortWithError(404, err)
return
}
sub := &model.Patient{}
srv.data.Model(service).Where("id = ?", payload.Patient).Association("Subscriptions").Find(sub)
if payload.Active && sub.ID != 0 {
log.Printf("No update needed: %v %v", payload.Patient, service.ServiceID)
c.Status(200)
return
} else if !payload.Active && sub.ID == 0 {
log.Printf("No update needed: %v %v", payload.Patient, service.ServiceID)
c.Status(200)
return
}
patient := &model.Patient{}
if err := srv.data.Where("id = ?", payload.Patient).Find(patient).Error; err != nil {
c.AbortWithError(404, err)
return
}
_, err := srv.subscribePatients(service.Connection, service, srv.applyPolicy(payload.Active, service), []model.Patient{*patient})
if err != nil {
log.Println("err: %v", err)
c.AbortWithError(400, err)
return
}
if payload.Active {
srv.data.Model(service).Association("Subscriptions").Append(patient)
} else {
srv.data.Model(service).Association("Subscriptions").Delete(patient)
}
c.Status(201)
}
func (srv *HISServer) NewConnection(c *gin.Context) {
var payload struct {
URL string `json:"url"`
}
c.BindJSON(&payload)
conn, err := srv.register(payload.URL)
if err != nil {
c.AbortWithError(400, err)
return
}
c.JSON(201, conn)
}
func (srv *HISServer) ActivateConnection(c *gin.Context) {
connID := c.Param("id")
connection := &model.Connection{}
if err := srv.data.Preload("AuthConfig").Where("id = ?", connID).First(connection).Error; err != nil {
c.AbortWithError(404, err)
return
}
var payload struct {
PSK string `json:"psk"`
}
c.BindJSON(&payload)
conn, err := srv.activate(connection, payload.PSK)
if err != nil {
c.AbortWithError(400, err)
return
}
c.JSON(201, conn)
}
func (srv *HISServer) GetPatient(c *gin.Context) {
id := c.Param("id")
patient := &model.Patient{}
if err := srv.data.Where("patient_id = ?", id).First(patient).Error; err == nil {
f, err := os.Open(path.Join("./data/patients", patient.FileBase+".edi"))
if err != nil {
c.Error(err)
return
}
io.Copy(c.Writer, f)
return
}
c.JSON(404, nil)
}
func (srv *HISServer) GetFHIRPatient(c *gin.Context) {
id := c.Query("id")
patient := &model.Patient{}
if err := srv.data.Where("patient_id = ?", id).First(patient).Error; err == nil {
f, err := os.Open(path.Join("./data/patients", patient.FileBase+".fhir.json"))
if err != nil {
c.Error(err)
return
}
io.Copy(c.Writer, f)
return
}
c.JSON(404, nil)
}
func NewServer(addr string) *HISServer {
srv := &HISServer{srv: &http.Server{
Addr: addr,
Handler: gin.Default(),
}}
return srv
}