feat!: rewrite ruby api and frontend with stable-url autosourced feeds#775
Closed
gildesmarais wants to merge 102 commits intomasterfrom
Closed
feat!: rewrite ruby api and frontend with stable-url autosourced feeds#775gildesmarais wants to merge 102 commits intomasterfrom
gildesmarais wants to merge 102 commits intomasterfrom
Conversation
3cd0291 to
26085bf
Compare
26085bf to
a58186e
Compare
- Deleted test scripts for auto-source and URL restrictions as they are no longer needed. - Removed unused dependencies from package.json and package-lock.json to streamline the project. - Updated the dev script to improve clarity in error messages regarding port conflicts.
Reduce misleading accent usage in the result view so non-interactive labels read as informational, not clickable. Align action grammar by left-aligning the reset CTA and tightening vertical spacing around result metadata for a denser, more consistent layout. Expose friendlier strategy display names from the API (Standard (recommended), JavaScript pages) so frontend selectors communicate intent instead of implementation details. Includes matching test/mock updates to preserve contract and UI behavior guarantees.
…tions Anchor architecture delivery to the current code reality so execution can proceed autonomously with low migration risk. The plan now explicitly validates assumptions against existing Roda routes, config/auth/feed boundaries, and current OpenAPI/frontend tooling before proposing changes. Sequence decisions prioritize reversible slices: typed config and observability before boundary contract migration, with async refresh intentionally last due to operational risk. Outlier handling is documented via commit packaging guidance so cross-cutting work remains explicit and rollback boundaries stay clear.
Introduce a browser-level smoke check so delivery can be self-verified beyond unit/contract tests. Given Alpine-based dev containers, Playwright now targets the container-native Chromium path for reliable headless execution instead of relying on incompatible downloaded browser binaries. Keep scope intentionally narrow: one smoke scenario that validates onboarding/auth mode transitions, with Vitest explicitly excluding e2e specs to avoid framework cross-collection. This provides an immediate autonomous verification path while preserving existing test semantics and pre-commit gates.
Establish explicit architectural decisions before implementation so autonomous delivery has clear guardrails and rollback boundaries. The ADR set locks migration policy for Data contracts, request-context observability, typed config snapshots, OpenAPI-generated frontend client workflow, and async feed refresh strategy. This keeps later implementation commits focused on execution rather than design churn.
…able feed runtime Execute the backend architecture phases by introducing immutable boundary contracts and typed config snapshots while keeping compatibility adapters for existing hash consumers. Request context is now initialized at middleware entry and propagated into security/error observability so auth, token usage, and failures correlate by request metadata. A cache-first feed runtime with feature-flagged async refresh is introduced behind safe defaults, with extraction into dedicated handler modules to keep the Roda app surface maintainable. Outlier note: this is intentionally cross-cutting because these runtime contracts interlock; rollback remains bounded via adapters and ASYNC_FEED_REFRESH_ENABLED=false.
Replace handwritten API wiring with generated OpenAPI client usage so frontend behavior tracks backend contract changes by construction. Add generation and verification commands plus Makefile integration to fail fast on spec/client drift instead of relying on manual sync. Hook and test updates preserve existing UX behavior while adapting error handling to generated client semantics.
Establishes phase-gated governance artifacts for AppContext, feature flags, observability events, and API contract policy. Locks a code-backed assumptions baseline in the delivery plan so implementation decisions stay grounded in current behavior. Decision: preserve doc-first phase gates while keeping lightweight pre-work inventory in the flags model.
Outlier: combines AppContext and Flags foundations because boot wiring in app.rb now depends on both to remain runnable and testable in each commit. Decision: make dependency resolution explicit via AppContext and remove direct feature-flag ENV reads from runtime paths in favor of a typed registry. Adds fail-fast flag validation boundaries and a focused flag-spec suite to keep startup behavior deterministic.
Adds explicit event emission for auth, feed creation, feed rendering, and error handling using the documented schema fields. Decision: keep existing security logs intact and layer observability events additively to avoid operational blind spots during transition. Failure events are emitted at boundary points where outcome is final to reduce duplicate noise.
Extends contract enforcement beyond spec generation drift to include OpenAPI linting and frontend generated-client drift verification. Decision: keep policy checks in CI so contract integrity is enforced consistently across contributors and environments.
- Drop surface card from guest panel — all panels now share `workspace` open layout - Replace radio cards with a plain <select> for demo source (consistent with Strategy selector) - Align button vocabulary: btn--ghost for demo run, btn--accent for primary auth/convert actions - "Back to demo" demoted to btn--meta (gray housekeeping, not invitation) - Remove ~95 lines of dead CSS: surface*, onboarding-*, radio-card/list*, fieldset/legend, form--spacious, auth-form-actions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- useStrategies: init isLoading as !!token to prevent empty-select flash on first render - Strategy select always rendered (disabled + Loading… placeholder while fetching), no layout jump - Improved hint copy: "Direct fetch — works for most sites. Fast and safe." / "Headless browser — use for JavaScript-rendered pages (React, Angular, SPAs)." Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: lock feed pipeline behavior * feat!: add namespaced feed pipeline primitives * feat!: route feeds through shared synchronous service * refactor!: remove legacy feed-serving path * refactor!: finalize architecture cleanup * refactor!: remove dead feed helper * refactor!: normalize feed responses and stabilize openapi verification * style: relax rubocop * chore: update bundler deps * fix: harden feed loading and negotiated openapi docs * chore: refresh generated api types
* refactor: replace Astro frontend with Vite and Preact * fix: simplify result actions and surface feed errors * fix: accept escaped public feed tokens * fix: label the result feed url field * refactor: redesign frontend ui system * style: refine frontend typography and visual noise * chore: drop accidental typecheck gate changes * chore: add frontend typecheck quick-check * ci: add frontend guardrail checks * refactor: harden demo contract and simplify ui states * chore: remove dead frontend components * style: prettier * chore: refresh generated openapi client * chore: update npm deps * Refactor feed creation UX flow * Tighten feed result surface * Refine create flow support surfaces * Refine feed creation workspace UX * Simplify feed creation page layout * Align result screen with simplified create flow * Refine feed preview and utility surfaces * Refine token prompt interaction flow * Polish feed result presentation * Update frontend contract test for new flow * Expose json feed URLs and tighten recovery flow
This pull request introduces a major refactor of the backend code structure to adopt the Zeitwerk autoloader, improves backend maintainability, and updates development and linting workflows. It removes manual requires and directory-specific namespace wiring, reorganizes files under the `app/web/**` directory, and ensures that all code is autoloaded in a consistent, Rails-like fashion. Additionally, it adds a Zeitwerk eager-load check to the linting process and updates documentation accordingly. Several files are renamed or removed to reflect the new structure, and some error-handling and validation logic is refactored for clarity and consistency. **Backend autoloading and structure improvements:** - Introduced the `zeitwerk` gem and removed the `rack-unreloader` gem, enabling Zeitwerk-based autoloading for backend code under the `Html2rss::Web` namespace, and updated `app.rb` to use `Html2rss::Web::Boot` for setup and eager loading. [[1]](diffhunk://#diff-d09ea66f8227784ff4393d88a19836f321c915ae10031d16c93d67e6283ab55fR20-L25) [[2]](diffhunk://#diff-f965f92b425fb2f75d38b491b2625fe21b8af20b7666217546bce8a42b198ea4L9-R12) - Moved backend files into the `app/web/**` directory to match Zeitwerk's expectations, and removed manual `require` statements in favor of autoloading. [[1]](diffhunk://#diff-f965f92b425fb2f75d38b491b2625fe21b8af20b7666217546bce8a42b198ea4L9-R12) [[2]](diffhunk://#diff-17e1b7bf4af1869b4be3173dde8b6bffaff1e1e2a6e767b728c9a9f3685d490fL1-L52) [[3]](diffhunk://#diff-02e7c9c52be0b12bd7c0a8910a098e50de253b9654172526667dec2bfdba4970L1-L57) [[4]](diffhunk://#diff-884604050285643eb6fb29195c6342cd73a3abae7f3acba5b1bbc41a196d31e3L1-L70) [[5]](diffhunk://#diff-9df4f6ed17ac1a74c68891a27d537de8e617e0b99e7803fbf42ab523a7043bb6L1-L158) [[6]](diffhunk://#diff-f2b0bbe2a08fb1204f3985f426ee5aeda21d24f8d4b598e4162b43d87649b674L3-R11) [[7]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L6-R15) **Development and linting workflow updates:** - Added a Zeitwerk eager-load verification task (`rake zeitwerk:verify`) to the `Makefile` linting process and as part of `make ready`, ensuring loader drift is caught early in development. [[1]](diffhunk://#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52R61-R62) [[2]](diffhunk://#diff-ee98e028c59b193d58fde56ab4daf54d43c486ae674e63d50ddf300b07943e0fR142-R152) [[3]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R69-R72) **Error handling and validation refactoring:** - Updated error handling in API modules to reference error classes via the fully qualified `Html2rss::Web` namespace, and refactored validation logic in `CreateFeed` for clarity and maintainability. [[1]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L44-R43) [[2]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L60-R80) [[3]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281R90-R95) [[4]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L112-R123) [[5]](diffhunk://#diff-0764627d99177e329667ae8f5c98c756ac3d8ae43bdf768b3af26403f713e281L134-R141) **Roda plugin and middleware changes:** - Added the `:head` and `:not_allowed` plugins to Roda configuration, improving HTTP method handling. **Documentation updates:** - Updated `README.md` to explain the new backend file structure, Zeitwerk integration, and contributor guidelines for backend code placement.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 186 out of 211 changed files in this pull request and generated 9 comments.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
+15
to
+39
| environment: | ||
| RACK_ENV: production | ||
| PORT: 4000 | ||
| HTML2RSS_SECRET_KEY: your-generated-secret-key-here | ||
| HEALTH_CHECK_TOKEN: CHANGE_ME_HEALTH_CHECK_TOKEN | ||
| BROWSERLESS_IO_WEBSOCKET_URL: ws://browserless:4002 | ||
| BROWSERLESS_IO_API_TOKEN: 6R0W53R135510 | ||
|
|
||
| watchtower: | ||
| image: containrrr/watchtower | ||
| restart: unless-stopped | ||
| volumes: | ||
| - /var/run/docker.sock:/var/run/docker.sock | ||
| - "~/.docker/config.json:/config.json" | ||
| command: --cleanup --interval 7200 | ||
|
|
||
| browserless: | ||
| image: "ghcr.io/browserless/chromium" | ||
| restart: unless-stopped | ||
| ports: | ||
| - "127.0.0.1:4002:4002" | ||
| environment: | ||
| PORT: 4002 | ||
| CONCURRENT: 10 | ||
| TOKEN: 6R0W53R135510 |
Comment on lines
+17
to
+25
| def call(router) | ||
| router.on 'api', 'v1' do | ||
| RequestTarget.mark!(router, RequestTarget::API) | ||
| router.response['Content-Type'] = 'application/json' | ||
|
|
||
| HealthRoutes.call(router) | ||
| FeedRoutes.call(router) | ||
| MetadataRoutes.call(router) | ||
| end |
Comment on lines
+44
to
+46
| { | ||
| status: 200, | ||
| headers: { 'Content-Type': 'application/json' }, |
Comment on lines
+18
to
+25
| return HttpResponse.json( | ||
| buildFeedResponse({ | ||
| url: body.url, | ||
| feed_token: 'generated-token', | ||
| public_url: '/api/v1/feeds/generated-token', | ||
| json_public_url: '/api/v1/feeds/generated-token.json', | ||
| }) | ||
| ); |
Comment on lines
+66
to
+70
| http.post('/api/v1/feeds', async () => | ||
| HttpResponse.text('not-json', { | ||
| status: 200, | ||
| headers: { 'content-type': 'application/json' }, | ||
| }) |
Comment on lines
+88
to
+98
| def validate_account_configuration! | ||
| accounts = AccountManager.accounts | ||
| weak_tokens = accounts.select { |acc| acc[:token].length < 16 } | ||
| return unless weak_tokens.any? | ||
|
|
||
| weak_usernames = weak_tokens.map { |acc| acc[:username] }.join(', ') | ||
| SecurityLogger.log_config_validation_failure('account_tokens', "Weak tokens for users: #{weak_usernames}") | ||
| puts '❌ CRITICAL: Weak authentication tokens detected in production!' | ||
| puts ' All tokens must be at least 16 characters long.' | ||
| puts " Weak tokens found for users: #{weak_usernames}" | ||
| exit 1 |
Comment on lines
+13
to
+17
| def call(router) | ||
| mount_openapi_spec(router) | ||
| mount_strategies(router) | ||
| mount_root(router) | ||
| end |
app/web/routes/feed_pages.rb
Outdated
Comment on lines
+13
to
+23
| def call(router, index_renderer:) | ||
| router.get String do |feed_name| | ||
| next if feed_name.include?('.') && !feed_name.end_with?('.json', '.xml', '.rss') | ||
|
|
||
| RequestTarget.mark!(router, RequestTarget::FEED) | ||
| Feeds::Responder.call(request: router, target_kind: :static, identifier: feed_name) | ||
| end | ||
|
|
||
| router.root do | ||
| index_renderer.call(router) | ||
| end |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Member
Author
|
Rebuild git history in dedicated branch. #879 |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.