- Migration 000035: three new tables.
groups: id, name, kind (haendler|kuenstler|lager), created_by.
group_members: id, group_id, user_id, role (admin|member) + UNIQUE
constraint; indexed on user_id and group_id for fast membership lookup.
group_profiles: one-to-one with groups — description, categories (TEXT[]),
avatar_url, website_url; upserted so the profile always exists after group
creation.
- internal/domain/group package: model, repository (pgx), service, handler,
routes. Public routes: GET /groups/:id, GET /groups/:id/members.
Auth-gated routes: POST /groups, PATCH /groups/:id/profile,
POST /groups/:id/members, DELETE /groups/:id/members/:userId,
GET /users/me/groups.
- Authorization is group-scoped (not platform-role): UpdateProfile and
AddMember require group admin role; RemoveMember allows self-remove or
admin. Removing the last admin is blocked (ErrCannotRemoveLastAdmin).
Creator is automatically added as admin on group creation.
- Security tests (Iron Law): 8 PoC tests covering 401 for unauthenticated
requests on all auth-gated endpoints, 403 for non-admin on profile/member
write endpoints, self-remove happy path, last-admin guard, create happy
path (creator becomes admin), and public endpoint accessibility.
- Migration 000034: add users.status (pending|active|suspended) with
CHECK, users.approved_at, and users.role CHECK constraint covering
all 6 planned roles (gast, user, veranstalter, haendler, lager, admin).
Existing rows default to status='active' to preserve behaviour.
- Role and status constants in user/roles.go (single source of truth).
- User model extended with Status and ApprovedAt; all repository
RETURNING/Scan clauses updated accordingly.
- Repository interface extended with ListByStatus and SetStatus.
- Admin approval queue: GET /admin/users/pending, POST /admin/users/:id/approve,
POST /admin/users/:id/reject. Guarded by RequireAuth + RequireRole("admin").
Approve/reject revokes all target user sessions before updating status
(session cache carries stale role data; revocation forces re-login).
- SessionRevoker interface defined in user package to avoid import cycle
with auth package. auth.Repository satisfies it at the wiring layer.
- Login now gates suspended accounts (returns "account suspended" error
so new sessions cannot be created after rejection).
- Security tests (Iron Law): failing PoC tests written for all three
admin endpoints verifying 401 for unauthenticated, 403 for non-admin,
and correct status transitions + session revocation for admin.
Updates static/favicon.svg (was the old forest-green MedievalSharp signet),
regenerates favicon.ico, favicon-32.png, apple-touch-icon.png. Fixes
site.webmanifest theme_color and background_color to Burgund parchment.
Swaps app.html font preloads to EB Garamond + Cormorant Garamond and
theme-color meta tags to Burgund bg values.