Created
April 3, 2026 20:27
-
-
Save jghankins/9e820abbe8924e0dbade4edd4c1e057e to your computer and use it in GitHub Desktop.
Issue #192: RSS Feed Subscriptions for Community Categories
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # RSS Feed Subscriptions for Community Categories (#192) | |
| ## Overview | |
| Community categories can now auto-populate from RSS feeds. Categories marked as "Feed Only" serve as curated news feeds where members can comment, like, and share posts but cannot create new ones. Each category can subscribe to multiple RSS feeds, and an admin-chosen staff user is the "author" of imported posts. | |
| --- | |
| ## What's New | |
| ### Schema Changes | |
| **New tables:** | |
| - `rss_feeds` - stores feed subscriptions (URL, name, poster user, enabled flag, error tracking) | |
| - `rss_feed_items` - links imported RSS items to community posts via GUID for deduplication | |
| **Altered tables:** | |
| - `community_categories` - added `feed_only` (boolean, default false) | |
| - `rss_feeds` - added `last_error_notified_at` for notification throttling | |
| ### New Files | |
| | File | Purpose | | |
| |------|---------| | |
| | `lib/pewpros/community/rss_feed.ex` | RssFeed Ecto schema | | |
| | `lib/pewpros/community/rss_feed_item.ex` | RssFeedItem Ecto schema (dedup tracking) | | |
| | `lib/pewpros/community/rss_parser.ex` | RSS 2.0 + Atom feed parser (SweetXml) | | |
| | `lib/pewpros/workers/rss_feed_sync_worker.ex` | Oban worker - fetches & imports feed items | | |
| | `lib/pewpros_web/live/admin/community_live/rss_feeds.ex` | Admin LiveView for managing feeds | | |
| | `lib/pewpros_web/live/admin/community_live/rss_feeds.html.heex` | Admin feed management template | | |
| ### Modified Files | |
| | File | Change | | |
| |------|--------| | |
| | `lib/pewpros/community.ex` | RSS feed CRUD, `create_rss_post/2`, feed-only gating on `create_post` | | |
| | `lib/pewpros/community/category.ex` | Added `feed_only` field + `has_many :rss_feeds` | | |
| | `lib/pewpros/community/post.ex` | Added `has_one :rss_feed_item` for source attribution | | |
| | `lib/pewpros/notifications/notification.ex` | Added `rss_feed_error` notification type | | |
| | `lib/pewpros_web/community_serializer.ex` | Added `feed_only` to category JSON | | |
| | `lib/pewpros_web/controllers/api/v1/community_controller.ex` | 403 response for feed-only post creation | | |
| | `lib/pewpros_web/live/admin/community_live/categories.html.heex` | Type column + Feeds link | | |
| | `lib/pewpros_web/live/admin/community_live/category_form_component.ex` | Feed-only checkbox | | |
| | `lib/pewpros_web/live/community_live/feed.ex` | Track feed-only state, hide new post UI | | |
| | `lib/pewpros_web/live/community_live/feed.html.heex` | Hide "New Post" for feed-only, RSS source attribution | | |
| | `lib/pewpros_web/live/community_live/post_show.html.heex` | RSS source attribution on detail view | | |
| | `lib/pewpros_web/router.ex` | 3 new admin routes for feed management | | |
| | `config/config.exs` | Oban cron: `*/30 * * * *` for RssFeedSyncWorker | | |
| --- | |
| ## Features | |
| ### 1. Admin Feed Management (`/admin/community/categories/:id/feeds`) | |
| - Add/edit/delete RSS feeds per category | |
| - Select a staff user as the "poster" for imported items | |
| - Enable/disable individual feeds | |
| - **"Test Feed" button** - validates the URL is a live RSS/Atom feed before saving (shows item count + sample titles, or error message) | |
| - **"Fetch Now" button** - triggers an immediate Oban sync job for one feed | |
| - Status display: enabled/disabled badge, error count, last fetched timestamp | |
| ### 2. Automatic Feed Sync (Oban Cron) | |
| - Runs every 30 minutes via `RssFeedSyncWorker` | |
| - Fetches all enabled feeds, parses RSS 2.0 and Atom formats | |
| - Deduplicates by GUID per feed (no duplicate imports) | |
| - Creates community posts with title, description (stripped HTML), link, and images | |
| - No points or PubSub broadcast for RSS posts (they're automated, not user-generated) | |
| - Error tracking: increments `error_count`, stores `last_error` per feed | |
| ### 3. Dead Feed Notifications | |
| - After **3 consecutive failures**, notifies all admin/owner users via in-app notification | |
| - Notification type: `rss_feed_error` | |
| - **Throttled to once per 24 hours** per feed (via `last_error_notified_at`) | |
| - Resets automatically when the feed recovers | |
| ### 4. Feed-Only Categories | |
| - Categories marked `feed_only: true` block user post creation | |
| - Enforced at context level (`Community.create_post` returns `{:error, :feed_only_category}`) | |
| - Enforced in API (403 response) | |
| - Enforced in LiveView (hides "New Post" button, shows appropriate empty state) | |
| - Members can still comment, like, and share RSS posts | |
| ### 5. Source Attribution | |
| - RSS posts show "via {Feed Name}" next to the timestamp in both feed and detail views | |
| - Feed name links to the original article URL | |
| - Posts still show the chosen poster user as author | |
| --- | |
| ## RSS Parser Details | |
| - Supports **RSS 2.0** (`/rss/channel/item`) and **Atom** (`/feed/entry`) | |
| - Image extraction: checks `<enclosure>`, `<media:content>`, then first `<img>` in description | |
| - Date parsing: ISO 8601 (Atom) and RFC 2822 (RSS) | |
| - HTML stripping + entity decoding for clean text output | |
| - Descriptions capped at 2000 characters | |
| --- | |
| ## API Changes | |
| - `GET /api/v1/community/categories` - now includes `feed_only` boolean | |
| - `POST /api/v1/community/posts` - returns 403 if category is feed-only | |
| --- | |
| ## Testing | |
| - All 494 existing tests pass with 0 failures | |
| - Manual verification: imported 21 items from a live RSS feed, confirmed display with source attribution, confirmed feed-only gating |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment