Split bunyip-web's admin handlers and routes (DEV-517) #455

Merged
longjacksonle merged 4 commits from refactor/DEV-517-bunyip-web-admin-split into main 2026-08-04 16:23:36 +02:00

Closes the bunyip-web half of DEV-517. Vas' note on the ticket: bunyip-web's admin panel had deviated too far from a8n-tools' structure (a8n splits handlers and isolates routing in routes/admin.rs), so realign the panel toward a8n as part of this ticket. That is what this does. No behaviour change: same routes, same handlers, same 59 admin tests.

What moved

bunyip-web/src/handlers/admin.rs was 7,965 lines and 155 handlers. It is now handlers/admin/, one module per section banner the file already carried: dashboard, audit, error_log, seed, ip_bans, rate_limits, users, memberships, feedback, applications, application_groups, entitlements, tier_settings, email_config, auto_ban_settings, backup, stripe, plus tests. Largest file is now users.rs at 1,148 lines.

main.rs wired all 80 admin routes inline. They now live in bunyip-web/src/routes/admin.rs behind routes() -> Router<AppState>, merged by main, which drops from 574 lines to 285.

Three things the original banners did not line up with

  • The 59 unit tests were in ten #[cfg(test)] modules interleaved through the file tail, and each reaches across several sections (the markup tests alone touch applications, auto-ban, email, tier and users). Tearing them apart would have meant rewriting them, so they move together into handlers/admin/tests.rs, which glob-imports each section module to re-expose the items under one name space. Every use super::... inside the test modules is unchanged.
  • The application-documentation handlers and DocForm had been appended under the Stripe banner. They move to applications.
  • The SMTP handlers (email, email_save, email_test, EmailSettingsForm, email_update_body) had been appended under the auto-ban banner. They move to email_config, which until now held only the settings-card markup.

Visibility

Helpers that were private to one flat module are now private to a submodule. The ones another section or the tests reach are pub(super): visible across the admin module tree, still invisible outside it. Same set of items, same call sites, nothing newly public on the crate surface.

Five helpers that more than one section uses moved up into handlers/admin/mod.rs: title_case and pager (already shared), PageQuery (was in the memberships section, used by feedback and rate_limits), refuse_non_super_admin (was in ip_bans, used by rate_limits) and with_attachment_hardening (was in feedback, used by backup and seed).

Review notes

The commits are ordered so the mechanical parts are separable: the verbatim split, then the item moves, then the import prune, then the route extraction.

Route table equivalence was checked mechanically, not by eye: the same 80 path literals and the same 87 handler references, in the same order, before and after. just check-build, just check-clippy and just check-fmt are clean, and cargo test -p bunyip-web is 177 passed, the same as main.

Not in this PR

The other DEV-517 follow-up, reconciling a8n's users table with bunyip's, is deliberately out. a8n has no feature that reads any of the eight columns it is missing, and the sqlx 0.7-vs-0.8 split blocks sharing the User row even once the columns match, so the a8n side starts with the sqlx upgrade instead (separate PR on a8n-tools/saas).

Closes the bunyip-web half of DEV-517. Vas' note on the ticket: bunyip-web's admin panel had deviated too far from a8n-tools' structure (a8n splits handlers and isolates routing in `routes/admin.rs`), so realign the panel toward a8n as part of this ticket. That is what this does. No behaviour change: same routes, same handlers, same 59 admin tests. ### What moved `bunyip-web/src/handlers/admin.rs` was 7,965 lines and 155 handlers. It is now `handlers/admin/`, one module per section banner the file already carried: dashboard, audit, error_log, seed, ip_bans, rate_limits, users, memberships, feedback, applications, application_groups, entitlements, tier_settings, email_config, auto_ban_settings, backup, stripe, plus `tests`. Largest file is now `users.rs` at 1,148 lines. `main.rs` wired all 80 admin routes inline. They now live in `bunyip-web/src/routes/admin.rs` behind `routes() -> Router<AppState>`, merged by `main`, which drops from 574 lines to 285. ### Three things the original banners did not line up with - The 59 unit tests were in ten `#[cfg(test)]` modules interleaved through the file tail, and each reaches across several sections (the markup tests alone touch applications, auto-ban, email, tier and users). Tearing them apart would have meant rewriting them, so they move together into `handlers/admin/tests.rs`, which glob-imports each section module to re-expose the items under one name space. Every `use super::...` inside the test modules is unchanged. - The application-documentation handlers and `DocForm` had been appended under the Stripe banner. They move to `applications`. - The SMTP handlers (`email`, `email_save`, `email_test`, `EmailSettingsForm`, `email_update_body`) had been appended under the auto-ban banner. They move to `email_config`, which until now held only the settings-card markup. ### Visibility Helpers that were private to one flat module are now private to a submodule. The ones another section or the tests reach are `pub(super)`: visible across the admin module tree, still invisible outside it. Same set of items, same call sites, nothing newly public on the crate surface. Five helpers that more than one section uses moved up into `handlers/admin/mod.rs`: `title_case` and `pager` (already shared), `PageQuery` (was in the memberships section, used by feedback and rate_limits), `refuse_non_super_admin` (was in ip_bans, used by rate_limits) and `with_attachment_hardening` (was in feedback, used by backup and seed). ### Review notes The commits are ordered so the mechanical parts are separable: the verbatim split, then the item moves, then the import prune, then the route extraction. Route table equivalence was checked mechanically, not by eye: the same 80 path literals and the same 87 handler references, in the same order, before and after. `just check-build`, `just check-clippy` and `just check-fmt` are clean, and `cargo test -p bunyip-web` is 177 passed, the same as main. ### Not in this PR The other DEV-517 follow-up, reconciling a8n's `users` table with bunyip's, is deliberately out. a8n has no feature that reads any of the eight columns it is missing, and the sqlx 0.7-vs-0.8 split blocks sharing the `User` row even once the columns match, so the a8n side starts with the sqlx upgrade instead (separate PR on a8n-tools/saas).
bunyip-web/src/handlers/admin.rs had grown to 7,965 lines and 155 handlers, far past a8n-tools' split layout that DEV-517 calls for realigning it toward. Split it verbatim into `handlers/admin/`, one module per section banner the file already carried: dashboard, audit, error_log, seed, ip_bans, rate_limits, users, memberships, feedback, applications, application_groups, entitlements, tier_settings, email_config, auto_ban_settings, backup, stripe.

