1
0
Fork 0
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:
Anton Larionov 2025-05-19 15:49:03 +02:00
parent 81ec32a8b6
commit aabc4c199f
13 changed files with 54 additions and 11 deletions

View file

@ -30,9 +30,11 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
}
var rssbridgeURL string
var rssbridgeToken string
intg, err := h.store.Integration(request.UserID(r))
if err == nil && intg != nil && intg.RSSBridgeEnabled {
rssbridgeURL = intg.RSSBridgeURL
rssbridgeToken = intg.RSSBridgeToken
}
requestBuilder := fetcher.NewRequestBuilder()
@ -50,6 +52,7 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
subscriptions, localizedError := subscription.NewSubscriptionFinder(requestBuilder).FindSubscriptions(
subscriptionDiscoveryRequest.URL,
rssbridgeURL,
rssbridgeToken,
)
if localizedError != nil {

View file

@ -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 ''`)
return err
},
func(tx *sql.Tx, _ string) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN rssbridge_token text default '';
`
_, err = tx.Exec(sql)
return
},
}

View file

@ -418,11 +418,13 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
requestBuilder.WithProxyRotator(proxyrotator.ProxyRotatorInstance)
var rssBridgeURL string
var rssBridgeToken string
if intg, err := h.store.Integration(userID); err == nil && intg != nil && intg.RSSBridgeEnabled {
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 {
json.ServerError(w, r, localizedError.Error())
return

View file

@ -24,13 +24,16 @@ type BridgeMeta struct {
Name string `json:"name"`
}
func DetectBridges(rssBridgeURL, websiteURL string) ([]*Bridge, error) {
func DetectBridges(rssBridgeURL, rssBridgeToken, websiteURL string) ([]*Bridge, error) {
endpointURL, err := url.Parse(rssBridgeURL)
if err != nil {
return nil, fmt.Errorf("RSS-Bridge: unable to parse bridge URL: %w", err)
}
values := endpointURL.Query()
if rssBridgeToken != "" {
values.Add("token", rssBridgeToken)
}
values.Add("action", "findfeed")
values.Add("format", "atom")
values.Add("url", websiteURL)
@ -78,6 +81,15 @@ func DetectBridges(rssBridgeURL, websiteURL string) ([]*Bridge, error) {
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

View file

@ -295,6 +295,7 @@
"form.integration.readwise_api_key": "Readwise Reader 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_token": "RSS-Bridge authentication token",
"form.integration.rssbridge_url": "RSS-Bridge server URL",
"form.integration.shaarli_activate": "Save articles to Shaarli",
"form.integration.shaarli_api_secret": "Shaarli API Secret",

View file

@ -297,6 +297,7 @@
"form.integration.readwise_api_key": "Токен доступа в Readwise",
"form.integration.readwise_api_key_link": "Получить токен доступа Readwise",
"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.shaarli_activate": "Сохранить статьи в Shaarli",
"form.integration.shaarli_api_secret": "Секретный ключ Shaarli API",

View file

@ -90,6 +90,7 @@ type Integration struct {
WebhookSecret string
RSSBridgeEnabled bool
RSSBridgeURL string
RSSBridgeToken string
OmnivoreEnabled bool
OmnivoreAPIKey string
OmnivoreURL string

View file

@ -48,7 +48,7 @@ func (f *SubscriptionFinder) FeedResponseInfo() *model.FeedCreationRequestFromSu
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))
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.
if rssBridgeURL != "" {
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
} else if len(subscriptions) > 0 {
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
}
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.String("website_url", websiteURL),
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 {
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.String("website_url", websiteURL),
slog.String("rssbridge_url", rssBridgeURL),
slog.String("rssbridge_token", rssBridgeToken),
slog.Int("nb_bridges", len(bridges)),
)

View file

@ -219,7 +219,8 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
pushover_user,
pushover_token,
pushover_device,
pushover_prefix
pushover_prefix,
rssbridge_token
FROM
integrations
WHERE
@ -338,6 +339,7 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.PushoverToken,
&integration.PushoverDevice,
&integration.PushoverPrefix,
&integration.RSSBridgeToken,
)
switch {
case err == sql.ErrNoRows:
@ -464,9 +466,10 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
pushover_user=$107,
pushover_token=$108,
pushover_device=$109,
pushover_prefix=$110
pushover_prefix=$110,
rssbridge_token=$111
WHERE
user_id=$111
user_id=$112
`
_, err := s.db.Exec(
query,
@ -580,6 +583,7 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
integration.PushoverToken,
integration.PushoverDevice,
integration.PushoverPrefix,
integration.RSSBridgeToken,
integration.UserID,
)

View file

@ -513,6 +513,9 @@
<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">
<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">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>

View file

@ -93,6 +93,7 @@ type IntegrationForm struct {
WebhookSecret string
RSSBridgeEnabled bool
RSSBridgeURL string
RSSBridgeToken string
OmnivoreEnabled bool
OmnivoreAPIKey string
OmnivoreURL string
@ -204,6 +205,7 @@ func (i IntegrationForm) Merge(integration *model.Integration) {
integration.WebhookURL = i.WebhookURL
integration.RSSBridgeEnabled = i.RSSBridgeEnabled
integration.RSSBridgeURL = i.RSSBridgeURL
integration.RSSBridgeToken = i.RSSBridgeToken
integration.OmnivoreEnabled = i.OmnivoreEnabled
integration.OmnivoreAPIKey = i.OmnivoreAPIKey
integration.OmnivoreURL = i.OmnivoreURL
@ -318,6 +320,7 @@ func NewIntegrationForm(r *http.Request) *IntegrationForm {
WebhookURL: r.FormValue("webhook_url"),
RSSBridgeEnabled: r.FormValue("rssbridge_enabled") == "1",
RSSBridgeURL: r.FormValue("rssbridge_url"),
RSSBridgeToken: r.FormValue("rssbridge_token"),
OmnivoreEnabled: r.FormValue("omnivore_enabled") == "1",
OmnivoreAPIKey: r.FormValue("omnivore_api_key"),
OmnivoreURL: r.FormValue("omnivore_url"),

View file

@ -107,6 +107,7 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
WebhookSecret: integration.WebhookSecret,
RSSBridgeEnabled: integration.RSSBridgeEnabled,
RSSBridgeURL: integration.RSSBridgeURL,
RSSBridgeToken: integration.RSSBridgeToken,
OmnivoreEnabled: integration.OmnivoreEnabled,
OmnivoreAPIKey: integration.OmnivoreAPIKey,
OmnivoreURL: integration.OmnivoreURL,

View file

@ -53,8 +53,10 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
}
var rssBridgeURL string
var rssBridgeToken string
if intg, err := h.store.Integration(user.ID); err == nil && intg != nil && intg.RSSBridgeEnabled {
rssBridgeURL = intg.RSSBridgeURL
rssBridgeToken = intg.RSSBridgeToken
}
requestBuilder := fetcher.NewRequestBuilder()
@ -73,6 +75,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
subscriptions, localizedError := subscriptionFinder.FindSubscriptions(
subscriptionForm.URL,
rssBridgeURL,
rssBridgeToken,
)
if localizedError != nil {
v.Set("form", subscriptionForm)