Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
f46ccfa
feat: astro frontend
gildesmarais Sep 9, 2025
e7cf89f
integrate astro
gildesmarais Sep 9, 2025
dbb68c5
auto reload
gildesmarais Sep 9, 2025
2bf5773
feat: enhance development experience with live reload and new Ruby se…
gildesmarais Sep 14, 2025
92a065b
feat: enhance auto source functionality with authentication and usage…
gildesmarais Sep 14, 2025
0d4965c
refactor: remove outdated test scripts and clean up package dependencies
gildesmarais Sep 15, 2025
edf4072
autosource views in astro
gildesmarais Sep 18, 2025
870327c
clean roda app
gildesmarais Sep 18, 2025
cae7703
unify make lint & ~fix
gildesmarais Sep 18, 2025
632b40f
fix ctrl+c make dev
gildesmarais Sep 18, 2025
47b982b
make lintfix
gildesmarais Sep 18, 2025
6ba5790
integrate astro-starlight
gildesmarais Sep 18, 2025
270bdb7
add api to return available request strategies
gildesmarais Sep 18, 2025
7162569
slim frontend, add ux-friendly autosource auth using hmac + docs
gildesmarais Sep 18, 2025
d031291
improve auto_source ux
gildesmarais Sep 18, 2025
80d6726
style and ux
gildesmarais Sep 18, 2025
caa892d
refactor: roda app structure / routes
gildesmarais Sep 19, 2025
3bfa3ac
tool-versions it is
gildesmarais Sep 19, 2025
85683fb
ruby clean
gildesmarais Sep 19, 2025
08b3d4d
refactor: enhance routing and response handling; improve frontend vis…
gildesmarais Sep 19, 2025
3dec638
simplecov coverage
gildesmarais Sep 19, 2025
7f7a39e
csp + error
gildesmarais Sep 19, 2025
02ac635
rack attack
gildesmarais Sep 20, 2025
df5600b
log for rack-attack
gildesmarais Sep 20, 2025
35a5798
roda structure
gildesmarais Sep 20, 2025
175d504
clean unreloader setup
gildesmarais Sep 20, 2025
cd47adf
preact
gildesmarais Sep 23, 2025
1bb7df4
api/v1, legacy clean, token in url path and zlib'ed
gildesmarais Sep 24, 2025
d6123a3
Remove obsolete specs for Html2rss::Web components including AutoSour…
gildesmarais Sep 25, 2025
886c754
Refactor auto source configuration and improve request handling
gildesmarais Sep 25, 2025
f003084
unify ci workflows
gildesmarais Sep 26, 2025
4061876
docs
gildesmarais Sep 27, 2025
405eec5
css
gildesmarais Sep 27, 2025
d7c24e6
ask api/v1/strategies
gildesmarais Sep 27, 2025
170dac4
Remove rack-attack middleware
gildesmarais Jan 25, 2026
ed109c5
Bind feed strategy in tokens
gildesmarais Jan 25, 2026
44b7866
Surface UI auth and preview errors
gildesmarais Jan 25, 2026
4b2b917
Tighten README
gildesmarais Jan 25, 2026
c510adb
ci: fix double runs
gildesmarais Jan 25, 2026
805eb31
style: prettier
gildesmarais Jan 25, 2026
50d2f7f
Document devcontainer workflow
gildesmarais Jan 25, 2026
34ead2c
Add make ready pre-commit gate
gildesmarais Jan 25, 2026
d13773c
Refactor spec doubles for Html2rss feeds
gildesmarais Jan 25, 2026
cc3c9d4
Fix devcontainer up workflow
gildesmarais Jan 25, 2026
524edd3
Align dev ports to 4000-range
gildesmarais Jan 25, 2026
0c2f47e
Clarify agent collaboration rules
gildesmarais Jan 25, 2026
60eb9d2
Fix legacy feed lookup and cache TTL
gildesmarais Jan 25, 2026
34d9ef2
style: agents relax
gildesmarais Jan 25, 2026
9441698
fix(api): add explicit readiness and liveness health routes
gildesmarais Feb 28, 2026
aa22c5e
fix(cache): coerce feed ttl before computing max-age
gildesmarais Feb 28, 2026
3b3371c
fix(errors): sanitize unexpected 500 responses for API clients
gildesmarais Feb 28, 2026
3769137
fix(frontend-tests): migrate shared msw handlers to v2 api
gildesmarais Feb 28, 2026
523fc72
chore(ops): align default service port to 4000 across runtime and docs
gildesmarais Feb 28, 2026
4283318
fix(smoke): make docker probes runnable in real container environments
gildesmarais Feb 28, 2026
ce7f35d
refactor(errors): centralize api/xml exception rendering policy
gildesmarais Feb 28, 2026
03f2c75
refactor(env): centralize auto-source flag policy in environment vali…
gildesmarais Feb 28, 2026
effcae0
fix(auth): enforce 401-before-403 semantics on feed endpoints
gildesmarais Feb 28, 2026
ef5d8ff
refactor(smoke): split feed probe checks by auth and feature state
gildesmarais Feb 28, 2026
859897b
refactor(cache): share ttl parsing across legacy and api feed paths
gildesmarais Feb 28, 2026
e323d26
refactor(routes): simplify api v1 json rendering flow
gildesmarais Feb 28, 2026
72cf4dd
fix(smoke): make auto-source mode explicit via smoke env flag
gildesmarais Feb 28, 2026
fa91fd2
refactor(api): centralize core error codes and messages for v1 contract
gildesmarais Feb 28, 2026
c9ec339
test(api): share health and feeds error-contract request examples
gildesmarais Feb 28, 2026
93909e0
ci(smoke): run docker smoke matrix for auto-source enabled and disabled
gildesmarais Feb 28, 2026
d774dec
refactor(api-feeds): split create/show flows into command modules
gildesmarais Feb 28, 2026
f6a54f2
refactor(accounts): remove mutable cache and keep immutable per-call …
gildesmarais Feb 28, 2026
9f7e7ee
refactor(core): restore account snapshot cache and align v2 runtime d…
gildesmarais Feb 28, 2026
81b4740
docs(migration): expand v2 guide to full master-to-v2 rewrite coverage
gildesmarais Feb 28, 2026
358a903
refactor(api): harden v1 responses and generate OpenAPI from request …
gildesmarais Feb 28, 2026
3b01a79
chore(devx): expose and document OpenAPI regeneration workflow
gildesmarais Feb 28, 2026
2cf7d22
feat(api): publish OpenAPI spec endpoint
gildesmarais Feb 28, 2026
276f97b
fix(dev): make startup checks container-safe
gildesmarais Feb 28, 2026
bc46838
fix(review): address PR 775 security and config comments
gildesmarais Feb 28, 2026
7f26f4d
feat(frontend): unify app states under one form-first UX language
gildesmarais Feb 28, 2026
69008bc
refactor(frontend): simplify signed-in hierarchy and demote bookmarklet
gildesmarais Mar 1, 2026
db2eaf4
fix(dev): expose and stabilize Astro dev server
gildesmarais Mar 1, 2026
8ac0680
docs(api): add essential YARD docs for v1 boundaries
gildesmarais Mar 1, 2026
c475120
docs(yard): enforce typed public method docs
gildesmarais Mar 1, 2026
5d4498a
feat(frontend): streamline convert and result screens
gildesmarais Mar 1, 2026
3f1edcb
fix(ui): clarify result affordances and strategy names
gildesmarais Mar 1, 2026
cd49f5b
docs(architecture): ratify phased delivery plan with validated assump…
gildesmarais Mar 1, 2026
cc61b7f
test(e2e): add headless frontend smoke verification in dev container
gildesmarais Mar 1, 2026
eceb8fe
docs(adr): codify architecture rollout decisions for full delivery
gildesmarais Mar 1, 2026
d9ebff6
refactor(runtime): adopt typed config, request context, and async-cap…
gildesmarais Mar 1, 2026
b349ade
feat(frontend-api): enforce OpenAPI-generated client contract flow
gildesmarais Mar 1, 2026
61cf0b2
docs(architecture): define revamp governance contracts
gildesmarais Mar 1, 2026
2df75c5
refactor(core): introduce AppContext with centralized flags wiring
gildesmarais Mar 1, 2026
25c531d
feat(observability): emit structured events on critical request paths
gildesmarais Mar 1, 2026
750bece
feat(api-contract): enforce OpenAPI lint and client drift in CI
gildesmarais Mar 1, 2026
9e6955b
refactor(frontend): unify panel layout and purge dead CSS
gildesmarais Mar 1, 2026
d180d14
fix(frontend): expose strategy selection reliably in member view
gildesmarais Mar 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FROM ruby:3.4.6-alpine3.21