No handler body changed. `mod.rs` re-exports each submodule, so every `handlers::admin::<fn>` path in the router still resolves and the route wiring is untouched by this commit.

The helpers that more than one section uses moved up into `mod.rs`: `title_case` and `pager` (already shared), plus `PageQuery` (was in the memberships section, used by feedback and rate_limits), `refuse_non_super_admin` (was in ip_bans, used by rate_limits) and `with_attachment_hardening` (was in feedback, used by backup and seed). The three helpers with a single cross-section caller stayed in their own module and became `pub(super)`: `group_assignment_form`, `email_settings_content`, `rate_limit_row`.

Each submodule starts from the original file's import list; the next commit prunes it per module.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FcwfAHYwMmiAYbMrCc7cJG
Follow-up to the mechanical split: three things the section banners in the old single file did not line up with.

The 59 admin unit tests sat in ten `#[cfg(test)]` modules interleaved through the tail of the file, and each one reaches across several sections (the markup tests alone touch applications, auto-ban, email, tier and users). Splitting them apart would have meant rewriting the tests, so they move together into `handlers/admin/tests.rs`, which glob-imports each section module to re-expose the items under one name space. The `use super::...` lines inside every test module are unchanged, and the count is the same 59 before and after.

The application-documentation handlers (`application_docs`, `application_doc_create`, `application_doc_update`, `application_doc_delete`) and their `DocForm` had been appended under the Stripe banner; they move to `applications`. The SMTP handlers (`email`, `email_save`, `email_test`, `EmailSettingsForm`, `email_update_body`) had been appended under the auto-ban banner; they move to `email_config`, which until now held only the settings-card markup.

The helpers those tests exercise were private to one flat module and are now private to a submodule, so the ones the tests reach are `pub(super)`: visible across the admin module tree, still invisible outside it. Same set of items, same call sites.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FcwfAHYwMmiAYbMrCc7cJG
Each split module started from the original file's whole `use` block; `cargo fix` cuts each one down to what that section actually references. Also renames the anonymous `mod tests` inside `handlers/admin/tests.rs` to `request_body_tests`, which is what it covers and which clears `clippy::module_inception`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FcwfAHYwMmiAYbMrCc7cJG
refactor(web): move the admin route table into routes/admin.rs (DEV-517)
All checks were successful
E2E PR gate / Install + reachability (no deployment secrets) (pull_request) Successful in 23s
Check / fmt + clippy + build + tests (pull_request) Successful in 14m46s
Create release / Create release from merged PR (pull_request) Has been skipped
5b6f8b2136
The panel's 80 routes were wired inline in `main.rs`, which is the other half of the deviation from a8n-tools' layout that DEV-517 names: a8n keeps `routes/` separate from `handlers/`. `bunyip-web/src/routes/admin.rs` now owns the table behind a `routes() -> Router<AppState>`, and `main` merges it. `main.rs` drops from 574 lines to 285.

Verified route-for-route: the same 80 path literals and the same 87 handler references, in the same order, before and after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FcwfAHYwMmiAYbMrCc7cJG
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-08-04 16:16:43 +02:00
longjacksonle deleted branch refactor/DEV-517-bunyip-web-admin-split 2026-08-04 16:23:37 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!455
No description provided.