github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/frontend/frontend.go (about) 1 // Copyright 2020 Readium Foundation. All rights reserved. 2 // Use of this source code is governed by a BSD-style license 3 // that can be found in the LICENSE file exposed on Github (readium) in the project repository. 4 5 package main 6 7 import ( 8 "database/sql" 9 "fmt" 10 "log" 11 "os" 12 "os/signal" 13 "path/filepath" 14 "runtime" 15 "strconv" 16 "strings" 17 "syscall" 18 19 auth "github.com/abbot/go-http-auth" 20 _ "github.com/go-sql-driver/mysql" 21 _ "github.com/lib/pq" 22 _ "github.com/mattn/go-sqlite3" 23 _ "github.com/microsoft/go-mssqldb" 24 25 "github.com/readium/readium-lcp-server/config" 26 frontend "github.com/readium/readium-lcp-server/frontend/server" 27 "github.com/readium/readium-lcp-server/frontend/webdashboard" 28 "github.com/readium/readium-lcp-server/frontend/weblicense" 29 "github.com/readium/readium-lcp-server/frontend/webpublication" 30 "github.com/readium/readium-lcp-server/frontend/webpurchase" 31 "github.com/readium/readium-lcp-server/frontend/webrepository" 32 "github.com/readium/readium-lcp-server/frontend/webuser" 33 ) 34 35 func main() { 36 var static, configFile string 37 var err error 38 39 if configFile = os.Getenv("READIUM_FRONTEND_CONFIG"); configFile == "" { 40 configFile = "config.yaml" 41 } 42 config.ReadConfig(configFile) 43 log.Println("Config from " + configFile) 44 45 err = config.SetPublicUrls() 46 if err != nil { 47 panic(err) 48 } 49 50 //log.Println("LCP server = " + config.Config.LcpServer.PublicBaseUrl) 51 //log.Println("using login " + config.Config.LcpUpdateAuth.Username) 52 53 driver, cnxn := config.GetDatabase(config.Config.FrontendServer.Database) 54 log.Println("Database driver " + driver) 55 56 db, err := sql.Open(driver, cnxn) 57 if err != nil { 58 panic(err) 59 } 60 if driver == "sqlite3" && !strings.Contains(cnxn, "_journal") { 61 _, err = db.Exec("PRAGMA journal_mode = WAL") 62 if err != nil { 63 panic(err) 64 } 65 } 66 67 repoManager, err := webrepository.Init(config.Config.FrontendServer) 68 if err != nil { 69 panic(err) 70 } 71 72 publicationDB, err := webpublication.Init(db) 73 if err != nil { 74 panic(err) 75 } 76 77 userDB, err := webuser.Open(db) 78 if err != nil { 79 panic(err) 80 } 81 82 purchaseDB, err := webpurchase.Init(db) 83 if err != nil { 84 panic(err) 85 } 86 87 dashboardDB, err := webdashboard.Init(db) 88 if err != nil { 89 panic(err) 90 } 91 92 licenseDB, err := weblicense.Init(db) 93 if err != nil { 94 panic(err) 95 } 96 97 static = config.Config.FrontendServer.Directory 98 if static == "" { 99 _, file, _, _ := runtime.Caller(0) 100 here := filepath.Dir(file) 101 static = filepath.Join(here, "../frontend/manage") 102 } 103 104 filepathConfigJs := filepath.Join(static, "config.js") 105 fileConfigJs, err := os.Create(filepathConfigJs) 106 if err != nil { 107 panic(err) 108 } 109 110 defer func() { 111 if err := fileConfigJs.Close(); err != nil { 112 panic(err) 113 } 114 }() 115 116 configJs := ` 117 // This file is automatically generated, and git-ignored. 118 // To ignore your local changes, use: 119 // git update-index --assume-unchanged frontend/manage/config.js 120 window.Config = {` 121 configJs += "\n\tfrontend: {url: '" + config.Config.FrontendServer.PublicBaseUrl + "' },\n" 122 configJs += "\tlcp: {url: '" + config.Config.LcpServer.PublicBaseUrl + "', user: '" + config.Config.LcpUpdateAuth.Username + "', password: '" + config.Config.LcpUpdateAuth.Password + "'},\n" 123 configJs += "\tlsd: {url: '" + config.Config.LsdServer.PublicBaseUrl + "', user: '" + config.Config.LsdNotifyAuth.Username + "', password: '" + config.Config.LsdNotifyAuth.Password + "'}\n}" 124 125 // log.Println("manage/index.html config.js:") 126 // log.Println(configJs) 127 128 fileConfigJs.WriteString(configJs) 129 HandleSignals() 130 131 // basic authentication, optional in the frontend server. 132 // Authentication is used for getting user info from a license id. 133 var authenticator *auth.BasicAuth 134 authFile := config.Config.LsdServer.AuthFile 135 if authFile != "" { 136 _, err = os.Stat(authFile) 137 if err != nil { 138 panic(err) 139 } 140 htpasswd := auth.HtpasswdFileProvider(authFile) 141 authenticator = auth.NewBasicAuthenticator("Basic Realm", htpasswd) 142 } 143 144 s := frontend.New(":"+strconv.Itoa(config.Config.FrontendServer.Port), static, repoManager, publicationDB, userDB, dashboardDB, licenseDB, purchaseDB, authenticator) 145 log.Println("Frontend webserver for LCP running on " + config.Config.FrontendServer.Host + ":" + strconv.Itoa(config.Config.FrontendServer.Port)) 146 147 if err := s.ListenAndServe(); err != nil { 148 log.Println("Error " + err.Error()) 149 } 150 } 151 152 // HandleSignals handles system signals and adds a log before quitting 153 func HandleSignals() { 154 sigChan := make(chan os.Signal) 155 go func() { 156 stacktrace := make([]byte, 1<<20) 157 for sig := range sigChan { 158 switch sig { 159 case syscall.SIGQUIT: 160 length := runtime.Stack(stacktrace, true) 161 fmt.Println(string(stacktrace[:length])) 162 case syscall.SIGINT: 163 fallthrough 164 case syscall.SIGTERM: 165 fmt.Println("Shutting down...") 166 os.Exit(0) 167 } 168 } 169 }() 170 signal.Notify(sigChan, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM) 171 }