aboutsummaryrefslogtreecommitdiff
path: root/internal/integration/readeck/readeck.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/integration/readeck/readeck.go')
-rw-r--r--internal/integration/readeck/readeck.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/internal/integration/readeck/readeck.go b/internal/integration/readeck/readeck.go
new file mode 100644
index 00000000..0c4f4dc1
--- /dev/null
+++ b/internal/integration/readeck/readeck.go
@@ -0,0 +1,149 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package readeck // import "miniflux.app/v2/internal/integration/readeck"
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "mime/multipart"
+ "net/http"
+ "strings"
+ "time"
+
+ "miniflux.app/v2/internal/urllib"
+ "miniflux.app/v2/internal/version"
+)
+
+const defaultClientTimeout = 10 * time.Second
+
+type Client struct {
+ baseURL string
+ apiKey string
+ labels string
+ onlyURL bool
+}
+
+func NewClient(baseURL, apiKey, labels string, onlyURL bool) *Client {
+ return &Client{baseURL: baseURL, apiKey: apiKey, labels: labels, onlyURL: onlyURL}
+}
+
+func (c *Client) CreateBookmark(entryURL, entryTitle string, entryContent string) error {
+ if c.baseURL == "" || c.apiKey == "" {
+ return fmt.Errorf("readeck: missing base URL or API key")
+ }
+
+ apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/bookmarks/")
+ if err != nil {
+ return fmt.Errorf(`readeck: invalid API endpoint: %v`, err)
+ }
+
+ labelsSplitFn := func(c rune) bool {
+ return c == ',' || c == ' '
+ }
+ labelsSplit := strings.FieldsFunc(c.labels, labelsSplitFn)
+
+ var request *http.Request
+ if c.onlyURL {
+ requestBodyJson, err := json.Marshal(&readeckBookmark{
+ Url: entryURL,
+ Title: entryTitle,
+ Labels: labelsSplit,
+ })
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body: %v", err)
+ }
+ request, err = http.NewRequest(http.MethodPost, apiEndpoint, bytes.NewReader(requestBodyJson))
+ if err != nil {
+ return fmt.Errorf("readeck: unable to create request: %v", err)
+ }
+ request.Header.Set("Content-Type", "application/json")
+ } else {
+ requestBody := new(bytes.Buffer)
+ multipartWriter := multipart.NewWriter(requestBody)
+
+ urlPart, err := multipartWriter.CreateFormField("url")
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body (entry url): %v", err)
+ }
+ urlPart.Write([]byte(entryURL))
+
+ titlePart, err := multipartWriter.CreateFormField("title")
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body (entry title): %v", err)
+ }
+ titlePart.Write([]byte(entryTitle))
+
+ featurePart, err := multipartWriter.CreateFormField("feature_find_main")
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body (feature_find_main flag): %v", err)
+ }
+ featurePart.Write([]byte("false")) // false to disable readability
+
+ for _, label := range labelsSplit {
+ labelPart, err := multipartWriter.CreateFormField("labels")
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body (entry labels): %v", err)
+ }
+ labelPart.Write([]byte(label))
+ }
+
+ contentBodyHeader, err := json.Marshal(&partContentHeader{
+ Url: entryURL,
+ ContentHeader: contentHeader{ContentType: "text/html"},
+ })
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body (entry content header): %v", err)
+ }
+
+ contentPart, err := multipartWriter.CreateFormFile("resource", "blob")
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body (entry content): %v", err)
+ }
+ contentPart.Write(contentBodyHeader)
+ contentPart.Write([]byte("\n"))
+ contentPart.Write([]byte(entryContent))
+
+ err = multipartWriter.Close()
+ if err != nil {
+ return fmt.Errorf("readeck: unable to encode request body: %v", err)
+ }
+ request, err = http.NewRequest(http.MethodPost, apiEndpoint, requestBody)
+ if err != nil {
+ return fmt.Errorf("readeck: unable to create request: %v", err)
+ }
+ request.Header.Set("Content-Type", multipartWriter.FormDataContentType())
+ }
+
+ request.Header.Set("User-Agent", "Miniflux/"+version.Version)
+ request.Header.Set("Authorization", "Bearer "+c.apiKey)
+
+ httpClient := &http.Client{Timeout: defaultClientTimeout}
+ response, err := httpClient.Do(request)
+ if err != nil {
+ return fmt.Errorf("readeck: unable to send request: %v", err)
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode >= 400 {
+ return fmt.Errorf("readeck: unable to create bookmark: url=%s status=%d", apiEndpoint, response.StatusCode)
+ }
+
+ return nil
+}
+
+type readeckBookmark struct {
+ Url string `json:"url"`
+ Title string `json:"title"`
+ Labels []string `json:"labels,omitempty"`
+}
+
+type contentHeader struct {
+ ContentType string `json:"content-type"`
+}
+
+type partContentHeader struct {
+ Url string `json:"url"`
+ ContentHeader contentHeader `json:"headers"`
+}