SHELL ["/bin/sh", "-o", "pipefail", "-c"]

RUN apk add --no-cache \
bash \
build-base \
curl \
git \
libxml2-dev \
libxslt-dev \
nodejs \
npm \
openssl-dev \
python3 \
tzdata

ARG USER=vscode
ARG UID=1000
ARG GID=1000

RUN addgroup -g "$GID" "$USER" \
&& adduser -D -G "$USER" -u "$UID" "$USER"

WORKDIR /workspace
RUN chown -R "$USER":"$USER" /workspace

ENV BUNDLE_PATH=/usr/local/bundle

USER "$USER"

EXPOSE 4000

CMD ["sleep", "infinity"]
49 changes: 49 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Dev Container Workflow

This repository ships a single Dev Container for development. Open the project in VS Code (Dev
Containers extension) or GitHub Codespaces and use that environment for all work.

## What gets created

The devcontainer starts one service named `app` and exposes:

- **Port 4000:** Ruby app
- **Port 4001:** Astro dev server

The repo is mounted at `/workspace`. Bundler gems are cached in a Docker volume to speed up
future launches.

## Bootstrap

On first open, the Dev Container runs:

```
make setup
```

This installs Ruby and frontend dependencies inside the container.

If setup fails due to missing network access (e.g., GitHub DNS), rerun `make setup` once
network access is available.

