feat: roles system backend

This commit is contained in:
Florian Sylvain committed 2026-10-26 05:02:49 +01:00
1 parent 44cca46d5e
commit 8c6c6456da
50 files changed
+2578 -22

No files matched your search

+8
View File
@@ -111,6 +111,14 @@ The cache screen adds three ports in `internal/domain/image`: `VariantCacheRepos
At boot, `BackfillImageMetadataUseCase` (through `image.Repository.ListMissingMetadata` and `UpdateMetadata`) fills `filename`, `size`, `content_type`, `width`, `height` and `sha256` for image rows that lack them, reading the bytes through `image.Storage` (the files in `UPLOAD_DIR` with the local adapter, the bucket objects with the S3 adapter). It is idempotent, and a row whose file is missing or unreadable is left as it is without stopping the boot. The old `article_id` column and the `article_images` table are kept and never written.
### Roles and permissions
`internal/domain/role` holds the permission catalogue, the built-in roles and the pure rules over grants: `ValidateGrants`, `Role.Allows`, `Role.AllowsFor` and `ParseTypedKey`. A toggle permission only counts at scope `all`, a scoped one at `own` or `all`, and an owner-only permission only counts on the locked Owner role. A content permission can be narrowed or widened per content type with a key such as `content.update:blog`; the specific grant overrides the wildcard, and without an override the wildcard applies. The existence of the type key is not checked in the domain. `role.Repository.Save` is version checked: it updates the row only when the caller's version matches, bumps the version, replaces the grants in the same transaction and returns `ErrRoleConflict` otherwise. It never changes `built_in` or `locked`.
Built-in reconciliation: `database.SeedBuiltInRoles` creates the three built-in roles when they are missing and never overwrites an edited one. On every start it adds the catalogue keys a built-in role lacks: always for Owner, at scope `all`, and for Admin and Writer only while their version is still 1. It never removes or downgrades a grant and never bumps the version.
Created-by backfill: `database.BackfillCreatedBy` assigns articles (soft-deleted ones included) and entries whose `created_by` is 0 to the first owner by id. A database without an owner is left alone. Entries have no soft delete, so no unscoped query is needed for them.
## Frontend ↔ backend
```