diff --git a/AGENTS.md b/AGENTS.md index ea2e2c7d..16d702d8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,6 +67,21 @@ Preferred verification flow for docs/content changes: ## Docs Authoring Rules +### User Journey Funnel + +Maintain a directed "funnel" for documentation to maximize user success and conversion: + +1. **Phase 1: Quickstart (Local Demo)** — The primary entry point. Run `html2rss-web` with Docker and generate a feed from a page URL in minutes. +2. **Phase 2: Production (Deployment)** — The goal for invested users. Move to a stable, production-ready instance. +3. **Phase 3: Refinement (Custom Configs)** — Secondary optimization. Author custom YAML configs only when automatic generation needs precise control. + +**Rules for Funnel Maintenance:** + +- Avoid branching paths in introductory pages; always point toward the next phase in the funnel. +- Define "html2rss-web" as the primary interface and "page-to-RSS" as the primary workflow. +- Use "Feed Directory" consistently to refer to the pre-built feed catalog; avoid terms like "catalog", "included feeds", or "packaged configs" in user-facing docs. +- Do not introduce new terminology (e.g., "toolkit") or unrelated infrastructure concepts (e.g., "custom domains") unless they are essential to a specific guide. + ### Code Snippets In docs content (`src/content/docs/**`) and docs-supporting components: diff --git a/astro.config.mjs b/astro.config.mjs index 2cb7056f..b1775a3c 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -13,6 +13,19 @@ export default defineConfig({ "/components/html2rss": "/ruby-gem/", "/components/html2rss-configs": "/creating-custom-feeds/", "/components": "/", + "/web-application/how-to/deployment": "/web-application/deployment/", + "/web-application/how-to/automatic-updates": "/web-application/deployment/#auto-update-with-watchtower", + "/web-application/how-to/use-automatic-feed-generation": + "/web-application/guides/use-the-feed-directory/", + "/web-application/how-to/use-automatic-feed-generation/": + "/web-application/guides/use-the-feed-directory/", + "/web-application/how-to": "/web-application/guides/", + "/ruby-gem/how-to/dynamic-parameters": "/ruby-gem/guides/dynamic-parameters/", + "/ruby-gem/how-to/dynamic-parameters/": "/ruby-gem/guides/dynamic-parameters/", + "/ruby-gem/how-to": "/ruby-gem/guides/", + "/ruby-gem/tutorials": "/ruby-gem/guides/", + "/ruby-gem/tutorials/": "/ruby-gem/guides/", + "/web-application/guides/use-included-configs": "/web-application/guides/use-the-feed-directory/", }, build: { inlineStylesheets: "auto", @@ -249,7 +262,7 @@ export default defineConfig({ sidebar: [ { label: "Getting Started", - link: "/getting-started", + link: "/getting-started/", }, { label: "Feed Directory", @@ -257,7 +270,7 @@ export default defineConfig({ }, { label: "Create Custom Feeds", - link: "/creating-custom-feeds", + link: "/creating-custom-feeds/", }, { label: "Web Application", @@ -265,18 +278,15 @@ export default defineConfig({ items: [ "web-application", "web-application/getting-started", + "web-application/deployment", { - label: "How-to", - autogenerate: { directory: "web-application/how-to" }, + label: "Guides", + autogenerate: { directory: "web-application/guides" }, }, { label: "Reference", autogenerate: { directory: "web-application/reference" }, }, - { - label: "Tutorials", - autogenerate: { directory: "web-application/tutorials" }, - }, ], }, { @@ -286,22 +296,22 @@ export default defineConfig({ "ruby-gem", "ruby-gem/installation", { - label: "How-to", - autogenerate: { directory: "ruby-gem/how-to" }, - }, - { - label: "Reference", - autogenerate: { directory: "ruby-gem/reference" }, + label: "Guides", + autogenerate: { directory: "ruby-gem/guides" }, }, { label: "Tutorials", autogenerate: { directory: "ruby-gem/tutorials" }, }, + { + label: "Reference", + autogenerate: { directory: "ruby-gem/reference" }, + }, ], }, { label: "About", - link: "/about", + link: "/about/", }, { label: "Get Involved", diff --git a/examples/deployment/.env b/examples/deployment/.env index 62f8201d..2d62b809 100644 --- a/examples/deployment/.env +++ b/examples/deployment/.env @@ -1,26 +1,14 @@ -# Domain & routing -CADDY_HOST=example.com - -# Core runtime -RACK_ENV=production - -# Security +# Production secrets # Generate with: openssl rand -hex 32 HTML2RSS_SECRET_KEY=replace-with-64-hex-characters-generated-by-openssl-rand-hex-32 -# Authenticated health endpoint token -# Required by the documented Compose stack. -# If you build a custom stack and probe only /api/v1/health/live and /api/v1/health/ready, -# you can omit this value. -HEALTH_CHECK_TOKEN=replace-with-strong-health-token - -# Auto source (optional; keep false unless you need automatic feed generation) -AUTO_SOURCE_ENABLED=false +# Web UI / feed creation token +# Paste this into the web app when it asks for an access token. +HTML2RSS_ACCESS_TOKEN=replace-with-strong-access-token -# Observability (optional) -#SENTRY_DSN= +# Optional authenticated health token +# Set this only if you plan to use GET /api/v1/health instead of /api/v1/health/ready. +# HEALTH_CHECK_TOKEN=replace-with-strong-health-token -# Performance tuning (override defaults only when needed) -WEB_CONCURRENCY=2 -WEB_MAX_THREADS=5 -RACK_TIMEOUT_SERVICE_TIMEOUT=15 +# `AUTO_SOURCE_ENABLED=true` and `BOTASAURUS_SCRAPER_URL=http://botasaurus:4010` +# come from docker-compose.yml in this example. diff --git a/examples/deployment/docker-compose.yml b/examples/deployment/docker-compose.yml index 3774a619..3b796c3b 100644 --- a/examples/deployment/docker-compose.yml +++ b/examples/deployment/docker-compose.yml @@ -6,43 +6,13 @@ services: - path: .env required: false environment: + RACK_ENV: production PORT: 4000 + HTML2RSS_SECRET_KEY: ${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY} + HTML2RSS_ACCESS_TOKEN: ${HTML2RSS_ACCESS_TOKEN:?set HTML2RSS_ACCESS_TOKEN} + AUTO_SOURCE_ENABLED: "true" BOTASAURUS_SCRAPER_URL: http://botasaurus:4010 botasaurus: image: html2rss/botasaurus-scrape-api:latest restart: unless-stopped - - caddy: - image: caddy:2-alpine - depends_on: - - html2rss-web - command: - - caddy - - reverse-proxy - - --from - - ${CADDY_HOST} - - --to - - html2rss-web:4000 - ports: - - "80:80" - - "443:443" - volumes: - - caddy_data:/data - - watchtower: - image: containrrr/watchtower - depends_on: - - html2rss-web - - caddy - - botasaurus - command: - - --cleanup - - --interval - - "7200" - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - restart: unless-stopped - -volumes: - caddy_data: diff --git a/src/components/docs/AutoGenerationOptional.astro b/src/components/docs/AutoGenerationOptional.astro index 982682bb..225e558b 100644 --- a/src/components/docs/AutoGenerationOptional.astro +++ b/src/components/docs/AutoGenerationOptional.astro @@ -3,6 +3,7 @@ import { Aside } from "@astrojs/starlight/components"; --- diff --git a/src/components/docs/DockerComposeSnippet.astro b/src/components/docs/DockerComposeSnippet.astro index 0ced6c48..ee161f5c 100644 --- a/src/components/docs/DockerComposeSnippet.astro +++ b/src/components/docs/DockerComposeSnippet.astro @@ -1,6 +1,6 @@ --- import { Code } from "@astrojs/starlight/components"; -import { botasaurusImage, browserlessImage, caddyImage, watchtowerImage, webImage } from "../../data/docker"; +import { botasaurusImage, caddyImage, watchtowerImage, webImage } from "../../data/docker"; interface Props { variant: "minimal" | "productionCaddy" | "secure" | "watchtower" | "resourceGuardrails"; @@ -12,35 +12,15 @@ const snippets: Record = { minimal: `services: html2rss-web: image: ${webImage} - restart: unless-stopped ports: - "127.0.0.1:4000:4000" - env_file: - - path: .env - required: false environment: - RACK_ENV: production - PORT: 4000 - HTML2RSS_SECRET_KEY: \${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY} - HEALTH_CHECK_TOKEN: \${HEALTH_CHECK_TOKEN:?set HEALTH_CHECK_TOKEN} - SENTRY_DSN: \${SENTRY_DSN:-} - BROWSERLESS_IO_WEBSOCKET_URL: ws://browserless:4002 - BROWSERLESS_IO_API_TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN} + RACK_ENV: development + HTML2RSS_ACCESS_TOKEN: CHANGE_ME_ADMIN_TOKEN BOTASAURUS_SCRAPER_URL: http://botasaurus:4010 botasaurus: - image: ${botasaurusImage} - restart: unless-stopped - - browserless: - image: "${browserlessImage}" - restart: unless-stopped - ports: - - "127.0.0.1:4002:4002" - environment: - PORT: 4002 - CONCURRENT: 10 - TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN}`, + image: ${botasaurusImage}`, productionCaddy: `services: caddy: image: ${caddyImage} @@ -68,24 +48,15 @@ const snippets: Record = { RACK_ENV: production PORT: 4000 HTML2RSS_SECRET_KEY: \${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY} - HEALTH_CHECK_TOKEN: \${HEALTH_CHECK_TOKEN:?set HEALTH_CHECK_TOKEN} + HTML2RSS_ACCESS_TOKEN: \${HTML2RSS_ACCESS_TOKEN:?set HTML2RSS_ACCESS_TOKEN} + AUTO_SOURCE_ENABLED: "true" SENTRY_DSN: \${SENTRY_DSN:-} - BROWSERLESS_IO_WEBSOCKET_URL: ws://browserless:4002 - BROWSERLESS_IO_API_TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN} BOTASAURUS_SCRAPER_URL: http://botasaurus:4010 botasaurus: image: ${botasaurusImage} restart: unless-stopped - browserless: - image: "${browserlessImage}" - restart: unless-stopped - environment: - PORT: 4002 - CONCURRENT: 10 - TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN} - volumes: caddy_data:`, secure: `services: @@ -99,23 +70,14 @@ volumes: RACK_ENV: production PORT: 4000 HTML2RSS_SECRET_KEY: \${HTML2RSS_SECRET_KEY:?set HTML2RSS_SECRET_KEY} - HEALTH_CHECK_TOKEN: \${HEALTH_CHECK_TOKEN:?set HEALTH_CHECK_TOKEN} + HTML2RSS_ACCESS_TOKEN: \${HTML2RSS_ACCESS_TOKEN:?set HTML2RSS_ACCESS_TOKEN} + AUTO_SOURCE_ENABLED: "true" SENTRY_DSN: \${SENTRY_DSN:-} - BROWSERLESS_IO_WEBSOCKET_URL: ws://browserless:4002 - BROWSERLESS_IO_API_TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN} BOTASAURUS_SCRAPER_URL: http://botasaurus:4010 botasaurus: image: ${botasaurusImage} - restart: unless-stopped - - browserless: - image: "${browserlessImage}" - restart: unless-stopped - environment: - PORT: 4002 - CONCURRENT: 10 - TOKEN: \${BROWSERLESS_IO_API_TOKEN:?set BROWSERLESS_IO_API_TOKEN}`, + restart: unless-stopped`, watchtower: `services: watchtower: image: ${watchtowerImage} @@ -124,7 +86,7 @@ volumes: - /var/run/docker.sock:/var/run/docker.sock:ro # Optional for private registries only: # - "\${HOME}/.docker/config.json:/config.json:ro" - command: --cleanup --interval 7200 html2rss-web botasaurus browserless caddy`, + command: --cleanup --interval 7200 html2rss-web botasaurus caddy`, resourceGuardrails: `services: html2rss-web: image: ${webImage} diff --git a/src/content/docs/common-use-cases.mdx b/src/content/docs/common-use-cases.mdx index 4f58cc49..931b1a8a 100644 --- a/src/content/docs/common-use-cases.mdx +++ b/src/content/docs/common-use-cases.mdx @@ -91,6 +91,6 @@ Follow multiple open source projects and their updates. ## Next Steps -- **[Run html2rss-web with Docker](/web-application/getting-started)** to verify your own instance. -- **[Use automatic feed generation](/web-application/how-to/use-automatic-feed-generation/)** when you want direct page-URL conversion. +- **[Run html2rss-web with Docker](/web-application/getting-started/)** to verify your own instance. +- **[Use automatic feed generation](/web-application/guides/use-automatic-feed-generation/)** when you want direct page-URL conversion. - **[Create custom feeds](/creating-custom-feeds/)** when you need stable, reviewable extraction rules. diff --git a/src/content/docs/creating-custom-feeds.mdx b/src/content/docs/creating-custom-feeds.mdx index 0d782f8d..16382e65 100644 --- a/src/content/docs/creating-custom-feeds.mdx +++ b/src/content/docs/creating-custom-feeds.mdx @@ -9,7 +9,7 @@ import { Aside, Code } from "@astrojs/starlight/components"; When existing feeds or auto-sourcing are not enough, write a YAML config for the site you want to follow. -**Prerequisites:** You should be familiar with the [Getting Started](/getting-started) guide before diving into custom configurations. +**Prerequisites:** You should be familiar with the [Getting Started](/getting-started/) guide before diving into custom configurations.