## Common commands (run inside the container)

```
make dev # Ruby + Astro
make dev-ruby # Ruby only
make dev-frontend # Astro only
make test # Ruby + frontend tests
make ready # RuboCop + RSpec (pre-commit gate)
```

## Lint and tests

```
bundle exec rubocop -F
bundle exec rspec
```

## Notes

- All commands are expected to run inside the Dev Container.
- The default service command is `sleep infinity`; use the Make targets above to start servers.
50 changes: 26 additions & 24 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
{
"name": "html2rss-web",
"image": "mcr.microsoft.com/devcontainers/ruby:3.4",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"shutdownAction": "stopCompose",
"customizations": {
"vscode": {
"extensions": [
"redhat.vscode-yaml",
"esbenp.prettier-vscode",
"github.copilot",
"github.copilot-chat",
"shopify.ruby-lsp"
"rebornix.ruby",
"astro-build.astro-vscode",
"esbenp.prettier-vscode"
],
"settings": {
"ruby.rubocop.executePath": "bundle exec",
"ruby.format": "rubocop",
"ruby.lint": {
"rubocop": true
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.associations": {
"*.erb": "erb"
}
"*.astro": "astro"
},
"prettier.configPath": "./frontend/prettier.config.js",
"ruby.format": "rubocop",
"ruby.lint": { "rubocop": true },
"editor.tabSize": 2,
"editor.insertSpaces": true,
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}
}
},
"postCreateCommand": "make setup",
"postStartCommand": "echo '🚀 html2rss-web Development Environment Ready!' && echo '' && echo '📋 Quick Start Commands:' && echo ' make dev # Start development server' && echo ' make test # Run tests' && echo ' make lint # Run linter' && echo ' make fix # Auto-fix linting issues' && echo ' make help # Show all commands' && echo '' && echo '🌐 Server will be available at: http://localhost:3000' && echo '📁 Project files are in: /workspaces/html2rss-web' && echo '' && echo '💡 Tip: Use Ctrl+C to stop the development server' && echo ''",
"forwardPorts": [
3000
],
"forwardPorts": [4000, 4001],
"portsAttributes": {
"3000": {
"label": "html2rss-web",
"4000": {
"label": "Ruby App",
"onAutoForward": "notify"
},
"4001": {
"label": "Astro Dev Server",
"onAutoForward": "silent"
}
},
"postCreateCommand": "bash -lc \"make setup || (echo 'make setup failed; rerun once network access is available.' && exit 0)\"",
"remoteUser": "vscode"
}
21 changes: 21 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ../:/workspace:cached
- bundle-cache:/usr/local/bundle
ports:
- "4000:4000"
- "4001:4001"
environment:
- RACK_ENV=development
- BUNDLE_PATH=/usr/local/bundle
- PORT=4000
command: sleep infinity
user: vscode
working_dir: /workspace

