Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add linkwarden integration #2356

Merged
merged 4 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions internal/database/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,4 +846,13 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN linkwarden_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN linkwarden_url text default '';
ALTER TABLE integrations ADD COLUMN linkwarden_api_key text default '';
`
_, err = tx.Exec(sql)
return err
},
}
22 changes: 22 additions & 0 deletions internal/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"miniflux.app/v2/internal/integration/instapaper"
"miniflux.app/v2/internal/integration/linkace"
"miniflux.app/v2/internal/integration/linkding"
"miniflux.app/v2/internal/integration/linkwarden"
"miniflux.app/v2/internal/integration/matrixbot"
"miniflux.app/v2/internal/integration/notion"
"miniflux.app/v2/internal/integration/nunuxkeeper"
Expand Down Expand Up @@ -228,6 +229,27 @@ func SendEntry(entry *model.Entry, userIntegrations *model.Integration) {
}
}

if userIntegrations.LinkwardenEnabled {
slog.Debug("Sending entry to linkwarden",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
)

client := linkwarden.NewClient(
userIntegrations.LinkwardenURL,
userIntegrations.LinkwardenAPIKey,
)
if err := client.CreateBookmark(entry.URL, entry.Title); err != nil {
slog.Error("Unable to send entry to Linkwarden",
slog.Int64("user_id", userIntegrations.UserID),
slog.Int64("entry_id", entry.ID),
slog.String("entry_url", entry.URL),
slog.Any("error", err),
)
}
}

if userIntegrations.ReadwiseEnabled {
slog.Debug("Sending entry to Readwise",
slog.Int64("user_id", userIntegrations.UserID),
Expand Down
80 changes: 80 additions & 0 deletions internal/integration/linkwarden/linkwarden.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package linkwarden // import "miniflux.app/v2/internal/integration/linkwarden"

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"

"miniflux.app/v2/internal/urllib"
"miniflux.app/v2/internal/version"
)

const defaultClientTimeout = 10 * time.Second

type Client struct {
baseURL string
apiKey string
}

func NewClient(baseURL, apiKey string) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey}
}

func (c *Client) CreateBookmark(entryURL, entryTitle string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("linkwarden: missing base URL or API key")
}

apiEndpoint, err := urllib.JoinBaseURLAndPath(c.baseURL, "/api/v1/links")
if err != nil {
return fmt.Errorf(`linkwarden: invalid API endpoint: %v`, err)
}

requestBody, err := json.Marshal(&linkwardenBookmark{
Url: entryURL,
Name: "",
Description: "",
Tags: []string{},
Collection: map[string]interface{}{},
})

if err != nil {
return fmt.Errorf("linkwarden: unable to encode request body: %v", err)
}

request, err := http.NewRequest(http.MethodPost, apiEndpoint, bytes.NewReader(requestBody))
if err != nil {
return fmt.Errorf("linkwarden: unable to create request: %v", err)
}

request.Header.Set("Content-Type", "application/json")
request.Header.Set("User-Agent", "Miniflux/"+version.Version)
request.AddCookie(&http.Cookie{Name: "__Secure-next-auth.session-token", Value: c.apiKey})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to make it work with this cookie when using the linkwarden Docker image. However, next-auth.session-token works for me. Is the cookie name different when using HTTP or HTTPS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested with HTTPS, so that sounds reasonable

request.AddCookie(&http.Cookie{Name: "next-auth.session-token", Value: c.apiKey})

httpClient := &http.Client{Timeout: defaultClientTimeout}
response, err := httpClient.Do(request)
if err != nil {
return fmt.Errorf("linkwarden: unable to send request: %v", err)
}
defer response.Body.Close()

if response.StatusCode >= 400 {
return fmt.Errorf("linkwarden: unable to create link: url=%s status=%d", apiEndpoint, response.StatusCode)
}

return nil
}

type linkwardenBookmark struct {
Url string `json:"url"`
Name string `json:"name"`
Description string `json:"description"`
Tags []string `json:"tags"`
Collection map[string]interface{} `json:"collection"`
}
3 changes: 3 additions & 0 deletions internal/locale/translations/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding API-Schlüssel",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Lesezeichen als ungelesen markieren",
"form.integration.linkwarden_activate": "Artikel in Linkwarden speichern",
"form.integration.linkwarden_endpoint": "Linkwarden API-Endpunkt",
"form.integration.linkwarden_api_key": "Linkwarden API-Schlüssel",
"form.integration.matrix_bot_activate": "Neue Artikel in Matrix übertragen",
"form.integration.matrix_bot_user": "Benutzername für Matrix",
"form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/el_EL.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Κλειδί API Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Σημείωση του σελιδοδείκτη ως μη αναγνωσμένου",
"form.integration.linkwarden_activate": "Αποθήκευση άρθρων στο Linkwarden",
"form.integration.linkwarden_endpoint": "Τελικό σημείο Linkwarden API",
"form.integration.linkwarden_api_key": "Κλειδί API Linkwarden",
"form.integration.matrix_bot_activate": "Μεταφορά νέων άρθρων στο Matrix",
"form.integration.matrix_bot_user": "Όνομα χρήστη για το Matrix",
"form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding API key",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Mark bookmark as unread",
"form.integration.linkwarden_activate": "Save entries to Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API Endpoint",
"form.integration.linkwarden_api_key": "Linkwarden API key",
"form.integration.matrix_bot_activate": "Push new entries to Matrix",
"form.integration.matrix_bot_user": "Username for Matrix",
"form.integration.matrix_bot_password": "Password for Matrix user",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/es_ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Clave de API de Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Marcar marcador como no leído",
"form.integration.linkwarden_activate": "Enviar artículos a Linkwarden",
"form.integration.linkwarden_endpoint": "Acceso API de Linkwarden",
"form.integration.linkwarden_api_key": "Clave de API de Linkwarden",
"form.integration.matrix_bot_activate": "Transferir nuevos artículos a Matrix",
"form.integration.matrix_bot_user": "Nombre de usuario para Matrix",
"form.integration.matrix_bot_password": "Contraseña para el usuario de Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/fi_FI.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding API-avain",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Merkitse kirjanmerkki lukemattomaksi",
"form.integration.linkwarden_activate": "Tallenna artikkelit Linkkiin",
"form.integration.linkwarden_endpoint": "Linkwarden API-päätepiste",
"form.integration.linkwarden_api_key": "Linkwarden API-avain",
"form.integration.matrix_bot_activate": "Siirrä uudet artikkelit Matrixiin",
"form.integration.matrix_bot_user": "Matrixin käyttäjätunnus",
"form.integration.matrix_bot_password": "Matrix-käyttäjän salasana",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Clé d'API de Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Marquer le lien comme non lu",
"form.integration.linkwarden_activate": "Sauvegarder les articles vers Linkwarden",
"form.integration.linkwarden_endpoint": "URL de l'API de Linkwarden",
"form.integration.linkwarden_api_key": "Clé d'API de Linkwarden",
"form.integration.matrix_bot_activate": "Envoyer les nouveaux articles vers Matrix",
"form.integration.matrix_bot_user": "Nom de l'utilisateur Matrix",
"form.integration.matrix_bot_password": "Mot de passe de l'utilisateur Matrix",
Expand Down
5 changes: 4 additions & 1 deletion internal/locale/translations/hi_IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "लिंकिंग एपीआई कुंजी",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "बुकमार्क को अपठित के रूप में चिह्नित करें",
"form.integration.linkwarden_activate": "Save entries to Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API Endpoint",
"form.integration.linkwarden_api_key": "Linkwarden API key",
"form.integration.matrix_bot_activate": "नए लेखों को मैट्रिक्स में स्थानांतरित करें",
"form.integration.matrix_bot_user": "मैट्रिक्स के लिए उपयोगकर्ता नाम",
"form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड",
Expand Down Expand Up @@ -515,4 +518,4 @@
"error.feed_not_found": "This feed does not exist or does not belong to this user.",
"error.unable_to_detect_rssbridge": "Unable to detect feed using RSS-Bridge: %v.",
"error.feed_format_not_detected": "Unable to detect feed format: %v."
}
}
3 changes: 3 additions & 0 deletions internal/locale/translations/id_ID.json
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@
"form.integration.linkding_api_key": "Kunci API Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Tandai markah sebagai belum dibaca",
"form.integration.linkwarden_activate": "Simpan artikel ke Linkwarden",
"form.integration.linkwarden_endpoint": "Titik URL API Linkwarden",
"form.integration.linkwarden_api_key": "Kunci API Linkwarden",
"form.integration.matrix_bot_activate": "Kirim entri baru ke Matrix",
"form.integration.matrix_bot_user": "Nama Pengguna Matrix",
"form.integration.matrix_bot_password": "Kata Sandi Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/it_IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "API key dell'account Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Segna i preferiti come non letti",
"form.integration.linkwarden_activate": "Salva gli articoli su Linkwarden",
"form.integration.linkwarden_endpoint": "Endpoint dell'API di Linkwarden",
"form.integration.linkwarden_api_key": "API key dell'account Linkwarden",
"form.integration.matrix_bot_activate": "Trasferimento di nuovi articoli a Matrix",
"form.integration.matrix_bot_user": "Nome utente per Matrix",
"form.integration.matrix_bot_password": "Password per l'utente Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/ja_JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding の API key",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "ブックマークを未読にする",
"form.integration.linkwarden_activate": "Linkwarden に記事を保存する",
"form.integration.linkwarden_endpoint": "Linkwarden の API Endpoint",
"form.integration.linkwarden_api_key": "Linkwarden の API key",
"form.integration.matrix_bot_activate": "新しい記事をMatrixに転送する",
"form.integration.matrix_bot_user": "Matrixのユーザー名",
"form.integration.matrix_bot_password": "Matrixユーザ用パスワード",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/nl_NL.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding API-sleutel",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Markeer bookmark als gelezen",
"form.integration.linkwarden_activate": "Opslaan naar Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden URL",
"form.integration.linkwarden_api_key": "Linkwarden API-sleutel",
"form.integration.matrix_bot_activate": "Nieuwe artikelen overbrengen naar Matrix",
"form.integration.matrix_bot_user": "Gebruikersnaam voor Matrix",
"form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/pl_PL.json
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,9 @@
"form.integration.linkding_api_key": "Linkding API key",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Zaznacz zakładkę jako nieprzeczytaną",
"form.integration.linkwarden_activate": "Zapisz artykuły do Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden URL",
"form.integration.linkwarden_api_key": "Linkwarden API key",
"form.integration.matrix_bot_activate": "Przenieś nowe artykuły do Matrix",
"form.integration.matrix_bot_user": "Nazwa użytkownika dla Matrix",
"form.integration.matrix_bot_password": "Hasło dla użytkownika Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/pt_BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Chave de API do Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Salvar marcador como não lido",
"form.integration.linkwarden_activate": "Salvar itens no Linkwarden",
"form.integration.linkwarden_endpoint": "Endpoint de API do Linkwarden",
"form.integration.linkwarden_api_key": "Chave de API do Linkwarden",
"form.integration.matrix_bot_activate": "Transferir novos artigos para o Matrix",
"form.integration.matrix_bot_user": "Nome de utilizador para Matrix",
"form.integration.matrix_bot_password": "Palavra-passe para utilizador da Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/ru_RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,9 @@
"form.integration.linkding_api_key": "API-ключ Linkding",
"form.integration.linkding_tags": "Теги Linkding",
"form.integration.linkding_bookmark": "Помечать закладки как непрочитанное",
"form.integration.linkwarden_activate": "Сохранять статьи в Linkwarden",
"form.integration.linkwarden_endpoint": "Конечная точка Linkwarden API",
"form.integration.linkwarden_api_key": "API-ключ Linkwarden",
"form.integration.matrix_bot_activate": "Репостить новые статьи в Matrix",
"form.integration.matrix_bot_user": "Имя пользователя Matrix",
"form.integration.matrix_bot_password": "Пароль пользователя Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/tr_TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding API Anahtarı",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Yer imini okunmadı olarak işaretle",
"form.integration.linkwarden_activate": "Makaleleri Linkwarden'e kaydet",
"form.integration.linkwarden_endpoint": "Linkwarden API Uç Noktası",
"form.integration.linkwarden_api_key": "Linkwarden API Anahtarı",
"form.integration.matrix_bot_activate": "Yeni makaleleri Matrix'e aktarın",
"form.integration.matrix_bot_user": "Matrix için Kullanıcı Adı",
"form.integration.matrix_bot_password": "Matrix kullanıcısı için şifre",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/uk_UA.json
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@
"form.integration.linkding_api_key": "Ключ API Linkding",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "Відмічати закладку як непрочитану",
"form.integration.linkwarden_activate": "Зберігати статті до Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API Endpoint",
"form.integration.linkwarden_api_key": "Ключ API Linkwarden",
"form.integration.matrix_bot_activate": "Перенесення нових статей в Матрицю",
"form.integration.matrix_bot_user": "Ім'я користувача для Matrix",
"form.integration.matrix_bot_password": "Пароль для користувача Matrix",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,9 @@
"form.integration.linkding_api_key": "Linkding API 密钥",
"form.integration.linkding_tags": "Linkding 默认标签",
"form.integration.linkding_bookmark": "标记为未读",
"form.integration.linkwarden_activate": "保存文章到 Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API 端点",
"form.integration.linkwarden_api_key": "Linkwarden API 密钥",
"form.integration.matrix_bot_activate": "将新文章推送到 Matrix",
"form.integration.matrix_bot_user": "Matrix Bot 用户名",
"form.integration.matrix_bot_password": "Matrix Bot 密码",
Expand Down
3 changes: 3 additions & 0 deletions internal/locale/translations/zh_TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@
"form.integration.linkding_api_key": "Linkding API 金鑰",
"form.integration.linkding_tags": "Linkding Tags",
"form.integration.linkding_bookmark": "標記為未讀",
"form.integration.linkwarden_activate": "儲存文章到 Linkwarden",
"form.integration.linkwarden_endpoint": "Linkwarden API 端點",
"form.integration.linkwarden_api_key": "Linkwarden API 金鑰",
"form.integration.matrix_bot_activate": "推送文章到 Matrix",
"form.integration.matrix_bot_user": "Matrix 的用戶名",
"form.integration.matrix_bot_password": "Matrix 的密碼",
Expand Down
3 changes: 3 additions & 0 deletions internal/model/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ type Integration struct {
LinkdingAPIKey string
LinkdingTags string
LinkdingMarkAsUnread bool
LinkwardenEnabled bool
LinkwardenURL string
LinkwardenAPIKey string
MatrixBotEnabled bool
MatrixBotUser string
MatrixBotPassword string
Expand Down
17 changes: 15 additions & 2 deletions internal/storage/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
linkding_api_key,
linkding_tags,
linkding_mark_as_unread,
linkwarden_enabled,
linkwarden_url,
linkwarden_api_key,
matrix_bot_enabled,
matrix_bot_user,
matrix_bot_password,
Expand Down Expand Up @@ -247,6 +250,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.LinkdingAPIKey,
&integration.LinkdingTags,
&integration.LinkdingMarkAsUnread,
&integration.LinkwardenEnabled,
&integration.LinkwardenURL,
&integration.LinkwardenAPIKey,
&integration.MatrixBotEnabled,
&integration.MatrixBotUser,
&integration.MatrixBotPassword,
Expand Down Expand Up @@ -362,9 +368,12 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
rssbridge_url=$73,
omnivore_enabled=$74,
omnivore_api_key=$75,
omnivore_url=$76
omnivore_url=$76,
linkwarden_enabled=$77,
linkwarden_url=$78,
linkwarden_api_key=$79
WHERE
user_id=$77
user_id=$80
`
_, err := s.db.Exec(
query,
Expand Down Expand Up @@ -444,6 +453,9 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
integration.OmnivoreEnabled,
integration.OmnivoreAPIKey,
integration.OmnivoreURL,
integration.LinkwardenEnabled,
integration.LinkwardenURL,
integration.LinkwardenAPIKey,
integration.UserID,
)

Expand Down Expand Up @@ -475,6 +487,7 @@ func (s *Storage) HasSaveEntry(userID int64) (result bool) {
pocket_enabled='t' OR
linkace_enabled='t' OR
linkding_enabled='t' OR
linkwarden_enabled='t' OR
apprise_enabled='t' OR
shiori_enabled='t' OR
shaarli_enabled='t' OR
Expand Down
Loading
Loading