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 { "externalURL" : extRpcAddr } )
}
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 ( & registrations )
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
}