mirror of
https://github.com/miniflux/v2.git
synced 2025-06-27 16:36:00 +00:00
feat(rssbridge): support auth token for RSS-Bridge
This commit is contained in:
parent
81ec32a8b6
commit
aabc4c199f
13 changed files with 54 additions and 11 deletions
|
@ -30,9 +30,11 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
var rssbridgeURL string
|
var rssbridgeURL string
|
||||||
|
var rssbridgeToken string
|
||||||
intg, err := h.store.Integration(request.UserID(r))
|
intg, err := h.store.Integration(request.UserID(r))
|
||||||
if err == nil && intg != nil && intg.RSSBridgeEnabled {
|
if err == nil && intg != nil && intg.RSSBridgeEnabled {
|
||||||
rssbridgeURL = intg.RSSBridgeURL
|
rssbridgeURL = intg.RSSBridgeURL
|
||||||
|
rssbridgeToken = intg.RSSBridgeToken
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBuilder := fetcher.NewRequestBuilder()
|
requestBuilder := fetcher.NewRequestBuilder()
|
||||||
|
@ -50,6 +52,7 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
|
||||||
subscriptions, localizedError := subscription.NewSubscriptionFinder(requestBuilder).FindSubscriptions(
|
subscriptions, localizedError := subscription.NewSubscriptionFinder(requestBuilder).FindSubscriptions(
|
||||||
subscriptionDiscoveryRequest.URL,
|
subscriptionDiscoveryRequest.URL,
|
||||||
rssbridgeURL,
|
rssbridgeURL,
|
||||||
|
rssbridgeToken,
|
||||||
)
|
)
|
||||||
|
|
||||||
if localizedError != nil {
|
if localizedError != nil {
|
||||||
|
|
|
@ -1066,4 +1066,11 @@ var migrations = []func(tx *sql.Tx, driver string) error{
|
||||||
_, err = tx.Exec(`ALTER TABLE feeds ADD COLUMN proxy_url text default ''`)
|
_, err = tx.Exec(`ALTER TABLE feeds ADD COLUMN proxy_url text default ''`)
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
func(tx *sql.Tx, _ string) (err error) {
|
||||||
|
sql := `
|
||||||
|
ALTER TABLE integrations ADD COLUMN rssbridge_token text default '';
|
||||||
|
`
|
||||||
|
_, err = tx.Exec(sql)
|
||||||
|
return
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,11 +418,13 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
requestBuilder.WithProxyRotator(proxyrotator.ProxyRotatorInstance)
|
requestBuilder.WithProxyRotator(proxyrotator.ProxyRotatorInstance)
|
||||||
|
|
||||||
var rssBridgeURL string
|
var rssBridgeURL string
|
||||||
|
var rssBridgeToken string
|
||||||
if intg, err := h.store.Integration(userID); err == nil && intg != nil && intg.RSSBridgeEnabled {
|
if intg, err := h.store.Integration(userID); err == nil && intg != nil && intg.RSSBridgeEnabled {
|
||||||
rssBridgeURL = intg.RSSBridgeURL
|
rssBridgeURL = intg.RSSBridgeURL
|
||||||
|
rssBridgeToken = intg.RSSBridgeToken
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions, localizedError := mfs.NewSubscriptionFinder(requestBuilder).FindSubscriptions(feedURL, rssBridgeURL)
|
subscriptions, localizedError := mfs.NewSubscriptionFinder(requestBuilder).FindSubscriptions(feedURL, rssBridgeURL, rssBridgeToken)
|
||||||
if localizedError != nil {
|
if localizedError != nil {
|
||||||
json.ServerError(w, r, localizedError.Error())
|
json.ServerError(w, r, localizedError.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -24,13 +24,16 @@ type BridgeMeta struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetectBridges(rssBridgeURL, websiteURL string) ([]*Bridge, error) {
|
func DetectBridges(rssBridgeURL, rssBridgeToken, websiteURL string) ([]*Bridge, error) {
|
||||||
endpointURL, err := url.Parse(rssBridgeURL)
|
endpointURL, err := url.Parse(rssBridgeURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("RSS-Bridge: unable to parse bridge URL: %w", err)
|
return nil, fmt.Errorf("RSS-Bridge: unable to parse bridge URL: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
values := endpointURL.Query()
|
values := endpointURL.Query()
|
||||||
|
if rssBridgeToken != "" {
|
||||||
|
values.Add("token", rssBridgeToken)
|
||||||
|
}
|
||||||
values.Add("action", "findfeed")
|
values.Add("action", "findfeed")
|
||||||
values.Add("format", "atom")
|
values.Add("format", "atom")
|
||||||
values.Add("url", websiteURL)
|
values.Add("url", websiteURL)
|
||||||
|
@ -78,6 +81,15 @@ func DetectBridges(rssBridgeURL, websiteURL string) ([]*Bridge, error) {
|
||||||
slog.String("url", bridge.URL),
|
slog.String("url", bridge.URL),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rssBridgeToken != "" {
|
||||||
|
bridge.URL = bridge.URL + "&token=" + rssBridgeToken
|
||||||
|
|
||||||
|
slog.Debug("Appended token to RSS bridge URL",
|
||||||
|
slog.String("name", bridge.BridgeMeta.Name),
|
||||||
|
slog.String("url", bridge.URL),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bridgeResponse, nil
|
return bridgeResponse, nil
|
||||||
|
|
|
@ -295,6 +295,7 @@
|
||||||
"form.integration.readwise_api_key": "Readwise Reader Access Token",
|
"form.integration.readwise_api_key": "Readwise Reader Access Token",
|
||||||
"form.integration.readwise_api_key_link": "Get your Readwise Access Token",
|
"form.integration.readwise_api_key_link": "Get your Readwise Access Token",
|
||||||
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
|
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
|
||||||
|
"form.integration.rssbridge_token": "RSS-Bridge authentication token",
|
||||||
"form.integration.rssbridge_url": "RSS-Bridge server URL",
|
"form.integration.rssbridge_url": "RSS-Bridge server URL",
|
||||||
"form.integration.shaarli_activate": "Save articles to Shaarli",
|
"form.integration.shaarli_activate": "Save articles to Shaarli",
|
||||||
"form.integration.shaarli_api_secret": "Shaarli API Secret",
|
"form.integration.shaarli_api_secret": "Shaarli API Secret",
|
||||||
|
@ -608,4 +609,4 @@
|
||||||
"time_elapsed.yesterday": "yesterday",
|
"time_elapsed.yesterday": "yesterday",
|
||||||
"tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s",
|
"tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s",
|
||||||
"tooltip.logged_user": "Logged in as %s"
|
"tooltip.logged_user": "Logged in as %s"
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,6 +297,7 @@
|
||||||
"form.integration.readwise_api_key": "Токен доступа в Readwise",
|
"form.integration.readwise_api_key": "Токен доступа в Readwise",
|
||||||
"form.integration.readwise_api_key_link": "Получить токен доступа Readwise",
|
"form.integration.readwise_api_key_link": "Получить токен доступа Readwise",
|
||||||
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
|
"form.integration.rssbridge_activate": "Check RSS-Bridge when adding subscriptions",
|
||||||
|
"form.integration.rssbridge_token": "RSS-Bridge authentication token",
|
||||||
"form.integration.rssbridge_url": "RSS-Bridge server URL",
|
"form.integration.rssbridge_url": "RSS-Bridge server URL",
|
||||||
"form.integration.shaarli_activate": "Сохранить статьи в Shaarli",
|
"form.integration.shaarli_activate": "Сохранить статьи в Shaarli",
|
||||||
"form.integration.shaarli_api_secret": "Секретный ключ Shaarli API",
|
"form.integration.shaarli_api_secret": "Секретный ключ Shaarli API",
|
||||||
|
@ -625,4 +626,4 @@
|
||||||
"time_elapsed.yesterday": "вчера",
|
"time_elapsed.yesterday": "вчера",
|
||||||
"tooltip.keyboard_shortcuts": "Сочетания клавиш: %s",
|
"tooltip.keyboard_shortcuts": "Сочетания клавиш: %s",
|
||||||
"tooltip.logged_user": "Авторизован как %s"
|
"tooltip.logged_user": "Авторизован как %s"
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ type Integration struct {
|
||||||
WebhookSecret string
|
WebhookSecret string
|
||||||
RSSBridgeEnabled bool
|
RSSBridgeEnabled bool
|
||||||
RSSBridgeURL string
|
RSSBridgeURL string
|
||||||
|
RSSBridgeToken string
|
||||||
OmnivoreEnabled bool
|
OmnivoreEnabled bool
|
||||||
OmnivoreAPIKey string
|
OmnivoreAPIKey string
|
||||||
OmnivoreURL string
|
OmnivoreURL string
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (f *SubscriptionFinder) FeedResponseInfo() *model.FeedCreationRequestFromSu
|
||||||
return f.feedResponseInfo
|
return f.feedResponseInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *SubscriptionFinder) FindSubscriptions(websiteURL, rssBridgeURL string) (Subscriptions, *locale.LocalizedErrorWrapper) {
|
func (f *SubscriptionFinder) FindSubscriptions(websiteURL, rssBridgeURL string, rssBridgeToken string) (Subscriptions, *locale.LocalizedErrorWrapper) {
|
||||||
responseHandler := fetcher.NewResponseHandler(f.requestBuilder.ExecuteRequest(websiteURL))
|
responseHandler := fetcher.NewResponseHandler(f.requestBuilder.ExecuteRequest(websiteURL))
|
||||||
defer responseHandler.Close()
|
defer responseHandler.Close()
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ func (f *SubscriptionFinder) FindSubscriptions(websiteURL, rssBridgeURL string)
|
||||||
// Step 5) Check if the website URL can use RSS-Bridge.
|
// Step 5) Check if the website URL can use RSS-Bridge.
|
||||||
if rssBridgeURL != "" {
|
if rssBridgeURL != "" {
|
||||||
slog.Debug("Try to detect feeds with RSS-Bridge", slog.String("website_url", websiteURL))
|
slog.Debug("Try to detect feeds with RSS-Bridge", slog.String("website_url", websiteURL))
|
||||||
if subscriptions, localizedError := f.FindSubscriptionsFromRSSBridge(websiteURL, rssBridgeURL); localizedError != nil {
|
if subscriptions, localizedError := f.FindSubscriptionsFromRSSBridge(websiteURL, rssBridgeURL, rssBridgeToken); localizedError != nil {
|
||||||
return nil, localizedError
|
return nil, localizedError
|
||||||
} else if len(subscriptions) > 0 {
|
} else if len(subscriptions) > 0 {
|
||||||
slog.Debug("Subscriptions found from RSS-Bridge", slog.String("website_url", websiteURL), slog.Any("subscriptions", subscriptions))
|
slog.Debug("Subscriptions found from RSS-Bridge", slog.String("website_url", websiteURL), slog.Any("subscriptions", subscriptions))
|
||||||
|
@ -254,13 +254,14 @@ func (f *SubscriptionFinder) FindSubscriptionsFromWellKnownURLs(websiteURL strin
|
||||||
return subscriptions, nil
|
return subscriptions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *SubscriptionFinder) FindSubscriptionsFromRSSBridge(websiteURL, rssBridgeURL string) (Subscriptions, *locale.LocalizedErrorWrapper) {
|
func (f *SubscriptionFinder) FindSubscriptionsFromRSSBridge(websiteURL, rssBridgeURL string, rssBridgeToken string) (Subscriptions, *locale.LocalizedErrorWrapper) {
|
||||||
slog.Debug("Trying to detect feeds using RSS-Bridge",
|
slog.Debug("Trying to detect feeds using RSS-Bridge",
|
||||||
slog.String("website_url", websiteURL),
|
slog.String("website_url", websiteURL),
|
||||||
slog.String("rssbridge_url", rssBridgeURL),
|
slog.String("rssbridge_url", rssBridgeURL),
|
||||||
|
slog.String("rssbridge_token", rssBridgeToken),
|
||||||
)
|
)
|
||||||
|
|
||||||
bridges, err := rssbridge.DetectBridges(rssBridgeURL, websiteURL)
|
bridges, err := rssbridge.DetectBridges(rssBridgeURL, rssBridgeToken, websiteURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, locale.NewLocalizedErrorWrapper(err, "error.unable_to_detect_rssbridge", err)
|
return nil, locale.NewLocalizedErrorWrapper(err, "error.unable_to_detect_rssbridge", err)
|
||||||
}
|
}
|
||||||
|
@ -268,6 +269,7 @@ func (f *SubscriptionFinder) FindSubscriptionsFromRSSBridge(websiteURL, rssBridg
|
||||||
slog.Debug("RSS-Bridge results",
|
slog.Debug("RSS-Bridge results",
|
||||||
slog.String("website_url", websiteURL),
|
slog.String("website_url", websiteURL),
|
||||||
slog.String("rssbridge_url", rssBridgeURL),
|
slog.String("rssbridge_url", rssBridgeURL),
|
||||||
|
slog.String("rssbridge_token", rssBridgeToken),
|
||||||
slog.Int("nb_bridges", len(bridges)),
|
slog.Int("nb_bridges", len(bridges)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,8 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
|
||||||
pushover_user,
|
pushover_user,
|
||||||
pushover_token,
|
pushover_token,
|
||||||
pushover_device,
|
pushover_device,
|
||||||
pushover_prefix
|
pushover_prefix,
|
||||||
|
rssbridge_token
|
||||||
FROM
|
FROM
|
||||||
integrations
|
integrations
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -338,6 +339,7 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
|
||||||
&integration.PushoverToken,
|
&integration.PushoverToken,
|
||||||
&integration.PushoverDevice,
|
&integration.PushoverDevice,
|
||||||
&integration.PushoverPrefix,
|
&integration.PushoverPrefix,
|
||||||
|
&integration.RSSBridgeToken,
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
|
@ -464,9 +466,10 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
|
||||||
pushover_user=$107,
|
pushover_user=$107,
|
||||||
pushover_token=$108,
|
pushover_token=$108,
|
||||||
pushover_device=$109,
|
pushover_device=$109,
|
||||||
pushover_prefix=$110
|
pushover_prefix=$110,
|
||||||
|
rssbridge_token=$111
|
||||||
WHERE
|
WHERE
|
||||||
user_id=$111
|
user_id=$112
|
||||||
`
|
`
|
||||||
_, err := s.db.Exec(
|
_, err := s.db.Exec(
|
||||||
query,
|
query,
|
||||||
|
@ -580,6 +583,7 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
|
||||||
integration.PushoverToken,
|
integration.PushoverToken,
|
||||||
integration.PushoverDevice,
|
integration.PushoverDevice,
|
||||||
integration.PushoverPrefix,
|
integration.PushoverPrefix,
|
||||||
|
integration.RSSBridgeToken,
|
||||||
integration.UserID,
|
integration.UserID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -513,6 +513,9 @@
|
||||||
<label for="form-rssbridge-url">{{ t "form.integration.rssbridge_url" }}</label>
|
<label for="form-rssbridge-url">{{ t "form.integration.rssbridge_url" }}</label>
|
||||||
<input type="url" name="rssbridge_url" id="form-rssbridge-url" value="{{ .form.RSSBridgeURL }}" spellcheck="false">
|
<input type="url" name="rssbridge_url" id="form-rssbridge-url" value="{{ .form.RSSBridgeURL }}" spellcheck="false">
|
||||||
|
|
||||||
|
<label for="form-rssbridge-token">{{ t "form.integration.rssbridge_token" }}</label>
|
||||||
|
<input type="password" name="rssbridge_token" id="form-rssbridge-token" value="{{ .form.RSSBridgeToken }}" spellcheck="false">
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
|
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -93,6 +93,7 @@ type IntegrationForm struct {
|
||||||
WebhookSecret string
|
WebhookSecret string
|
||||||
RSSBridgeEnabled bool
|
RSSBridgeEnabled bool
|
||||||
RSSBridgeURL string
|
RSSBridgeURL string
|
||||||
|
RSSBridgeToken string
|
||||||
OmnivoreEnabled bool
|
OmnivoreEnabled bool
|
||||||
OmnivoreAPIKey string
|
OmnivoreAPIKey string
|
||||||
OmnivoreURL string
|
OmnivoreURL string
|
||||||
|
@ -204,6 +205,7 @@ func (i IntegrationForm) Merge(integration *model.Integration) {
|
||||||
integration.WebhookURL = i.WebhookURL
|
integration.WebhookURL = i.WebhookURL
|
||||||
integration.RSSBridgeEnabled = i.RSSBridgeEnabled
|
integration.RSSBridgeEnabled = i.RSSBridgeEnabled
|
||||||
integration.RSSBridgeURL = i.RSSBridgeURL
|
integration.RSSBridgeURL = i.RSSBridgeURL
|
||||||
|
integration.RSSBridgeToken = i.RSSBridgeToken
|
||||||
integration.OmnivoreEnabled = i.OmnivoreEnabled
|
integration.OmnivoreEnabled = i.OmnivoreEnabled
|
||||||
integration.OmnivoreAPIKey = i.OmnivoreAPIKey
|
integration.OmnivoreAPIKey = i.OmnivoreAPIKey
|
||||||
integration.OmnivoreURL = i.OmnivoreURL
|
integration.OmnivoreURL = i.OmnivoreURL
|
||||||
|
@ -318,6 +320,7 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm {
|
||||||
WebhookURL: r.FormValue("webhook_url"),
|
WebhookURL: r.FormValue("webhook_url"),
|
||||||
RSSBridgeEnabled: r.FormValue("rssbridge_enabled") == "1",
|
RSSBridgeEnabled: r.FormValue("rssbridge_enabled") == "1",
|
||||||
RSSBridgeURL: r.FormValue("rssbridge_url"),
|
RSSBridgeURL: r.FormValue("rssbridge_url"),
|
||||||
|
RSSBridgeToken: r.FormValue("rssbridge_token"),
|
||||||
OmnivoreEnabled: r.FormValue("omnivore_enabled") == "1",
|
OmnivoreEnabled: r.FormValue("omnivore_enabled") == "1",
|
||||||
OmnivoreAPIKey: r.FormValue("omnivore_api_key"),
|
OmnivoreAPIKey: r.FormValue("omnivore_api_key"),
|
||||||
OmnivoreURL: r.FormValue("omnivore_url"),
|
OmnivoreURL: r.FormValue("omnivore_url"),
|
||||||
|
|
|
@ -107,6 +107,7 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
|
||||||
WebhookSecret: integration.WebhookSecret,
|
WebhookSecret: integration.WebhookSecret,
|
||||||
RSSBridgeEnabled: integration.RSSBridgeEnabled,
|
RSSBridgeEnabled: integration.RSSBridgeEnabled,
|
||||||
RSSBridgeURL: integration.RSSBridgeURL,
|
RSSBridgeURL: integration.RSSBridgeURL,
|
||||||
|
RSSBridgeToken: integration.RSSBridgeToken,
|
||||||
OmnivoreEnabled: integration.OmnivoreEnabled,
|
OmnivoreEnabled: integration.OmnivoreEnabled,
|
||||||
OmnivoreAPIKey: integration.OmnivoreAPIKey,
|
OmnivoreAPIKey: integration.OmnivoreAPIKey,
|
||||||
OmnivoreURL: integration.OmnivoreURL,
|
OmnivoreURL: integration.OmnivoreURL,
|
||||||
|
|
|
@ -53,8 +53,10 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var rssBridgeURL string
|
var rssBridgeURL string
|
||||||
|
var rssBridgeToken string
|
||||||
if intg, err := h.store.Integration(user.ID); err == nil && intg != nil && intg.RSSBridgeEnabled {
|
if intg, err := h.store.Integration(user.ID); err == nil && intg != nil && intg.RSSBridgeEnabled {
|
||||||
rssBridgeURL = intg.RSSBridgeURL
|
rssBridgeURL = intg.RSSBridgeURL
|
||||||
|
rssBridgeToken = intg.RSSBridgeToken
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBuilder := fetcher.NewRequestBuilder()
|
requestBuilder := fetcher.NewRequestBuilder()
|
||||||
|
@ -73,6 +75,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
|
||||||
subscriptions, localizedError := subscriptionFinder.FindSubscriptions(
|
subscriptions, localizedError := subscriptionFinder.FindSubscriptions(
|
||||||
subscriptionForm.URL,
|
subscriptionForm.URL,
|
||||||
rssBridgeURL,
|
rssBridgeURL,
|
||||||
|
rssBridgeToken,
|
||||||
)
|
)
|
||||||
if localizedError != nil {
|
if localizedError != nil {
|
||||||
v.Set("form", subscriptionForm)
|
v.Set("form", subscriptionForm)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue