Settings sidebar: two sections, User and Admin (admin sections split into their own pages) #192

Closed
opened 2026-09-26 15:29:05 +00:00 by kayg · 18 comments
Owner

Owner 2026-09-26: the Settings sidebar has two headed sections, User and Admin. Standard users see only the User items (no section headers when there is only one section). Admins also see the Admin section.

  • User: Account, Appearance, Notifications, Calendars, AI (personal), Photos, Plugins (per-user view).
  • Admin: today's single 'Admin' page is split into sidebar items of its own: Users, Invitations, Sign-in, Server configuration (and any other current admin groups), plus instance-wide controls that live elsewhere today (e.g. enabling/disabling optional plugins for the server, instance AI provider keys, backups if present). Audit every settings screen for admin-only controls and move them into the Admin section; nothing admin-only remains in User pages.
  • Security: admin items are not rendered for non-admins AND the server routes stay admin-only (DESIGN §41: security on the route); search results for admin settings stay admin-only (existing rule).
  • Deep links: keep existing settings URLs working (redirect old /settings/admin#group links to the new pages).
  • Sidebar section headers use the same style as other sidebar section labels; same insets, keyboard and screen-reader semantics (a labelled group per section).
    Coordinate with the Appearance job (#183) editing Appearance: merge dev before final gates. Screenshots as admin and as a standard user, light and dark, 1440 and 390.
Owner 2026-09-26: the Settings sidebar has two headed sections, **User** and **Admin**. Standard users see only the User items (no section headers when there is only one section). Admins also see the Admin section. - User: Account, Appearance, Notifications, Calendars, AI (personal), Photos, Plugins (per-user view). - Admin: today's single 'Admin' page is split into sidebar items of its own: Users, Invitations, Sign-in, Server configuration (and any other current admin groups), plus instance-wide controls that live elsewhere today (e.g. enabling/disabling optional plugins for the server, instance AI provider keys, backups if present). Audit every settings screen for admin-only controls and move them into the Admin section; nothing admin-only remains in User pages. - Security: admin items are not rendered for non-admins AND the server routes stay admin-only (DESIGN §41: security on the route); search results for admin settings stay admin-only (existing rule). - Deep links: keep existing settings URLs working (redirect old /settings/admin#group links to the new pages). - Sidebar section headers use the same style as other sidebar section labels; same insets, keyboard and screen-reader semantics (a labelled group per section). Coordinate with the Appearance job (#183) editing Appearance: merge dev before final gates. Screenshots as admin and as a standard user, light and dark, 1440 and 390.
Author
Owner

Starting #192 on job/settings-sections from dev at e976bff6c7. I will split Admin settings into route-backed pages, separate User and Admin navigation, move instance controls, preserve legacy links, and verify role visibility and routes.

Starting #192 on job/settings-sections from dev at e976bff6c74b3131266b3baeb33ab318edccd46e. I will split Admin settings into route-backed pages, separate User and Admin navigation, move instance controls, preserve legacy links, and verify role visibility and routes.
Author
Owner

Finding: the existing Plugins User page rendered an instance-wide toggle for admins and hid the per-user toggle from them. This mixed two scopes. The other admin controls were already in the Admin pane (Users, Invitations, Sign-in, Server configuration, Backups). The change gives each group a page and puts the instance plugin toggle on Admin → Plugins; User → Plugins always presents only per-user controls.

Finding: the existing Plugins User page rendered an instance-wide toggle for admins and hid the per-user toggle from them. This mixed two scopes. The other admin controls were already in the Admin pane (Users, Invitations, Sign-in, Server configuration, Backups). The change gives each group a page and puts the instance plugin toggle on Admin → Plugins; User → Plugins always presents only per-user controls.
Author
Owner

Finding while merging dev at f6418fc72d750d941f23554ad16381f6dd400fd6: the incoming branch adds an Admin → System screen for native media codec information (apps/web/src/routes/settings/admin/SystemGroup.svelte). I integrated it as its own /settings/admin/system page and kept the previous /settings/admin#system link working. I also added a live production screenshot check for that page.

Finding while merging `dev` at `f6418fc72d750d941f23554ad16381f6dd400fd6`: the incoming branch adds an Admin → System screen for native media codec information (`apps/web/src/routes/settings/admin/SystemGroup.svelte`). I integrated it as its own `/settings/admin/system` page and kept the previous `/settings/admin#system` link working. I also added a live production screenshot check for that page.
Author
Owner

Finding: the settings unit test covers settingsLocation('/settings/admin', '#invitations'), but the live browser flow only visits the new /settings/admin/<page> routes (apps/web/e2e/shell.mjs). I am adding a production-server E2E assertion that the old /settings/admin#invitations link resolves to /settings/admin/invitations and opens Invitations.

Finding: the settings unit test covers `settingsLocation('/settings/admin', '#invitations')`, but the live browser flow only visits the new `/settings/admin/<page>` routes (`apps/web/e2e/shell.mjs`). I am adding a production-server E2E assertion that the old `/settings/admin#invitations` link resolves to `/settings/admin/invitations` and opens Invitations.
Author
Owner

Finding: the real production screenshot run did not reach the local server. bun run screenshots exited 1 because cargo run -p calternal-server failed while compiling web-push with error[E0463]: can't find crate for jwt_simple. I am checking the build state and will retry after isolating the shared-host contention.

Finding: the real production screenshot run did not reach the local server. `bun run screenshots` exited 1 because `cargo run -p calternal-server` failed while compiling `web-push` with `error[E0463]: can't find crate for jwt_simple`. I am checking the build state and will retry after isolating the shared-host contention.
Author
Owner

Build diagnosis: the failed screenshot run's target/debug/deps/libjwt_simple-35bc0a3b607ce527.rlib and .rmeta are both zero bytes (mtime Sep 27 01:13 UTC). I will clear only the jwt-simple package output and retry the production server build after the active shared-host browser run ends.

Build diagnosis: the failed screenshot run's `target/debug/deps/libjwt_simple-35bc0a3b607ce527.rlib` and `.rmeta` are both zero bytes (mtime Sep 27 01:13 UTC). I will clear only the `jwt-simple` package output and retry the production server build after the active shared-host browser run ends.
Author
Owner

Finding: the production screenshot flow reached setup confirmation and completed passkey login with HTTP 200, then failed its stale /today route assertion (apps/web/e2e/shell.mjs:860). The current /today route redirects to /calendar/<view>/<date> and the page renders .calendar (apps/web/src/routes/today/+page.ts, apps/web/src/routes/calendar/[view]/[date]/+page.svelte). The flow also waits on a /api/v1/notes/daily request that this route no longer makes. I am updating the E2E setup handoff check to follow the current Calendar route before retrying screenshots.

Finding: the production screenshot flow reached setup confirmation and completed passkey login with HTTP 200, then failed its stale `/today` route assertion (`apps/web/e2e/shell.mjs:860`). The current `/today` route redirects to `/calendar/<view>/<date>` and the page renders `.calendar` (`apps/web/src/routes/today/+page.ts`, `apps/web/src/routes/calendar/[view]/[date]/+page.svelte`). The flow also waits on a `/api/v1/notes/daily` request that this route no longer makes. I am updating the E2E setup handoff check to follow the current Calendar route before retrying screenshots.
Author
Owner

Update: the full shell screenshot flow also has Today-page assumptions in unrelated calendar checks. I kept this Settings job narrow and added SHELL3_SETTINGS_SCREENSHOT_ONLY=1 to the existing real-production shell harness. It reuses the real owner setup and passkey flow, creates a real invitation and member, checks role visibility, legacy Admin hash navigation and the member's 403 response, and captures the eight requested Settings variants. The helper is committed as ed5dcb7c and its Node syntax check passes; the production browser run is pending an active parallel browser job on the shared host.

Update: the full shell screenshot flow also has Today-page assumptions in unrelated calendar checks. I kept this Settings job narrow and added `SHELL3_SETTINGS_SCREENSHOT_ONLY=1` to the existing real-production shell harness. It reuses the real owner setup and passkey flow, creates a real invitation and member, checks role visibility, legacy Admin hash navigation and the member's 403 response, and captures the eight requested Settings variants. The helper is committed as `ed5dcb7c` and its Node syntax check passes; the production browser run is pending an active parallel browser job on the shared host.
Author
Owner

Finding: the targeted production flow captured all four Admin Settings variants, then stopped in createInvite. Its exact heading locator matched both the new page H1 and the existing Invitations group H2, so Playwright raised a strict-mode error. The page heading and group heading intentionally share the label; I am narrowing the helper to the group H2 before rerunning.

Finding: the targeted production flow captured all four Admin Settings variants, then stopped in `createInvite`. Its exact heading locator matched both the new page H1 and the existing Invitations group H2, so Playwright raised a strict-mode error. The page heading and group heading intentionally share the label; I am narrowing the helper to the group H2 before rerunning.
Author
Owner

Adversarial round on the merged branch completed its restart probe with 0 findings. Round 1 reported FINDINGS 0. Round 2 reported 7 findings: four SLOW latency/load observations (journal race seed 5: 6.8s; timezone log creates: 6.1s and 5.7s; public Edit oversized body: 17.0s/413), one SLOW thumbnail generation timeout at 30s, and two non-SLOW share-options observations: after the thumbnail timeout, the public 256/1024 thumbnail endpoints remained 404; a later Notes API create did not return 201, producing share setup edit Note: Notes API did not create the editable Note. The server stayed alive. I am checking these two observations under lower host load before final classification. The Settings production screenshot flow and workspace gates have passed.

Adversarial round on the merged branch completed its restart probe with 0 findings. Round 1 reported `FINDINGS 0`. Round 2 reported 7 findings: four SLOW latency/load observations (journal race seed 5: 6.8s; timezone log creates: 6.1s and 5.7s; public Edit oversized body: 17.0s/413), one SLOW thumbnail generation timeout at 30s, and two non-SLOW share-options observations: after the thumbnail timeout, the public 256/1024 thumbnail endpoints remained 404; a later Notes API create did not return 201, producing `share setup edit Note: Notes API did not create the editable Note`. The server stayed alive. I am checking these two observations under lower host load before final classification. The Settings production screenshot flow and workspace gates have passed.
Author
Owner

After merging current dev (21ee397d) before the final gates, cargo test stopped in calternal-collab --test shared_notes: owner_editor_viewer_and_live_revoke failed because the editor client did not converge after Share revoke. The test had passed on the earlier integrated tree. Its output logged Share row is absent and then timed out at shared Note clients did not converge: editor close after Share revoke (finished in 4.14s). I will rerun this crate in isolation after the active shared-host adversarial runs stop, then report whether it reproduces.

After merging current `dev` (`21ee397d`) before the final gates, `cargo test` stopped in `calternal-collab --test shared_notes`: `owner_editor_viewer_and_live_revoke` failed because the editor client did not converge after Share revoke. The test had passed on the earlier integrated tree. Its output logged `Share row is absent` and then timed out at `shared Note clients did not converge: editor close after Share revoke` (finished in 4.14s). I will rerun this crate in isolation after the active shared-host adversarial runs stop, then report whether it reproduces.
Author
Owner

Follow-up on the thumbnail observations: two other concurrent adversarial runs on the shared host reached the same share-options sequence and both emitted SLOW thumbnail was not generated within 30 s followed by {256: 404, 1024: 404}. This shows the 404 is downstream of the delayed derivative worker under current host load. The remaining non-SLOW observation is the Notes API create helper failure; I am checking whether it recurs when the host load is lower.

Follow-up on the thumbnail observations: two other concurrent adversarial runs on the shared host reached the same share-options sequence and both emitted `SLOW thumbnail was not generated within 30 s` followed by `{256: 404, 1024: 404}`. This shows the 404 is downstream of the delayed derivative worker under current host load. The remaining non-SLOW observation is the Notes API create helper failure; I am checking whether it recurs when the host load is lower.
Author
Owner

The targeted cargo test -p calternal-collab --test shared_notes -- --nocapture rerun also failed while a separate workspace adversarial run and test run were still active. This attempt timed out at the test's 3-second Note save did not start wait (the earlier workspace run timed out at editor close after Share revoke, 1.1 seconds). The test remains ambiguous under shared host load, so I will retry after that load ends before classifying it.

The targeted `cargo test -p calternal-collab --test shared_notes -- --nocapture` rerun also failed while a separate workspace adversarial run and test run were still active. This attempt timed out at the test's 3-second `Note save did not start` wait (the earlier workspace run timed out at `editor close after Share revoke`, 1.1 seconds). The test remains ambiguous under shared host load, so I will retry after that load ends before classifying it.
Author
Owner

The isolated collaboration integration test has now passed: cargo test -p calternal-collab --test shared_notes -- --nocapture reported 1 passed; 0 failed in 3.87s. It passed after the concurrent workspace Cargo process ended, while other API adversarial probes were still active. The earlier failures occurred during overlapping Cargo builds/tests and timed out at the test's 3-second save or 1.1-second close wait. I will rerun the full workspace test gate now.

The isolated collaboration integration test has now passed: `cargo test -p calternal-collab --test shared_notes -- --nocapture` reported `1 passed; 0 failed` in 3.87s. It passed after the concurrent workspace Cargo process ended, while other API adversarial probes were still active. The earlier failures occurred during overlapping Cargo builds/tests and timed out at the test's 3-second save or 1.1-second close wait. I will rerun the full workspace test gate now.
Author
Owner

The second post-merge cargo test run failed in calternal-plugin-files::media::tests::timeout_kills_the_whole_media_process_group: after the test sent SIGKILL, /proc/<child>/stat still reported the decoder descendant as R after the test's 10-second poll. The media crate was not changed by the latest dev merge. This is a process-cleanup check, so I am isolating it before deciding whether it is a real regression or host-load timing.

The second post-merge `cargo test` run failed in `calternal-plugin-files::media::tests::timeout_kills_the_whole_media_process_group`: after the test sent SIGKILL, `/proc/<child>/stat` still reported the decoder descendant as `R` after the test's 10-second poll. The media crate was not changed by the latest `dev` merge. This is a process-cleanup check, so I am isolating it before deciding whether it is a real regression or host-load timing.
Author
Owner

On the post-merge adversarial run, the upload during user purge returned HTTP 201 but took 3.26s, exceeding the probe's 2s latency threshold (write during user purge: upload took 3.26s; expected under 2 s). The host was running other workspace tests and adversarial servers. I have recorded this as a latency finding; the request succeeded and the purge integrity checks continue.

On the post-merge adversarial run, the upload during user purge returned HTTP 201 but took 3.26s, exceeding the probe's 2s latency threshold (`write during user purge: upload took 3.26s; expected under 2 s`). The host was running other workspace tests and adversarial servers. I have recorded this as a latency finding; the request succeeded and the purge integrity checks continue.
Author
Owner

Completed Forgejo #192 on job/settings-sections.

Built

  • Split Settings into User and Admin groups. Admin has separate Users, Invitations, Sign-in, Server configuration, Backups, Plugins, and System pages. Instance Plugins controls live under Admin → Plugins; per-user plugin settings remain under User → Plugins.
  • Preserved legacy /settings/admin#group links by mapping them to canonical section routes. Members are redirected from direct Admin routes to Account, and the Admin config API continues to return 403 for a member.
  • Added production browser coverage for role separation, legacy links, member redirects, and API authorization.
  • Captured eight production screenshots for Admin and member views across paper and tokyo-night themes at desktop and mobile sizes.

Files

  • apps/web/src/routes/settings/sections.ts
  • apps/web/src/routes/settings/sections.test.ts
  • apps/web/src/routes/settings/[...path]/+page.svelte
  • apps/web/src/routes/settings/admin/AdminSection.svelte
  • apps/web/src/routes/settings/plugins/PluginsSection.svelte
  • apps/web/src/lib/search/providers.ts
  • apps/web/e2e/shell.mjs
  • packages/ui/src/components/FloatingSidebar.svelte
  • artifacts/settings-sections/settings-sections-admin-1440-paper.png
  • artifacts/settings-sections/settings-sections-admin-390-paper.png
  • artifacts/settings-sections/settings-sections-admin-1440-tokyo-night.png
  • artifacts/settings-sections/settings-sections-admin-390-tokyo-night.png
  • artifacts/settings-sections/settings-sections-member-1440-paper.png
  • artifacts/settings-sections/settings-sections-member-390-paper.png
  • artifacts/settings-sections/settings-sections-member-1440-tokyo-night.png
  • artifacts/settings-sections/settings-sections-member-390-tokyo-night.png

Head

3612e3db93c514da1a23d56def375828b3e0b109 (includes the latest dev tip 21ee397d).

Gate output

  • cargo fmt --check: exit status 0; no output.
  • cargo clippy --all-targets -- -D warnings output:
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.66s
    
  • Full workspace cargo test with RUST_TEST_THREADS=4: exit status 0. Final crate output:
    test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.95s
  • bun run --cwd apps/web check:
    svelte-check found 0 errors and 0 warnings
  • bun run --cwd apps/web test:
    Test Files 69 passed (69)
    Tests 540 passed (540)
    JSDOM printed its existing CSS parse and Window's scrollTo() warnings; the command exited 0.
  • Production Settings browser flow:
    PASS Settings sections: role visibility, legacy links, member route and API access
  • git diff --check: exit status 0; no output.
  • Cleanup: Removed 19135 files, 16.2GiB total; apps/web/build was removed.

Adversarial round after latest dev merge

The server stayed alive. The 916-operation authorization matrix completed without an authorization finding. Round one reported three SLOW latency measurements; hostile-byte checks reported FINDINGS 0. Round two reported 24 findings: 22 SLOW latency measurements, one upload during user purge that returned 201 in 3.26s against a 2s threshold, and thumbnail 404s after the probe had already reported a SLOW 30s derivative timeout. The restart probe reported 0 findings. The 3.26s timing observation is filed in this issue. The thumbnail 404s were repeated in other concurrent runs and followed the explicit SLOW timeout. An earlier Notes API helper failure did not recur in the post-merge round or in two other share-option runs. The runner exited 1 because it reports SLOW timings and the threshold observations.

Known gaps and decisions

  • The general bun run screenshots flow still has stale /today route assumptions and fails before its full shell capture. The Settings-only production browser flow passed and produced the review artifacts listed above.
  • The first default-parallel workspace test attempts hit timing-sensitive collaboration and media process tests while other Cargo jobs were active. The collaboration test passed in isolation; the media process test passed in isolation and in the full workspace run with four test threads. The final full workspace gate passed with RUST_TEST_THREADS=4.
  • Decision not specified in docs/DESIGN.md: bare /settings/admin opens Admin → Users. Legacy Admin hash links map to canonical section routes to preserve existing links.
Completed Forgejo #192 on `job/settings-sections`. ## Built - Split Settings into User and Admin groups. Admin has separate Users, Invitations, Sign-in, Server configuration, Backups, Plugins, and System pages. Instance Plugins controls live under Admin → Plugins; per-user plugin settings remain under User → Plugins. - Preserved legacy `/settings/admin#group` links by mapping them to canonical section routes. Members are redirected from direct Admin routes to Account, and the Admin config API continues to return 403 for a member. - Added production browser coverage for role separation, legacy links, member redirects, and API authorization. - Captured eight production screenshots for Admin and member views across paper and tokyo-night themes at desktop and mobile sizes. ## Files - `apps/web/src/routes/settings/sections.ts` - `apps/web/src/routes/settings/sections.test.ts` - `apps/web/src/routes/settings/[...path]/+page.svelte` - `apps/web/src/routes/settings/admin/AdminSection.svelte` - `apps/web/src/routes/settings/plugins/PluginsSection.svelte` - `apps/web/src/lib/search/providers.ts` - `apps/web/e2e/shell.mjs` - `packages/ui/src/components/FloatingSidebar.svelte` - `artifacts/settings-sections/settings-sections-admin-1440-paper.png` - `artifacts/settings-sections/settings-sections-admin-390-paper.png` - `artifacts/settings-sections/settings-sections-admin-1440-tokyo-night.png` - `artifacts/settings-sections/settings-sections-admin-390-tokyo-night.png` - `artifacts/settings-sections/settings-sections-member-1440-paper.png` - `artifacts/settings-sections/settings-sections-member-390-paper.png` - `artifacts/settings-sections/settings-sections-member-1440-tokyo-night.png` - `artifacts/settings-sections/settings-sections-member-390-tokyo-night.png` ## Head `3612e3db93c514da1a23d56def375828b3e0b109` (includes the latest `dev` tip `21ee397d`). ## Gate output - `cargo fmt --check`: exit status 0; no output. - `cargo clippy --all-targets -- -D warnings` output: ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.66s ``` - Full workspace `cargo test` with `RUST_TEST_THREADS=4`: exit status 0. Final crate output: `test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.95s` - `bun run --cwd apps/web check`: `svelte-check found 0 errors and 0 warnings` - `bun run --cwd apps/web test`: ` Test Files 69 passed (69)` ` Tests 540 passed (540)` JSDOM printed its existing CSS parse and `Window's scrollTo()` warnings; the command exited 0. - Production Settings browser flow: `PASS Settings sections: role visibility, legacy links, member route and API access` - `git diff --check`: exit status 0; no output. - Cleanup: `Removed 19135 files, 16.2GiB total`; `apps/web/build` was removed. ## Adversarial round after latest dev merge The server stayed alive. The 916-operation authorization matrix completed without an authorization finding. Round one reported three SLOW latency measurements; hostile-byte checks reported `FINDINGS 0`. Round two reported 24 findings: 22 SLOW latency measurements, one upload during user purge that returned 201 in 3.26s against a 2s threshold, and thumbnail 404s after the probe had already reported a SLOW 30s derivative timeout. The restart probe reported `0 findings`. The 3.26s timing observation is filed in this issue. The thumbnail 404s were repeated in other concurrent runs and followed the explicit SLOW timeout. An earlier Notes API helper failure did not recur in the post-merge round or in two other share-option runs. The runner exited 1 because it reports SLOW timings and the threshold observations. ## Known gaps and decisions - The general `bun run screenshots` flow still has stale `/today` route assumptions and fails before its full shell capture. The Settings-only production browser flow passed and produced the review artifacts listed above. - The first default-parallel workspace test attempts hit timing-sensitive collaboration and media process tests while other Cargo jobs were active. The collaboration test passed in isolation; the media process test passed in isolation and in the full workspace run with four test threads. The final full workspace gate passed with `RUST_TEST_THREADS=4`. - Decision not specified in `docs/DESIGN.md`: bare `/settings/admin` opens Admin → Users. Legacy Admin hash links map to canonical section routes to preserve existing links.
Author
Owner

Merged into dev at 7bc249fd after Claude's review (User/Admin sidebar sections, admin pages split, standard users see User only). Deploy status on #203.

Merged into dev at 7bc249fd after Claude's review (User/Admin sidebar sections, admin pages split, standard users see User only). Deploy status on #203.
kayg closed this issue 2026-09-27 08:46:53 +00:00
Sign in to join this conversation.
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
kayg/calternal#192
No description provided.