@qwickapps/server 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/core/control-panel.d.ts.map +1 -1
- package/dist/core/control-panel.js +41 -0
- package/dist/core/control-panel.js.map +1 -1
- package/dist/core/guards.d.ts.map +1 -1
- package/dist/core/guards.js +77 -0
- package/dist/core/guards.js.map +1 -1
- package/dist/core/health-manager.d.ts +4 -0
- package/dist/core/health-manager.d.ts.map +1 -1
- package/dist/core/health-manager.js +6 -1
- package/dist/core/health-manager.js.map +1 -1
- package/dist/core/plugin-registry.d.ts +55 -5
- package/dist/core/plugin-registry.d.ts.map +1 -1
- package/dist/core/plugin-registry.js +57 -19
- package/dist/core/plugin-registry.js.map +1 -1
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/api-keys/api-keys-plugin.d.ts +46 -0
- package/dist/plugins/api-keys/api-keys-plugin.d.ts.map +1 -0
- package/dist/plugins/api-keys/api-keys-plugin.js +329 -0
- package/dist/plugins/api-keys/api-keys-plugin.js.map +1 -0
- package/dist/plugins/api-keys/index.d.ts +14 -0
- package/dist/plugins/api-keys/index.d.ts.map +1 -0
- package/dist/plugins/api-keys/index.js +17 -0
- package/dist/plugins/api-keys/index.js.map +1 -0
- package/dist/plugins/api-keys/middleware/bearer-token-auth.d.ts +74 -0
- package/dist/plugins/api-keys/middleware/bearer-token-auth.d.ts.map +1 -0
- package/dist/plugins/api-keys/middleware/bearer-token-auth.js +201 -0
- package/dist/plugins/api-keys/middleware/bearer-token-auth.js.map +1 -0
- package/dist/plugins/api-keys/middleware/index.d.ts +7 -0
- package/dist/plugins/api-keys/middleware/index.d.ts.map +1 -0
- package/dist/plugins/api-keys/middleware/index.js +7 -0
- package/dist/plugins/api-keys/middleware/index.js.map +1 -0
- package/dist/plugins/api-keys/stores/index.d.ts +7 -0
- package/dist/plugins/api-keys/stores/index.d.ts.map +1 -0
- package/dist/plugins/api-keys/stores/index.js +7 -0
- package/dist/plugins/api-keys/stores/index.js.map +1 -0
- package/dist/plugins/api-keys/stores/postgres-store.d.ts +34 -0
- package/dist/plugins/api-keys/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/api-keys/stores/postgres-store.js +360 -0
- package/dist/plugins/api-keys/stores/postgres-store.js.map +1 -0
- package/dist/plugins/api-keys/types.d.ts +268 -0
- package/dist/plugins/api-keys/types.d.ts.map +1 -0
- package/dist/plugins/api-keys/types.js +56 -0
- package/dist/plugins/api-keys/types.js.map +1 -0
- package/dist/plugins/auth/auth-plugin.d.ts.map +1 -1
- package/dist/plugins/auth/auth-plugin.js +17 -1
- package/dist/plugins/auth/auth-plugin.js.map +1 -1
- package/dist/plugins/auth/auth-plugin.test.js +133 -0
- package/dist/plugins/auth/auth-plugin.test.js.map +1 -1
- package/dist/plugins/auth/env-config.d.ts.map +1 -1
- package/dist/plugins/auth/env-config.js +6 -2
- package/dist/plugins/auth/env-config.js.map +1 -1
- package/dist/plugins/auth/types.d.ts +10 -0
- package/dist/plugins/auth/types.d.ts.map +1 -1
- package/dist/plugins/auth/types.js.map +1 -1
- package/dist/plugins/devices/__tests__/token-utils.test.js +4 -2
- package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -1
- package/dist/plugins/frontend-app-plugin.d.ts.map +1 -1
- package/dist/plugins/frontend-app-plugin.js +21 -4
- package/dist/plugins/frontend-app-plugin.js.map +1 -1
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/qwickbrain/index.d.ts +25 -0
- package/dist/plugins/qwickbrain/index.d.ts.map +1 -0
- package/dist/plugins/qwickbrain/index.js +24 -0
- package/dist/plugins/qwickbrain/index.js.map +1 -0
- package/dist/plugins/qwickbrain/qwickbrain-plugin.d.ts +23 -0
- package/dist/plugins/qwickbrain/qwickbrain-plugin.d.ts.map +1 -0
- package/dist/plugins/qwickbrain/qwickbrain-plugin.js +528 -0
- package/dist/plugins/qwickbrain/qwickbrain-plugin.js.map +1 -0
- package/dist/plugins/qwickbrain/types.d.ts +131 -0
- package/dist/plugins/qwickbrain/types.d.ts.map +1 -0
- package/dist/plugins/qwickbrain/types.js +9 -0
- package/dist/plugins/qwickbrain/types.js.map +1 -0
- package/dist/plugins/users/__tests__/postgres-store.test.js +1 -0
- package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -1
- package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
- package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
- package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
- package/dist/plugins/users/stores/postgres-store.js +59 -1
- package/dist/plugins/users/stores/postgres-store.js.map +1 -1
- package/dist/plugins/users/types.d.ts +22 -0
- package/dist/plugins/users/types.d.ts.map +1 -1
- package/dist-ui/assets/index-5nX8fM1a.js +469 -0
- package/dist-ui/assets/index-5nX8fM1a.js.map +1 -0
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/api/controlPanelApi.d.ts +68 -0
- package/dist-ui-lib/components/index.d.ts +2 -1
- package/dist-ui-lib/index.js +2642 -2281
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/pages/APIKeysPage.d.ts +13 -0
- package/dist-ui-lib/pages/AcceptInvitationPage.d.ts +28 -0
- package/package.json +3 -2
- package/src/core/control-panel.ts +47 -0
- package/src/core/guards.ts +89 -0
- package/src/core/health-manager.ts +6 -1
- package/src/core/plugin-registry.ts +123 -25
- package/src/core/types.ts +2 -0
- package/src/index.ts +11 -0
- package/src/plugins/api-keys/api-keys-plugin.ts +397 -0
- package/src/plugins/api-keys/index.ts +49 -0
- package/src/plugins/api-keys/middleware/bearer-token-auth.ts +250 -0
- package/src/plugins/api-keys/middleware/index.ts +12 -0
- package/src/plugins/api-keys/stores/index.ts +7 -0
- package/src/plugins/api-keys/stores/postgres-store.ts +487 -0
- package/src/plugins/api-keys/types.ts +243 -0
- package/src/plugins/auth/auth-plugin.test.ts +167 -0
- package/src/plugins/auth/auth-plugin.ts +17 -1
- package/src/plugins/auth/env-config.ts +6 -2
- package/src/plugins/auth/types.ts +10 -0
- package/src/plugins/devices/__tests__/token-utils.test.ts +4 -2
- package/src/plugins/frontend-app-plugin.ts +24 -4
- package/src/plugins/index.ts +15 -0
- package/src/plugins/qwickbrain/index.ts +33 -0
- package/src/plugins/qwickbrain/qwickbrain-plugin.ts +642 -0
- package/src/plugins/qwickbrain/types.ts +146 -0
- package/src/plugins/users/__tests__/postgres-store.test.ts +1 -0
- package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
- package/src/plugins/users/stores/postgres-store.ts +69 -0
- package/src/plugins/users/types.ts +25 -0
- package/ui/src/App.tsx +6 -1
- package/ui/src/api/controlPanelApi.ts +206 -37
- package/ui/src/components/index.ts +6 -0
- package/ui/src/pages/APIKeysPage.tsx +661 -0
- package/ui/src/pages/AcceptInvitationPage.tsx +169 -0
- package/ui/src/pages/UsersPage.tsx +225 -2
- package/dist-ui/assets/index-CynOqPkb.js +0 -469
- package/dist-ui/assets/index-CynOqPkb.js.map +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QwickBrain Plugin Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the QwickBrain MCP proxy plugin.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for the QwickBrain plugin
|
|
11
|
+
*/
|
|
12
|
+
export interface QwickBrainPluginConfig {
|
|
13
|
+
/**
|
|
14
|
+
* Base URL of the QwickBrain instance (via Tailscale)
|
|
15
|
+
* Example: "http://macmini.tailnet-xxx.ts.net:8080"
|
|
16
|
+
*/
|
|
17
|
+
qwickbrainUrl: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Request timeout in milliseconds
|
|
21
|
+
* @default 30000
|
|
22
|
+
*/
|
|
23
|
+
timeout?: number;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Which MCP tools to expose publicly
|
|
27
|
+
* Use '*' to expose all tools, or provide a list of tool names
|
|
28
|
+
* @default '*'
|
|
29
|
+
*/
|
|
30
|
+
exposedTools?: string[] | '*';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* API configuration
|
|
34
|
+
*/
|
|
35
|
+
api?: {
|
|
36
|
+
/**
|
|
37
|
+
* Enable API routes
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* API route prefix (mounted under /api)
|
|
44
|
+
* @default '/mcp'
|
|
45
|
+
*/
|
|
46
|
+
prefix?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Authentication configuration
|
|
51
|
+
*/
|
|
52
|
+
auth?: {
|
|
53
|
+
/**
|
|
54
|
+
* Require authentication for MCP tool endpoints
|
|
55
|
+
* When true, /mcp/tools and /mcp/tools/:name require valid auth
|
|
56
|
+
* Status endpoint (/mcp/status) is always public
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
required?: boolean;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Roles required to access MCP tools (optional)
|
|
63
|
+
* If set, user must have at least one of these roles
|
|
64
|
+
*/
|
|
65
|
+
allowedRoles?: string[];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Rate limiting configuration
|
|
70
|
+
*/
|
|
71
|
+
rateLimit?: MCPRateLimitConfig;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Enable debug logging
|
|
75
|
+
* @default false
|
|
76
|
+
*/
|
|
77
|
+
debug?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* MCP Tool definition from QwickBrain
|
|
82
|
+
*/
|
|
83
|
+
export interface MCPToolDefinition {
|
|
84
|
+
name: string;
|
|
85
|
+
description: string;
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: 'object';
|
|
88
|
+
properties: Record<string, unknown>;
|
|
89
|
+
required?: string[];
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* MCP Tool call request
|
|
95
|
+
*/
|
|
96
|
+
export interface MCPToolCallRequest {
|
|
97
|
+
name: string;
|
|
98
|
+
arguments: Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* MCP Tool call response
|
|
103
|
+
*/
|
|
104
|
+
export interface MCPToolCallResponse {
|
|
105
|
+
content: Array<{
|
|
106
|
+
type: 'text' | 'image' | 'resource';
|
|
107
|
+
text?: string;
|
|
108
|
+
data?: string;
|
|
109
|
+
mimeType?: string;
|
|
110
|
+
}>;
|
|
111
|
+
isError?: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* QwickBrain connection status
|
|
116
|
+
*/
|
|
117
|
+
export interface QwickBrainConnectionStatus {
|
|
118
|
+
connected: boolean;
|
|
119
|
+
lastCheck: Date;
|
|
120
|
+
latencyMs?: number;
|
|
121
|
+
error?: string;
|
|
122
|
+
tailscaleStatus?: 'connected' | 'disconnected' | 'unknown';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Rate limit configuration for MCP requests
|
|
127
|
+
*/
|
|
128
|
+
export interface MCPRateLimitConfig {
|
|
129
|
+
/**
|
|
130
|
+
* Enable rate limiting
|
|
131
|
+
* @default true
|
|
132
|
+
*/
|
|
133
|
+
enabled?: boolean;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Requests per minute per client
|
|
137
|
+
* @default 60
|
|
138
|
+
*/
|
|
139
|
+
perClientPerMinute?: number;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Total requests per minute (global)
|
|
143
|
+
* @default 1000
|
|
144
|
+
*/
|
|
145
|
+
globalPerMinute?: number;
|
|
146
|
+
}
|
|
@@ -44,6 +44,7 @@ describe('Users Plugin', () => {
|
|
|
44
44
|
external_id: 'auth0|abc123',
|
|
45
45
|
provider: 'auth0',
|
|
46
46
|
picture: 'https://example.com/avatar.jpg',
|
|
47
|
+
status: 'active',
|
|
47
48
|
created_at: new Date('2025-01-01'),
|
|
48
49
|
updated_at: new Date('2025-01-01'),
|
|
49
50
|
last_login_at: new Date('2025-12-13'),
|
|
@@ -64,6 +65,8 @@ describe('Users Plugin', () => {
|
|
|
64
65
|
delete: vi.fn().mockResolvedValue(true),
|
|
65
66
|
search: vi.fn().mockResolvedValue({ users: [mockUser], total: 1, page: 1, limit: 20, totalPages: 1 }),
|
|
66
67
|
updateLastLogin: vi.fn().mockResolvedValue(undefined),
|
|
68
|
+
getByInvitationToken: vi.fn().mockResolvedValue(mockUser),
|
|
69
|
+
acceptInvitation: vi.fn().mockResolvedValue(mockUser),
|
|
67
70
|
shutdown: vi.fn().mockResolvedValue(undefined),
|
|
68
71
|
});
|
|
69
72
|
|
|
@@ -72,6 +72,9 @@ export function postgresUserStore(config: PostgresUserStoreConfig): UserStore {
|
|
|
72
72
|
external_id VARCHAR(255),
|
|
73
73
|
provider VARCHAR(50),
|
|
74
74
|
picture TEXT,
|
|
75
|
+
status VARCHAR(20) DEFAULT 'active',
|
|
76
|
+
invitation_token VARCHAR(255),
|
|
77
|
+
invitation_expires_at TIMESTAMPTZ,
|
|
75
78
|
metadata JSONB DEFAULT '{}',
|
|
76
79
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
77
80
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
@@ -80,6 +83,41 @@ export function postgresUserStore(config: PostgresUserStoreConfig): UserStore {
|
|
|
80
83
|
|
|
81
84
|
CREATE INDEX IF NOT EXISTS idx_${usersTable}_email ON ${usersTableFull}(email);
|
|
82
85
|
CREATE INDEX IF NOT EXISTS idx_${usersTable}_external_id ON ${usersTableFull}(external_id, provider);
|
|
86
|
+
CREATE INDEX IF NOT EXISTS idx_${usersTable}_invitation_token ON ${usersTableFull}(invitation_token);
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_${usersTable}_status ON ${usersTableFull}(status);
|
|
88
|
+
`);
|
|
89
|
+
|
|
90
|
+
// Add new columns to existing tables (migration)
|
|
91
|
+
await getPool().query(`
|
|
92
|
+
DO $$
|
|
93
|
+
BEGIN
|
|
94
|
+
IF NOT EXISTS (
|
|
95
|
+
SELECT 1 FROM information_schema.columns
|
|
96
|
+
WHERE table_schema = '${schema}'
|
|
97
|
+
AND table_name = '${usersTable}'
|
|
98
|
+
AND column_name = 'status'
|
|
99
|
+
) THEN
|
|
100
|
+
ALTER TABLE ${usersTableFull} ADD COLUMN status VARCHAR(20) DEFAULT 'active';
|
|
101
|
+
END IF;
|
|
102
|
+
|
|
103
|
+
IF NOT EXISTS (
|
|
104
|
+
SELECT 1 FROM information_schema.columns
|
|
105
|
+
WHERE table_schema = '${schema}'
|
|
106
|
+
AND table_name = '${usersTable}'
|
|
107
|
+
AND column_name = 'invitation_token'
|
|
108
|
+
) THEN
|
|
109
|
+
ALTER TABLE ${usersTableFull} ADD COLUMN invitation_token VARCHAR(255);
|
|
110
|
+
END IF;
|
|
111
|
+
|
|
112
|
+
IF NOT EXISTS (
|
|
113
|
+
SELECT 1 FROM information_schema.columns
|
|
114
|
+
WHERE table_schema = '${schema}'
|
|
115
|
+
AND table_name = '${usersTable}'
|
|
116
|
+
AND column_name = 'invitation_expires_at'
|
|
117
|
+
) THEN
|
|
118
|
+
ALTER TABLE ${usersTableFull} ADD COLUMN invitation_expires_at TIMESTAMPTZ;
|
|
119
|
+
END IF;
|
|
120
|
+
END $$;
|
|
83
121
|
`);
|
|
84
122
|
},
|
|
85
123
|
|
|
@@ -263,6 +301,7 @@ export function postgresUserStore(config: PostgresUserStoreConfig): UserStore {
|
|
|
263
301
|
const {
|
|
264
302
|
query,
|
|
265
303
|
provider,
|
|
304
|
+
status,
|
|
266
305
|
page = 1,
|
|
267
306
|
limit = 20,
|
|
268
307
|
sortBy = 'created_at',
|
|
@@ -285,6 +324,12 @@ export function postgresUserStore(config: PostgresUserStoreConfig): UserStore {
|
|
|
285
324
|
paramIndex++;
|
|
286
325
|
}
|
|
287
326
|
|
|
327
|
+
if (status) {
|
|
328
|
+
conditions.push(`status = $${paramIndex}`);
|
|
329
|
+
values.push(status);
|
|
330
|
+
paramIndex++;
|
|
331
|
+
}
|
|
332
|
+
|
|
288
333
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
289
334
|
|
|
290
335
|
// Validate sort column to prevent SQL injection
|
|
@@ -322,6 +367,30 @@ export function postgresUserStore(config: PostgresUserStoreConfig): UserStore {
|
|
|
322
367
|
await getPool().query(`UPDATE ${usersTableFull} SET last_login_at = NOW() WHERE id = $1`, [id]);
|
|
323
368
|
},
|
|
324
369
|
|
|
370
|
+
async getByInvitationToken(token: string): Promise<User | null> {
|
|
371
|
+
const result = await getPool().query(
|
|
372
|
+
`SELECT * FROM ${usersTableFull} WHERE invitation_token = $1 AND invitation_expires_at > NOW()`,
|
|
373
|
+
[token]
|
|
374
|
+
);
|
|
375
|
+
return (result.rows[0] as User) || null;
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
async acceptInvitation(token: string): Promise<User | null> {
|
|
379
|
+
const result = await getPool().query(
|
|
380
|
+
`UPDATE ${usersTableFull}
|
|
381
|
+
SET status = 'active',
|
|
382
|
+
invitation_token = NULL,
|
|
383
|
+
invitation_expires_at = NULL,
|
|
384
|
+
updated_at = NOW()
|
|
385
|
+
WHERE invitation_token = $1
|
|
386
|
+
AND invitation_expires_at > NOW()
|
|
387
|
+
AND status = 'invited'
|
|
388
|
+
RETURNING *`,
|
|
389
|
+
[token]
|
|
390
|
+
);
|
|
391
|
+
return (result.rows[0] as User) || null;
|
|
392
|
+
},
|
|
393
|
+
|
|
325
394
|
async shutdown(): Promise<void> {
|
|
326
395
|
// Pool is managed externally, nothing to do here
|
|
327
396
|
},
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* User status in the system
|
|
14
|
+
*/
|
|
15
|
+
export type UserStatus = 'invited' | 'active' | 'suspended';
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* User record in the database
|
|
14
19
|
*/
|
|
@@ -25,6 +30,12 @@ export interface User {
|
|
|
25
30
|
provider?: string;
|
|
26
31
|
/** Profile picture URL */
|
|
27
32
|
picture?: string;
|
|
33
|
+
/** User status */
|
|
34
|
+
status: UserStatus;
|
|
35
|
+
/** Invitation token (set when user is invited) */
|
|
36
|
+
invitation_token?: string;
|
|
37
|
+
/** Invitation expiration timestamp */
|
|
38
|
+
invitation_expires_at?: Date;
|
|
28
39
|
/** Additional metadata (JSON) */
|
|
29
40
|
metadata?: Record<string, unknown>;
|
|
30
41
|
/** When the user was created */
|
|
@@ -90,6 +101,8 @@ export interface UserSearchParams {
|
|
|
90
101
|
query?: string;
|
|
91
102
|
/** Filter by provider */
|
|
92
103
|
provider?: string;
|
|
104
|
+
/** Filter by status */
|
|
105
|
+
status?: UserStatus;
|
|
93
106
|
/** Page number (1-indexed) */
|
|
94
107
|
page?: number;
|
|
95
108
|
/** Items per page */
|
|
@@ -182,6 +195,18 @@ export interface UserStore {
|
|
|
182
195
|
*/
|
|
183
196
|
updateLastLogin(id: string): Promise<void>;
|
|
184
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Get a user by invitation token (only if invitation is valid and not expired)
|
|
200
|
+
*/
|
|
201
|
+
getByInvitationToken(token: string): Promise<User | null>;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Accept an invitation by token.
|
|
205
|
+
* Sets status to 'active' and clears invitation token fields.
|
|
206
|
+
* Returns the updated user or null if token is invalid/expired.
|
|
207
|
+
*/
|
|
208
|
+
acceptInvitation(token: string): Promise<User | null>;
|
|
209
|
+
|
|
185
210
|
/**
|
|
186
211
|
* Shutdown the store
|
|
187
212
|
*/
|
package/ui/src/App.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import { AuthPage } from './pages/AuthPage';
|
|
|
18
18
|
import { RateLimitPage } from './pages/RateLimitPage';
|
|
19
19
|
import { NotificationsPage } from './pages/NotificationsPage';
|
|
20
20
|
import { IntegrationsPage } from './pages/IntegrationsPage';
|
|
21
|
+
import { APIKeysPage } from './pages/APIKeysPage';
|
|
21
22
|
import { PluginPage } from './pages/PluginPage';
|
|
22
23
|
import { NotFoundPage } from './pages/NotFoundPage';
|
|
23
24
|
import { api, type MenuContribution } from './api/controlPanelApi';
|
|
@@ -41,10 +42,11 @@ const coreNavigationItems: NavigationItem[] = [
|
|
|
41
42
|
// Built-in optional navigation items - shown if corresponding plugin is registered
|
|
42
43
|
const builtInPluginNavItems: Record<string, NavigationItem> = {
|
|
43
44
|
users: { id: 'users', label: 'Users', route: '/users', icon: 'people' },
|
|
45
|
+
'api-keys': { id: 'api-keys', label: 'API Keys', route: '/api-keys', icon: 'key' },
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
// Routes that have dedicated page components
|
|
47
|
-
const dedicatedRoutes = new Set(['/', '/plugins', '/logs', '/system', '/users', '/entitlements', '/auth', '/rate-limits', '/notifications', '/integrations']);
|
|
49
|
+
const dedicatedRoutes = new Set(['/', '/plugins', '/logs', '/system', '/users', '/entitlements', '/auth', '/rate-limits', '/notifications', '/integrations', '/api-keys']);
|
|
48
50
|
|
|
49
51
|
// Package version - injected at build time or fallback
|
|
50
52
|
const SERVER_VERSION = '1.0.0';
|
|
@@ -222,6 +224,9 @@ export function App() {
|
|
|
222
224
|
{registeredPlugins.has('ai-proxy') && (
|
|
223
225
|
<Route path="/integrations" element={<IntegrationsPage />} />
|
|
224
226
|
)}
|
|
227
|
+
{registeredPlugins.has('api-keys') && (
|
|
228
|
+
<Route path="/api-keys" element={<APIKeysPage />} />
|
|
229
|
+
)}
|
|
225
230
|
|
|
226
231
|
{/* Dynamic plugin routes - render generic PluginPage for non-dedicated routes */}
|
|
227
232
|
{pluginMenuItems
|