From 8c4c1022c3ddbba60bbd4d92782f2fc4dcdd7c64 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Fri, 26 Jan 2024 22:07:30 -0500 Subject: [PATCH] refactor(errors): handle api / app errors better --- api/api.go | 10 +- api/app-routes.go | 220 +++++++++++++++++++++++++++-------------- api/auth.go | 6 +- api/common.go | 234 ++++++++++++++++++++++---------------------- api/ko-routes.go | 81 ++++++++------- database/manager.go | 4 +- 6 files changed, 316 insertions(+), 239 deletions(-) diff --git a/api/api.go b/api/api.go index 4ede92c..5aecdcd 100644 --- a/api/api.go +++ b/api/api.go @@ -114,8 +114,8 @@ func (api *API) registerWebAppRoutes() { api.Router.GET("/progress", api.authWebAppMiddleware, api.appGetProgress) api.Router.GET("/documents", api.authWebAppMiddleware, api.appGetDocuments) api.Router.GET("/documents/:document", api.authWebAppMiddleware, api.appGetDocument) - api.Router.GET("/documents/:document/cover", api.authWebAppMiddleware, api.getDocumentCover) - api.Router.GET("/documents/:document/file", api.authWebAppMiddleware, api.downloadDocument) + api.Router.GET("/documents/:document/cover", api.authWebAppMiddleware, api.createGetCoverHandler(appErrorPage)) + api.Router.GET("/documents/:document/file", api.authWebAppMiddleware, api.createDownloadDocumentHandler(appErrorPage)) api.Router.GET("/login", api.appGetLogin) api.Router.GET("/logout", api.authWebAppMiddleware, api.appAuthLogout) api.Router.GET("/register", api.appGetRegister) @@ -153,7 +153,7 @@ func (api *API) registerKOAPIRoutes(apiGroup *gin.RouterGroup) { koGroup := apiGroup.Group("/ko") // KO Sync Routes (WebApp Uses - Progress & Activity) - koGroup.GET("/documents/:document/file", api.authKOMiddleware, api.downloadDocument) + koGroup.GET("/documents/:document/file", api.authKOMiddleware, api.createDownloadDocumentHandler(apiErrorPage)) koGroup.GET("/syncs/progress/:document", api.authKOMiddleware, api.koGetProgress) koGroup.GET("/users/auth", api.authKOMiddleware, api.koAuthorizeUser) koGroup.POST("/activity", api.authKOMiddleware, api.koAddActivities) @@ -181,8 +181,8 @@ func (api *API) registerOPDSRoutes(apiGroup *gin.RouterGroup) { opdsGroup.GET("/", api.authOPDSMiddleware, api.opdsEntry) opdsGroup.GET("/search.xml", api.authOPDSMiddleware, api.opdsSearchDescription) opdsGroup.GET("/documents", api.authOPDSMiddleware, api.opdsDocuments) - opdsGroup.GET("/documents/:document/cover", api.authOPDSMiddleware, api.getDocumentCover) - opdsGroup.GET("/documents/:document/file", api.authOPDSMiddleware, api.downloadDocument) + opdsGroup.GET("/documents/:document/cover", api.authOPDSMiddleware, api.createGetCoverHandler(apiErrorPage)) + opdsGroup.GET("/documents/:document/file", api.authOPDSMiddleware, api.createDownloadDocumentHandler(apiErrorPage)) } func (api *API) generateTemplates() *multitemplate.Renderer { diff --git a/api/app-routes.go b/api/app-routes.go index 3045a83..8afd938 100644 --- a/api/app-routes.go +++ b/api/app-routes.go @@ -13,7 +13,6 @@ import ( "mime/multipart" "net/http" "os" - "path" "path/filepath" "reflect" "sort" @@ -154,14 +153,14 @@ func (api *API) appGetDocuments(c *gin.Context) { }) if err != nil { log.Error("GetDocumentsWithStats DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentsWithStats DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentsWithStats DB Error: %v", err)) return } length, err := api.DB.Queries.GetDocumentsSize(api.DB.Ctx, query) if err != nil { log.Error("GetDocumentsSize DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentsSize DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentsSize DB Error: %v", err)) return } @@ -193,7 +192,7 @@ func (api *API) appGetDocument(c *gin.Context) { var rDocID requestDocumentID if err := c.ShouldBindUri(&rDocID); err != nil { log.Error("Invalid URI Bind") - errorPage(c, http.StatusNotFound, "Invalid document.") + appErrorPage(c, http.StatusNotFound, "Invalid document.") return } @@ -203,7 +202,7 @@ func (api *API) appGetDocument(c *gin.Context) { }) if err != nil { log.Error("GetDocumentWithStats DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentsWithStats DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentsWithStats DB Error: %v", err)) return } @@ -232,7 +231,7 @@ func (api *API) appGetProgress(c *gin.Context) { progress, err := api.DB.Queries.GetProgress(api.DB.Ctx, progressFilter) if err != nil { log.Error("GetProgress DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetActivity DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetActivity DB Error: %v", err)) return } @@ -259,7 +258,7 @@ func (api *API) appGetActivity(c *gin.Context) { activity, err := api.DB.Queries.GetActivity(api.DB.Ctx, activityFilter) if err != nil { log.Error("GetActivity DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetActivity DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetActivity DB Error: %v", err)) return } @@ -275,7 +274,7 @@ func (api *API) appGetHome(c *gin.Context) { graphData, err := api.DB.Queries.GetDailyReadStats(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetDailyReadStats DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDailyReadStats DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDailyReadStats DB Error: %v", err)) return } log.Debug("GetDailyReadStats DB Performance: ", time.Since(start)) @@ -284,7 +283,7 @@ func (api *API) appGetHome(c *gin.Context) { databaseInfo, err := api.DB.Queries.GetDatabaseInfo(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetDatabaseInfo DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDatabaseInfo DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDatabaseInfo DB Error: %v", err)) return } log.Debug("GetDatabaseInfo DB Performance: ", time.Since(start)) @@ -293,7 +292,7 @@ func (api *API) appGetHome(c *gin.Context) { streaks, err := api.DB.Queries.GetUserStreaks(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetUserStreaks DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUserStreaks DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUserStreaks DB Error: %v", err)) return } log.Debug("GetUserStreaks DB Performance: ", time.Since(start)) @@ -302,7 +301,7 @@ func (api *API) appGetHome(c *gin.Context) { userStatistics, err := api.DB.Queries.GetUserStatistics(api.DB.Ctx) if err != nil { log.Error("GetUserStatistics DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUserStatistics DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUserStatistics DB Error: %v", err)) return } log.Debug("GetUserStatistics DB Performance: ", time.Since(start)) @@ -323,14 +322,14 @@ func (api *API) appGetSettings(c *gin.Context) { user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetUser DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUser DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUser DB Error: %v", err)) return } devices, err := api.DB.Queries.GetDevices(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetDevices DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDevices DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDevices DB Error: %v", err)) return } @@ -351,10 +350,10 @@ func (api *API) appGetAdminLogs(c *gin.Context) { templateVars, _ := api.getBaseTemplateVars("admin-logs", c) // Open Log File - logPath := path.Join(api.Config.ConfigPath, "logs/antholume.log") + logPath := filepath.Join(api.Config.ConfigPath, "logs/antholume.log") logFile, err := os.Open(logPath) if err != nil { - errorPage(c, http.StatusBadRequest, "Missing AnthoLume log file.") + appErrorPage(c, http.StatusBadRequest, "Missing AnthoLume log file.") return } defer logFile.Close() @@ -392,7 +391,7 @@ func (api *API) appGetAdminUsers(c *gin.Context) { users, err := api.DB.Queries.GetUsers(api.DB.Ctx) if err != nil { log.Error("GetUsers DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUsers DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUsers DB Error: %v", err)) return } @@ -411,7 +410,7 @@ func (api *API) appPerformAdminAction(c *gin.Context) { var rAdminAction requestAdminAction if err := c.ShouldBind(&rAdminAction); err != nil { log.Error("Invalid Form Bind: ", err) - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -465,7 +464,7 @@ func (api *API) appGetSearch(c *gin.Context) { // Search searchResults, err := search.SearchBook(*sParams.Query, *sParams.Source) if err != nil { - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("Search Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("Search Error: %v", err)) return } @@ -505,7 +504,7 @@ func (api *API) appGetDocumentProgress(c *gin.Context) { var rDoc requestDocumentID if err := c.ShouldBindUri(&rDoc); err != nil { log.Error("Invalid URI Bind") - errorPage(c, http.StatusNotFound, "Invalid document.") + appErrorPage(c, http.StatusNotFound, "Invalid document.") return } @@ -516,7 +515,7 @@ func (api *API) appGetDocumentProgress(c *gin.Context) { if err != nil && err != sql.ErrNoRows { log.Error("UpsertDocument DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpsertDocument DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpsertDocument DB Error: %v", err)) return } @@ -526,7 +525,7 @@ func (api *API) appGetDocumentProgress(c *gin.Context) { }) if err != nil { log.Error("GetDocumentWithStats DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentWithStats DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentWithStats DB Error: %v", err)) return } @@ -550,7 +549,7 @@ func (api *API) appGetDevices(c *gin.Context) { if err != nil && err != sql.ErrNoRows { log.Error("GetDevices DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDevices DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDevices DB Error: %v", err)) return } @@ -561,7 +560,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { var rDocUpload requestDocumentUpload if err := c.ShouldBind(&rDocUpload); err != nil { log.Error("Invalid Form Bind") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -574,14 +573,14 @@ func (api *API) appUploadNewDocument(c *gin.Context) { uploadedFile, err := rDocUpload.DocumentFile.Open() if err != nil { log.Error("File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to open file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to open file.") return } fileMime, err := mimetype.DetectReader(uploadedFile) if err != nil { log.Error("MIME Error") - errorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") + appErrorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") return } fileExtension := fileMime.Extension() @@ -589,7 +588,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { // Validate Extension if !slices.Contains([]string{".epub"}, fileExtension) { log.Error("Invalid FileType: ", fileExtension) - errorPage(c, http.StatusBadRequest, "Invalid filetype.") + appErrorPage(c, http.StatusBadRequest, "Invalid filetype.") return } @@ -597,7 +596,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { tempFile, err := os.CreateTemp("", "book") if err != nil { log.Warn("Temp File Create Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to create temp file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to create temp file.") return } defer os.Remove(tempFile.Name()) @@ -607,7 +606,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { err = c.SaveUploadedFile(rDocUpload.DocumentFile, tempFile.Name()) if err != nil { log.Error("File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to save file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to save file.") return } @@ -615,7 +614,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { metadataInfo, err := metadata.GetMetadata(tempFile.Name()) if err != nil { log.Warn("GetMetadata Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to acquire file metadata.") + appErrorPage(c, http.StatusInternalServerError, "Unable to acquire file metadata.") return } @@ -623,7 +622,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { partialMD5, err := utils.CalculatePartialMD5(tempFile.Name()) if err != nil { log.Warn("Partial MD5 Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to calculate partial MD5.") + appErrorPage(c, http.StatusInternalServerError, "Unable to calculate partial MD5.") return } @@ -638,7 +637,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { fileHash, err := getFileMD5(tempFile.Name()) if err != nil { log.Error("MD5 Hash Failure: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to calculate MD5.") + appErrorPage(c, http.StatusInternalServerError, "Unable to calculate MD5.") return } @@ -646,7 +645,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { wordCount, err := metadata.GetWordCount(tempFile.Name()) if err != nil { log.Error("Word Count Failure: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to calculate word count.") + appErrorPage(c, http.StatusInternalServerError, "Unable to calculate word count.") return } @@ -675,7 +674,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { destFile, err := os.Create(safePath) if err != nil { log.Error("Dest File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to save file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to save file.") return } defer destFile.Close() @@ -683,7 +682,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { // Copy File if _, err = io.Copy(destFile, tempFile); err != nil { log.Error("Copy Temp File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to save file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to save file.") return } @@ -698,7 +697,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) { Filepath: &fileName, }); err != nil { log.Error("UpsertDocument DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpsertDocument DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpsertDocument DB Error: %v", err)) return } @@ -709,14 +708,14 @@ func (api *API) appEditDocument(c *gin.Context) { var rDocID requestDocumentID if err := c.ShouldBindUri(&rDocID); err != nil { log.Error("Invalid URI Bind") - errorPage(c, http.StatusNotFound, "Invalid document.") + appErrorPage(c, http.StatusNotFound, "Invalid document.") return } var rDocEdit requestDocumentEdit if err := c.ShouldBind(&rDocEdit); err != nil { log.Error("Invalid Form Bind") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -730,7 +729,7 @@ func (api *API) appEditDocument(c *gin.Context) { rDocEdit.CoverGBID == nil && rDocEdit.CoverFile == nil { log.Error("Missing Form Values") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -744,14 +743,14 @@ func (api *API) appEditDocument(c *gin.Context) { uploadedFile, err := rDocEdit.CoverFile.Open() if err != nil { log.Error("File Error") - errorPage(c, http.StatusInternalServerError, "Unable to open file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to open file.") return } fileMime, err := mimetype.DetectReader(uploadedFile) if err != nil { log.Error("MIME Error") - errorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") + appErrorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") return } fileExtension := fileMime.Extension() @@ -759,7 +758,7 @@ func (api *API) appEditDocument(c *gin.Context) { // Validate Extension if !slices.Contains([]string{".jpg", ".png"}, fileExtension) { log.Error("Invalid FileType: ", fileExtension) - errorPage(c, http.StatusBadRequest, "Invalid filetype.") + appErrorPage(c, http.StatusBadRequest, "Invalid filetype.") return } @@ -771,7 +770,7 @@ func (api *API) appEditDocument(c *gin.Context) { err = c.SaveUploadedFile(rDocEdit.CoverFile, safePath) if err != nil { log.Error("File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to save file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to save file.") return } @@ -795,7 +794,7 @@ func (api *API) appEditDocument(c *gin.Context) { Coverfile: coverFileName, }); err != nil { log.Error("UpsertDocument DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpsertDocument DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpsertDocument DB Error: %v", err)) return } @@ -807,18 +806,18 @@ func (api *API) appDeleteDocument(c *gin.Context) { var rDocID requestDocumentID if err := c.ShouldBindUri(&rDocID); err != nil { log.Error("Invalid URI Bind") - errorPage(c, http.StatusNotFound, "Invalid document.") + appErrorPage(c, http.StatusNotFound, "Invalid document.") return } changed, err := api.DB.Queries.DeleteDocument(api.DB.Ctx, rDocID.DocumentID) if err != nil { log.Error("DeleteDocument DB Error") - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("DeleteDocument DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("DeleteDocument DB Error: %v", err)) return } if changed == 0 { log.Error("DeleteDocument DB Error") - errorPage(c, http.StatusNotFound, "Invalid document.") + appErrorPage(c, http.StatusNotFound, "Invalid document.") return } @@ -829,14 +828,14 @@ func (api *API) appIdentifyDocument(c *gin.Context) { var rDocID requestDocumentID if err := c.ShouldBindUri(&rDocID); err != nil { log.Error("Invalid URI Bind") - errorPage(c, http.StatusNotFound, "Invalid document.") + appErrorPage(c, http.StatusNotFound, "Invalid document.") return } var rDocIdentify requestDocumentIdentify if err := c.ShouldBind(&rDocIdentify); err != nil { log.Error("Invalid Form Bind") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -854,7 +853,7 @@ func (api *API) appIdentifyDocument(c *gin.Context) { // Validate Values if rDocIdentify.ISBN == nil && rDocIdentify.Title == nil && rDocIdentify.Author == nil { log.Error("Invalid Form") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -897,7 +896,7 @@ func (api *API) appIdentifyDocument(c *gin.Context) { }) if err != nil { log.Error("GetDocumentWithStats DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentWithStats DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocumentWithStats DB Error: %v", err)) return } @@ -911,7 +910,7 @@ func (api *API) appSaveNewDocument(c *gin.Context) { var rDocAdd requestDocumentAdd if err := c.ShouldBind(&rDocAdd); err != nil { log.Error("Invalid Form Bind") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -1069,14 +1068,14 @@ func (api *API) appEditSettings(c *gin.Context) { var rUserSettings requestSettingsEdit if err := c.ShouldBind(&rUserSettings); err != nil { log.Error("Invalid Form Bind") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } // Validate Something Exists if rUserSettings.Password == nil && rUserSettings.NewPassword == nil && rUserSettings.TimeOffset == nil { log.Error("Missing Form Values") - errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") + appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.") return } @@ -1114,7 +1113,7 @@ func (api *API) appEditSettings(c *gin.Context) { _, err := api.DB.Queries.UpdateUser(api.DB.Ctx, newUserSettings) if err != nil { log.Error("UpdateUser DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpdateUser DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("UpdateUser DB Error: %v", err)) return } @@ -1122,7 +1121,7 @@ func (api *API) appEditSettings(c *gin.Context) { user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetUser DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUser DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUser DB Error: %v", err)) return } @@ -1130,7 +1129,7 @@ func (api *API) appEditSettings(c *gin.Context) { devices, err := api.DB.Queries.GetDevices(api.DB.Ctx, auth.UserName) if err != nil { log.Error("GetDevices DB Error: ", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDevices DB Error: %v", err)) + appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDevices DB Error: %v", err)) return } @@ -1143,7 +1142,7 @@ func (api *API) appEditSettings(c *gin.Context) { } func (api *API) appDemoModeError(c *gin.Context) { - errorPage(c, http.StatusUnauthorized, "Not Allowed in Demo Mode") + appErrorPage(c, http.StatusUnauthorized, "Not Allowed in Demo Mode") } func (api *API) getDocumentsWordCount(documents []database.GetDocumentsWithStatsRow) error { @@ -1221,7 +1220,7 @@ func bindQueryParams(c *gin.Context, defaultLimit int64) queryParams { return qParams } -func errorPage(c *gin.Context, errorCode int, errorMessage string) { +func appErrorPage(c *gin.Context, errorCode int, errorMessage string) { var errorHuman string = "We're not even sure what happened." switch errorCode { @@ -1324,14 +1323,14 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte uploadedFile, err := rAdminAction.RestoreFile.Open() if err != nil { log.Error("File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to open file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to open file.") return } fileMime, err := mimetype.DetectReader(uploadedFile) if err != nil { log.Error("MIME Error") - errorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") + appErrorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") return } fileExtension := fileMime.Extension() @@ -1339,7 +1338,7 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte // Validate Extension if !slices.Contains([]string{".zip"}, fileExtension) { log.Error("Invalid FileType: ", fileExtension) - errorPage(c, http.StatusBadRequest, "Invalid filetype.") + appErrorPage(c, http.StatusBadRequest, "Invalid filetype.") return } @@ -1347,7 +1346,7 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte tempFile, err := os.CreateTemp("", "restore") if err != nil { log.Warn("Temp File Create Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to create temp file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to create temp file.") return } defer os.Remove(tempFile.Name()) @@ -1357,7 +1356,7 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte err = c.SaveUploadedFile(rAdminAction.RestoreFile, tempFile.Name()) if err != nil { log.Error("File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to save file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to save file.") return } @@ -1365,23 +1364,23 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte fileInfo, err := tempFile.Stat() if err != nil { log.Error("File Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to read file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to read file.") return } // Create ZIP Reader - r, err := zip.NewReader(tempFile, fileInfo.Size()) + zipReader, err := zip.NewReader(tempFile, fileInfo.Size()) if err != nil { log.Error("ZIP Error: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to read zip.") + appErrorPage(c, http.StatusInternalServerError, "Unable to read zip.") return } // Validate ZIP Contents hasDBFile := false hasUnknownFile := false - for _, f := range r.File { - fileName := strings.TrimPrefix(f.Name, "/") + for _, file := range zipReader.File { + fileName := strings.TrimPrefix(file.Name, "/") if fileName == "antholume.db" { hasDBFile = true break @@ -1394,20 +1393,20 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte // Invalid ZIP if !hasDBFile { log.Error("Invalid ZIP File - Missing DB") - errorPage(c, http.StatusInternalServerError, "Invalid Restore ZIP - Missing DB") + appErrorPage(c, http.StatusInternalServerError, "Invalid Restore ZIP - Missing DB") return } else if hasUnknownFile { log.Error("Invalid ZIP File - Invalid File(s)") - errorPage(c, http.StatusInternalServerError, "Invalid Restore ZIP - Invalid File(s)") + appErrorPage(c, http.StatusInternalServerError, "Invalid Restore ZIP - Invalid File(s)") return } // Create Backup File - backupFilePath := path.Join(api.Config.ConfigPath, fmt.Sprintf("backup/AnthoLumeBackup_%s.zip", time.Now().Format("20060102"))) + backupFilePath := filepath.Join(api.Config.ConfigPath, fmt.Sprintf("backup/AnthoLumeBackup_%s.zip", time.Now().Format("20060102"))) backupFile, err := os.Create(backupFilePath) if err != nil { log.Error("Unable to create backup file: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to create backup file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to create backup file.") return } defer backupFile.Close() @@ -1417,7 +1416,26 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte err = api.createBackup(w, []string{"covers", "documents"}) if err != nil { log.Error("Unable to save backup file: ", err) - errorPage(c, http.StatusInternalServerError, "Unable to save backup file.") + appErrorPage(c, http.StatusInternalServerError, "Unable to save backup file.") + return + } + + // Remove Data + err = api.removeData() + if err != nil { + appErrorPage(c, http.StatusInternalServerError, "Unable to delete data.") + return + } + + // Restore Data + err = api.restoreData(zipReader) + if err != nil { + appErrorPage(c, http.StatusInternalServerError, "Unable to restore data.") + + // Panic? + + log.Panic("Oh no") + return } @@ -1425,6 +1443,56 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte // - Extract from temp directory } +func (api *API) restoreData(zipReader *zip.Reader) error { + for _, file := range zipReader.File { + rc, err := file.Open() + if err != nil { + return err + } + defer rc.Close() + + destPath := filepath.Join(api.Config.DataPath, file.Name) + destFile, err := os.Create(destPath) + if err != nil { + fmt.Println("Error creating destination file:", err) + return err + } + defer destFile.Close() + + // Copy the contents from the zip file to the destination file. + if _, err := io.Copy(destFile, rc); err != nil { + fmt.Println("Error copying file contents:", err) + return err + } + + fmt.Printf("Extracted: %s\n", destPath) + } + + return nil +} + +func (api *API) removeData() error { + allPaths := []string{ + "covers", + "documents", + "antholume.db", + "antholume.db-wal", + "antholume.db-shm", + } + + for _, name := range allPaths { + fullPath := filepath.Join(api.Config.DataPath, name) + err := os.RemoveAll(fullPath) + if err != nil { + log.Errorf("Unable to delete %s: %v", name, err) + return err + } + + } + + return nil +} + func (api *API) createBackup(w io.Writer, directories []string) error { ar := zip.NewWriter(w) @@ -1448,7 +1516,7 @@ func (api *API) createBackup(w io.Writer, directories []string) error { folderName := filepath.Base(filepath.Dir(currentPath)) // Create File in Export - newF, err := ar.Create(path.Join(folderName, fileName)) + newF, err := ar.Create(filepath.Join(folderName, fileName)) if err != nil { return err } @@ -1464,7 +1532,7 @@ func (api *API) createBackup(w io.Writer, directories []string) error { // Get DB Path fileName := fmt.Sprintf("%s.db", api.Config.DBName) - dbLocation := path.Join(api.Config.ConfigPath, fileName) + dbLocation := filepath.Join(api.Config.ConfigPath, fileName) // Copy Database File dbFile, err := os.Open(dbLocation) @@ -1481,7 +1549,7 @@ func (api *API) createBackup(w io.Writer, directories []string) error { // Backup Covers & Documents for _, dir := range directories { - err = filepath.WalkDir(path.Join(api.Config.DataPath, dir), exportWalker) + err = filepath.WalkDir(filepath.Join(api.Config.DataPath, dir), exportWalker) if err != nil { return err } diff --git a/api/auth.go b/api/auth.go index 03596a6..663bf53 100644 --- a/api/auth.go +++ b/api/auth.go @@ -135,7 +135,7 @@ func (api *API) authAdminWebAppMiddleware(c *gin.Context) { } } - errorPage(c, http.StatusUnauthorized, "Admin Permissions Required") + appErrorPage(c, http.StatusUnauthorized, "Admin Permissions Required") c.Abort() return } @@ -175,7 +175,7 @@ func (api *API) appAuthFormLogin(c *gin.Context) { func (api *API) appAuthFormRegister(c *gin.Context) { if !api.Config.RegistrationEnabled { - errorPage(c, http.StatusUnauthorized, "Nice try. Registration is disabled.") + appErrorPage(c, http.StatusUnauthorized, "Nice try. Registration is disabled.") return } @@ -236,7 +236,7 @@ func (api *API) appAuthFormRegister(c *gin.Context) { } session := sessions.Default(c) if err := setSession(session, auth); err != nil { - errorPage(c, http.StatusUnauthorized, "Unauthorized.") + appErrorPage(c, http.StatusUnauthorized, "Unauthorized.") return } diff --git a/api/common.go b/api/common.go index 3bbffa5..5c59f4a 100644 --- a/api/common.go +++ b/api/common.go @@ -11,130 +11,134 @@ import ( "reichard.io/antholume/metadata" ) -func (api *API) downloadDocument(c *gin.Context) { - var rDoc requestDocumentID - if err := c.ShouldBindUri(&rDoc); err != nil { - log.Error("Invalid URI Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) - return - } - - // Get Document - document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) - if err != nil { - log.Error("GetDocument DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Document"}) - return - } - - if document.Filepath == nil { - log.Error("Document Doesn't Have File:", rDoc.DocumentID) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Document Doesn't Exist"}) - return - } - - // Derive Storage Location - filePath := filepath.Join(api.Config.DataPath, "documents", *document.Filepath) - - // Validate File Exists - _, err = os.Stat(filePath) - if os.IsNotExist(err) { - log.Error("File Doesn't Exist:", rDoc.DocumentID) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Document Doesn't Exists"}) - return - } - - // Force Download (Security) - c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(*document.Filepath))) - c.File(filePath) -} - -func (api *API) getDocumentCover(c *gin.Context) { - var rDoc requestDocumentID - if err := c.ShouldBindUri(&rDoc); err != nil { - log.Error("Invalid URI Bind") - errorPage(c, http.StatusNotFound, "Invalid cover.") - return - } - - // Validate Document Exists in DB - document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) - if err != nil { - log.Error("GetDocument DB Error:", err) - errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocument DB Error: %v", err)) - return - } - - // Handle Identified Document - if document.Coverfile != nil { - if *document.Coverfile == "UNKNOWN" { - c.FileFromFS("assets/images/no-cover.jpg", http.FS(api.Assets)) +func (api *API) createDownloadDocumentHandler(errorFunc func(*gin.Context, int, string)) func(*gin.Context) { + return func(c *gin.Context) { + var rDoc requestDocumentID + if err := c.ShouldBindUri(&rDoc); err != nil { + log.Error("Invalid URI Bind") + errorFunc(c, http.StatusBadRequest, "Invalid Request") return } - // Derive Path - safePath := filepath.Join(api.Config.DataPath, "covers", *document.Coverfile) + // Get Document + document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) + if err != nil { + log.Error("GetDocument DB Error:", err) + errorFunc(c, http.StatusBadRequest, "Unknown Document") + return + } + + if document.Filepath == nil { + log.Error("Document Doesn't Have File:", rDoc.DocumentID) + errorFunc(c, http.StatusBadRequest, "Document Doesn't Exist") + return + } + + // Derive Storage Location + filePath := filepath.Join(api.Config.DataPath, "documents", *document.Filepath) // Validate File Exists - _, err = os.Stat(safePath) + _, err = os.Stat(filePath) + if os.IsNotExist(err) { + log.Error("File Doesn't Exist:", rDoc.DocumentID) + errorFunc(c, http.StatusBadRequest, "Document Doesn't Exist") + return + } + + // Force Download + c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(*document.Filepath))) + c.File(filePath) + } +} + +func (api *API) createGetCoverHandler(errorFunc func(*gin.Context, int, string)) func(*gin.Context) { + return func(c *gin.Context) { + var rDoc requestDocumentID + if err := c.ShouldBindUri(&rDoc); err != nil { + log.Error("Invalid URI Bind") + errorFunc(c, http.StatusNotFound, "Invalid cover.") + return + } + + // Validate Document Exists in DB + document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) if err != nil { - log.Error("File Should But Doesn't Exist:", err) + log.Error("GetDocument DB Error:", err) + errorFunc(c, http.StatusInternalServerError, fmt.Sprintf("GetDocument DB Error: %v", err)) + return + } + + // Handle Identified Document + if document.Coverfile != nil { + if *document.Coverfile == "UNKNOWN" { + c.FileFromFS("assets/images/no-cover.jpg", http.FS(api.Assets)) + return + } + + // Derive Path + safePath := filepath.Join(api.Config.DataPath, "covers", *document.Coverfile) + + // Validate File Exists + _, err = os.Stat(safePath) + if err != nil { + log.Error("File Should But Doesn't Exist:", err) + c.FileFromFS("assets/images/no-cover.jpg", http.FS(api.Assets)) + return + } + + c.File(safePath) + return + } + + // Attempt Metadata + var coverDir string = filepath.Join(api.Config.DataPath, "covers") + var coverFile string = "UNKNOWN" + + // Identify Documents & Save Covers + metadataResults, err := metadata.SearchMetadata(metadata.GBOOK, metadata.MetadataInfo{ + Title: document.Title, + Author: document.Author, + }) + + if err == nil && len(metadataResults) > 0 && metadataResults[0].ID != nil { + firstResult := metadataResults[0] + + // Save Cover + fileName, err := metadata.CacheCover(*firstResult.ID, coverDir, document.ID, false) + if err == nil { + coverFile = *fileName + } + + // Store First Metadata Result + if _, err = api.DB.Queries.AddMetadata(api.DB.Ctx, database.AddMetadataParams{ + DocumentID: document.ID, + Title: firstResult.Title, + Author: firstResult.Author, + Description: firstResult.Description, + Gbid: firstResult.ID, + Olid: nil, + Isbn10: firstResult.ISBN10, + Isbn13: firstResult.ISBN13, + }); err != nil { + log.Error("AddMetadata DB Error:", err) + } + } + + // Upsert Document + if _, err = api.DB.Queries.UpsertDocument(api.DB.Ctx, database.UpsertDocumentParams{ + ID: document.ID, + Coverfile: &coverFile, + }); err != nil { + log.Warn("UpsertDocument DB Error:", err) + } + + // Return Unknown Cover + if coverFile == "UNKNOWN" { c.FileFromFS("assets/images/no-cover.jpg", http.FS(api.Assets)) return } - c.File(safePath) - return + coverFilePath := filepath.Join(coverDir, coverFile) + c.File(coverFilePath) } - - // Attempt Metadata - var coverDir string = filepath.Join(api.Config.DataPath, "covers") - var coverFile string = "UNKNOWN" - - // Identify Documents & Save Covers - metadataResults, err := metadata.SearchMetadata(metadata.GBOOK, metadata.MetadataInfo{ - Title: document.Title, - Author: document.Author, - }) - - if err == nil && len(metadataResults) > 0 && metadataResults[0].ID != nil { - firstResult := metadataResults[0] - - // Save Cover - fileName, err := metadata.CacheCover(*firstResult.ID, coverDir, document.ID, false) - if err == nil { - coverFile = *fileName - } - - // Store First Metadata Result - if _, err = api.DB.Queries.AddMetadata(api.DB.Ctx, database.AddMetadataParams{ - DocumentID: document.ID, - Title: firstResult.Title, - Author: firstResult.Author, - Description: firstResult.Description, - Gbid: firstResult.ID, - Olid: nil, - Isbn10: firstResult.ISBN10, - Isbn13: firstResult.ISBN13, - }); err != nil { - log.Error("AddMetadata DB Error:", err) - } - } - - // Upsert Document - if _, err = api.DB.Queries.UpsertDocument(api.DB.Ctx, database.UpsertDocumentParams{ - ID: document.ID, - Coverfile: &coverFile, - }); err != nil { - log.Warn("UpsertDocument DB Error:", err) - } - - // Return Unknown Cover - if coverFile == "UNKNOWN" { - c.FileFromFS("assets/images/no-cover.jpg", http.FS(api.Assets)) - return - } - - coverFilePath := filepath.Join(coverDir, coverFile) - c.File(coverFilePath) } diff --git a/api/ko-routes.go b/api/ko-routes.go index b6ee58c..a394a8a 100644 --- a/api/ko-routes.go +++ b/api/ko-routes.go @@ -90,20 +90,20 @@ func (api *API) koCreateUser(c *gin.Context) { var rUser requestUser if err := c.ShouldBindJSON(&rUser); err != nil { log.Error("Invalid JSON Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid User Data"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid User Data") return } if rUser.Username == "" || rUser.Password == "" { log.Error("Invalid User - Empty Username or Password") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid User Data"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid User Data") return } hashedPassword, err := argon2.CreateHash(rUser.Password, argon2.DefaultParams) if err != nil { log.Error("Argon2 Hash Failure:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -113,13 +113,14 @@ func (api *API) koCreateUser(c *gin.Context) { }) if err != nil { log.Error("CreateUser DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid User Data"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid User Data") return } // User Exists if rows == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "User Already Exists"}) + log.Error("User Already Exists:", rUser.Username) + apiErrorPage(c, http.StatusBadRequest, "User Already Exists") return } @@ -137,7 +138,7 @@ func (api *API) koSetProgress(c *gin.Context) { var rPosition requestPosition if err := c.ShouldBindJSON(&rPosition); err != nil { log.Error("Invalid JSON Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Progress Data"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Progress Data") return } @@ -168,7 +169,7 @@ func (api *API) koSetProgress(c *gin.Context) { }) if err != nil { log.Error("UpdateProgress DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -187,7 +188,7 @@ func (api *API) koGetProgress(c *gin.Context) { var rDocID requestDocumentID if err := c.ShouldBindUri(&rDocID); err != nil { log.Error("Invalid URI Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -202,7 +203,7 @@ func (api *API) koGetProgress(c *gin.Context) { return } else if err != nil { log.Error("GetDocumentProgress DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Document") return } @@ -224,7 +225,7 @@ func (api *API) koAddActivities(c *gin.Context) { var rActivity requestActivity if err := c.ShouldBindJSON(&rActivity); err != nil { log.Error("Invalid JSON Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Activity"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Activity") return } @@ -232,7 +233,7 @@ func (api *API) koAddActivities(c *gin.Context) { tx, err := api.DB.DB.Begin() if err != nil { log.Error("Transaction Begin DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -253,7 +254,7 @@ func (api *API) koAddActivities(c *gin.Context) { ID: doc, }); err != nil { log.Error("UpsertDocument DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Document") return } } @@ -266,7 +267,7 @@ func (api *API) koAddActivities(c *gin.Context) { LastSynced: time.Now().UTC().Format(time.RFC3339), }); err != nil { log.Error("UpsertDevice DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Device") return } @@ -282,7 +283,7 @@ func (api *API) koAddActivities(c *gin.Context) { EndPercentage: float64(item.Page+1) / float64(item.Pages), }); err != nil { log.Error("AddActivity DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Activity"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Activity") return } } @@ -290,7 +291,7 @@ func (api *API) koAddActivities(c *gin.Context) { // Commit Transaction if err := tx.Commit(); err != nil { log.Error("Transaction Commit DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -308,7 +309,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) { var rCheckActivity requestCheckActivitySync if err := c.ShouldBindJSON(&rCheckActivity); err != nil { log.Error("Invalid JSON Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -320,7 +321,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) { LastSynced: time.Now().UTC().Format(time.RFC3339), }); err != nil { log.Error("UpsertDevice DB Error", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Device") return } @@ -333,7 +334,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) { lastActivity = time.UnixMilli(0).Format(time.RFC3339) } else if err != nil { log.Error("GetLastActivity DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -341,7 +342,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) { parsedTime, err := time.Parse(time.RFC3339, lastActivity) if err != nil { log.Error("Time Parse Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -354,7 +355,7 @@ func (api *API) koAddDocuments(c *gin.Context) { var rNewDocs requestDocument if err := c.ShouldBindJSON(&rNewDocs); err != nil { log.Error("Invalid JSON Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document(s)"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Document(s)") return } @@ -362,7 +363,7 @@ func (api *API) koAddDocuments(c *gin.Context) { tx, err := api.DB.DB.Begin() if err != nil { log.Error("Transaction Begin DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -383,7 +384,7 @@ func (api *API) koAddDocuments(c *gin.Context) { }) if err != nil { log.Error("UpsertDocument DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Document") return } } @@ -391,7 +392,7 @@ func (api *API) koAddDocuments(c *gin.Context) { // Commit Transaction if err := tx.Commit(); err != nil { log.Error("Transaction Commit DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Error") return } @@ -409,7 +410,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) { var rCheckDocs requestCheckDocumentSync if err := c.ShouldBindJSON(&rCheckDocs); err != nil { log.Error("Invalid JSON Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -422,7 +423,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) { }) if err != nil { log.Error("UpsertDevice DB Error", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Device") return } @@ -433,7 +434,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) { missingDocs, err = api.DB.Queries.GetMissingDocuments(api.DB.Ctx, rCheckDocs.Have) if err != nil { log.Error("GetMissingDocuments DB Error", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -441,7 +442,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) { deletedDocIDs, err = api.DB.Queries.GetDeletedDocuments(api.DB.Ctx, rCheckDocs.Have) if err != nil { log.Error("GetDeletedDocuments DB Error", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -449,14 +450,14 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) { jsonHaves, err := json.Marshal(rCheckDocs.Have) if err != nil { log.Error("JSON Marshal Error", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } wantedDocs, err := api.DB.Queries.GetWantedDocuments(api.DB.Ctx, string(jsonHaves)) if err != nil { log.Error("GetWantedDocuments DB Error", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } @@ -500,14 +501,14 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { var rDoc requestDocumentID if err := c.ShouldBindUri(&rDoc); err != nil { log.Error("Invalid URI Bind") - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Request") return } fileData, err := c.FormFile("file") if err != nil { log.Error("File Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) + apiErrorPage(c, http.StatusBadRequest, "File Error") return } @@ -518,7 +519,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { if !slices.Contains([]string{".epub", ".html"}, fileExtension) { log.Error("Invalid FileType:", fileExtension) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Filetype"}) + apiErrorPage(c, http.StatusBadRequest, "Invalid Filetype") return } @@ -526,7 +527,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) if err != nil { log.Error("GetDocument DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Document"}) + apiErrorPage(c, http.StatusBadRequest, "Unknown Document") return } @@ -559,7 +560,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { err = c.SaveUploadedFile(fileData, safePath) if err != nil { log.Error("Save Failure:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) + apiErrorPage(c, http.StatusBadRequest, "File Error") return } } @@ -568,7 +569,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { fileHash, err := getFileMD5(safePath) if err != nil { log.Error("Hash Failure:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) + apiErrorPage(c, http.StatusBadRequest, "File Error") return } @@ -576,7 +577,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { wordCount, err := metadata.GetWordCount(safePath) if err != nil { log.Error("Word Count Failure:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) + apiErrorPage(c, http.StatusBadRequest, "File Error") return } @@ -588,7 +589,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { Words: &wordCount, }); err != nil { log.Error("UpsertDocument DB Error:", err) - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Document Error"}) + apiErrorPage(c, http.StatusBadRequest, "Document Error") return } @@ -598,7 +599,11 @@ func (api *API) koUploadExistingDocument(c *gin.Context) { } func (api *API) koDemoModeJSONError(c *gin.Context) { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Not Allowed in Demo Mode"}) + apiErrorPage(c, http.StatusUnauthorized, "Not Allowed in Demo Mode") +} + +func apiErrorPage(c *gin.Context, errorCode int, errorMessage string) { + c.AbortWithStatusJSON(errorCode, gin.H{"error": errorMessage}) } func (api *API) sanitizeInput(val any) *string { diff --git a/database/manager.go b/database/manager.go index 538e40f..64194cc 100644 --- a/database/manager.go +++ b/database/manager.go @@ -6,7 +6,7 @@ import ( "embed" _ "embed" "fmt" - "path" + "path/filepath" "time" "github.com/pressly/goose/v3" @@ -37,7 +37,7 @@ func NewMgr(c *config.Config) *DBManager { if c.DBType == "sqlite" || c.DBType == "memory" { var dbLocation string = ":memory:" if c.DBType == "sqlite" { - dbLocation = path.Join(c.ConfigPath, fmt.Sprintf("%s.db", c.DBName)) + dbLocation = filepath.Join(c.ConfigPath, fmt.Sprintf("%s.db", c.DBName)) } var err error