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

@ -0,0 +1,21 @@
import { apiFetch, type ApiResult } from '$lib/api/client';
import type { AuditLog, AuditListResponse } from '$lib/api/audit';
export type { AuditLog, AuditListResponse };
export async function listAdminAuditLogs(params?: {
before?: string;
before_id?: string;
resource_types?: string[];
actions?: string[];
limit?: number;
}): Promise<ApiResult<AuditListResponse>> {
const q = new URLSearchParams();
if (params?.before) q.set('before', params.before);
if (params?.before_id) q.set('before_id', params.before_id);
params?.resource_types?.forEach((t) => q.append('resource_type', t));
params?.actions?.forEach((a) => q.append('action', a));
if (params?.limit != null) q.set('limit', String(params.limit));
const qs = q.toString();
return apiFetch('GET', `/api/v1/admin/audit-logs${qs ? '?' + qs : ''}`);
}

View File

@ -13,7 +13,8 @@
IconChevron,
IconShield,
IconMembers,
IconUser
IconUser,
IconAudit
} from './icons';
let { collapsed = $bindable(false) }: { collapsed: boolean } = $props();
@ -26,7 +27,8 @@
const managementItems: NavItem[] = [
{ label: 'Users', icon: IconUser, href: '/admin/users' },
{ label: 'Teams', icon: IconMembers, href: '/admin/teams' }
{ label: 'Teams', icon: IconMembers, href: '/admin/teams' },
{ label: 'Audit', icon: IconAudit, href: '/admin/audit' }
];
const platformItems: NavItem[] = [