[add] search
This commit is contained in:
@@ -82,6 +82,7 @@ func (api *API) registerWebAppRoutes() {
|
||||
|
||||
render.AddFromFilesFuncs("login", helperFuncs, "templates/login.html")
|
||||
render.AddFromFilesFuncs("home", helperFuncs, "templates/base.html", "templates/home.html")
|
||||
render.AddFromFilesFuncs("search", helperFuncs, "templates/base.html", "templates/search.html")
|
||||
render.AddFromFilesFuncs("settings", helperFuncs, "templates/base.html", "templates/settings.html")
|
||||
render.AddFromFilesFuncs("activity", helperFuncs, "templates/base.html", "templates/activity.html")
|
||||
render.AddFromFilesFuncs("documents", helperFuncs, "templates/base.html", "templates/documents.html")
|
||||
@@ -107,6 +108,12 @@ func (api *API) registerWebAppRoutes() {
|
||||
api.Router.POST("/documents/:document/edit", api.authWebAppMiddleware, api.editDocument)
|
||||
api.Router.POST("/documents/:document/identify", api.authWebAppMiddleware, api.identifyDocument)
|
||||
api.Router.POST("/documents/:document/delete", api.authWebAppMiddleware, api.deleteDocument)
|
||||
|
||||
// Behind Configuration Flag
|
||||
if api.Config.SearchEnabled {
|
||||
api.Router.GET("/search", api.authWebAppMiddleware, api.createAppResourcesRoute("search"))
|
||||
api.Router.POST("/search", api.authWebAppMiddleware, api.saveNewDocument)
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) registerKOAPIRoutes(apiGroup *gin.RouterGroup) {
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
"reichard.io/bbank/database"
|
||||
"reichard.io/bbank/metadata"
|
||||
"reichard.io/bbank/search"
|
||||
"reichard.io/bbank/utils"
|
||||
)
|
||||
|
||||
type queryParams struct {
|
||||
@@ -25,6 +27,11 @@ type queryParams struct {
|
||||
Document *string `form:"document"`
|
||||
}
|
||||
|
||||
type searchParams struct {
|
||||
Query *string `form:"query"`
|
||||
BookType *string `form:"book_type"`
|
||||
}
|
||||
|
||||
type requestDocumentEdit struct {
|
||||
Title *string `form:"title"`
|
||||
Author *string `form:"author"`
|
||||
@@ -48,6 +55,13 @@ type requestSettingsEdit struct {
|
||||
TimeOffset *string `form:"time_offset"`
|
||||
}
|
||||
|
||||
type requestDocumentAdd struct {
|
||||
ID *string `form:"id"`
|
||||
Title *string `form:"title"`
|
||||
Author *string `form:"author"`
|
||||
BookType *string `form:"book_type"`
|
||||
}
|
||||
|
||||
func (api *API) webManifest(c *gin.Context) {
|
||||
c.Header("Content-Type", "application/manifest+json")
|
||||
c.File("./assets/manifest.json")
|
||||
@@ -60,6 +74,7 @@ func (api *API) createAppResourcesRoute(routeName string, args ...map[string]any
|
||||
templateVarsBase = args[0]
|
||||
}
|
||||
templateVarsBase["RouteName"] = routeName
|
||||
templateVarsBase["SearchEnabled"] = api.Config.SearchEnabled
|
||||
|
||||
return func(c *gin.Context) {
|
||||
var userID string
|
||||
@@ -174,6 +189,26 @@ func (api *API) createAppResourcesRoute(routeName string, args ...map[string]any
|
||||
},
|
||||
"Devices": devices,
|
||||
}
|
||||
} else if routeName == "search" {
|
||||
var sParams searchParams
|
||||
c.BindQuery(&sParams)
|
||||
|
||||
// Only Handle Query
|
||||
if sParams.BookType != nil && !slices.Contains([]string{"NON_FICTION", "FICTION"}, *sParams.BookType) {
|
||||
templateVars["SearchErrorMessage"] = "Invalid Book Type"
|
||||
} else if sParams.Query != nil && *sParams.Query == "" {
|
||||
templateVars["SearchErrorMessage"] = "Invalid Query"
|
||||
} else if sParams.BookType != nil && sParams.Query != nil {
|
||||
var bType search.BookType = search.BOOK_FICTION
|
||||
if *sParams.BookType == "NON_FICTION" {
|
||||
bType = search.BOOK_NON_FICTION
|
||||
}
|
||||
|
||||
// Search
|
||||
searchResults := search.SearchBook(*sParams.Query, bType)
|
||||
templateVars["Data"] = searchResults
|
||||
templateVars["BookType"] = *sParams.BookType
|
||||
}
|
||||
} else if routeName == "login" {
|
||||
templateVars["RegistrationEnabled"] = api.Config.RegistrationEnabled
|
||||
}
|
||||
@@ -430,7 +465,8 @@ func (api *API) identifyDocument(c *gin.Context) {
|
||||
|
||||
// Template Variables
|
||||
templateVars := gin.H{
|
||||
"RelBase": "../../",
|
||||
"RelBase": "../../",
|
||||
"SearchEnabled": api.Config.SearchEnabled,
|
||||
}
|
||||
|
||||
// Get Metadata
|
||||
@@ -479,6 +515,103 @@ func (api *API) identifyDocument(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "document", templateVars)
|
||||
}
|
||||
|
||||
func (api *API) saveNewDocument(c *gin.Context) {
|
||||
var rDocAdd requestDocumentAdd
|
||||
if err := c.ShouldBind(&rDocAdd); err != nil {
|
||||
log.Error("[saveNewDocument] Invalid Form Bind")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate Form Exists
|
||||
if rDocAdd.ID == nil ||
|
||||
rDocAdd.BookType == nil ||
|
||||
rDocAdd.Title == nil ||
|
||||
rDocAdd.Author == nil {
|
||||
log.Error("[saveNewDocument] Missing Form Values")
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Request"})
|
||||
return
|
||||
}
|
||||
|
||||
var bType search.BookType = search.BOOK_FICTION
|
||||
if *rDocAdd.BookType == "NON_FICTION" {
|
||||
bType = search.BOOK_NON_FICTION
|
||||
}
|
||||
|
||||
// Save Book
|
||||
tempFilePath, err := search.SaveBook(*rDocAdd.ID, bType)
|
||||
if err != nil {
|
||||
log.Warn("[saveNewDocument] Temp File Error: ", err)
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate Partial MD5 ID
|
||||
partialMD5, err := utils.CalculatePartialMD5(tempFilePath)
|
||||
if err != nil {
|
||||
log.Warn("[saveNewDocument] Partial MD5 Error: ", err)
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive Extension on MIME
|
||||
fileMime, err := mimetype.DetectFile(tempFilePath)
|
||||
fileExtension := fileMime.Extension()
|
||||
|
||||
// Derive Filename
|
||||
var fileName string
|
||||
if *rDocAdd.Author != "" {
|
||||
fileName = fileName + *rDocAdd.Author
|
||||
} else {
|
||||
fileName = fileName + "Unknown"
|
||||
}
|
||||
|
||||
if *rDocAdd.Title != "" {
|
||||
fileName = fileName + " - " + *rDocAdd.Title
|
||||
} else {
|
||||
fileName = fileName + " - Unknown"
|
||||
}
|
||||
|
||||
// Remove Slashes
|
||||
fileName = strings.ReplaceAll(fileName, "/", "")
|
||||
|
||||
// Derive & Sanitize File Name
|
||||
fileName = "." + filepath.Clean(fmt.Sprintf("/%s [%s]%s", fileName, partialMD5, fileExtension))
|
||||
|
||||
// Generate Storage Path
|
||||
safePath := filepath.Join(api.Config.DataPath, "documents", fileName)
|
||||
|
||||
// Move File
|
||||
if err := os.Rename(tempFilePath, safePath); err != nil {
|
||||
log.Warn("[saveNewDocument] Move Temp File Error: ", err)
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Get MD5 Hash
|
||||
fileHash, err := getFileMD5(safePath)
|
||||
if err != nil {
|
||||
log.Error("[saveNewDocument] Hash Failure:", err)
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Upsert Document
|
||||
if _, err = api.DB.Queries.UpsertDocument(api.DB.Ctx, database.UpsertDocumentParams{
|
||||
ID: partialMD5,
|
||||
Title: rDocAdd.Title,
|
||||
Author: rDocAdd.Author,
|
||||
Md5: fileHash,
|
||||
Filepath: &fileName,
|
||||
}); err != nil {
|
||||
log.Error("[saveNewDocument] UpsertDocument DB Error:", err)
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusFound, fmt.Sprintf("./documents/%s", partialMD5))
|
||||
}
|
||||
|
||||
func (api *API) editSettings(c *gin.Context) {
|
||||
rUser, _ := c.Get("AuthorizedUser")
|
||||
|
||||
@@ -555,7 +688,8 @@ func (api *API) editSettings(c *gin.Context) {
|
||||
"Settings": gin.H{
|
||||
"TimeOffset": *user.TimeOffset,
|
||||
},
|
||||
"Devices": devices,
|
||||
"Devices": devices,
|
||||
"SearchEnabled": api.Config.SearchEnabled,
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "settings", templateVars)
|
||||
|
||||
Reference in New Issue
Block a user