|
|
@@ -9,6 +9,7 @@ import (
|
|
|
|
|
|
"miniflux.app/v2/internal/model"
|
|
|
"miniflux.app/v2/internal/storage"
|
|
|
+ "miniflux.app/v2/internal/validator"
|
|
|
)
|
|
|
|
|
|
// Handler handles the logic for OPML import/export.
|
|
|
@@ -31,6 +32,24 @@ func (h *Handler) Export(userID int64) (string, error) {
|
|
|
SiteURL: feed.SiteURL,
|
|
|
Description: feed.Description,
|
|
|
CategoryName: feed.Category.Title,
|
|
|
+
|
|
|
+ ScraperRules: feed.ScraperRules,
|
|
|
+ RewriteRules: feed.RewriteRules,
|
|
|
+ UrlRewriteRules: feed.UrlRewriteRules,
|
|
|
+ BlocklistRules: feed.BlocklistRules,
|
|
|
+ KeeplistRules: feed.KeeplistRules,
|
|
|
+ BlockFilterEntryRules: feed.BlockFilterEntryRules,
|
|
|
+ KeepFilterEntryRules: feed.KeepFilterEntryRules,
|
|
|
+ UserAgent: feed.UserAgent,
|
|
|
+ Crawler: feed.Crawler,
|
|
|
+ IgnoreHTTPCache: feed.IgnoreHTTPCache,
|
|
|
+ FetchViaProxy: feed.FetchViaProxy,
|
|
|
+ Disabled: feed.Disabled,
|
|
|
+ NoMediaPlayer: feed.NoMediaPlayer,
|
|
|
+ HideGlobally: feed.HideGlobally,
|
|
|
+ AllowSelfSignedCertificates: feed.AllowSelfSignedCertificates,
|
|
|
+ DisableHTTP2: feed.DisableHTTP2,
|
|
|
+ IgnoreEntryUpdates: feed.IgnoreEntryUpdates,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -45,42 +64,105 @@ func (h *Handler) Import(userID int64, data io.Reader) error {
|
|
|
}
|
|
|
|
|
|
for _, subscription := range subscriptions {
|
|
|
- if !h.store.FeedURLExists(userID, subscription.FeedURL) {
|
|
|
- var category *model.Category
|
|
|
- var err error
|
|
|
-
|
|
|
- if subscription.CategoryName == "" {
|
|
|
- category, err = h.store.FirstCategory(userID)
|
|
|
- if err != nil {
|
|
|
- return fmt.Errorf("opml: unable to find first category: %w", err)
|
|
|
- }
|
|
|
- } else {
|
|
|
- category, err = h.store.CategoryByTitle(userID, subscription.CategoryName)
|
|
|
- if err != nil {
|
|
|
- return fmt.Errorf("opml: unable to search category by title: %w", err)
|
|
|
- }
|
|
|
-
|
|
|
- if category == nil {
|
|
|
- category, err = h.store.CreateCategory(userID, &model.CategoryCreationRequest{Title: subscription.CategoryName})
|
|
|
- if err != nil {
|
|
|
- return fmt.Errorf(`opml: unable to create this category: %q`, subscription.CategoryName)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- feed := &model.Feed{
|
|
|
- UserID: userID,
|
|
|
- Title: subscription.Title,
|
|
|
- FeedURL: subscription.FeedURL,
|
|
|
- SiteURL: subscription.SiteURL,
|
|
|
- Description: subscription.Description,
|
|
|
- Category: category,
|
|
|
- }
|
|
|
-
|
|
|
- if err := h.store.CreateFeed(feed); err != nil {
|
|
|
- return fmt.Errorf(`opml: unable to create this feed: %q`, subscription.FeedURL)
|
|
|
- }
|
|
|
+ if h.store.FeedURLExists(userID, subscription.FeedURL) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ category, err := h.resolveCategory(userID, subscription.CategoryName)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if validationErr := validateSubscription(userID, category.ID, h.store, subscription); validationErr != nil {
|
|
|
+ return fmt.Errorf(`opml: invalid feed settings for %q: %w`, subscription.FeedURL, validationErr)
|
|
|
+ }
|
|
|
+
|
|
|
+ feed := &model.Feed{
|
|
|
+ UserID: userID,
|
|
|
+ Title: subscription.Title,
|
|
|
+ FeedURL: subscription.FeedURL,
|
|
|
+ SiteURL: subscription.SiteURL,
|
|
|
+ Description: subscription.Description,
|
|
|
+ Category: category,
|
|
|
+ }
|
|
|
+ applySubscriptionSettings(feed, subscription)
|
|
|
+ if err := h.store.CreateFeed(feed); err != nil {
|
|
|
+ return fmt.Errorf(`opml: unable to create this feed: %q`, subscription.FeedURL)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (h *Handler) resolveCategory(userID int64, categoryName string) (*model.Category, error) {
|
|
|
+ if categoryName == "" {
|
|
|
+ category, err := h.store.FirstCategory(userID)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("opml: unable to find first category: %w", err)
|
|
|
}
|
|
|
+ return category, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ category, err := h.store.CategoryByTitle(userID, categoryName)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("opml: unable to search category by title: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if category == nil {
|
|
|
+ category, err = h.store.CreateCategory(userID, &model.CategoryCreationRequest{Title: categoryName})
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf(`opml: unable to create this category: %q`, categoryName)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return category, nil
|
|
|
+}
|
|
|
+
|
|
|
+func applySubscriptionSettings(feed *model.Feed, s subcription) {
|
|
|
+ feed.ScraperRules = s.ScraperRules
|
|
|
+ feed.RewriteRules = s.RewriteRules
|
|
|
+ feed.UrlRewriteRules = s.UrlRewriteRules
|
|
|
+ feed.BlocklistRules = s.BlocklistRules
|
|
|
+ feed.KeeplistRules = s.KeeplistRules
|
|
|
+ feed.BlockFilterEntryRules = s.BlockFilterEntryRules
|
|
|
+ feed.KeepFilterEntryRules = s.KeepFilterEntryRules
|
|
|
+ feed.UserAgent = s.UserAgent
|
|
|
+ feed.Crawler = s.Crawler
|
|
|
+ feed.IgnoreHTTPCache = s.IgnoreHTTPCache
|
|
|
+ feed.FetchViaProxy = s.FetchViaProxy
|
|
|
+ feed.Disabled = s.Disabled
|
|
|
+ feed.NoMediaPlayer = s.NoMediaPlayer
|
|
|
+ feed.HideGlobally = s.HideGlobally
|
|
|
+ feed.AllowSelfSignedCertificates = s.AllowSelfSignedCertificates
|
|
|
+ feed.DisableHTTP2 = s.DisableHTTP2
|
|
|
+ feed.IgnoreEntryUpdates = s.IgnoreEntryUpdates
|
|
|
+}
|
|
|
+
|
|
|
+func validateSubscription(userID, categoryID int64, store *storage.Storage, s subcription) error {
|
|
|
+ feedCreationRequest := &model.FeedCreationRequest{
|
|
|
+ FeedURL: s.FeedURL,
|
|
|
+ CategoryID: categoryID,
|
|
|
+ UserAgent: s.UserAgent,
|
|
|
+ Crawler: s.Crawler,
|
|
|
+ IgnoreEntryUpdates: s.IgnoreEntryUpdates,
|
|
|
+ Disabled: s.Disabled,
|
|
|
+ NoMediaPlayer: s.NoMediaPlayer,
|
|
|
+ IgnoreHTTPCache: s.IgnoreHTTPCache,
|
|
|
+ AllowSelfSignedCertificates: s.AllowSelfSignedCertificates,
|
|
|
+ FetchViaProxy: s.FetchViaProxy,
|
|
|
+ HideGlobally: s.HideGlobally,
|
|
|
+ DisableHTTP2: s.DisableHTTP2,
|
|
|
+ ScraperRules: s.ScraperRules,
|
|
|
+ RewriteRules: s.RewriteRules,
|
|
|
+ BlocklistRules: s.BlocklistRules,
|
|
|
+ KeeplistRules: s.KeeplistRules,
|
|
|
+ BlockFilterEntryRules: s.BlockFilterEntryRules,
|
|
|
+ KeepFilterEntryRules: s.KeepFilterEntryRules,
|
|
|
+ UrlRewriteRules: s.UrlRewriteRules,
|
|
|
+ }
|
|
|
+
|
|
|
+ if validationErr := validator.ValidateFeedCreation(store, userID, feedCreationRequest); validationErr != nil {
|
|
|
+ return validationErr.Error()
|
|
|
}
|
|
|
|
|
|
return nil
|