# johnwarne/upvote-rss architecture

> Architecture diagram of the GitHub repository johnwarne/upvote-rss, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-01). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/johnwarne/upvote-rss
- Repository: https://github.com/johnwarne/upvote-rss

## Overview

#### Purpose and Entry Points
Upvote RSS is a PHP 8.2+ service that turns popular posts from Reddit, Lemmy, Hacker News, Lobsters, PieFed, Mbin, and GitHub into enriched RSS feeds. `index.php` serves the feed-builder/preview UI; `app.php` is the likely request orchestration bootstrap; `ajax.php` supports UI-driven operations. Feed consumers request parameterized URLs and receive RSS through `views/rss.php`.

Paths: `index.php`, `app.php`, `ajax.php`, `views/rss.php`

#### Request and Presentation Layer
The browser UI collects platform, filtering, enrichment, and output options. Client JavaScript drives interactive controls and likely asynchronous preview/progress behavior. HTML previews and RSS are separate rendering targets, allowing the same assembled posts to be viewed in-browser or consumed by feed readers.

Paths: `views/html.php`, `js/script.js`, `progress-sse.php`

#### Platform Abstraction and Ingestion
`community.php` and `post.php` define the common domain boundary for source-specific implementations. Per-platform community adapters fetch listings/history and apply source semantics; post adapters normalize items, media, links, comments, and metadata into the shared feed model. Reddit authentication is isolated from community retrieval.

Paths: `classes/community.php`, `classes/post.php`, `classes/communities/reddit.php`, `classes/posts/github.php`, `classes/auth/reddit.php`

#### Filtering and Feed Assembly
The orchestration layer uses normalized posts to apply score, threshold, average-posts-per-day, age, NSFW, and pinned-comment options. `rss.php` represents the feed-generation boundary, combining selected/enriched posts into the RSS response. Query-string options make feed generation stateless from the consumer perspective, except for caches and authentication state.

Paths: `classes/rss.php`, `classes/parser.php`, `functions.php`

#### Article Enrichment Pipeline
When requested, external article URLs are fetched and converted into clean article content plus featured media. The parser abstraction selects built-in Readability.php or configured Readability.js/Mercury services; Browserless can sit upstream for JavaScript-dependent pages. Enriched webpage results are cacheable and become input to summaries and RSS rendering.

Paths: `classes/webpage-analyzer.php`, `classes/parsers/readability-php.php`, `classes/parsers/readability-js.php`, `classes/parsers/mercury.php`

#### AI Summarization Boundary
A factory chooses from configured LLM providers and supports provider fallback. The summarizer interface separates article-content preparation from vendor HTTP/API implementations. Providers include local Ollama and hosted OpenAI, Gemini, Anthropic, Mistral, DeepSeek, and generic OpenAI-compatible endpoints.

Paths: `classes/summarizer-factory.php`, `classes/summarizer.php`, `classes/summarizers/ollama.php`, `classes/summarizers/openai-compatible.php`

#### Runtime, Caching, and Operations
The primary runtime is PHP, packaged as a Docker image served by Caddy. Caching abstracts filesystem defaults with optional Redis; APCu is used where available for fast local state such as auth/progress. Docker entrypoint configuration handles container startup and mounted `/app/cache` persistence. External dependencies include source platform APIs, Reddit OAuth, parser services, Browserless, Redis, and LLM APIs.

Paths: `classes/cache.php`, `docker/Dockerfile`, `docker/Caddyfile`, `docker/entrypoint.sh`

