github.com/soulteary/pocket-bookcase@v0.0.0-20240428065142-0b5a9a0fc98a/internal/webserver/handler-api-ext.go (about)

     1  package webserver
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"net/http"
    10  	"os"
    11  	fp "path/filepath"
    12  	"strconv"
    13  
    14  	"github.com/julienschmidt/httprouter"
    15  	"github.com/soulteary/pocket-bookcase/internal/core"
    16  	"github.com/soulteary/pocket-bookcase/internal/model"
    17  )
    18  
    19  // ApiInsertViaExtension is handler for POST /api/bookmarks/ext
    20  func (h *Handler) ApiInsertViaExtension(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    21  	ctx := r.Context()
    22  
    23  	// Make sure session still valid
    24  	err := h.validateSession(r)
    25  	checkError(err)
    26  
    27  	// Decode request
    28  	request := model.BookmarkDTO{}
    29  	err = json.NewDecoder(r.Body).Decode(&request)
    30  	checkError(err)
    31  
    32  	// Clean up bookmark URL
    33  	request.URL, err = core.RemoveUTMParams(request.URL)
    34  	if err != nil {
    35  		panic(fmt.Errorf("failed to clean URL: %v", err))
    36  	}
    37  
    38  	// Check if bookmark already exists.
    39  	book, exist, err := h.DB.GetBookmark(ctx, 0, request.URL)
    40  	if err != nil {
    41  		panic(fmt.Errorf("failed to get bookmark, URL: %v", err))
    42  	}
    43  
    44  	// If it already exists, we need to set ID and tags.
    45  	if exist {
    46  		book.HTML = request.HTML
    47  
    48  		mapOldTags := map[string]model.Tag{}
    49  		for _, oldTag := range book.Tags {
    50  			mapOldTags[oldTag.Name] = oldTag
    51  		}
    52  
    53  		for _, newTag := range request.Tags {
    54  			if _, tagExist := mapOldTags[newTag.Name]; !tagExist {
    55  				book.Tags = append(book.Tags, newTag)
    56  			}
    57  		}
    58  	} else if request.Title == "" {
    59  		request.Title = request.URL
    60  	}
    61  
    62  	// Since we are using extension, the extension might send the HTML content
    63  	// so no need to download it again here. However, if it's empty, it might be not HTML file
    64  	// so we download it here.
    65  	var contentType string
    66  	var contentBuffer io.Reader
    67  
    68  	if request.HTML == "" {
    69  		contentBuffer, contentType, _ = core.DownloadBookmark(request.URL)
    70  	} else {
    71  		contentType = "text/html; charset=UTF-8"
    72  		contentBuffer = bytes.NewBufferString(request.HTML)
    73  	}
    74  
    75  	// Save the bookmark with whatever we already have downloaded
    76  	// since we need the ID in order to download the archive
    77  	// Only when old bookmark is not exists.
    78  	if !exist {
    79  		books, err := h.DB.SaveBookmarks(ctx, true, request)
    80  		if err != nil {
    81  			log.Printf("error saving bookmark before downloading content: %s", err)
    82  			return
    83  		}
    84  		book = books[0]
    85  	}
    86  
    87  	// At this point the web page already downloaded.
    88  	// Time to process it.
    89  	if contentBuffer != nil {
    90  		book.CreateArchive = true
    91  		request := core.ProcessRequest{
    92  			DataDir:     h.DataDir,
    93  			Bookmark:    book,
    94  			Content:     contentBuffer,
    95  			ContentType: contentType,
    96  		}
    97  
    98  		var isFatalErr bool
    99  		book, isFatalErr, err = core.ProcessBookmark(h.dependencies, request)
   100  
   101  		if tmp, ok := contentBuffer.(io.ReadCloser); ok {
   102  			tmp.Close()
   103  		}
   104  
   105  		// If we can't process or update the saved bookmark, just log it and continue on with the
   106  		// request.
   107  		if err != nil && isFatalErr {
   108  			log.Printf("failed to process bookmark: %v", err)
   109  		} else if _, err := h.DB.SaveBookmarks(ctx, false, book); err != nil {
   110  			log.Printf("error saving bookmark after downloading content: %s", err)
   111  		}
   112  	}
   113  
   114  	// Return the new bookmark
   115  	w.Header().Set("Content-Type", "application/json")
   116  	err = json.NewEncoder(w).Encode(&book)
   117  	checkError(err)
   118  }
   119  
   120  // ApiDeleteViaExtension is handler for DELETE /api/bookmark/ext
   121  func (h *Handler) ApiDeleteViaExtension(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
   122  	ctx := r.Context()
   123  
   124  	// Make sure session still valid
   125  	err := h.validateSession(r)
   126  	checkError(err)
   127  
   128  	// Decode request
   129  	request := model.BookmarkDTO{}
   130  	err = json.NewDecoder(r.Body).Decode(&request)
   131  	checkError(err)
   132  
   133  	// Check if bookmark already exists.
   134  	book, exist, err := h.DB.GetBookmark(ctx, 0, request.URL)
   135  	checkError(err)
   136  
   137  	if exist {
   138  		// Delete bookmarks
   139  		err = h.DB.DeleteBookmarks(ctx, book.ID)
   140  		checkError(err)
   141  
   142  		// Delete thumbnail image and archives from local disk
   143  		strID := strconv.Itoa(book.ID)
   144  		imgPath := fp.Join(h.DataDir, "thumb", strID)
   145  		archivePath := fp.Join(h.DataDir, "archive", strID)
   146  
   147  		os.Remove(imgPath)
   148  		os.Remove(archivePath)
   149  	}
   150  
   151  	fmt.Fprint(w, 1)
   152  }