feat(admin): granular access control, custom roles, and the real logo - #363
Merged
Merged
Conversation
The bracket glyph was a stand-in that needed an accent tile to be legible. BrandMark is the artwork the public sites ship, so the tile comes off with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The row's columns add up to more than the 720px `.content-form` allows, so the name track collapsed to zero and every column printed on top of its neighbour. A list belongs in `.content-narrow`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The permission list moves to constants so it can back a Postgres enum, and a super admin can now write roles of their own — bounded to the same catalogue the built-in ones use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A super admin picks a role, a mail scope and per-resource permissions in one dialog, and can write custom roles from the same matrix. A member's exceptions are stored as a diff, so changing their role still moves everything nobody touched by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
drizzle-kit writes these unformatted; every snapshot already in the tree has been through Biome. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pratyush618
approved these changes
Sep 22, 2026
This branch was successfully deployed
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.
Members table was overlapping itself
.row-memberdeclaresminmax(0, 1fr) 104px 140px 92px minmax(0, 320px)with a 16px gap — 720px of tracks and gaps before the name column gets anything. The page was in.content-form, which caps the container at 720px and then takes 48px of padding out of it, so the1frtrack resolved to zero and every column printed on top of the one before it.The members page is a list, not a column of form fields. It moves to
.content-narrow, and the invite panel stays the thing that constrains itself.Granular access
A super admin can now say exactly what one person may do, and write roles of their own to say it once instead of five times.
Schema. The permission catalogue moves to
@byteveda/db/constantsso it can back a real Postgres enum —admin_permission, fifteenresource.verbvalues — rather than atext[]that any typo fits into. A newadmin_custom_rolestable holds roles somebody wrote: a key, a label, a description, and a permission array.admin_usersgainscustom_role_idand two override columns.Resolution.
role (or custom role) → + extra_permissions → − denied_permissions, in catalogue order. Denied is applied last and wins over the role and over a contradicting extra, which is the only ordering that makes "take this away" mean it.members.manageis stripped at every write and at resolution, so nothing stored can carry the grant that grants grants.Overrides are a diff, not a snapshot. The dialog sends the permissions the operator ticked; the server computes the difference against the role and stores that. Move somebody to another role later and everything nobody touched by hand follows the new role.
Deleting a role does not delete access. The built-in
rolecolumn is kept underneath a custom role rather than cleared, so the foreign key'sset nulldrops the member back onto something. Tidying up a role should not be a way to lock the last editor out of the console.Interface
added/removedflag wherever the grant differs from the role.+2 −1when somebody's grant no longer matches their role.Two of the three controls that lived in the table row have moved into the dialog. A role, a scope and fifteen permissions were never going to fit beside a name — the version that tried is the bug at the top of this description.
Logo
Markwas an inline bracket glyph that needed an accent tile behind it to be legible, and was never the logo. It now rendersBrandMark, the artwork the public sites ship. The tile comes off with the glyph. Reaches the rail, the login sheet, the error page and both subscription pages.apps/admin's vitest config learns to resolve a static image import the waynext builddoes — reading the real dimensions out of the PNG header — otherwise any test rendering a page with the logo on it throws insidenext/image.Checks
pnpm -r typecheckclean. 232 admin tests and 51 db tests pass, including nine new ones covering custom roles, override precedence and the reserved grant.next buildclean. Biome at baseline.Not visually verified —
/membersis behind GitHub OAuth. The layout fix is confirmed by track arithmetic.Migration
0009_custom_roleshas not been applied anywhere.apps/admin's build runsdb:deployfirst, so deploying carries it; locally it ispnpm --filter @byteveda/db db:deploy.🤖 Generated with Claude Code