feat: add namespace-based multi-user isolation

Implement namespace support across sessions, machines, and users for multi-user server deployments. Add access control with specific error reasons (namespace-missing, access-denied, not-found) and database schema updates with namespace columns and indexes.
This commit is contained in:
weishu
2025-12-31 21:56:01 +08:00
parent 460c393006
commit e821458af8
30 changed files with 737 additions and 124 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ React Mini App / PWA for monitoring and controlling hapi sessions.
## Runtime behavior
- When opened inside Telegram, auth uses Telegram WebApp init data.
- When opened in a normal browser, you can log in with the shared `CLI_API_TOKEN`.
- When opened in a normal browser, you can log in with `CLI_API_TOKEN:<namespace>` (or `CLI_API_TOKEN` for the default namespace).
- The login screen includes a top-right server picker; if unset, the app uses the same origin it was loaded from.
- Live updates come from the server via SSE.
+2 -2
View File
@@ -97,7 +97,7 @@ export function LoginPrompt(props: LoginPromptProps) {
? 'Enter your access token to bind this Telegram account'
: 'Enter your access token to continue'
const submitLabel = isBindMode ? 'Bind' : 'Sign In'
const helpText = 'Use the CLI_API_TOKEN from your server configuration'
const helpText = 'Use CLI_API_TOKEN:<namespace> from your server configuration (omit :<namespace> for default)'
return (
<div className="relative h-full flex items-center justify-center p-4">
@@ -177,7 +177,7 @@ export function LoginPrompt(props: LoginPromptProps) {
type="password"
value={accessToken}
onChange={(e) => setAccessToken(e.target.value)}
placeholder={isBindMode ? 'CLI_API_TOKEN' : 'Access Token'}
placeholder={isBindMode ? 'CLI_API_TOKEN:<namespace>' : 'CLI_API_TOKEN[:namespace]'}
autoComplete="current-password"
disabled={isLoading}
className="w-full px-3 py-2.5 rounded-lg border border-[var(--app-border)] bg-[var(--app-bg)] text-[var(--app-fg)] placeholder:text-[var(--app-hint)] focus:outline-none focus:ring-2 focus:ring-[var(--app-button)] focus:border-transparent disabled:opacity-50"
+6 -6
View File
@@ -198,9 +198,9 @@ export type SlashCommandsResponse = {
}
export type SyncEvent =
| { type: 'session-added'; sessionId: string; data?: unknown }
| { type: 'session-updated'; sessionId: string; data?: unknown }
| { type: 'session-removed'; sessionId: string }
| { type: 'message-received'; sessionId: string; message: DecryptedMessage }
| { type: 'machine-updated'; machineId: string; data?: unknown }
| { type: 'connection-changed'; data?: { status: string } }
| { type: 'session-added'; sessionId: string; data?: unknown; namespace?: string }
| { type: 'session-updated'; sessionId: string; data?: unknown; namespace?: string }
| { type: 'session-removed'; sessionId: string; namespace?: string }
| { type: 'message-received'; sessionId: string; message: DecryptedMessage; namespace?: string }
| { type: 'machine-updated'; machineId: string; data?: unknown; namespace?: string }
| { type: 'connection-changed'; data?: { status: string }; namespace?: string }