feat: owner accounts and in-app platform settings

- First registered account becomes admin (users.is_admin, backfilled
  to earliest account); better-auth mirror path does the same
- Public registration closes after bootstrap; admin can reopen it via
  GET/PUT /api/v1/settings (admin-gated) or SIGNUP_ENABLED env
- In-app settings override env vars; secrets stored in app_settings,
  token values never returned by the API
- Saving a Cloudflare Tunnel token pulls and runs a managed
  cloudflared sidecar on the backend's networks; clearing removes it;
  startup restores it from saved settings
- group_add DOCKER_GID grants the non-root backend user Docker socket
  access; install.sh probes the in-container socket gid
- Settings page gains an owner-only Platform section (registration
  toggle, tunnel token, link to Cloudflare dashboard); sign-up page
  stays reachable while registration is open
- OpenAPI spec + generated types updated; manual user creation now
  requires admin

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-21 18:05:22 +02:00
1 parent c3959d13e2
commit c16b32f56a
18 files changed
+802 -34

No files matched your search

+2
View File
@@ -21,6 +21,7 @@ export type AuthBootstrap = {
userCount: number;
mode: 'register' | 'login';
providers: string[];
signupEnabled: boolean;
};
export class AuthError extends Error {
@@ -67,6 +68,7 @@ export async function getAuthBootstrap(): Promise<AuthBootstrap> {
userCount: typeof payload?.user_count === 'number' ? payload.user_count : 0,
mode: payload?.mode === 'register' ? 'register' : 'login',
providers,
signupEnabled: Boolean(payload?.signup_enabled),
};
}