refactor(errors): handle api / app errors better
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Evan Reichard 2024-01-26 22:07:30 -05:00
parent fd8b6bcdc1
commit 8c4c1022c3
6 changed files with 316 additions and 239 deletions

View File

@ -114,8 +114,8 @@ func (api *API) registerWebAppRoutes() {
api.Router.GET("/progress", api.authWebAppMiddleware, api.appGetProgress) api.Router.GET("/progress", api.authWebAppMiddleware, api.appGetProgress)
api.Router.GET("/documents", api.authWebAppMiddleware, api.appGetDocuments) api.Router.GET("/documents", api.authWebAppMiddleware, api.appGetDocuments)
api.Router.GET("/documents/:document", api.authWebAppMiddleware, api.appGetDocument) api.Router.GET("/documents/:document", api.authWebAppMiddleware, api.appGetDocument)
api.Router.GET("/documents/:document/cover", api.authWebAppMiddleware, api.getDocumentCover) api.Router.GET("/documents/:document/cover", api.authWebAppMiddleware, api.createGetCoverHandler(appErrorPage))
api.Router.GET("/documents/:document/file", api.authWebAppMiddleware, api.downloadDocument) api.Router.GET("/documents/:document/file", api.authWebAppMiddleware, api.createDownloadDocumentHandler(appErrorPage))
api.Router.GET("/login", api.appGetLogin) api.Router.GET("/login", api.appGetLogin)
api.Router.GET("/logout", api.authWebAppMiddleware, api.appAuthLogout) api.Router.GET("/logout", api.authWebAppMiddleware, api.appAuthLogout)
api.Router.GET("/register", api.appGetRegister) api.Router.GET("/register", api.appGetRegister)
@ -153,7 +153,7 @@ func (api *API) registerKOAPIRoutes(apiGroup *gin.RouterGroup) {
koGroup := apiGroup.Group("/ko") koGroup := apiGroup.Group("/ko")
// KO Sync Routes (WebApp Uses - Progress & Activity) // 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("/syncs/progress/:document", api.authKOMiddleware, api.koGetProgress)
koGroup.GET("/users/auth", api.authKOMiddleware, api.koAuthorizeUser) koGroup.GET("/users/auth", api.authKOMiddleware, api.koAuthorizeUser)
koGroup.POST("/activity", api.authKOMiddleware, api.koAddActivities) 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("/", api.authOPDSMiddleware, api.opdsEntry)
opdsGroup.GET("/search.xml", api.authOPDSMiddleware, api.opdsSearchDescription) opdsGroup.GET("/search.xml", api.authOPDSMiddleware, api.opdsSearchDescription)
opdsGroup.GET("/documents", api.authOPDSMiddleware, api.opdsDocuments) opdsGroup.GET("/documents", api.authOPDSMiddleware, api.opdsDocuments)
opdsGroup.GET("/documents/:document/cover", api.authOPDSMiddleware, api.getDocumentCover) opdsGroup.GET("/documents/:document/cover", api.authOPDSMiddleware, api.createGetCoverHandler(apiErrorPage))
opdsGroup.GET("/documents/:document/file", api.authOPDSMiddleware, api.downloadDocument) opdsGroup.GET("/documents/:document/file", api.authOPDSMiddleware, api.createDownloadDocumentHandler(apiErrorPage))
} }
func (api *API) generateTemplates() *multitemplate.Renderer { func (api *API) generateTemplates() *multitemplate.Renderer {

View File

@ -13,7 +13,6 @@ import (
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"sort" "sort"
@ -154,14 +153,14 @@ func (api *API) appGetDocuments(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("GetDocumentsWithStats DB Error: ", err) 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 return
} }
length, err := api.DB.Queries.GetDocumentsSize(api.DB.Ctx, query) length, err := api.DB.Queries.GetDocumentsSize(api.DB.Ctx, query)
if err != nil { if err != nil {
log.Error("GetDocumentsSize DB Error: ", err) 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 return
} }
@ -193,7 +192,7 @@ func (api *API) appGetDocument(c *gin.Context) {
var rDocID requestDocumentID var rDocID requestDocumentID
if err := c.ShouldBindUri(&rDocID); err != nil { if err := c.ShouldBindUri(&rDocID); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
errorPage(c, http.StatusNotFound, "Invalid document.") appErrorPage(c, http.StatusNotFound, "Invalid document.")
return return
} }
@ -203,7 +202,7 @@ func (api *API) appGetDocument(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("GetDocumentWithStats DB Error: ", err) 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 return
} }
@ -232,7 +231,7 @@ func (api *API) appGetProgress(c *gin.Context) {
progress, err := api.DB.Queries.GetProgress(api.DB.Ctx, progressFilter) progress, err := api.DB.Queries.GetProgress(api.DB.Ctx, progressFilter)
if err != nil { if err != nil {
log.Error("GetProgress DB Error: ", err) 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 return
} }
@ -259,7 +258,7 @@ func (api *API) appGetActivity(c *gin.Context) {
activity, err := api.DB.Queries.GetActivity(api.DB.Ctx, activityFilter) activity, err := api.DB.Queries.GetActivity(api.DB.Ctx, activityFilter)
if err != nil { if err != nil {
log.Error("GetActivity DB Error: ", err) 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 return
} }
@ -275,7 +274,7 @@ func (api *API) appGetHome(c *gin.Context) {
graphData, err := api.DB.Queries.GetDailyReadStats(api.DB.Ctx, auth.UserName) graphData, err := api.DB.Queries.GetDailyReadStats(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetDailyReadStats DB Error: ", err) 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 return
} }
log.Debug("GetDailyReadStats DB Performance: ", time.Since(start)) 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) databaseInfo, err := api.DB.Queries.GetDatabaseInfo(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetDatabaseInfo DB Error: ", err) 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 return
} }
log.Debug("GetDatabaseInfo DB Performance: ", time.Since(start)) 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) streaks, err := api.DB.Queries.GetUserStreaks(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetUserStreaks DB Error: ", err) 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 return
} }
log.Debug("GetUserStreaks DB Performance: ", time.Since(start)) 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) userStatistics, err := api.DB.Queries.GetUserStatistics(api.DB.Ctx)
if err != nil { if err != nil {
log.Error("GetUserStatistics DB Error: ", err) 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 return
} }
log.Debug("GetUserStatistics DB Performance: ", time.Since(start)) 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) user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetUser DB Error: ", err) 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 return
} }
devices, err := api.DB.Queries.GetDevices(api.DB.Ctx, auth.UserName) devices, err := api.DB.Queries.GetDevices(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetDevices DB Error: ", err) 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 return
} }
@ -351,10 +350,10 @@ func (api *API) appGetAdminLogs(c *gin.Context) {
templateVars, _ := api.getBaseTemplateVars("admin-logs", c) templateVars, _ := api.getBaseTemplateVars("admin-logs", c)
// Open Log File // 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) logFile, err := os.Open(logPath)
if err != nil { if err != nil {
errorPage(c, http.StatusBadRequest, "Missing AnthoLume log file.") appErrorPage(c, http.StatusBadRequest, "Missing AnthoLume log file.")
return return
} }
defer logFile.Close() defer logFile.Close()
@ -392,7 +391,7 @@ func (api *API) appGetAdminUsers(c *gin.Context) {
users, err := api.DB.Queries.GetUsers(api.DB.Ctx) users, err := api.DB.Queries.GetUsers(api.DB.Ctx)
if err != nil { if err != nil {
log.Error("GetUsers DB Error: ", err) 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 return
} }
@ -411,7 +410,7 @@ func (api *API) appPerformAdminAction(c *gin.Context) {
var rAdminAction requestAdminAction var rAdminAction requestAdminAction
if err := c.ShouldBind(&rAdminAction); err != nil { if err := c.ShouldBind(&rAdminAction); err != nil {
log.Error("Invalid Form Bind: ", err) 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 return
} }
@ -465,7 +464,7 @@ func (api *API) appGetSearch(c *gin.Context) {
// Search // Search
searchResults, err := search.SearchBook(*sParams.Query, *sParams.Source) searchResults, err := search.SearchBook(*sParams.Query, *sParams.Source)
if err != nil { if err != nil {
errorPage(c, http.StatusInternalServerError, fmt.Sprintf("Search Error: %v", err)) appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("Search Error: %v", err))
return return
} }
@ -505,7 +504,7 @@ func (api *API) appGetDocumentProgress(c *gin.Context) {
var rDoc requestDocumentID var rDoc requestDocumentID
if err := c.ShouldBindUri(&rDoc); err != nil { if err := c.ShouldBindUri(&rDoc); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
errorPage(c, http.StatusNotFound, "Invalid document.") appErrorPage(c, http.StatusNotFound, "Invalid document.")
return return
} }
@ -516,7 +515,7 @@ func (api *API) appGetDocumentProgress(c *gin.Context) {
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
log.Error("UpsertDocument DB Error: ", err) 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 return
} }
@ -526,7 +525,7 @@ func (api *API) appGetDocumentProgress(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("GetDocumentWithStats DB Error: ", err) 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 return
} }
@ -550,7 +549,7 @@ func (api *API) appGetDevices(c *gin.Context) {
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
log.Error("GetDevices DB Error: ", err) 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 return
} }
@ -561,7 +560,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
var rDocUpload requestDocumentUpload var rDocUpload requestDocumentUpload
if err := c.ShouldBind(&rDocUpload); err != nil { if err := c.ShouldBind(&rDocUpload); err != nil {
log.Error("Invalid Form Bind") log.Error("Invalid Form Bind")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -574,14 +573,14 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
uploadedFile, err := rDocUpload.DocumentFile.Open() uploadedFile, err := rDocUpload.DocumentFile.Open()
if err != nil { if err != nil {
log.Error("File Error: ", err) log.Error("File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to open file.") appErrorPage(c, http.StatusInternalServerError, "Unable to open file.")
return return
} }
fileMime, err := mimetype.DetectReader(uploadedFile) fileMime, err := mimetype.DetectReader(uploadedFile)
if err != nil { if err != nil {
log.Error("MIME Error") log.Error("MIME Error")
errorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") appErrorPage(c, http.StatusInternalServerError, "Unable to detect filetype.")
return return
} }
fileExtension := fileMime.Extension() fileExtension := fileMime.Extension()
@ -589,7 +588,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
// Validate Extension // Validate Extension
if !slices.Contains([]string{".epub"}, fileExtension) { if !slices.Contains([]string{".epub"}, fileExtension) {
log.Error("Invalid FileType: ", fileExtension) log.Error("Invalid FileType: ", fileExtension)
errorPage(c, http.StatusBadRequest, "Invalid filetype.") appErrorPage(c, http.StatusBadRequest, "Invalid filetype.")
return return
} }
@ -597,7 +596,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
tempFile, err := os.CreateTemp("", "book") tempFile, err := os.CreateTemp("", "book")
if err != nil { if err != nil {
log.Warn("Temp File Create Error: ", err) 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 return
} }
defer os.Remove(tempFile.Name()) defer os.Remove(tempFile.Name())
@ -607,7 +606,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
err = c.SaveUploadedFile(rDocUpload.DocumentFile, tempFile.Name()) err = c.SaveUploadedFile(rDocUpload.DocumentFile, tempFile.Name())
if err != nil { if err != nil {
log.Error("File Error: ", err) log.Error("File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to save file.") appErrorPage(c, http.StatusInternalServerError, "Unable to save file.")
return return
} }
@ -615,7 +614,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
metadataInfo, err := metadata.GetMetadata(tempFile.Name()) metadataInfo, err := metadata.GetMetadata(tempFile.Name())
if err != nil { if err != nil {
log.Warn("GetMetadata Error: ", err) log.Warn("GetMetadata Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to acquire file metadata.") appErrorPage(c, http.StatusInternalServerError, "Unable to acquire file metadata.")
return return
} }
@ -623,7 +622,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
partialMD5, err := utils.CalculatePartialMD5(tempFile.Name()) partialMD5, err := utils.CalculatePartialMD5(tempFile.Name())
if err != nil { if err != nil {
log.Warn("Partial MD5 Error: ", err) 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 return
} }
@ -638,7 +637,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
fileHash, err := getFileMD5(tempFile.Name()) fileHash, err := getFileMD5(tempFile.Name())
if err != nil { if err != nil {
log.Error("MD5 Hash Failure: ", err) log.Error("MD5 Hash Failure: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to calculate MD5.") appErrorPage(c, http.StatusInternalServerError, "Unable to calculate MD5.")
return return
} }
@ -646,7 +645,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
wordCount, err := metadata.GetWordCount(tempFile.Name()) wordCount, err := metadata.GetWordCount(tempFile.Name())
if err != nil { if err != nil {
log.Error("Word Count Failure: ", err) 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 return
} }
@ -675,7 +674,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
destFile, err := os.Create(safePath) destFile, err := os.Create(safePath)
if err != nil { if err != nil {
log.Error("Dest File Error: ", err) log.Error("Dest File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to save file.") appErrorPage(c, http.StatusInternalServerError, "Unable to save file.")
return return
} }
defer destFile.Close() defer destFile.Close()
@ -683,7 +682,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
// Copy File // Copy File
if _, err = io.Copy(destFile, tempFile); err != nil { if _, err = io.Copy(destFile, tempFile); err != nil {
log.Error("Copy Temp File Error: ", err) log.Error("Copy Temp File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to save file.") appErrorPage(c, http.StatusInternalServerError, "Unable to save file.")
return return
} }
@ -698,7 +697,7 @@ func (api *API) appUploadNewDocument(c *gin.Context) {
Filepath: &fileName, Filepath: &fileName,
}); err != nil { }); err != nil {
log.Error("UpsertDocument DB Error: ", err) 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 return
} }
@ -709,14 +708,14 @@ func (api *API) appEditDocument(c *gin.Context) {
var rDocID requestDocumentID var rDocID requestDocumentID
if err := c.ShouldBindUri(&rDocID); err != nil { if err := c.ShouldBindUri(&rDocID); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
errorPage(c, http.StatusNotFound, "Invalid document.") appErrorPage(c, http.StatusNotFound, "Invalid document.")
return return
} }
var rDocEdit requestDocumentEdit var rDocEdit requestDocumentEdit
if err := c.ShouldBind(&rDocEdit); err != nil { if err := c.ShouldBind(&rDocEdit); err != nil {
log.Error("Invalid Form Bind") log.Error("Invalid Form Bind")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -730,7 +729,7 @@ func (api *API) appEditDocument(c *gin.Context) {
rDocEdit.CoverGBID == nil && rDocEdit.CoverGBID == nil &&
rDocEdit.CoverFile == nil { rDocEdit.CoverFile == nil {
log.Error("Missing Form Values") log.Error("Missing Form Values")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -744,14 +743,14 @@ func (api *API) appEditDocument(c *gin.Context) {
uploadedFile, err := rDocEdit.CoverFile.Open() uploadedFile, err := rDocEdit.CoverFile.Open()
if err != nil { if err != nil {
log.Error("File Error") log.Error("File Error")
errorPage(c, http.StatusInternalServerError, "Unable to open file.") appErrorPage(c, http.StatusInternalServerError, "Unable to open file.")
return return
} }
fileMime, err := mimetype.DetectReader(uploadedFile) fileMime, err := mimetype.DetectReader(uploadedFile)
if err != nil { if err != nil {
log.Error("MIME Error") log.Error("MIME Error")
errorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") appErrorPage(c, http.StatusInternalServerError, "Unable to detect filetype.")
return return
} }
fileExtension := fileMime.Extension() fileExtension := fileMime.Extension()
@ -759,7 +758,7 @@ func (api *API) appEditDocument(c *gin.Context) {
// Validate Extension // Validate Extension
if !slices.Contains([]string{".jpg", ".png"}, fileExtension) { if !slices.Contains([]string{".jpg", ".png"}, fileExtension) {
log.Error("Invalid FileType: ", fileExtension) log.Error("Invalid FileType: ", fileExtension)
errorPage(c, http.StatusBadRequest, "Invalid filetype.") appErrorPage(c, http.StatusBadRequest, "Invalid filetype.")
return return
} }
@ -771,7 +770,7 @@ func (api *API) appEditDocument(c *gin.Context) {
err = c.SaveUploadedFile(rDocEdit.CoverFile, safePath) err = c.SaveUploadedFile(rDocEdit.CoverFile, safePath)
if err != nil { if err != nil {
log.Error("File Error: ", err) log.Error("File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to save file.") appErrorPage(c, http.StatusInternalServerError, "Unable to save file.")
return return
} }
@ -795,7 +794,7 @@ func (api *API) appEditDocument(c *gin.Context) {
Coverfile: coverFileName, Coverfile: coverFileName,
}); err != nil { }); err != nil {
log.Error("UpsertDocument DB Error: ", err) 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 return
} }
@ -807,18 +806,18 @@ func (api *API) appDeleteDocument(c *gin.Context) {
var rDocID requestDocumentID var rDocID requestDocumentID
if err := c.ShouldBindUri(&rDocID); err != nil { if err := c.ShouldBindUri(&rDocID); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
errorPage(c, http.StatusNotFound, "Invalid document.") appErrorPage(c, http.StatusNotFound, "Invalid document.")
return return
} }
changed, err := api.DB.Queries.DeleteDocument(api.DB.Ctx, rDocID.DocumentID) changed, err := api.DB.Queries.DeleteDocument(api.DB.Ctx, rDocID.DocumentID)
if err != nil { if err != nil {
log.Error("DeleteDocument DB Error") 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 return
} }
if changed == 0 { if changed == 0 {
log.Error("DeleteDocument DB Error") log.Error("DeleteDocument DB Error")
errorPage(c, http.StatusNotFound, "Invalid document.") appErrorPage(c, http.StatusNotFound, "Invalid document.")
return return
} }
@ -829,14 +828,14 @@ func (api *API) appIdentifyDocument(c *gin.Context) {
var rDocID requestDocumentID var rDocID requestDocumentID
if err := c.ShouldBindUri(&rDocID); err != nil { if err := c.ShouldBindUri(&rDocID); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
errorPage(c, http.StatusNotFound, "Invalid document.") appErrorPage(c, http.StatusNotFound, "Invalid document.")
return return
} }
var rDocIdentify requestDocumentIdentify var rDocIdentify requestDocumentIdentify
if err := c.ShouldBind(&rDocIdentify); err != nil { if err := c.ShouldBind(&rDocIdentify); err != nil {
log.Error("Invalid Form Bind") log.Error("Invalid Form Bind")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -854,7 +853,7 @@ func (api *API) appIdentifyDocument(c *gin.Context) {
// Validate Values // Validate Values
if rDocIdentify.ISBN == nil && rDocIdentify.Title == nil && rDocIdentify.Author == nil { if rDocIdentify.ISBN == nil && rDocIdentify.Title == nil && rDocIdentify.Author == nil {
log.Error("Invalid Form") log.Error("Invalid Form")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -897,7 +896,7 @@ func (api *API) appIdentifyDocument(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("GetDocumentWithStats DB Error: ", err) 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 return
} }
@ -911,7 +910,7 @@ func (api *API) appSaveNewDocument(c *gin.Context) {
var rDocAdd requestDocumentAdd var rDocAdd requestDocumentAdd
if err := c.ShouldBind(&rDocAdd); err != nil { if err := c.ShouldBind(&rDocAdd); err != nil {
log.Error("Invalid Form Bind") log.Error("Invalid Form Bind")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -1069,14 +1068,14 @@ func (api *API) appEditSettings(c *gin.Context) {
var rUserSettings requestSettingsEdit var rUserSettings requestSettingsEdit
if err := c.ShouldBind(&rUserSettings); err != nil { if err := c.ShouldBind(&rUserSettings); err != nil {
log.Error("Invalid Form Bind") log.Error("Invalid Form Bind")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
// Validate Something Exists // Validate Something Exists
if rUserSettings.Password == nil && rUserSettings.NewPassword == nil && rUserSettings.TimeOffset == nil { if rUserSettings.Password == nil && rUserSettings.NewPassword == nil && rUserSettings.TimeOffset == nil {
log.Error("Missing Form Values") log.Error("Missing Form Values")
errorPage(c, http.StatusBadRequest, "Invalid or missing form values.") appErrorPage(c, http.StatusBadRequest, "Invalid or missing form values.")
return return
} }
@ -1114,7 +1113,7 @@ func (api *API) appEditSettings(c *gin.Context) {
_, err := api.DB.Queries.UpdateUser(api.DB.Ctx, newUserSettings) _, err := api.DB.Queries.UpdateUser(api.DB.Ctx, newUserSettings)
if err != nil { if err != nil {
log.Error("UpdateUser DB Error: ", err) 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 return
} }
@ -1122,7 +1121,7 @@ func (api *API) appEditSettings(c *gin.Context) {
user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName) user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetUser DB Error: ", err) 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 return
} }
@ -1130,7 +1129,7 @@ func (api *API) appEditSettings(c *gin.Context) {
devices, err := api.DB.Queries.GetDevices(api.DB.Ctx, auth.UserName) devices, err := api.DB.Queries.GetDevices(api.DB.Ctx, auth.UserName)
if err != nil { if err != nil {
log.Error("GetDevices DB Error: ", err) 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 return
} }
@ -1143,7 +1142,7 @@ func (api *API) appEditSettings(c *gin.Context) {
} }
func (api *API) appDemoModeError(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 { func (api *API) getDocumentsWordCount(documents []database.GetDocumentsWithStatsRow) error {
@ -1221,7 +1220,7 @@ func bindQueryParams(c *gin.Context, defaultLimit int64) queryParams {
return qParams 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." var errorHuman string = "We're not even sure what happened."
switch errorCode { switch errorCode {
@ -1324,14 +1323,14 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
uploadedFile, err := rAdminAction.RestoreFile.Open() uploadedFile, err := rAdminAction.RestoreFile.Open()
if err != nil { if err != nil {
log.Error("File Error: ", err) log.Error("File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to open file.") appErrorPage(c, http.StatusInternalServerError, "Unable to open file.")
return return
} }
fileMime, err := mimetype.DetectReader(uploadedFile) fileMime, err := mimetype.DetectReader(uploadedFile)
if err != nil { if err != nil {
log.Error("MIME Error") log.Error("MIME Error")
errorPage(c, http.StatusInternalServerError, "Unable to detect filetype.") appErrorPage(c, http.StatusInternalServerError, "Unable to detect filetype.")
return return
} }
fileExtension := fileMime.Extension() fileExtension := fileMime.Extension()
@ -1339,7 +1338,7 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
// Validate Extension // Validate Extension
if !slices.Contains([]string{".zip"}, fileExtension) { if !slices.Contains([]string{".zip"}, fileExtension) {
log.Error("Invalid FileType: ", fileExtension) log.Error("Invalid FileType: ", fileExtension)
errorPage(c, http.StatusBadRequest, "Invalid filetype.") appErrorPage(c, http.StatusBadRequest, "Invalid filetype.")
return return
} }
@ -1347,7 +1346,7 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
tempFile, err := os.CreateTemp("", "restore") tempFile, err := os.CreateTemp("", "restore")
if err != nil { if err != nil {
log.Warn("Temp File Create Error: ", err) 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 return
} }
defer os.Remove(tempFile.Name()) 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()) err = c.SaveUploadedFile(rAdminAction.RestoreFile, tempFile.Name())
if err != nil { if err != nil {
log.Error("File Error: ", err) log.Error("File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to save file.") appErrorPage(c, http.StatusInternalServerError, "Unable to save file.")
return return
} }
@ -1365,23 +1364,23 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
fileInfo, err := tempFile.Stat() fileInfo, err := tempFile.Stat()
if err != nil { if err != nil {
log.Error("File Error: ", err) log.Error("File Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to read file.") appErrorPage(c, http.StatusInternalServerError, "Unable to read file.")
return return
} }
// Create ZIP Reader // Create ZIP Reader
r, err := zip.NewReader(tempFile, fileInfo.Size()) zipReader, err := zip.NewReader(tempFile, fileInfo.Size())
if err != nil { if err != nil {
log.Error("ZIP Error: ", err) log.Error("ZIP Error: ", err)
errorPage(c, http.StatusInternalServerError, "Unable to read zip.") appErrorPage(c, http.StatusInternalServerError, "Unable to read zip.")
return return
} }
// Validate ZIP Contents // Validate ZIP Contents
hasDBFile := false hasDBFile := false
hasUnknownFile := false hasUnknownFile := false
for _, f := range r.File { for _, file := range zipReader.File {
fileName := strings.TrimPrefix(f.Name, "/") fileName := strings.TrimPrefix(file.Name, "/")
if fileName == "antholume.db" { if fileName == "antholume.db" {
hasDBFile = true hasDBFile = true
break break
@ -1394,20 +1393,20 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
// Invalid ZIP // Invalid ZIP
if !hasDBFile { if !hasDBFile {
log.Error("Invalid ZIP File - Missing DB") 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 return
} else if hasUnknownFile { } else if hasUnknownFile {
log.Error("Invalid ZIP File - Invalid File(s)") 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 return
} }
// Create Backup File // 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) backupFile, err := os.Create(backupFilePath)
if err != nil { if err != nil {
log.Error("Unable to create backup file: ", err) 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 return
} }
defer backupFile.Close() defer backupFile.Close()
@ -1417,7 +1416,26 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
err = api.createBackup(w, []string{"covers", "documents"}) err = api.createBackup(w, []string{"covers", "documents"})
if err != nil { if err != nil {
log.Error("Unable to save backup file: ", err) 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 return
} }
@ -1425,6 +1443,56 @@ func (api *API) processRestoreFile(rAdminAction requestAdminAction, c *gin.Conte
// - Extract from temp directory // - 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 { func (api *API) createBackup(w io.Writer, directories []string) error {
ar := zip.NewWriter(w) ar := zip.NewWriter(w)
@ -1448,7 +1516,7 @@ func (api *API) createBackup(w io.Writer, directories []string) error {
folderName := filepath.Base(filepath.Dir(currentPath)) folderName := filepath.Base(filepath.Dir(currentPath))
// Create File in Export // Create File in Export
newF, err := ar.Create(path.Join(folderName, fileName)) newF, err := ar.Create(filepath.Join(folderName, fileName))
if err != nil { if err != nil {
return err return err
} }
@ -1464,7 +1532,7 @@ func (api *API) createBackup(w io.Writer, directories []string) error {
// Get DB Path // Get DB Path
fileName := fmt.Sprintf("%s.db", api.Config.DBName) 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 // Copy Database File
dbFile, err := os.Open(dbLocation) dbFile, err := os.Open(dbLocation)
@ -1481,7 +1549,7 @@ func (api *API) createBackup(w io.Writer, directories []string) error {
// Backup Covers & Documents // Backup Covers & Documents
for _, dir := range directories { 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 { if err != nil {
return err return err
} }

View File

@ -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() c.Abort()
return return
} }
@ -175,7 +175,7 @@ func (api *API) appAuthFormLogin(c *gin.Context) {
func (api *API) appAuthFormRegister(c *gin.Context) { func (api *API) appAuthFormRegister(c *gin.Context) {
if !api.Config.RegistrationEnabled { if !api.Config.RegistrationEnabled {
errorPage(c, http.StatusUnauthorized, "Nice try. Registration is disabled.") appErrorPage(c, http.StatusUnauthorized, "Nice try. Registration is disabled.")
return return
} }
@ -236,7 +236,7 @@ func (api *API) appAuthFormRegister(c *gin.Context) {
} }
session := sessions.Default(c) session := sessions.Default(c)
if err := setSession(session, auth); err != nil { if err := setSession(session, auth); err != nil {
errorPage(c, http.StatusUnauthorized, "Unauthorized.") appErrorPage(c, http.StatusUnauthorized, "Unauthorized.")
return return
} }

View File

@ -11,11 +11,12 @@ import (
"reichard.io/antholume/metadata" "reichard.io/antholume/metadata"
) )
func (api *API) downloadDocument(c *gin.Context) { func (api *API) createDownloadDocumentHandler(errorFunc func(*gin.Context, int, string)) func(*gin.Context) {
return func(c *gin.Context) {
var rDoc requestDocumentID var rDoc requestDocumentID
if err := c.ShouldBindUri(&rDoc); err != nil { if err := c.ShouldBindUri(&rDoc); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) errorFunc(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -23,13 +24,13 @@ func (api *API) downloadDocument(c *gin.Context) {
document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID)
if err != nil { if err != nil {
log.Error("GetDocument DB Error:", err) log.Error("GetDocument DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Document"}) errorFunc(c, http.StatusBadRequest, "Unknown Document")
return return
} }
if document.Filepath == nil { if document.Filepath == nil {
log.Error("Document Doesn't Have File:", rDoc.DocumentID) log.Error("Document Doesn't Have File:", rDoc.DocumentID)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Document Doesn't Exist"}) errorFunc(c, http.StatusBadRequest, "Document Doesn't Exist")
return return
} }
@ -40,20 +41,22 @@ func (api *API) downloadDocument(c *gin.Context) {
_, err = os.Stat(filePath) _, err = os.Stat(filePath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Error("File Doesn't Exist:", rDoc.DocumentID) log.Error("File Doesn't Exist:", rDoc.DocumentID)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Document Doesn't Exists"}) errorFunc(c, http.StatusBadRequest, "Document Doesn't Exist")
return return
} }
// Force Download (Security) // Force Download
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(*document.Filepath))) c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(*document.Filepath)))
c.File(filePath) c.File(filePath)
} }
}
func (api *API) getDocumentCover(c *gin.Context) { func (api *API) createGetCoverHandler(errorFunc func(*gin.Context, int, string)) func(*gin.Context) {
return func(c *gin.Context) {
var rDoc requestDocumentID var rDoc requestDocumentID
if err := c.ShouldBindUri(&rDoc); err != nil { if err := c.ShouldBindUri(&rDoc); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
errorPage(c, http.StatusNotFound, "Invalid cover.") errorFunc(c, http.StatusNotFound, "Invalid cover.")
return return
} }
@ -61,7 +64,7 @@ func (api *API) getDocumentCover(c *gin.Context) {
document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID)
if err != nil { if err != nil {
log.Error("GetDocument DB Error:", err) log.Error("GetDocument DB Error:", err)
errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetDocument DB Error: %v", err)) errorFunc(c, http.StatusInternalServerError, fmt.Sprintf("GetDocument DB Error: %v", err))
return return
} }
@ -138,3 +141,4 @@ func (api *API) getDocumentCover(c *gin.Context) {
coverFilePath := filepath.Join(coverDir, coverFile) coverFilePath := filepath.Join(coverDir, coverFile)
c.File(coverFilePath) c.File(coverFilePath)
} }
}

View File

@ -90,20 +90,20 @@ func (api *API) koCreateUser(c *gin.Context) {
var rUser requestUser var rUser requestUser
if err := c.ShouldBindJSON(&rUser); err != nil { if err := c.ShouldBindJSON(&rUser); err != nil {
log.Error("Invalid JSON Bind") log.Error("Invalid JSON Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid User Data"}) apiErrorPage(c, http.StatusBadRequest, "Invalid User Data")
return return
} }
if rUser.Username == "" || rUser.Password == "" { if rUser.Username == "" || rUser.Password == "" {
log.Error("Invalid User - Empty Username or 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 return
} }
hashedPassword, err := argon2.CreateHash(rUser.Password, argon2.DefaultParams) hashedPassword, err := argon2.CreateHash(rUser.Password, argon2.DefaultParams)
if err != nil { if err != nil {
log.Error("Argon2 Hash Failure:", err) log.Error("Argon2 Hash Failure:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -113,13 +113,14 @@ func (api *API) koCreateUser(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("CreateUser DB Error:", err) log.Error("CreateUser DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid User Data"}) apiErrorPage(c, http.StatusBadRequest, "Invalid User Data")
return return
} }
// User Exists // User Exists
if rows == 0 { 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 return
} }
@ -137,7 +138,7 @@ func (api *API) koSetProgress(c *gin.Context) {
var rPosition requestPosition var rPosition requestPosition
if err := c.ShouldBindJSON(&rPosition); err != nil { if err := c.ShouldBindJSON(&rPosition); err != nil {
log.Error("Invalid JSON Bind") log.Error("Invalid JSON Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Progress Data"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Progress Data")
return return
} }
@ -168,7 +169,7 @@ func (api *API) koSetProgress(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("UpdateProgress DB Error:", err) log.Error("UpdateProgress DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -187,7 +188,7 @@ func (api *API) koGetProgress(c *gin.Context) {
var rDocID requestDocumentID var rDocID requestDocumentID
if err := c.ShouldBindUri(&rDocID); err != nil { if err := c.ShouldBindUri(&rDocID); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -202,7 +203,7 @@ func (api *API) koGetProgress(c *gin.Context) {
return return
} else if err != nil { } else if err != nil {
log.Error("GetDocumentProgress DB Error:", err) log.Error("GetDocumentProgress DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Document")
return return
} }
@ -224,7 +225,7 @@ func (api *API) koAddActivities(c *gin.Context) {
var rActivity requestActivity var rActivity requestActivity
if err := c.ShouldBindJSON(&rActivity); err != nil { if err := c.ShouldBindJSON(&rActivity); err != nil {
log.Error("Invalid JSON Bind") log.Error("Invalid JSON Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Activity"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Activity")
return return
} }
@ -232,7 +233,7 @@ func (api *API) koAddActivities(c *gin.Context) {
tx, err := api.DB.DB.Begin() tx, err := api.DB.DB.Begin()
if err != nil { if err != nil {
log.Error("Transaction Begin DB Error:", err) log.Error("Transaction Begin DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -253,7 +254,7 @@ func (api *API) koAddActivities(c *gin.Context) {
ID: doc, ID: doc,
}); err != nil { }); err != nil {
log.Error("UpsertDocument DB Error:", err) log.Error("UpsertDocument DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Document")
return return
} }
} }
@ -266,7 +267,7 @@ func (api *API) koAddActivities(c *gin.Context) {
LastSynced: time.Now().UTC().Format(time.RFC3339), LastSynced: time.Now().UTC().Format(time.RFC3339),
}); err != nil { }); err != nil {
log.Error("UpsertDevice DB Error:", err) log.Error("UpsertDevice DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Device")
return return
} }
@ -282,7 +283,7 @@ func (api *API) koAddActivities(c *gin.Context) {
EndPercentage: float64(item.Page+1) / float64(item.Pages), EndPercentage: float64(item.Page+1) / float64(item.Pages),
}); err != nil { }); err != nil {
log.Error("AddActivity DB Error:", err) log.Error("AddActivity DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Activity"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Activity")
return return
} }
} }
@ -290,7 +291,7 @@ func (api *API) koAddActivities(c *gin.Context) {
// Commit Transaction // Commit Transaction
if err := tx.Commit(); err != nil { if err := tx.Commit(); err != nil {
log.Error("Transaction Commit DB Error:", err) log.Error("Transaction Commit DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -308,7 +309,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) {
var rCheckActivity requestCheckActivitySync var rCheckActivity requestCheckActivitySync
if err := c.ShouldBindJSON(&rCheckActivity); err != nil { if err := c.ShouldBindJSON(&rCheckActivity); err != nil {
log.Error("Invalid JSON Bind") log.Error("Invalid JSON Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -320,7 +321,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) {
LastSynced: time.Now().UTC().Format(time.RFC3339), LastSynced: time.Now().UTC().Format(time.RFC3339),
}); err != nil { }); err != nil {
log.Error("UpsertDevice DB Error", err) log.Error("UpsertDevice DB Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Device")
return return
} }
@ -333,7 +334,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) {
lastActivity = time.UnixMilli(0).Format(time.RFC3339) lastActivity = time.UnixMilli(0).Format(time.RFC3339)
} else if err != nil { } else if err != nil {
log.Error("GetLastActivity DB Error:", err) log.Error("GetLastActivity DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -341,7 +342,7 @@ func (api *API) koCheckActivitySync(c *gin.Context) {
parsedTime, err := time.Parse(time.RFC3339, lastActivity) parsedTime, err := time.Parse(time.RFC3339, lastActivity)
if err != nil { if err != nil {
log.Error("Time Parse Error:", err) log.Error("Time Parse Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -354,7 +355,7 @@ func (api *API) koAddDocuments(c *gin.Context) {
var rNewDocs requestDocument var rNewDocs requestDocument
if err := c.ShouldBindJSON(&rNewDocs); err != nil { if err := c.ShouldBindJSON(&rNewDocs); err != nil {
log.Error("Invalid JSON Bind") log.Error("Invalid JSON Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document(s)"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Document(s)")
return return
} }
@ -362,7 +363,7 @@ func (api *API) koAddDocuments(c *gin.Context) {
tx, err := api.DB.DB.Begin() tx, err := api.DB.DB.Begin()
if err != nil { if err != nil {
log.Error("Transaction Begin DB Error:", err) log.Error("Transaction Begin DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -383,7 +384,7 @@ func (api *API) koAddDocuments(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("UpsertDocument DB Error:", err) log.Error("UpsertDocument DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Document"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Document")
return return
} }
} }
@ -391,7 +392,7 @@ func (api *API) koAddDocuments(c *gin.Context) {
// Commit Transaction // Commit Transaction
if err := tx.Commit(); err != nil { if err := tx.Commit(); err != nil {
log.Error("Transaction Commit DB Error:", err) log.Error("Transaction Commit DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Error")
return return
} }
@ -409,7 +410,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) {
var rCheckDocs requestCheckDocumentSync var rCheckDocs requestCheckDocumentSync
if err := c.ShouldBindJSON(&rCheckDocs); err != nil { if err := c.ShouldBindJSON(&rCheckDocs); err != nil {
log.Error("Invalid JSON Bind") log.Error("Invalid JSON Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -422,7 +423,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) {
}) })
if err != nil { if err != nil {
log.Error("UpsertDevice DB Error", err) log.Error("UpsertDevice DB Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Device")
return return
} }
@ -433,7 +434,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) {
missingDocs, err = api.DB.Queries.GetMissingDocuments(api.DB.Ctx, rCheckDocs.Have) missingDocs, err = api.DB.Queries.GetMissingDocuments(api.DB.Ctx, rCheckDocs.Have)
if err != nil { if err != nil {
log.Error("GetMissingDocuments DB Error", err) log.Error("GetMissingDocuments DB Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -441,7 +442,7 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) {
deletedDocIDs, err = api.DB.Queries.GetDeletedDocuments(api.DB.Ctx, rCheckDocs.Have) deletedDocIDs, err = api.DB.Queries.GetDeletedDocuments(api.DB.Ctx, rCheckDocs.Have)
if err != nil { if err != nil {
log.Error("GetDeletedDocuments DB Error", err) log.Error("GetDeletedDocuments DB Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -449,14 +450,14 @@ func (api *API) koCheckDocumentsSync(c *gin.Context) {
jsonHaves, err := json.Marshal(rCheckDocs.Have) jsonHaves, err := json.Marshal(rCheckDocs.Have)
if err != nil { if err != nil {
log.Error("JSON Marshal Error", err) log.Error("JSON Marshal Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
wantedDocs, err := api.DB.Queries.GetWantedDocuments(api.DB.Ctx, string(jsonHaves)) wantedDocs, err := api.DB.Queries.GetWantedDocuments(api.DB.Ctx, string(jsonHaves))
if err != nil { if err != nil {
log.Error("GetWantedDocuments DB Error", err) log.Error("GetWantedDocuments DB Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
@ -500,14 +501,14 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
var rDoc requestDocumentID var rDoc requestDocumentID
if err := c.ShouldBindUri(&rDoc); err != nil { if err := c.ShouldBindUri(&rDoc); err != nil {
log.Error("Invalid URI Bind") log.Error("Invalid URI Bind")
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Request")
return return
} }
fileData, err := c.FormFile("file") fileData, err := c.FormFile("file")
if err != nil { if err != nil {
log.Error("File Error:", err) log.Error("File Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) apiErrorPage(c, http.StatusBadRequest, "File Error")
return return
} }
@ -518,7 +519,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
if !slices.Contains([]string{".epub", ".html"}, fileExtension) { if !slices.Contains([]string{".epub", ".html"}, fileExtension) {
log.Error("Invalid FileType:", fileExtension) log.Error("Invalid FileType:", fileExtension)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Filetype"}) apiErrorPage(c, http.StatusBadRequest, "Invalid Filetype")
return return
} }
@ -526,7 +527,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID) document, err := api.DB.Queries.GetDocument(api.DB.Ctx, rDoc.DocumentID)
if err != nil { if err != nil {
log.Error("GetDocument DB Error:", err) log.Error("GetDocument DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Document"}) apiErrorPage(c, http.StatusBadRequest, "Unknown Document")
return return
} }
@ -559,7 +560,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
err = c.SaveUploadedFile(fileData, safePath) err = c.SaveUploadedFile(fileData, safePath)
if err != nil { if err != nil {
log.Error("Save Failure:", err) log.Error("Save Failure:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) apiErrorPage(c, http.StatusBadRequest, "File Error")
return return
} }
} }
@ -568,7 +569,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
fileHash, err := getFileMD5(safePath) fileHash, err := getFileMD5(safePath)
if err != nil { if err != nil {
log.Error("Hash Failure:", err) log.Error("Hash Failure:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) apiErrorPage(c, http.StatusBadRequest, "File Error")
return return
} }
@ -576,7 +577,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
wordCount, err := metadata.GetWordCount(safePath) wordCount, err := metadata.GetWordCount(safePath)
if err != nil { if err != nil {
log.Error("Word Count Failure:", err) log.Error("Word Count Failure:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "File Error"}) apiErrorPage(c, http.StatusBadRequest, "File Error")
return return
} }
@ -588,7 +589,7 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
Words: &wordCount, Words: &wordCount,
}); err != nil { }); err != nil {
log.Error("UpsertDocument DB Error:", err) log.Error("UpsertDocument DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Document Error"}) apiErrorPage(c, http.StatusBadRequest, "Document Error")
return return
} }
@ -598,7 +599,11 @@ func (api *API) koUploadExistingDocument(c *gin.Context) {
} }
func (api *API) koDemoModeJSONError(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 { func (api *API) sanitizeInput(val any) *string {

View File

@ -6,7 +6,7 @@ import (
"embed" "embed"
_ "embed" _ "embed"
"fmt" "fmt"
"path" "path/filepath"
"time" "time"
"github.com/pressly/goose/v3" "github.com/pressly/goose/v3"
@ -37,7 +37,7 @@ func NewMgr(c *config.Config) *DBManager {
if c.DBType == "sqlite" || c.DBType == "memory" { if c.DBType == "sqlite" || c.DBType == "memory" {
var dbLocation string = ":memory:" var dbLocation string = ":memory:"
if c.DBType == "sqlite" { 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 var err error