feat(account): wire premium accounts end to end - #171
Conversation
📝 WalkthroughWalkthroughChangesMAT-132 adds typed premium-account state, isolated plugin credentials, secure just-in-time source resolution, account rotation, atomic download association updates, SQLite persistence, IPC/runtime wiring, and frontend status/event handling. Premium account runtime
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Merging this PR will degrade performance by 0.85%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | from_status_code_200 |
94.2 ns | 123.3 ns | -23.65% |
| ❌ | from_status_code_404 |
123.9 ns | 153.1 ns | -19.06% |
| ❌ | normalize_max_concurrent |
150 ns | 179.2 ns | -16.28% |
| ⚡ | from_status_code_500 |
123.9 ns | 94.7 ns | +30.79% |
| ⚡ | split |
501.1 ns | 442.8 ns | +13.17% |
| ⚡ | full_lifecycle |
288.9 ns | 259.7 ns | +11.23% |
| ⚡ | reject_invalid |
610.3 ns | 551.9 ns | +10.57% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/mat-132-premium-accounts (13123fd) with main (5b55d38)
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
There was a problem hiding this comment.
cubic analysis
All reported issues were addressed across 80 files
Linked issue analysis
Linked issue: MAT-132: [Lot 2] Brancher les comptes premium de bout en bout
| Status | Acceptance criteria | Notes |
|---|---|---|
| ✅ | R-01 — The add/modify account flow triggers a real validation via the applicative port | A PluginAccountValidator adapter was added and CommandBus calls the validator on add/update flows; validation outcome plumbing is applied to persist and publish results. |
| ✅ | R-02 — Account secrets are never persisted in plain text (SQLite, logs, frontend events) | Persistent models and DTOs were changed to store only opaque refs/metadata; credential material is handled via dedicated credential slots/keyring and redaction of sensitive resolved URLs is implemented. |
| ✅ | R-03 — A premium download selects only enabled, valid, and hoster-compatible accounts | Account selection and validations updated to prefer validated/eligible accounts; download start and link resolution validate account compatibility and enforce module association. |
| ✅ | R-04 — Plugin receives the expected credential via the dedicated host function without direct keyring access | Plugin capability surface now uses per-instance credential slots and PluginLoader.extract_hoster_link accepts an explicit credential parameter; registry/loader set credential scope for the call so plugins don't read keyring directly. |
| ✅ | R-05 — Authentication, expiry, quota and cooldown produce typed, observable account states in the UI | Typed DomainError values and an AccountStatus enum were added; mapping utilities apply typed statuses and UI/read-models/events were updated to expose them; front-end hooks and localization added. |
| ✅ | R-06 — Integration tests cover success, missing credential, expired account and rotation to a second account | New unit/integration-style tests exercise premium resolution and rotation scenarios; CI verification in the PR shows the test suites passed. |
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (1)
src-tauri/src/adapters/driven/sqlite/migrations/m20260716_000010_wire_premium_accounts.rs (1)
30-37: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider indexing
downloads.account_ref.
has_account_reference(indownload_repo.rs) filters on this column, and is used to gate account deletion. Without an index, that lookup is a full table scan ofdownloadsas the table grows.♻️ Suggested addition
manager .alter_table( Table::alter() .table(Downloads::Table) .add_column(ColumnDef::new(Downloads::AccountRef).text().null()) .to_owned(), ) - .await + .await?; + manager + .create_index( + Index::create() + .name("idx_downloads_account_ref") + .table(Downloads::Table) + .col(Downloads::AccountRef) + .to_owned(), + ) + .await🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src-tauri/src/adapters/driven/sqlite/migrations/m20260716_000010_wire_premium_accounts.rs` around lines 30 - 37, Add an index for the nullable Downloads::AccountRef column in the same migration that adds it, using the project's existing SeaORM index migration pattern. Ensure the index targets account_ref so has_account_reference lookups in download_repo.rs avoid full-table scans.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src-tauri/src/adapters/driven/plugin/registry.rs`:
- Around line 170-185: Update call_plugin_inner’s registry-entry lookup to
validate entry.enabled while holding the registry access, returning the existing
disabled-plugin error before cloning handles, creating CredentialScope, or
invoking the plugin. Add a regression test covering a plugin disabled after the
loader pre-check and verify no credential is injected or execution occurs.
In `@src-tauri/src/application/commands/premium_account_rotation.rs`:
- Around line 58-84: Update the download-association validation around
compare_and_set_account_reference so it always revalidates the expected
association, including when the account has not rotated, and detects concurrent
reassignment or deletion before returning a premium URL. Make the explicit
“download account association changed during resolution” failure non-rotatable
in the rotation decision logic near is_rotatable, while preserving rotation for
other eligible errors.
In `@src-tauri/src/application/commands/redownload.rs`:
- Around line 459-477: Update redownload_rejects_an_orphaned_account_reference
to provide an empty InMemoryAccountRepo and credential store when constructing
the bus, so account lookup reaches the orphaned-reference path. Change the
assertion from AppError::Validation(_) to AppError::NotFound while preserving
the existing missing-account setup.
In `@src-tauri/src/application/commands/resolve_premium_source.rs`:
- Around line 78-88: Update ResolvePremiumSourceHandler::handle so the account
lock acquisition and synchronous resolve_locked work run inside a Tokio
spawn_blocking boundary, while preserving per-account serialization and
returning the same Result<ExtractedHosterLink, DomainError>. Publish success
only after the blocking resolution completes successfully, and propagate join or
resolution errors through the existing error type.
In `@src-tauri/src/application/commands/update_account.rs`:
- Around line 127-136: Update the account-update command flow after publishing
the committed outcome and validation event to inspect
AccountValidationAttempt.error and return its mapped error when present;
otherwise keep returning Ok(()). Preserve the existing
sync_validation_availability and event publication behavior, matching
handle_validate_account’s error mapping.
In `@src-tauri/src/application/services/account_rotator.rs`:
- Around line 8-10: Update the exhaustion commit path and next_account to share
one synchronization boundary, so selection cannot occur between persisting an
exhausted Account and updating the in-memory cache. Ensure next_account’s final
availability check observes the same coordinated state, preserving rollback or
consistent cache state if persistence fails.
In `@src-tauri/src/domain/ports/driven/download_repository.rs`:
- Around line 61-84: The default has_account_reference implementation is unsafe
because it scans mutable download states through separate queries and can miss
references during transitions. Make has_account_reference a required
DownloadRepository trait method, then implement a single adapter-level EXISTS
query filtering directly by account_id, preserving the Result<bool, DomainError>
contract.
In `@src-tauri/src/domain/ports/driven/hoster_link.rs`:
- Around line 1-16: Replace the derived Debug implementation on
ExtractedHosterLink with a custom redacted formatter, matching the existing
ResolvedDownloadSource behavior so direct_url is never emitted verbatim while
the remaining fields remain available in diagnostics.
In `@src/views/LinkGrabberView/__tests__/LinkGrabberView.test.tsx`:
- Around line 185-195: Update the test setup in “passes the source URL and
premium account association to download_start” so the mocked link-resolution
response uses the distinct directUrl as resolvedUrl while retaining sourceUrl as
originalUrl. Ensure the assertion at the download_start invocation verifies the
resolved URL is not forwarded, making the token-leak check meaningful.
---
Nitpick comments:
In
`@src-tauri/src/adapters/driven/sqlite/migrations/m20260716_000010_wire_premium_accounts.rs`:
- Around line 30-37: Add an index for the nullable Downloads::AccountRef column
in the same migration that adds it, using the project's existing SeaORM index
migration pattern. Ensure the index targets account_ref so has_account_reference
lookups in download_repo.rs avoid full-table scans.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6bc94f0b-a88c-4e1f-96a5-df719763b713
📒 Files selected for processing (80)
CHANGELOG.mdregistry/registry.tomlsrc-tauri/src/adapters/driven/network/download_engine.rssrc-tauri/src/adapters/driven/network/mod.rssrc-tauri/src/adapters/driven/network/nat64.rssrc-tauri/src/adapters/driven/network/safe_url.rssrc-tauri/src/adapters/driven/network/safe_url_tests.rssrc-tauri/src/adapters/driven/network/segment_worker.rssrc-tauri/src/adapters/driven/plugin/account_validator.rssrc-tauri/src/adapters/driven/plugin/capabilities.rssrc-tauri/src/adapters/driven/plugin/extism_loader.rssrc-tauri/src/adapters/driven/plugin/host_functions.rssrc-tauri/src/adapters/driven/plugin/hoster_contract.rssrc-tauri/src/adapters/driven/plugin/hoster_contract_tests.rssrc-tauri/src/adapters/driven/plugin/mod.rssrc-tauri/src/adapters/driven/plugin/registry.rssrc-tauri/src/adapters/driven/sqlite/account_repo.rssrc-tauri/src/adapters/driven/sqlite/connection.rssrc-tauri/src/adapters/driven/sqlite/download_repo.rssrc-tauri/src/adapters/driven/sqlite/entities/account.rssrc-tauri/src/adapters/driven/sqlite/entities/download.rssrc-tauri/src/adapters/driven/sqlite/migrations/m20260716_000010_wire_premium_accounts.rssrc-tauri/src/adapters/driven/sqlite/migrations/mod.rssrc-tauri/src/adapters/driving/tauri_ipc.rssrc-tauri/src/application/command_bus.rssrc-tauri/src/application/commands/add_account.rssrc-tauri/src/application/commands/delete_account.rssrc-tauri/src/application/commands/mod.rssrc-tauri/src/application/commands/premium_account_resolution.rssrc-tauri/src/application/commands/premium_account_rotation.rssrc-tauri/src/application/commands/redownload.rssrc-tauri/src/application/commands/resolve_links.rssrc-tauri/src/application/commands/resolve_premium_source.rssrc-tauri/src/application/commands/resolve_premium_source_test_support.rssrc-tauri/src/application/commands/resolve_premium_source_tests.rssrc-tauri/src/application/commands/start_download.rssrc-tauri/src/application/commands/tests_support.rssrc-tauri/src/application/commands/update_account.rssrc-tauri/src/application/commands/validate_account.rssrc-tauri/src/application/queries/list_accounts.rssrc-tauri/src/application/read_models/account_view.rssrc-tauri/src/application/services/account_operation_locks.rssrc-tauri/src/application/services/account_rotator.rssrc-tauri/src/application/services/account_selector.rssrc-tauri/src/application/services/account_state.rssrc-tauri/src/application/services/mod.rssrc-tauri/src/domain/error.rssrc-tauri/src/domain/event.rssrc-tauri/src/domain/model/account.rssrc-tauri/src/domain/model/download.rssrc-tauri/src/domain/ports/driven/account_repository.rssrc-tauri/src/domain/ports/driven/account_validator.rssrc-tauri/src/domain/ports/driven/download_repository.rssrc-tauri/src/domain/ports/driven/download_source_resolver.rssrc-tauri/src/domain/ports/driven/hoster_link.rssrc-tauri/src/domain/ports/driven/mod.rssrc-tauri/src/domain/ports/driven/plugin_loader.rssrc-tauri/src/lib.rssrc-tauri/tests/app_state_wiring.rssrc/hooks/__tests__/useAccountEvents.test.tssrc/hooks/useAccountEvents.tssrc/i18n/locales/en.jsonsrc/i18n/locales/fr.jsonsrc/layouts/AppLayout.tsxsrc/layouts/__tests__/AppLayout.test.tsxsrc/types/account.tssrc/types/events.tssrc/views/AccountsView/AccountRow.tsxsrc/views/AccountsView/AccountsView.tsxsrc/views/AccountsView/__tests__/AccountRow.test.tsxsrc/views/AccountsView/__tests__/AccountsView.test.tsxsrc/views/AccountsView/__tests__/statusUtils.test.tssrc/views/AccountsView/statusUtils.tssrc/views/AccountsView/useAccountStatusNow.tssrc/views/LinkGrabberView/LinkGrabberView.tsxsrc/views/LinkGrabberView/__tests__/LinkGrabberView.test.tsxsrc/views/LinkGrabberView/__tests__/LinkRow.test.tsxsrc/views/LinkGrabberView/__tests__/MediaGrabberDialog.test.tsxsrc/views/LinkGrabberView/__tests__/ResolvedLinksSection.test.tsxsrc/views/LinkGrabberView/types.ts
|
Addressed the review-body index nitpick in 38c12d3: migration 10 now creates |
There was a problem hiding this comment.
All reported issues were addressed across 32 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Summary
Acceptance coverage
Covers MAT-132 R01–R06: real validation, secret boundaries, eligible-account selection, dedicated credential host function, observable typed failures, and integration scenarios for success/missing/expired/quota/cooldown/rotation.
Verification
Dependency and rollout
Depends on mpiton/vortex-mod-1fichier#3 and its immutable v1.1.0 release assets. This PR deliberately leaves registry/registry.toml on the currently published 1fichier v1.0.0 entry. Update the registry only after v1.1.0 exists and its WASM + manifest checksums can be verified.
The SQLite migration is additive. The principal runtime risk is account-state concurrency during JIT rotation; regression tests cover pause/delete races, CAS account_ref updates, orphan prevention, and no row resurrection.
Linear: https://linear.app/mpiton/issue/MAT-132/lot-2-brancher-les-comptes-premium-de-bout-en-bout
Summary by cubic
End-to-end premium account wiring: real plugin-backed validation with typed status, secrets in the OS keyring, eligible-account selection and rotation, and just‑in‑time premium URL resolution inside the native engine with stronger network safety. Covers MAT-132 R01–R06 and updates the registry to
vortex-mod-1fichierv1.1.0.New Features
statusandcooldown_until; store only account metadata; secrets remain in the keyring; per‑account operation locks; prevent deleting accounts still referenced by downloads.LinkGrabberpassesmoduleNameandaccountIdtodownload_startwhile keeping only the source URL in IPC and persistence.account_refon quota/cooldown withaccount-exhaustedevents.Migration
accounts.status,accounts.cooldown_until,downloads.account_ref(opaque string).unverified.Written for commit 13123fd. Summary will update on new commits.
Summary by CodeRabbit