volumes:
bundle-cache:
71 changes: 49 additions & 22 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,57 @@
## Overview

- Ruby web app that converts websites into RSS 2.0 feeds.
- Built with **Roda**, using the **html2rss** gem (+ `html2rss-configs`).
- **Principle:** _All features must work without JavaScript._ JS is only progressive enhancement.
- Built with **Roda** backend + **Astro** frontend, using the **html2rss** gem (+ `html2rss-configs`).
- **Frontend:** Modern Astro-based UI with component architecture, served alongside Ruby backend.

## Documentation website of core dependencies

Search these pages before using them. Find examples, plugins, UI components, and configuration options.

### Roda

1. https://roda.jeremyevans.net/documentation.html

### Astro & Starlight

1. https://docs.astro.build/en/getting-started/
2. https://starlight.astro.build/getting-started/

### html2rss

1. If available, find source locally in: `../html2rss`.
2. source code on github: https://github.com/html2rss/html2rss

### Test and Linters

1. https://docs.rubocop.org/rubocop/cops.html
2. https://docs.rubocop.org/rubocop-rspec/cops_rspec.html
3. https://rspec.info/features/3-13/rspec-expectations/built-in-matchers/
4. https://www.betterspecs.org/

Fix rubocop `RSpec/MultipleExpectations` adding rspec tag `:aggregate_failures`.

## Core Rules

- ✅ Use **Roda routing with `hash_branch`**. Keep routes small.
- ✅ Put logic into `helpers/` or `app/`, not inline in routes.
- ✅ Organise Roda routes via dedicated modules (e.g. `Html2rss::Web::Routes::*`), keeping the main app class thin.
- ✅ Keep helper modules minimal: define entrypoints with `class << self` and push implementation helpers under `private`; avoid `module_function` unless mirroring existing conventions.
- ✅ Validate all inputs. Pass outbound requests through **SSRF filter**.
- ✅ Add caching headers where appropriate (`Rack::Cache`).
- ✅ Errors: friendly messages for users, detailed logging internally.
- ✅ CSS: Water.css + small overrides in `public/styles.css`.
- ✅ Specs: RSpec, unit + integration, use VCR for external requests.

## Don’t
- ✅ **Frontend**: Use Astro components in `frontend/src/`. Keep it simple.
- ✅ **CSS**: Use frontend styles provided by Astro Starlight.
- ✅ **Specs**: RSpec for Ruby, build tests for frontend.
- ✅ When a spec needs to tweak environment variables, wrap the example in `ClimateControl.modify` so state is restored automatically.

- ❌ Don’t depend on JS for core flows.
- ❌ Don’t bypass SSRF filter or weaken CSP.
- ❌ Don’t add databases, ORMs, or background jobs.
- ❌ Don’t leak stack traces or secrets in responses.
## Don't

## Project Structure

- `app.rb` – main Roda app
- `app/` – core modules (config, cache, ssrf, health)
- `routes/` – route handlers (`hash_branch`)
- `helpers/` – pure helper modules (`module_function`)
- `views/` – ERB templates
- `public/` – static assets (CSS/JS, minimal)
- `config/feeds.yml` – feed definitions
- `spec/` – RSpec tests + VCR cassettes
- ❌ Don't use Ruby's URI class or addressable gem directly. Strictly use `Html2rss::Url` only.
- ❌ Don't bypass SSRF filter or weaken CSP.
- ❌ Don't add databases, ORMs, or background jobs.
- ❌ Don't leak stack traces or secrets in responses.
- ❌ Don't reach into private API with `send(...)`; expose what you need at the module level instead.
- ❌ Don't modify `frontend/dist/` - it's generated by build process.
- ❌ NEVER expose the auth token a user provides.

## Environment

Expand All @@ -41,6 +62,12 @@
- `HEALTH_CHECK_USERNAME`, `HEALTH_CHECK_PASSWORD`
- `SENTRY_DSN` (optional)

### Verification Steps

- Run `ruby -c app.rb` to check syntax
- Run `bundle exec rspec` to verify tests
- Check `bundle install` removes unused dependencies

## Style

- Add `# frozen_string_literal: true`
Expand Down
17 changes: 0 additions & 17 deletions .github/workflows/bundle-update.yml

This file was deleted.

Loading