@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
|
@@ -81,10 +81,15 @@ export interface LogSource {
|
|
|
81
81
|
// ==================
|
|
82
82
|
// Users API Types
|
|
83
83
|
// ==================
|
|
84
|
+
export type UserStatus = 'invited' | 'active' | 'suspended';
|
|
85
|
+
|
|
84
86
|
export interface User {
|
|
85
87
|
id: string;
|
|
86
88
|
email: string;
|
|
87
89
|
name?: string;
|
|
90
|
+
status: UserStatus;
|
|
91
|
+
invitation_token?: string;
|
|
92
|
+
invitation_expires_at?: string;
|
|
88
93
|
created_at?: string;
|
|
89
94
|
updated_at?: string;
|
|
90
95
|
last_login?: string;
|
|
@@ -98,6 +103,30 @@ export interface UsersResponse {
|
|
|
98
103
|
limit: number;
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
export interface InviteUserRequest {
|
|
107
|
+
email: string;
|
|
108
|
+
name?: string;
|
|
109
|
+
role?: string;
|
|
110
|
+
metadata?: Record<string, unknown>;
|
|
111
|
+
expiresInDays?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface InvitationResponse {
|
|
115
|
+
user: User;
|
|
116
|
+
token: string;
|
|
117
|
+
inviteLink: string;
|
|
118
|
+
expiresAt: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface AcceptInvitationRequest {
|
|
122
|
+
token: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface AcceptInvitationResponse {
|
|
126
|
+
success: boolean;
|
|
127
|
+
user: User;
|
|
128
|
+
}
|
|
129
|
+
|
|
101
130
|
// ==================
|
|
102
131
|
// Bans API Types
|
|
103
132
|
// ==================
|
|
@@ -116,6 +145,44 @@ export interface BansResponse {
|
|
|
116
145
|
total: number;
|
|
117
146
|
}
|
|
118
147
|
|
|
148
|
+
// ==================
|
|
149
|
+
// API Keys Types
|
|
150
|
+
// ==================
|
|
151
|
+
export interface ApiKey {
|
|
152
|
+
id: string;
|
|
153
|
+
name: string;
|
|
154
|
+
key_prefix: string;
|
|
155
|
+
key_type: 'm2m' | 'pat';
|
|
156
|
+
scopes: Array<'read' | 'write' | 'admin'>;
|
|
157
|
+
last_used_at: string | null;
|
|
158
|
+
expires_at: string | null;
|
|
159
|
+
is_active: boolean;
|
|
160
|
+
created_at: string;
|
|
161
|
+
updated_at: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface ApiKeyWithPlaintext extends ApiKey {
|
|
165
|
+
key: string; // Only available on creation
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface ApiKeysResponse {
|
|
169
|
+
keys: ApiKey[];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface CreateApiKeyRequest {
|
|
173
|
+
name: string;
|
|
174
|
+
key_type: 'm2m' | 'pat';
|
|
175
|
+
scopes: Array<'read' | 'write' | 'admin'>;
|
|
176
|
+
expires_at?: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface UpdateApiKeyRequest {
|
|
180
|
+
name?: string;
|
|
181
|
+
scopes?: Array<'read' | 'write' | 'admin'>;
|
|
182
|
+
expires_at?: string;
|
|
183
|
+
is_active?: boolean;
|
|
184
|
+
}
|
|
185
|
+
|
|
119
186
|
// ==================
|
|
120
187
|
// Entitlements API Types
|
|
121
188
|
// ==================
|
|
@@ -444,13 +511,25 @@ class ControlPanelApi {
|
|
|
444
511
|
return this.baseUrl;
|
|
445
512
|
}
|
|
446
513
|
|
|
514
|
+
/**
|
|
515
|
+
* Internal fetch wrapper that includes credentials for Basic Auth support.
|
|
516
|
+
* Using 'same-origin' ensures the browser sends stored Basic Auth credentials
|
|
517
|
+
* without embedding them in the URL (which would cause fetch to fail).
|
|
518
|
+
*/
|
|
519
|
+
private async _fetch(url: string, options?: RequestInit): Promise<Response> {
|
|
520
|
+
return fetch(url, {
|
|
521
|
+
...options,
|
|
522
|
+
credentials: 'same-origin',
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
447
526
|
/**
|
|
448
527
|
* Generic fetch method for API requests.
|
|
449
528
|
* Automatically prepends the base URL and /api prefix.
|
|
450
529
|
*/
|
|
451
530
|
async fetch<T = unknown>(path: string, options?: RequestInit): Promise<T> {
|
|
452
531
|
const url = `${this.baseUrl}/api${path.startsWith('/') ? path : `/${path}`}`;
|
|
453
|
-
const response = await
|
|
532
|
+
const response = await this._fetch(url, {
|
|
454
533
|
...options,
|
|
455
534
|
headers: {
|
|
456
535
|
'Content-Type': 'application/json',
|
|
@@ -494,7 +573,7 @@ class ControlPanelApi {
|
|
|
494
573
|
|
|
495
574
|
private async checkEndpoint(path: string): Promise<boolean> {
|
|
496
575
|
try {
|
|
497
|
-
const response = await
|
|
576
|
+
const response = await this._fetch(`${this.baseUrl}${path}`, { method: 'HEAD' });
|
|
498
577
|
// 200, 401, 403 mean the endpoint exists (might need auth)
|
|
499
578
|
// 404 means it doesn't exist
|
|
500
579
|
return response.status !== 404;
|
|
@@ -517,7 +596,7 @@ class ControlPanelApi {
|
|
|
517
596
|
if (options.page) params.set('page', options.page.toString());
|
|
518
597
|
if (options.search) params.set('q', options.search);
|
|
519
598
|
|
|
520
|
-
const response = await
|
|
599
|
+
const response = await this._fetch(`${this.baseUrl}/api/users?${params}`);
|
|
521
600
|
if (!response.ok) {
|
|
522
601
|
throw new Error(`Users request failed: ${response.statusText}`);
|
|
523
602
|
}
|
|
@@ -525,19 +604,53 @@ class ControlPanelApi {
|
|
|
525
604
|
}
|
|
526
605
|
|
|
527
606
|
async getUserById(id: string): Promise<User> {
|
|
528
|
-
const response = await
|
|
607
|
+
const response = await this._fetch(`${this.baseUrl}/api/users/${id}`);
|
|
529
608
|
if (!response.ok) {
|
|
530
609
|
throw new Error(`User request failed: ${response.statusText}`);
|
|
531
610
|
}
|
|
532
611
|
return response.json();
|
|
533
612
|
}
|
|
534
613
|
|
|
614
|
+
async inviteUser(request: InviteUserRequest): Promise<InvitationResponse> {
|
|
615
|
+
const response = await this._fetch(`${this.baseUrl}/api/users/invite`, {
|
|
616
|
+
method: 'POST',
|
|
617
|
+
headers: { 'Content-Type': 'application/json' },
|
|
618
|
+
body: JSON.stringify(request),
|
|
619
|
+
});
|
|
620
|
+
if (!response.ok) {
|
|
621
|
+
const error = await response.json().catch(() => ({}));
|
|
622
|
+
throw new Error(error.error || `Invite user failed: ${response.statusText}`);
|
|
623
|
+
}
|
|
624
|
+
return response.json();
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
async acceptInvitation(token: string): Promise<AcceptInvitationResponse> {
|
|
628
|
+
const response = await this._fetch(`${this.baseUrl}/api/users/accept-invitation/${encodeURIComponent(token)}`);
|
|
629
|
+
if (!response.ok) {
|
|
630
|
+
const error = await response.json().catch(() => ({}));
|
|
631
|
+
throw new Error(error.error || `Accept invitation failed: ${response.statusText}`);
|
|
632
|
+
}
|
|
633
|
+
return response.json();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async getInvitations(): Promise<UsersResponse> {
|
|
637
|
+
const params = new URLSearchParams();
|
|
638
|
+
params.set('status', 'invited');
|
|
639
|
+
params.set('limit', '100');
|
|
640
|
+
|
|
641
|
+
const response = await this._fetch(`${this.baseUrl}/api/users?${params}`);
|
|
642
|
+
if (!response.ok) {
|
|
643
|
+
throw new Error(`Invitations request failed: ${response.statusText}`);
|
|
644
|
+
}
|
|
645
|
+
return response.json();
|
|
646
|
+
}
|
|
647
|
+
|
|
535
648
|
// ==================
|
|
536
649
|
// Bans API
|
|
537
650
|
// ==================
|
|
538
651
|
|
|
539
652
|
async getBans(): Promise<BansResponse> {
|
|
540
|
-
const response = await
|
|
653
|
+
const response = await this._fetch(`${this.baseUrl}/api/bans`);
|
|
541
654
|
if (!response.ok) {
|
|
542
655
|
throw new Error(`Bans request failed: ${response.statusText}`);
|
|
543
656
|
}
|
|
@@ -553,7 +666,7 @@ class ControlPanelApi {
|
|
|
553
666
|
duration = Math.max(0, Math.floor((expiresDate.getTime() - now.getTime()) / 1000));
|
|
554
667
|
}
|
|
555
668
|
|
|
556
|
-
const response = await
|
|
669
|
+
const response = await this._fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`, {
|
|
557
670
|
method: 'POST',
|
|
558
671
|
headers: { 'Content-Type': 'application/json' },
|
|
559
672
|
body: JSON.stringify({ reason, duration }),
|
|
@@ -565,7 +678,7 @@ class ControlPanelApi {
|
|
|
565
678
|
}
|
|
566
679
|
|
|
567
680
|
async unbanUser(email: string): Promise<void> {
|
|
568
|
-
const response = await
|
|
681
|
+
const response = await this._fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`, {
|
|
569
682
|
method: 'DELETE',
|
|
570
683
|
});
|
|
571
684
|
if (!response.ok) {
|
|
@@ -574,7 +687,7 @@ class ControlPanelApi {
|
|
|
574
687
|
}
|
|
575
688
|
|
|
576
689
|
async checkBan(email: string): Promise<{ banned: boolean; ban?: Ban }> {
|
|
577
|
-
const response = await
|
|
690
|
+
const response = await this._fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`);
|
|
578
691
|
if (!response.ok) {
|
|
579
692
|
throw new Error(`Ban check failed: ${response.statusText}`);
|
|
580
693
|
}
|
|
@@ -588,7 +701,7 @@ class ControlPanelApi {
|
|
|
588
701
|
// ==================
|
|
589
702
|
|
|
590
703
|
async getEntitlements(email: string): Promise<EntitlementResult> {
|
|
591
|
-
const response = await
|
|
704
|
+
const response = await this._fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}`);
|
|
592
705
|
if (!response.ok) {
|
|
593
706
|
throw new Error(`Entitlements request failed: ${response.statusText}`);
|
|
594
707
|
}
|
|
@@ -596,7 +709,7 @@ class ControlPanelApi {
|
|
|
596
709
|
}
|
|
597
710
|
|
|
598
711
|
async refreshEntitlements(email: string): Promise<EntitlementResult> {
|
|
599
|
-
const response = await
|
|
712
|
+
const response = await this._fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/refresh`, {
|
|
600
713
|
method: 'POST',
|
|
601
714
|
});
|
|
602
715
|
if (!response.ok) {
|
|
@@ -606,7 +719,7 @@ class ControlPanelApi {
|
|
|
606
719
|
}
|
|
607
720
|
|
|
608
721
|
async checkEntitlement(email: string, entitlement: string): Promise<{ has: boolean }> {
|
|
609
|
-
const response = await
|
|
722
|
+
const response = await this._fetch(
|
|
610
723
|
`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/check/${encodeURIComponent(entitlement)}`
|
|
611
724
|
);
|
|
612
725
|
if (!response.ok) {
|
|
@@ -616,7 +729,7 @@ class ControlPanelApi {
|
|
|
616
729
|
}
|
|
617
730
|
|
|
618
731
|
async getAvailableEntitlements(): Promise<EntitlementDefinition[]> {
|
|
619
|
-
const response = await
|
|
732
|
+
const response = await this._fetch(`${this.baseUrl}/api/entitlements/available`);
|
|
620
733
|
if (!response.ok) {
|
|
621
734
|
throw new Error(`Available entitlements request failed: ${response.statusText}`);
|
|
622
735
|
}
|
|
@@ -625,7 +738,7 @@ class ControlPanelApi {
|
|
|
625
738
|
}
|
|
626
739
|
|
|
627
740
|
async grantEntitlement(email: string, entitlement: string): Promise<void> {
|
|
628
|
-
const response = await
|
|
741
|
+
const response = await this._fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}`, {
|
|
629
742
|
method: 'POST',
|
|
630
743
|
headers: { 'Content-Type': 'application/json' },
|
|
631
744
|
body: JSON.stringify({ entitlement }),
|
|
@@ -637,7 +750,7 @@ class ControlPanelApi {
|
|
|
637
750
|
}
|
|
638
751
|
|
|
639
752
|
async revokeEntitlement(email: string, entitlement: string): Promise<void> {
|
|
640
|
-
const response = await
|
|
753
|
+
const response = await this._fetch(
|
|
641
754
|
`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/${encodeURIComponent(entitlement)}`,
|
|
642
755
|
{ method: 'DELETE' }
|
|
643
756
|
);
|
|
@@ -647,7 +760,7 @@ class ControlPanelApi {
|
|
|
647
760
|
}
|
|
648
761
|
|
|
649
762
|
async invalidateEntitlementCache(email: string): Promise<void> {
|
|
650
|
-
const response = await
|
|
763
|
+
const response = await this._fetch(`${this.baseUrl}/api/entitlements/cache/${encodeURIComponent(email)}`, {
|
|
651
764
|
method: 'DELETE',
|
|
652
765
|
});
|
|
653
766
|
if (!response.ok) {
|
|
@@ -656,7 +769,7 @@ class ControlPanelApi {
|
|
|
656
769
|
}
|
|
657
770
|
|
|
658
771
|
async getEntitlementsStatus(): Promise<EntitlementsStatus> {
|
|
659
|
-
const response = await
|
|
772
|
+
const response = await this._fetch(`${this.baseUrl}/api/entitlements/status`);
|
|
660
773
|
if (!response.ok) {
|
|
661
774
|
throw new Error(`Entitlements status request failed: ${response.statusText}`);
|
|
662
775
|
}
|
|
@@ -668,7 +781,7 @@ class ControlPanelApi {
|
|
|
668
781
|
// ==================
|
|
669
782
|
|
|
670
783
|
async getHealth(): Promise<HealthResponse> {
|
|
671
|
-
const response = await
|
|
784
|
+
const response = await this._fetch(`${this.baseUrl}/api/health`);
|
|
672
785
|
if (!response.ok) {
|
|
673
786
|
throw new Error(`Health check failed: ${response.statusText}`);
|
|
674
787
|
}
|
|
@@ -676,7 +789,7 @@ class ControlPanelApi {
|
|
|
676
789
|
}
|
|
677
790
|
|
|
678
791
|
async getInfo(): Promise<InfoResponse> {
|
|
679
|
-
const response = await
|
|
792
|
+
const response = await this._fetch(`${this.baseUrl}/api/info`);
|
|
680
793
|
if (!response.ok) {
|
|
681
794
|
throw new Error(`Info request failed: ${response.statusText}`);
|
|
682
795
|
}
|
|
@@ -684,7 +797,7 @@ class ControlPanelApi {
|
|
|
684
797
|
}
|
|
685
798
|
|
|
686
799
|
async getDiagnostics(): Promise<DiagnosticsResponse> {
|
|
687
|
-
const response = await
|
|
800
|
+
const response = await this._fetch(`${this.baseUrl}/api/diagnostics`);
|
|
688
801
|
if (!response.ok) {
|
|
689
802
|
throw new Error(`Diagnostics request failed: ${response.statusText}`);
|
|
690
803
|
}
|
|
@@ -692,7 +805,7 @@ class ControlPanelApi {
|
|
|
692
805
|
}
|
|
693
806
|
|
|
694
807
|
async getConfig(): Promise<ConfigResponse> {
|
|
695
|
-
const response = await
|
|
808
|
+
const response = await this._fetch(`${this.baseUrl}/api/config`);
|
|
696
809
|
if (!response.ok) {
|
|
697
810
|
throw new Error(`Config request failed: ${response.statusText}`);
|
|
698
811
|
}
|
|
@@ -713,7 +826,7 @@ class ControlPanelApi {
|
|
|
713
826
|
if (options.limit) params.set('limit', options.limit.toString());
|
|
714
827
|
if (options.page) params.set('page', options.page.toString());
|
|
715
828
|
|
|
716
|
-
const response = await
|
|
829
|
+
const response = await this._fetch(`${this.baseUrl}/api/logs?${params}`);
|
|
717
830
|
if (!response.ok) {
|
|
718
831
|
throw new Error(`Logs request failed: ${response.statusText}`);
|
|
719
832
|
}
|
|
@@ -721,7 +834,7 @@ class ControlPanelApi {
|
|
|
721
834
|
}
|
|
722
835
|
|
|
723
836
|
async getLogSources(): Promise<LogSource[]> {
|
|
724
|
-
const response = await
|
|
837
|
+
const response = await this._fetch(`${this.baseUrl}/api/logs/sources`);
|
|
725
838
|
if (!response.ok) {
|
|
726
839
|
throw new Error(`Log sources request failed: ${response.statusText}`);
|
|
727
840
|
}
|
|
@@ -734,7 +847,7 @@ class ControlPanelApi {
|
|
|
734
847
|
// ==================
|
|
735
848
|
|
|
736
849
|
async getPlugins(): Promise<PluginsResponse> {
|
|
737
|
-
const response = await
|
|
850
|
+
const response = await this._fetch(`${this.baseUrl}/api/plugins`);
|
|
738
851
|
if (!response.ok) {
|
|
739
852
|
throw new Error(`Plugins request failed: ${response.statusText}`);
|
|
740
853
|
}
|
|
@@ -742,7 +855,7 @@ class ControlPanelApi {
|
|
|
742
855
|
}
|
|
743
856
|
|
|
744
857
|
async getPluginDetail(id: string): Promise<PluginDetailResponse> {
|
|
745
|
-
const response = await
|
|
858
|
+
const response = await this._fetch(`${this.baseUrl}/api/plugins/${encodeURIComponent(id)}`);
|
|
746
859
|
if (!response.ok) {
|
|
747
860
|
if (response.status === 404) {
|
|
748
861
|
throw new Error(`Plugin not found: ${id}`);
|
|
@@ -757,7 +870,7 @@ class ControlPanelApi {
|
|
|
757
870
|
// ==================
|
|
758
871
|
|
|
759
872
|
async getUiContributions(): Promise<UiContributionsResponse> {
|
|
760
|
-
const response = await
|
|
873
|
+
const response = await this._fetch(`${this.baseUrl}/api/ui-contributions`);
|
|
761
874
|
if (!response.ok) {
|
|
762
875
|
throw new Error(`UI contributions request failed: ${response.statusText}`);
|
|
763
876
|
}
|
|
@@ -769,7 +882,7 @@ class ControlPanelApi {
|
|
|
769
882
|
// ==================
|
|
770
883
|
|
|
771
884
|
async getAuthConfigStatus(): Promise<AuthConfigStatus> {
|
|
772
|
-
const response = await
|
|
885
|
+
const response = await this._fetch(`${this.baseUrl}/api/auth/config/status`);
|
|
773
886
|
if (!response.ok) {
|
|
774
887
|
// Return disabled state if endpoint not available
|
|
775
888
|
if (response.status === 404) {
|
|
@@ -781,7 +894,7 @@ class ControlPanelApi {
|
|
|
781
894
|
}
|
|
782
895
|
|
|
783
896
|
async getAuthConfig(): Promise<AuthConfigStatus> {
|
|
784
|
-
const response = await
|
|
897
|
+
const response = await this._fetch(`${this.baseUrl}/api/auth/config`);
|
|
785
898
|
if (!response.ok) {
|
|
786
899
|
if (response.status === 404) {
|
|
787
900
|
return { state: 'disabled', adapter: null };
|
|
@@ -795,7 +908,7 @@ class ControlPanelApi {
|
|
|
795
908
|
* Update auth configuration (save to database for hot-reload)
|
|
796
909
|
*/
|
|
797
910
|
async updateAuthConfig(request: UpdateAuthConfigRequest): Promise<{ success: boolean; message: string }> {
|
|
798
|
-
const response = await
|
|
911
|
+
const response = await this._fetch(`${this.baseUrl}/api/auth/config`, {
|
|
799
912
|
method: 'PUT',
|
|
800
913
|
headers: { 'Content-Type': 'application/json' },
|
|
801
914
|
body: JSON.stringify(request),
|
|
@@ -811,7 +924,7 @@ class ControlPanelApi {
|
|
|
811
924
|
* Delete auth configuration (revert to environment variables)
|
|
812
925
|
*/
|
|
813
926
|
async deleteAuthConfig(): Promise<{ success: boolean; message: string }> {
|
|
814
|
-
const response = await
|
|
927
|
+
const response = await this._fetch(`${this.baseUrl}/api/auth/config`, {
|
|
815
928
|
method: 'DELETE',
|
|
816
929
|
});
|
|
817
930
|
if (!response.ok) {
|
|
@@ -825,7 +938,7 @@ class ControlPanelApi {
|
|
|
825
938
|
* Test auth provider connection without saving
|
|
826
939
|
*/
|
|
827
940
|
async testAuthProvider(request: TestProviderRequest): Promise<TestProviderResponse> {
|
|
828
|
-
const response = await
|
|
941
|
+
const response = await this._fetch(`${this.baseUrl}/api/auth/test-provider`, {
|
|
829
942
|
method: 'POST',
|
|
830
943
|
headers: { 'Content-Type': 'application/json' },
|
|
831
944
|
body: JSON.stringify(request),
|
|
@@ -841,7 +954,7 @@ class ControlPanelApi {
|
|
|
841
954
|
* Test current auth provider connection (uses existing env/runtime config)
|
|
842
955
|
*/
|
|
843
956
|
async testCurrentAuthProvider(): Promise<TestProviderResponse> {
|
|
844
|
-
const response = await
|
|
957
|
+
const response = await this._fetch(`${this.baseUrl}/api/auth/test-current`, {
|
|
845
958
|
method: 'POST',
|
|
846
959
|
headers: { 'Content-Type': 'application/json' },
|
|
847
960
|
});
|
|
@@ -857,7 +970,7 @@ class ControlPanelApi {
|
|
|
857
970
|
// ==================
|
|
858
971
|
|
|
859
972
|
async getRateLimitConfig(): Promise<RateLimitConfig> {
|
|
860
|
-
const response = await
|
|
973
|
+
const response = await this._fetch(`${this.baseUrl}/api/rate-limit/config`);
|
|
861
974
|
if (!response.ok) {
|
|
862
975
|
throw new Error(`Rate limit config request failed: ${response.statusText}`);
|
|
863
976
|
}
|
|
@@ -865,7 +978,7 @@ class ControlPanelApi {
|
|
|
865
978
|
}
|
|
866
979
|
|
|
867
980
|
async updateRateLimitConfig(updates: RateLimitConfigUpdateRequest): Promise<RateLimitConfigUpdateResponse> {
|
|
868
|
-
const response = await
|
|
981
|
+
const response = await this._fetch(`${this.baseUrl}/api/rate-limit/config`, {
|
|
869
982
|
method: 'PUT',
|
|
870
983
|
headers: { 'Content-Type': 'application/json' },
|
|
871
984
|
body: JSON.stringify(updates),
|
|
@@ -882,7 +995,7 @@ class ControlPanelApi {
|
|
|
882
995
|
// ==================
|
|
883
996
|
|
|
884
997
|
async getNotificationsStats(): Promise<NotificationsStatsResponse> {
|
|
885
|
-
const response = await
|
|
998
|
+
const response = await this._fetch(`${this.baseUrl}/api/notifications/stats`);
|
|
886
999
|
if (!response.ok) {
|
|
887
1000
|
throw new Error(`Notifications stats request failed: ${response.statusText}`);
|
|
888
1001
|
}
|
|
@@ -890,7 +1003,7 @@ class ControlPanelApi {
|
|
|
890
1003
|
}
|
|
891
1004
|
|
|
892
1005
|
async getNotificationsClients(): Promise<NotificationsClientsResponse> {
|
|
893
|
-
const response = await
|
|
1006
|
+
const response = await this._fetch(`${this.baseUrl}/api/notifications/clients`);
|
|
894
1007
|
if (!response.ok) {
|
|
895
1008
|
throw new Error(`Notifications clients request failed: ${response.statusText}`);
|
|
896
1009
|
}
|
|
@@ -898,7 +1011,7 @@ class ControlPanelApi {
|
|
|
898
1011
|
}
|
|
899
1012
|
|
|
900
1013
|
async disconnectNotificationsClient(clientId: string): Promise<{ success: boolean }> {
|
|
901
|
-
const response = await
|
|
1014
|
+
const response = await this._fetch(`${this.baseUrl}/api/notifications/clients/${encodeURIComponent(clientId)}`, {
|
|
902
1015
|
method: 'DELETE',
|
|
903
1016
|
});
|
|
904
1017
|
if (!response.ok) {
|
|
@@ -909,7 +1022,7 @@ class ControlPanelApi {
|
|
|
909
1022
|
}
|
|
910
1023
|
|
|
911
1024
|
async forceNotificationsReconnect(): Promise<{ success: boolean; message: string }> {
|
|
912
|
-
const response = await
|
|
1025
|
+
const response = await this._fetch(`${this.baseUrl}/api/notifications/reconnect`, {
|
|
913
1026
|
method: 'POST',
|
|
914
1027
|
});
|
|
915
1028
|
if (!response.ok) {
|
|
@@ -918,6 +1031,62 @@ class ControlPanelApi {
|
|
|
918
1031
|
}
|
|
919
1032
|
return response.json();
|
|
920
1033
|
}
|
|
1034
|
+
|
|
1035
|
+
// ==================
|
|
1036
|
+
// API Keys API
|
|
1037
|
+
// ==================
|
|
1038
|
+
|
|
1039
|
+
async getApiKeys(): Promise<ApiKeysResponse> {
|
|
1040
|
+
const response = await this._fetch(`${this.baseUrl}/api/api-keys`);
|
|
1041
|
+
if (!response.ok) {
|
|
1042
|
+
throw new Error(`API keys request failed: ${response.statusText}`);
|
|
1043
|
+
}
|
|
1044
|
+
return response.json();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
async createApiKey(request: CreateApiKeyRequest): Promise<ApiKeyWithPlaintext> {
|
|
1048
|
+
const response = await this._fetch(`${this.baseUrl}/api/api-keys`, {
|
|
1049
|
+
method: 'POST',
|
|
1050
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1051
|
+
body: JSON.stringify(request),
|
|
1052
|
+
});
|
|
1053
|
+
if (!response.ok) {
|
|
1054
|
+
const error = await response.json().catch(() => ({}));
|
|
1055
|
+
throw new Error(error.error || `API key creation failed: ${response.statusText}`);
|
|
1056
|
+
}
|
|
1057
|
+
return response.json();
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
async getApiKey(keyId: string): Promise<ApiKey> {
|
|
1061
|
+
const response = await this._fetch(`${this.baseUrl}/api/api-keys/${encodeURIComponent(keyId)}`);
|
|
1062
|
+
if (!response.ok) {
|
|
1063
|
+
throw new Error(`API key request failed: ${response.statusText}`);
|
|
1064
|
+
}
|
|
1065
|
+
return response.json();
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
async updateApiKey(keyId: string, updates: UpdateApiKeyRequest): Promise<ApiKey> {
|
|
1069
|
+
const response = await this._fetch(`${this.baseUrl}/api/api-keys/${encodeURIComponent(keyId)}`, {
|
|
1070
|
+
method: 'PUT',
|
|
1071
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1072
|
+
body: JSON.stringify(updates),
|
|
1073
|
+
});
|
|
1074
|
+
if (!response.ok) {
|
|
1075
|
+
const error = await response.json().catch(() => ({}));
|
|
1076
|
+
throw new Error(error.error || `API key update failed: ${response.statusText}`);
|
|
1077
|
+
}
|
|
1078
|
+
return response.json();
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
async deleteApiKey(keyId: string): Promise<void> {
|
|
1082
|
+
const response = await this._fetch(`${this.baseUrl}/api/api-keys/${encodeURIComponent(keyId)}`, {
|
|
1083
|
+
method: 'DELETE',
|
|
1084
|
+
});
|
|
1085
|
+
if (!response.ok) {
|
|
1086
|
+
const error = await response.json().catch(() => ({}));
|
|
1087
|
+
throw new Error(error.error || `API key deletion failed: ${response.statusText}`);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
921
1090
|
}
|
|
922
1091
|
|
|
923
1092
|
export const api = new ControlPanelApi();
|
|
@@ -18,6 +18,7 @@ export { SystemPage } from '../pages/SystemPage';
|
|
|
18
18
|
export { NotFoundPage } from '../pages/NotFoundPage';
|
|
19
19
|
export { UsersPage, type UsersPageProps } from '../pages/UsersPage';
|
|
20
20
|
export { EntitlementsPage, type EntitlementsPageProps } from '../pages/EntitlementsPage';
|
|
21
|
+
export { AcceptInvitationPage, type AcceptInvitationPageProps } from '../pages/AcceptInvitationPage';
|
|
21
22
|
|
|
22
23
|
// Re-export dashboard widget system (legacy context-based + new plugin-based)
|
|
23
24
|
export {
|
|
@@ -50,8 +51,13 @@ export type {
|
|
|
50
51
|
LogsResponse,
|
|
51
52
|
LogSource,
|
|
52
53
|
// User management types
|
|
54
|
+
UserStatus,
|
|
53
55
|
User,
|
|
54
56
|
UsersResponse,
|
|
57
|
+
InviteUserRequest,
|
|
58
|
+
InvitationResponse,
|
|
59
|
+
AcceptInvitationRequest,
|
|
60
|
+
AcceptInvitationResponse,
|
|
55
61
|
Ban,
|
|
56
62
|
BansResponse,
|
|
57
63
|
EntitlementDefinition,
|