1
0
forked from wrenn/wrenn
## What's new

Compliance, audit, and account lifecycle improvements — admin actions are now fully auditable, user data is properly anonymized on deletion, and OAuth signup flow gives users control over their profile.

### Audit

- Added audit logging for all admin actions (user activate/deactivate, team BYOC toggle, team delete, template delete, build create/cancel)
- Added admin audit page with infinite scroll and hierarchical filters
- Fixed audit log team assignment — admin/host actions now correctly land under PlatformTeamID
- Anonymize audit logs on user hard-delete (actor name, IDs, emails stripped)
- Deduplicated audit logger internals (665 → 374 lines, no behavior change)

### Authentication

- Separated GitHub OAuth login/signup flows — login no longer auto-creates accounts
- Added name confirmation dialog for new GitHub signups

### Account Lifecycle

- Email notification sent when account is permanently deleted after grace period
- Audit log anonymization tied to user purge (per-user transactional)

### UX

- Removed accent gradient bars from admin host dialogs (border + shadow only)
- Frontend renders deleted users as styled badge in audit log view

### Others

- Version bump
- Bug fixes

Reviewed-on: wrenn/wrenn#36
This commit is contained in:
2026-04-21 10:11:49 +00:00
parent 23dca7d9ff
commit 52ad21c339
25 changed files with 1200 additions and 443 deletions

View File

@ -392,6 +392,7 @@ func (h *teamHandler) Leave(w http.ResponseWriter, r *http.Request) {
// SetBYOC handles PUT /v1/admin/teams/{id}/byoc (admin only).
// Enables or disables the BYOC feature flag for a team.
func (h *teamHandler) SetBYOC(w http.ResponseWriter, r *http.Request) {
ac := auth.MustFromContext(r.Context())
teamIDStr := chi.URLParam(r, "id")
teamID, err := id.ParseTeamID(teamIDStr)
@ -414,6 +415,7 @@ func (h *teamHandler) SetBYOC(w http.ResponseWriter, r *http.Request) {
return
}
h.audit.LogTeamSetBYOC(r.Context(), ac, teamID, req.Enabled)
w.WriteHeader(http.StatusNoContent)
}
@ -484,6 +486,7 @@ func (h *teamHandler) AdminListTeams(w http.ResponseWriter, r *http.Request) {
// AdminDeleteTeam handles DELETE /v1/admin/teams/{id}
// Soft-deletes a team and destroys all its active sandboxes.
func (h *teamHandler) AdminDeleteTeam(w http.ResponseWriter, r *http.Request) {
ac := auth.MustFromContext(r.Context())
teamIDStr := chi.URLParam(r, "id")
teamID, err := id.ParseTeamID(teamIDStr)
@ -498,5 +501,6 @@ func (h *teamHandler) AdminDeleteTeam(w http.ResponseWriter, r *http.Request) {
return
}
h.audit.LogTeamDelete(r.Context(), ac, teamID)
w.WriteHeader(http.StatusNoContent)
}