diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index 62ba7be07..f2fa24d95 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -2790,6 +2790,7 @@ export enum SubscribeErrorCode { AlreadySubscribed = 'ALREADY_SUBSCRIBED', BadRequest = 'BAD_REQUEST', ExceededMaxSubscriptions = 'EXCEEDED_MAX_SUBSCRIPTIONS', + InvalidUrl = 'INVALID_URL', NotFound = 'NOT_FOUND', Unauthorized = 'UNAUTHORIZED' } diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index cbcdc1808..ac0db6c82 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -2190,6 +2190,7 @@ enum SubscribeErrorCode { ALREADY_SUBSCRIBED BAD_REQUEST EXCEEDED_MAX_SUBSCRIPTIONS + INVALID_URL NOT_FOUND UNAUTHORIZED } diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index 120dedc95..5ec3adc9f 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -1,6 +1,6 @@ import axios from 'axios' import { parseHTML } from 'linkedom' -import { Brackets } from 'typeorm' +import { Brackets, In } from 'typeorm' import { DEFAULT_SUBSCRIPTION_FOLDER, Subscription, @@ -193,9 +193,18 @@ export const subscribeResolver = authorized< }, }) + // validate rss feed + const feed = await parseFeed(input.url) + if (!feed) { + return { + errorCodes: [SubscribeErrorCode.NotFound], + } + } + const feedUrl = feed.url + // find existing subscription const existingSubscription = await getRepository(Subscription).findOneBy({ - url: input.url, + url: In([feedUrl, input.url]), // check both user provided url and parsed url user: { id: uid }, type: SubscriptionType.Rss, }) @@ -218,7 +227,7 @@ export const subscribeResolver = authorized< // create a cloud task to fetch rss feed item for resub subscription await enqueueRssFeedFetch({ userIds: [uid], - url: input.url, + url: feedUrl, subscriptionIds: [updatedSubscription.id], scheduledDates: [new Date()], // fetch immediately fetchedDates: [updatedSubscription.lastFetchedAt || null], @@ -232,16 +241,19 @@ export const subscribeResolver = authorized< } } - // create new rss subscription - const MAX_RSS_SUBSCRIPTIONS = env.subscription.feed.max - // validate rss feed - const feed = await parseFeed(input.url) - if (!feed) { + if (feedUrl !== input.url) { + log.info('feed url is different from user provided url', { + feedUrl, + inputUrl: input.url, + }) return { - errorCodes: [SubscribeErrorCode.NotFound], + errorCodes: [SubscribeErrorCode.InvalidUrl], } } + // create new rss subscription + const MAX_RSS_SUBSCRIPTIONS = env.subscription.feed.max + // limit number of rss subscriptions to max const results = (await getRepository(Subscription).query( `insert into omnivore.subscriptions (name, url, description, type, user_id, icon, is_private, fetch_content, folder) @@ -251,7 +263,7 @@ export const subscribeResolver = authorized< returning *;`, [ feed.title, - feed.url, + feedUrl, feed.description, SubscriptionType.Rss, uid, @@ -275,7 +287,7 @@ export const subscribeResolver = authorized< // create a cloud task to fetch rss feed item for the new subscription await enqueueRssFeedFetch({ userIds: [uid], - url: feed.url, + url: feedUrl, subscriptionIds: [newSubscription.id], scheduledDates: [new Date()], // fetch immediately fetchedDates: [null], diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index 4df9a5b35..ac1fdd125 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -1737,6 +1737,7 @@ const schema = gql` NOT_FOUND ALREADY_SUBSCRIBED EXCEEDED_MAX_SUBSCRIPTIONS + INVALID_URL } union AddPopularReadResult = AddPopularReadSuccess | AddPopularReadError