github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/frontend/server/server.go (about) 1 // Copyright (c) 2016 Readium Foundation 2 // 3 // Redistribution and use in source and binary forms, with or without modification, 4 // are permitted provided that the following conditions are met: 5 // 6 // 1. Redistributions of source code must retain the above copyright notice, this 7 // list of conditions and the following disclaimer. 8 // 2. Redistributions in binary form must reproduce the above copyright notice, 9 // this list of conditions and the following disclaimer in the documentation and/or 10 // other materials provided with the distribution. 11 // 3. Neither the name of the organization nor the names of its contributors may be 12 // used to endorse or promote products derived from this software without specific 13 // prior written permission 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 package frontend 27 28 import ( 29 "crypto/tls" 30 "encoding/base64" 31 "fmt" 32 "io/ioutil" 33 "log" 34 "net/http" 35 "time" 36 37 auth "github.com/abbot/go-http-auth" 38 "github.com/claudiu/gocron" 39 "github.com/gorilla/mux" 40 "github.com/readium/readium-lcp-server/api" 41 "github.com/readium/readium-lcp-server/config" 42 staticapi "github.com/readium/readium-lcp-server/frontend/api" 43 "github.com/readium/readium-lcp-server/frontend/webdashboard" 44 "github.com/readium/readium-lcp-server/frontend/weblicense" 45 "github.com/readium/readium-lcp-server/frontend/webpublication" 46 "github.com/readium/readium-lcp-server/frontend/webpurchase" 47 "github.com/readium/readium-lcp-server/frontend/webrepository" 48 "github.com/readium/readium-lcp-server/frontend/webuser" 49 ) 50 51 //Server struct contains server info and db interfaces 52 type Server struct { 53 http.Server 54 readonly bool 55 cert *tls.Certificate 56 repositories webrepository.WebRepository 57 publications webpublication.WebPublication 58 users webuser.WebUser 59 dashboard webdashboard.WebDashboard 60 license weblicense.WebLicense 61 purchases webpurchase.WebPurchase 62 } 63 64 // HandlerFunc defines a function handled by the server 65 type HandlerFunc func(w http.ResponseWriter, r *http.Request, s staticapi.IServer) 66 67 type HandlerPrivateFunc func(w http.ResponseWriter, r *auth.AuthenticatedRequest, s staticapi.IServer) 68 69 // New creates a new webserver (basic user interface) 70 func New( 71 bindAddr string, 72 tplPath string, 73 repositoryAPI webrepository.WebRepository, 74 publicationAPI webpublication.WebPublication, 75 userAPI webuser.WebUser, 76 dashboardAPI webdashboard.WebDashboard, 77 licenseAPI weblicense.WebLicense, 78 purchaseAPI webpurchase.WebPurchase, 79 basicAuth *auth.BasicAuth) *Server { 80 81 sr := api.CreateServerRouter(tplPath) 82 s := &Server{ 83 Server: http.Server{ 84 Handler: sr.N, 85 Addr: bindAddr, 86 WriteTimeout: 150 * time.Second, 87 ReadTimeout: 150 * time.Second, 88 MaxHeaderBytes: 1 << 20, 89 }, 90 repositories: repositoryAPI, 91 publications: publicationAPI, 92 users: userAPI, 93 dashboard: dashboardAPI, 94 license: licenseAPI, 95 purchases: purchaseAPI} 96 97 // Cron, get license status information 98 gocron.Start() 99 gocron.Every(10).Minutes().Do(fetchLicenseStatusesTask, s) 100 101 apiURLPrefix := "/api/v1" 102 103 // 104 // repositories of master files 105 // 106 repositoriesRoutesPathPrefix := apiURLPrefix + "/repositories" 107 repositoriesRoutes := sr.R.PathPrefix(repositoriesRoutesPathPrefix).Subrouter().StrictSlash(false) 108 // 109 s.handleFunc(repositoriesRoutes, "/master-files", staticapi.GetRepositoryMasterFiles).Methods("GET") 110 // 111 // dashboard 112 // 113 s.handleFunc(sr.R, "/dashboardInfos", staticapi.GetDashboardInfos).Methods("GET") 114 s.handleFunc(sr.R, "/dashboardBestSellers", staticapi.GetDashboardBestSellers).Methods("GET") 115 // 116 // publications 117 // 118 publicationsRoutesPathPrefix := apiURLPrefix + "/publications" 119 publicationsRoutes := sr.R.PathPrefix(publicationsRoutesPathPrefix).Subrouter().StrictSlash(false) 120 // 121 s.handleFunc(sr.R, publicationsRoutesPathPrefix, staticapi.GetPublications).Methods("GET") 122 // 123 s.handleFunc(sr.R, publicationsRoutesPathPrefix, staticapi.CreatePublication).Methods("POST") 124 // 125 s.handleFunc(sr.R, "/publicationUpload", staticapi.UploadPublication).Methods("POST") 126 // 127 s.handleFunc(publicationsRoutes, "/check-by-title", staticapi.CheckPublicationByTitle).Methods("GET") 128 // 129 s.handleFunc(publicationsRoutes, "/{id}", staticapi.GetPublication).Methods("GET") 130 s.handleFunc(publicationsRoutes, "/{id}", staticapi.UpdatePublication).Methods("PUT") 131 s.handleFunc(publicationsRoutes, "/{id}", staticapi.DeletePublication).Methods("DELETE") 132 // 133 // user functions 134 // 135 usersRoutesPathPrefix := apiURLPrefix + "/users" 136 usersRoutes := sr.R.PathPrefix(usersRoutesPathPrefix).Subrouter().StrictSlash(false) 137 // 138 s.handleFunc(sr.R, usersRoutesPathPrefix, staticapi.GetUsers).Methods("GET") 139 // 140 s.handleFunc(sr.R, usersRoutesPathPrefix, staticapi.CreateUser).Methods("POST") 141 // 142 s.handleFunc(usersRoutes, "/{id}", staticapi.GetUser).Methods("GET") 143 s.handleFunc(usersRoutes, "/{id}", staticapi.UpdateUser).Methods("PUT") 144 s.handleFunc(usersRoutes, "/{id}", staticapi.DeleteUser).Methods("DELETE") 145 // get all purchases for a given user 146 s.handleFunc(usersRoutes, "/{user_id}/purchases", staticapi.GetUserPurchases).Methods("GET") 147 148 // 149 // purchases 150 // 151 purchasesRoutesPathPrefix := apiURLPrefix + "/purchases" 152 purchasesRoutes := sr.R.PathPrefix(purchasesRoutesPathPrefix).Subrouter().StrictSlash(false) 153 // get all purchases 154 s.handleFunc(sr.R, purchasesRoutesPathPrefix, staticapi.GetPurchases).Methods("GET") 155 // create a purchase 156 s.handleFunc(sr.R, purchasesRoutesPathPrefix, staticapi.CreatePurchase).Methods("POST") 157 // update a purchase 158 s.handleFunc(purchasesRoutes, "/{id}", staticapi.UpdatePurchase).Methods("PUT") 159 // get a purchase by purchase id 160 s.handleFunc(purchasesRoutes, "/{id}", staticapi.GetPurchase).Methods("GET") 161 // get a license from the associated purchase id 162 s.handleFunc(purchasesRoutes, "/{id}/license", staticapi.GetPurchasedLicense).Methods("GET") 163 // 164 // licences 165 // 166 licenseRoutesPathPrefix := apiURLPrefix + "/licenses" 167 licenseRoutes := sr.R.PathPrefix(licenseRoutesPathPrefix).Subrouter().StrictSlash(false) 168 // 169 // get a list of licenses 170 s.handleFunc(sr.R, licenseRoutesPathPrefix, staticapi.GetFilteredLicenses).Methods("GET") 171 // get a license by id 172 s.handleFunc(licenseRoutes, "/{license_id}", staticapi.GetLicense).Methods("GET") 173 // get the user who owns a given license; this route is only set if authentication is in use 174 if basicAuth != nil { 175 s.handlePrivateFunc(licenseRoutes, "/{license_id}/user", staticapi.GetLicenseOwner, basicAuth).Methods("GET") 176 } 177 178 return s 179 } 180 181 // fetchLicenseStatusesTask fetchs from the Status Doc Server, and saves, locally, all license status documents. 182 // This is optimizing the visualization of status information in the UI. 183 func fetchLicenseStatusesTask(s *Server) { 184 fmt.Println("AUTOMATIC : Fetch and save all license status documents") 185 url := config.Config.LsdServer.PublicBaseUrl + "/licenses" 186 auth := config.Config.LsdNotifyAuth 187 188 // prepare the request 189 client := &http.Client{} 190 req, err := http.NewRequest("GET", url, nil) 191 if err != nil { 192 panic(err) 193 } 194 req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth.Username+":"+auth.Password))) 195 res, err := client.Do(req) 196 if err != nil { 197 log.Println("No http connection - no fetch this time") 198 return 199 } 200 201 // get all licence status documents from the lsd server 202 body, err := ioutil.ReadAll(res.Body) 203 if err != nil { 204 log.Println("Failed to read from the http connection - no fetch this time") 205 return 206 } 207 defer res.Body.Close() 208 209 // clear the db 210 err = s.license.PurgeDataBase() 211 if err != nil { 212 panic(err) 213 } 214 215 // fill the db 216 err = s.license.AddFromJSON(body) 217 if err != nil { 218 log.Printf("Unable to process JSON - no fetch this time - err %s\n", err.Error()) 219 } 220 } 221 222 // RepositoryAPI ( staticapi.IServer ) returns interface for repositories 223 func (server *Server) RepositoryAPI() webrepository.WebRepository { 224 return server.repositories 225 } 226 227 // PublicationAPI ( staticapi.IServer )returns DB interface for users 228 func (server *Server) PublicationAPI() webpublication.WebPublication { 229 return server.publications 230 } 231 232 //UserAPI ( staticapi.IServer )returns DB interface for users 233 func (server *Server) UserAPI() webuser.WebUser { 234 return server.users 235 } 236 237 //PurchaseAPI ( staticapi.IServer )returns DB interface for purchases 238 func (server *Server) PurchaseAPI() webpurchase.WebPurchase { 239 return server.purchases 240 } 241 242 //DashboardAPI ( staticapi.IServer )returns DB interface for dashboard 243 func (server *Server) DashboardAPI() webdashboard.WebDashboard { 244 return server.dashboard 245 } 246 247 //LicenseAPI ( staticapi.IServer )returns DB interface for license 248 func (server *Server) LicenseAPI() weblicense.WebLicense { 249 return server.license 250 } 251 252 // mux handle functions 253 func (server *Server) handleFunc(router *mux.Router, route string, fn HandlerFunc) *mux.Route { 254 return router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) { 255 fn(w, r, server) 256 }) 257 } 258 259 func (server *Server) handlePrivateFunc(router *mux.Router, route string, fn HandlerFunc, authenticator *auth.BasicAuth) *mux.Route { 260 return router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) { 261 if api.CheckAuth(authenticator, w, r) { 262 fn(w, r, server) 263 } 264 }) 265 }