package webserver import ( "bytes" "encoding/json" "fmt" "io" "log" "net/http" "os" fp "path/filepath" "strconv" "github.com/go-shiori/shiori/internal/core" "github.com/go-shiori/shiori/internal/model" "github.com/julienschmidt/httprouter" ) // ApiInsertViaExtension is handler for POST /api/bookmarks/ext func (h *Handler) ApiInsertViaExtension(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { ctx := r.Context() // Make sure session still valid err := h.validateSession(r) checkError(err) // Decode request request := model.BookmarkDTO{} err = json.NewDecoder(r.Body).Decode(&request) checkError(err) // Clean up bookmark URL request.URL, err = core.RemoveUTMParams(request.URL) if err != nil { panic(fmt.Errorf("failed to clean URL: %v", err)) } // Check if bookmark already exists. book, exist, err := h.DB.GetBookmark(ctx, 0, request.URL) if err != nil { panic(fmt.Errorf("failed to get bookmark, URL: %v", err)) } // If it already exists, we need to set ID and tags. if exist { book.HTML = request.HTML mapOldTags := map[string]model.Tag{} for _, oldTag := range book.Tags { mapOldTags[oldTag.Name] = oldTag } for _, newTag := range request.Tags { if _, tagExist := mapOldTags[newTag.Name]; !tagExist { book.Tags = append(book.Tags, newTag) } } } else if request.Title == "" { request.Title = request.URL } // Since we are using extension, the extension might send the HTML content // so no need to download it again here. However, if it's empty, it might be not HTML file // so we download it here. var contentType string var contentBuffer io.Reader if request.HTML == "" { contentBuffer, contentType, _ = core.DownloadBookmark(request.URL) } else { contentType = "text/html; charset=UTF-8" contentBuffer = bytes.NewBufferString(request.HTML) } // Save the bookmark with whatever we already have downloaded // since we need the ID in order to download the archive // Only when old bookmark is not exists. if !exist { books, err := h.DB.SaveBookmarks(ctx, true, request) if err != nil { log.Printf("error saving bookmark before downloading content: %s", err) return } book = books[0] } // At this point the web page already downloaded. // Time to process it. if contentBuffer != nil { book.CreateArchive = true request := core.ProcessRequest{ DataDir: h.DataDir, Bookmark: book, Content: contentBuffer, ContentType: contentType, } var isFatalErr bool book, isFatalErr, err = core.ProcessBookmark(h.dependencies, request) if tmp, ok := contentBuffer.(io.ReadCloser); ok { tmp.Close() } // If we can't process or update the saved bookmark, just log it and continue on with the // request. if err != nil && isFatalErr { log.Printf("failed to process bookmark: %v", err) } else if _, err := h.DB.SaveBookmarks(ctx, false, book); err != nil { log.Printf("error saving bookmark after downloading content: %s", err) } } // Return the new bookmark w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(&book) checkError(err) } // ApiDeleteViaExtension is handler for DELETE /api/bookmark/ext func (h *Handler) ApiDeleteViaExtension(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { ctx := r.Context() // Make sure session still valid err := h.validateSession(r) checkError(err) // Decode request request := model.BookmarkDTO{} err = json.NewDecoder(r.Body).Decode(&request) checkError(err) // Check if bookmark already exists. book, exist, err := h.DB.GetBookmark(ctx, 0, request.URL) checkError(err) if exist { // Delete bookmarks err = h.DB.DeleteBookmarks(ctx, book.ID) checkError(err) // Delete thumbnail image and archives from local disk strID := strconv.Itoa(book.ID) imgPath := fp.Join(h.DataDir, "thumb", strID) archivePath := fp.Join(h.DataDir, "archive", strID) os.Remove(imgPath) os.Remove(archivePath) } fmt.Fprint(w, 1) }