Main entry points: [`classes/rss.php`](https://github.com/johnwarne/upvote-rss/blob/main/classes/rss.php) (Feed assembly), [`app.php`](https://github.com/johnwarne/upvote-rss/blob/main/app.php) (Request bootstrap), [`classes/webpage-analyzer.php`](https://github.com/johnwarne/upvote-rss/blob/main/classes/webpage-analyzer.php) (Webpage analyzer), [`js/script.js`](https://github.com/johnwarne/upvote-rss/blob/main/js/script.js) (Interactive controls)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_entry["Request &amp; Rendering"]
  node_ui["Feed builder UI<br/>PHP view<br/>[index.php]"]
  node_client_js["Interactive controls<br/>browser JS<br/>[script.js]"]
  node_bootstrap["Request bootstrap<br/>PHP orchestration<br/>[app.php]"]
  node_ajax["AJAX operations<br/>PHP endpoint<br/>[ajax.php]"]
  node_progress["Progress stream<br/>SSE endpoint<br/>[progress-sse.php]"]
  node_html_view["HTML preview<br/>PHP view<br/>[html.php]"]
  node_rss_view["RSS output<br/>PHP view<br/>[rss.php]"]
end

subgraph group_feed["Feed Core"]
  node_feed_assembly["Feed assembly<br/>RSS service<br/>[rss.php]"]
  node_parser["Option parsing<br/>request parser<br/>[parser.php]"]
  node_shared_functions["Shared functions<br/>PHP utilities<br/>[functions.php]"]
end

subgraph group_sources["Source Ingestion"]
  node_domain["Community &amp; post model<br/>domain abstraction<br/>[community.php]"]
  node_reddit_community["Reddit community adapter<br/>source adapter<br/>[reddit.php]"]
  node_github_posts["GitHub post adapter<br/>source adapter<br/>[github.php]"]
  node_reddit_auth["Reddit OAuth<br/>authentication<br/>[reddit.php]"]
end

subgraph group_enrichment["Enrichment &amp; AI"]
  node_web_analyzer["Webpage analyzer<br/>enrichment pipeline"]
  node_content_parsers["Article parsers<br/>parser adapters"]
  node_summarizer_factory["Summarizer selection<br/>provider factory"]
  node_summarizer["Summarizer interface<br/>AI abstraction<br/>[summarizer.php]"]
  node_ollama["Ollama provider<br/>LLM adapter<br/>[ollama.php]"]
  node_openai_compatible["OpenAI-compatible provider<br/>LLM adapter"]
end

subgraph group_runtime["Runtime &amp; Operations"]
  node_cache[("Cache layer<br/>cache abstraction<br/>[cache.php]")]
  node_container{{"PHP/Caddy container<br/>Docker runtime"}}
end

node_ui -->|"enhanced by"| node_client_js
node_client_js -->|"async requests"| node_ajax
node_client_js -->|"progress updates"| node_progress
node_ui -->|"feed request"| node_bootstrap
node_ajax -->|"operations"| node_bootstrap
node_bootstrap -->|"parses options"| node_parser
node_parser -->|"filter settings"| node_feed_assembly
node_bootstrap -->|"builds feed"| node_feed_assembly
node_feed_assembly -->|"preview posts"| node_html_view
node_feed_assembly -->|"RSS posts"| node_rss_view
node_feed_assembly -->|"loads normalized posts"| node_domain
node_reddit_community -->|"implements"| node_domain
node_github_posts -->|"implements"| node_domain
node_reddit_community -->|"authenticated access"| node_reddit_auth
node_feed_assembly -->|"enriches articles"| node_web_analyzer
node_web_analyzer -->|"extracts content"| node_content_parsers
node_web_analyzer -->|"article content"| node_summarizer_factory
node_summarizer_factory -->|"selects"| node_summarizer
node_summarizer -->|"provider"| node_ollama
node_summarizer -->|"provider"| node_openai_compatible
node_feed_assembly -->|"cached results"| node_cache
node_web_analyzer -->|"cached webpages"| node_cache
node_container -->|"serves"| node_bootstrap

click node_ui "https://github.com/johnwarne/upvote-rss/blob/main/index.php"
click node_client_js "https://github.com/johnwarne/upvote-rss/blob/main/js/script.js"
click node_bootstrap "https://github.com/johnwarne/upvote-rss/blob/main/app.php"
click node_ajax "https://github.com/johnwarne/upvote-rss/blob/main/ajax.php"
click node_progress "https://github.com/johnwarne/upvote-rss/blob/main/progress-sse.php"
click node_html_view "https://github.com/johnwarne/upvote-rss/blob/main/views/html.php"
click node_rss_view "https://github.com/johnwarne/upvote-rss/blob/main/views/rss.php"
click node_feed_assembly "https://github.com/johnwarne/upvote-rss/blob/main/classes/rss.php"
click node_parser "https://github.com/johnwarne/upvote-rss/blob/main/classes/parser.php"
click node_shared_functions "https://github.com/johnwarne/upvote-rss/blob/main/functions.php"
click node_domain "https://github.com/johnwarne/upvote-rss/blob/main/classes/community.php"
click node_reddit_community "https://github.com/johnwarne/upvote-rss/blob/main/classes/communities/reddit.php"
click node_github_posts "https://github.com/johnwarne/upvote-rss/blob/main/classes/posts/github.php"
click node_reddit_auth "https://github.com/johnwarne/upvote-rss/blob/main/classes/auth/reddit.php"
click node_web_analyzer "https://github.com/johnwarne/upvote-rss/blob/main/classes/webpage-analyzer.php"
click node_content_parsers "https://github.com/johnwarne/upvote-rss/blob/main/classes/parsers/readability-php.php"
click node_summarizer_factory "https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizer-factory.php"
click node_summarizer "https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizer.php"
click node_ollama "https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizers/ollama.php"
click node_openai_compatible "https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizers/openai-compatible.php"
click node_cache "https://github.com/johnwarne/upvote-rss/blob/main/classes/cache.php"
click node_container "https://github.com/johnwarne/upvote-rss/blob/main/docker/Dockerfile"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_ui,node_client_js,node_bootstrap,node_ajax,node_progress,node_html_view,node_rss_view toneBlue
class node_feed_assembly,node_parser,node_shared_functions toneAmber
class node_domain,node_reddit_community,node_github_posts,node_reddit_auth toneMint
class node_web_analyzer,node_content_parsers,node_summarizer_factory,node_summarizer,node_ollama,node_openai_compatible toneRose
class node_cache,node_container toneIndigo
```

## Components

### Request & Rendering

- [Feed builder UI](https://github.com/johnwarne/upvote-rss/blob/main/index.php): PHP view · Collects feed and enrichment options. · `index.php`
- [Interactive controls](https://github.com/johnwarne/upvote-rss/blob/main/js/script.js): browser JS · `js/script.js`
- [Request bootstrap](https://github.com/johnwarne/upvote-rss/blob/main/app.php): PHP orchestration · `app.php`
- [AJAX operations](https://github.com/johnwarne/upvote-rss/blob/main/ajax.php): PHP endpoint · `ajax.php`
- [Progress stream](https://github.com/johnwarne/upvote-rss/blob/main/progress-sse.php): SSE endpoint · `progress-sse.php`
- [HTML preview](https://github.com/johnwarne/upvote-rss/blob/main/views/html.php): PHP view · `views/html.php`
- [RSS output](https://github.com/johnwarne/upvote-rss/blob/main/views/rss.php): PHP view · `views/rss.php`

### Feed Core

- [Feed assembly](https://github.com/johnwarne/upvote-rss/blob/main/classes/rss.php): RSS service · Applies request filters and produces selected feed posts. · `classes/rss.php`
- [Option parsing](https://github.com/johnwarne/upvote-rss/blob/main/classes/parser.php): request parser · `classes/parser.php`
- [Shared functions](https://github.com/johnwarne/upvote-rss/blob/main/functions.php): PHP utilities · `functions.php`

### Source Ingestion

- [Community & post model](https://github.com/johnwarne/upvote-rss/blob/main/classes/community.php): domain abstraction · Shared boundary for platform listings and normalized posts. · `classes/community.php`
- [Reddit community adapter](https://github.com/johnwarne/upvote-rss/blob/main/classes/communities/reddit.php): source adapter · `classes/communities/reddit.php`
- [GitHub post adapter](https://github.com/johnwarne/upvote-rss/blob/main/classes/posts/github.php): source adapter · Represents one of the source-specific normalizers. · `classes/posts/github.php`
- [Reddit OAuth](https://github.com/johnwarne/upvote-rss/blob/main/classes/auth/reddit.php): authentication · `classes/auth/reddit.php`

### Enrichment & AI

- [Webpage analyzer](https://github.com/johnwarne/upvote-rss/blob/main/classes/webpage-analyzer.php): enrichment pipeline · Fetches articles and extracts clean content and featured media. · `classes/webpage-analyzer.php`
- [Article parsers](https://github.com/johnwarne/upvote-rss/blob/main/classes/parsers/readability-php.php): parser adapters · Selects Readability PHP, Readability JS, or Mercury extraction. · `classes/parsers/readability-php.php`
- [Summarizer selection](https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizer-factory.php): provider factory · Chooses configured LLM provider and fallback. · `classes/summarizer-factory.php`
- [Summarizer interface](https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizer.php): AI abstraction · `classes/summarizer.php`
- [Ollama provider](https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizers/ollama.php): LLM adapter · `classes/summarizers/ollama.php`
- [OpenAI-compatible provider](https://github.com/johnwarne/upvote-rss/blob/main/classes/summarizers/openai-compatible.php): LLM adapter · Covers compatible hosted LLM endpoints. · `classes/summarizers/openai-compatible.php`

### Runtime & Operations

- [Cache layer](https://github.com/johnwarne/upvote-rss/blob/main/classes/cache.php): cache abstraction · Uses filesystem by default with optional Redis and local fast state. · `classes/cache.php`
- [PHP/Caddy container](https://github.com/johnwarne/upvote-rss/blob/main/docker/Dockerfile): Docker runtime · Serves the PHP application and persists mounted cache data. · `docker/Dockerfile`

## Connections

- Feed builder UI → Interactive controls: enhanced by
- Interactive controls → AJAX operations: async requests
- Interactive controls → Progress stream: progress updates
- Feed builder UI → Request bootstrap: feed request
- AJAX operations → Request bootstrap: operations
- Request bootstrap → Option parsing: parses options
- Option parsing → Feed assembly: filter settings
- Request bootstrap → Feed assembly: builds feed
- Feed assembly → HTML preview: preview posts
- Feed assembly → RSS output: RSS posts
- Feed assembly → Community & post model: loads normalized posts
- Reddit community adapter → Community & post model: implements
- GitHub post adapter → Community & post model: implements
- Reddit community adapter → Reddit OAuth: authenticated access
- Feed assembly → Webpage analyzer: enriches articles
- Webpage analyzer → Article parsers: extracts content
- Webpage analyzer → Summarizer selection: article content
- Summarizer selection → Summarizer interface: selects
- Summarizer interface → Ollama provider: provider
- Summarizer interface → OpenAI-compatible provider: provider
- Feed assembly → Cache layer: cached results
- Webpage analyzer → Cache layer: cached webpages
- PHP/Caddy container → Request bootstrap: serves
