@qwickapps/server 1.5.1 → 1.5.2

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/core/control-panel.d.ts.map +1 -1
  3. package/dist/core/control-panel.js +41 -0
  4. package/dist/core/control-panel.js.map +1 -1
  5. package/dist/core/guards.d.ts.map +1 -1
  6. package/dist/core/guards.js +77 -0
  7. package/dist/core/guards.js.map +1 -1
  8. package/dist/core/health-manager.d.ts +4 -0
  9. package/dist/core/health-manager.d.ts.map +1 -1
  10. package/dist/core/health-manager.js +6 -1
  11. package/dist/core/health-manager.js.map +1 -1
  12. package/dist/core/plugin-registry.d.ts +55 -5
  13. package/dist/core/plugin-registry.d.ts.map +1 -1
  14. package/dist/core/plugin-registry.js +57 -19
  15. package/dist/core/plugin-registry.js.map +1 -1
  16. package/dist/core/types.d.ts +2 -0
  17. package/dist/core/types.d.ts.map +1 -1
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/plugins/auth/auth-plugin.d.ts.map +1 -1
  23. package/dist/plugins/auth/auth-plugin.js +16 -0
  24. package/dist/plugins/auth/auth-plugin.js.map +1 -1
  25. package/dist/plugins/auth/auth-plugin.test.js +133 -0
  26. package/dist/plugins/auth/auth-plugin.test.js.map +1 -1
  27. package/dist/plugins/auth/env-config.d.ts.map +1 -1
  28. package/dist/plugins/auth/env-config.js +4 -0
  29. package/dist/plugins/auth/env-config.js.map +1 -1
  30. package/dist/plugins/auth/types.d.ts +10 -0
  31. package/dist/plugins/auth/types.d.ts.map +1 -1
  32. package/dist/plugins/auth/types.js.map +1 -1
  33. package/dist/plugins/devices/__tests__/token-utils.test.js +4 -2
  34. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -1
  35. package/dist/plugins/frontend-app-plugin.d.ts.map +1 -1
  36. package/dist/plugins/frontend-app-plugin.js +18 -4
  37. package/dist/plugins/frontend-app-plugin.js.map +1 -1
  38. package/dist/plugins/index.d.ts +2 -0
  39. package/dist/plugins/index.d.ts.map +1 -1
  40. package/dist/plugins/index.js +2 -0
  41. package/dist/plugins/index.js.map +1 -1
  42. package/dist/plugins/qwickbrain/index.d.ts +25 -0
  43. package/dist/plugins/qwickbrain/index.d.ts.map +1 -0
  44. package/dist/plugins/qwickbrain/index.js +24 -0
  45. package/dist/plugins/qwickbrain/index.js.map +1 -0
  46. package/dist/plugins/qwickbrain/qwickbrain-plugin.d.ts +23 -0
  47. package/dist/plugins/qwickbrain/qwickbrain-plugin.d.ts.map +1 -0
  48. package/dist/plugins/qwickbrain/qwickbrain-plugin.js +528 -0
  49. package/dist/plugins/qwickbrain/qwickbrain-plugin.js.map +1 -0
  50. package/dist/plugins/qwickbrain/types.d.ts +131 -0
  51. package/dist/plugins/qwickbrain/types.d.ts.map +1 -0
  52. package/dist/plugins/qwickbrain/types.js +9 -0
  53. package/dist/plugins/qwickbrain/types.js.map +1 -0
  54. package/dist-ui/assets/{index-CynOqPkb.js → index-BfC7mG5L.js} +2 -2
  55. package/dist-ui/assets/{index-CynOqPkb.js.map → index-BfC7mG5L.js.map} +1 -1
  56. package/dist-ui/index.html +1 -1
  57. package/dist-ui-lib/api/controlPanelApi.d.ts +6 -0
  58. package/dist-ui-lib/index.js +277 -266
  59. package/dist-ui-lib/index.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/core/control-panel.ts +47 -0
  62. package/src/core/guards.ts +89 -0
  63. package/src/core/health-manager.ts +6 -1
  64. package/src/core/plugin-registry.ts +123 -25
  65. package/src/core/types.ts +2 -0
  66. package/src/index.ts +11 -0
  67. package/src/plugins/auth/auth-plugin.test.ts +167 -0
  68. package/src/plugins/auth/auth-plugin.ts +16 -0
  69. package/src/plugins/auth/env-config.ts +4 -0
  70. package/src/plugins/auth/types.ts +10 -0
  71. package/src/plugins/devices/__tests__/token-utils.test.ts +4 -2
  72. package/src/plugins/frontend-app-plugin.ts +19 -4
  73. package/src/plugins/index.ts +15 -0
  74. package/src/plugins/qwickbrain/index.ts +33 -0
  75. package/src/plugins/qwickbrain/qwickbrain-plugin.ts +642 -0
  76. package/src/plugins/qwickbrain/types.ts +146 -0
  77. package/ui/src/api/controlPanelApi.ts +49 -37
@@ -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
+ }
@@ -444,13 +444,25 @@ class ControlPanelApi {
444
444
  return this.baseUrl;
445
445
  }
446
446
 
447
+ /**
448
+ * Internal fetch wrapper that includes credentials for Basic Auth support.
449
+ * Using 'same-origin' ensures the browser sends stored Basic Auth credentials
450
+ * without embedding them in the URL (which would cause fetch to fail).
451
+ */
452
+ private async _fetch(url: string, options?: RequestInit): Promise<Response> {
453
+ return fetch(url, {
454
+ ...options,
455
+ credentials: 'same-origin',
456
+ });
457
+ }
458
+
447
459
  /**
448
460
  * Generic fetch method for API requests.
449
461
  * Automatically prepends the base URL and /api prefix.
450
462
  */
451
463
  async fetch<T = unknown>(path: string, options?: RequestInit): Promise<T> {
452
464
  const url = `${this.baseUrl}/api${path.startsWith('/') ? path : `/${path}`}`;
453
- const response = await fetch(url, {
465
+ const response = await this._fetch(url, {
454
466
  ...options,
455
467
  headers: {
456
468
  'Content-Type': 'application/json',
@@ -494,7 +506,7 @@ class ControlPanelApi {
494
506
 
495
507
  private async checkEndpoint(path: string): Promise<boolean> {
496
508
  try {
497
- const response = await fetch(`${this.baseUrl}${path}`, { method: 'HEAD' });
509
+ const response = await this._fetch(`${this.baseUrl}${path}`, { method: 'HEAD' });
498
510
  // 200, 401, 403 mean the endpoint exists (might need auth)
499
511
  // 404 means it doesn't exist
500
512
  return response.status !== 404;
@@ -517,7 +529,7 @@ class ControlPanelApi {
517
529
  if (options.page) params.set('page', options.page.toString());
518
530
  if (options.search) params.set('q', options.search);
519
531
 
520
- const response = await fetch(`${this.baseUrl}/api/users?${params}`);
532
+ const response = await this._fetch(`${this.baseUrl}/api/users?${params}`);
521
533
  if (!response.ok) {
522
534
  throw new Error(`Users request failed: ${response.statusText}`);
523
535
  }
@@ -525,7 +537,7 @@ class ControlPanelApi {
525
537
  }
526
538
 
527
539
  async getUserById(id: string): Promise<User> {
528
- const response = await fetch(`${this.baseUrl}/api/users/${id}`);
540
+ const response = await this._fetch(`${this.baseUrl}/api/users/${id}`);
529
541
  if (!response.ok) {
530
542
  throw new Error(`User request failed: ${response.statusText}`);
531
543
  }
@@ -537,7 +549,7 @@ class ControlPanelApi {
537
549
  // ==================
538
550
 
539
551
  async getBans(): Promise<BansResponse> {
540
- const response = await fetch(`${this.baseUrl}/api/bans`);
552
+ const response = await this._fetch(`${this.baseUrl}/api/bans`);
541
553
  if (!response.ok) {
542
554
  throw new Error(`Bans request failed: ${response.statusText}`);
543
555
  }
@@ -553,7 +565,7 @@ class ControlPanelApi {
553
565
  duration = Math.max(0, Math.floor((expiresDate.getTime() - now.getTime()) / 1000));
554
566
  }
555
567
 
556
- const response = await fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`, {
568
+ const response = await this._fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`, {
557
569
  method: 'POST',
558
570
  headers: { 'Content-Type': 'application/json' },
559
571
  body: JSON.stringify({ reason, duration }),
@@ -565,7 +577,7 @@ class ControlPanelApi {
565
577
  }
566
578
 
567
579
  async unbanUser(email: string): Promise<void> {
568
- const response = await fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`, {
580
+ const response = await this._fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`, {
569
581
  method: 'DELETE',
570
582
  });
571
583
  if (!response.ok) {
@@ -574,7 +586,7 @@ class ControlPanelApi {
574
586
  }
575
587
 
576
588
  async checkBan(email: string): Promise<{ banned: boolean; ban?: Ban }> {
577
- const response = await fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`);
589
+ const response = await this._fetch(`${this.baseUrl}/api/bans/email/${encodeURIComponent(email)}`);
578
590
  if (!response.ok) {
579
591
  throw new Error(`Ban check failed: ${response.statusText}`);
580
592
  }
@@ -588,7 +600,7 @@ class ControlPanelApi {
588
600
  // ==================
589
601
 
590
602
  async getEntitlements(email: string): Promise<EntitlementResult> {
591
- const response = await fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}`);
603
+ const response = await this._fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}`);
592
604
  if (!response.ok) {
593
605
  throw new Error(`Entitlements request failed: ${response.statusText}`);
594
606
  }
@@ -596,7 +608,7 @@ class ControlPanelApi {
596
608
  }
597
609
 
598
610
  async refreshEntitlements(email: string): Promise<EntitlementResult> {
599
- const response = await fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/refresh`, {
611
+ const response = await this._fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/refresh`, {
600
612
  method: 'POST',
601
613
  });
602
614
  if (!response.ok) {
@@ -606,7 +618,7 @@ class ControlPanelApi {
606
618
  }
607
619
 
608
620
  async checkEntitlement(email: string, entitlement: string): Promise<{ has: boolean }> {
609
- const response = await fetch(
621
+ const response = await this._fetch(
610
622
  `${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/check/${encodeURIComponent(entitlement)}`
611
623
  );
612
624
  if (!response.ok) {
@@ -616,7 +628,7 @@ class ControlPanelApi {
616
628
  }
617
629
 
618
630
  async getAvailableEntitlements(): Promise<EntitlementDefinition[]> {
619
- const response = await fetch(`${this.baseUrl}/api/entitlements/available`);
631
+ const response = await this._fetch(`${this.baseUrl}/api/entitlements/available`);
620
632
  if (!response.ok) {
621
633
  throw new Error(`Available entitlements request failed: ${response.statusText}`);
622
634
  }
@@ -625,7 +637,7 @@ class ControlPanelApi {
625
637
  }
626
638
 
627
639
  async grantEntitlement(email: string, entitlement: string): Promise<void> {
628
- const response = await fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}`, {
640
+ const response = await this._fetch(`${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}`, {
629
641
  method: 'POST',
630
642
  headers: { 'Content-Type': 'application/json' },
631
643
  body: JSON.stringify({ entitlement }),
@@ -637,7 +649,7 @@ class ControlPanelApi {
637
649
  }
638
650
 
639
651
  async revokeEntitlement(email: string, entitlement: string): Promise<void> {
640
- const response = await fetch(
652
+ const response = await this._fetch(
641
653
  `${this.baseUrl}/api/entitlements/${encodeURIComponent(email)}/${encodeURIComponent(entitlement)}`,
642
654
  { method: 'DELETE' }
643
655
  );
@@ -647,7 +659,7 @@ class ControlPanelApi {
647
659
  }
648
660
 
649
661
  async invalidateEntitlementCache(email: string): Promise<void> {
650
- const response = await fetch(`${this.baseUrl}/api/entitlements/cache/${encodeURIComponent(email)}`, {
662
+ const response = await this._fetch(`${this.baseUrl}/api/entitlements/cache/${encodeURIComponent(email)}`, {
651
663
  method: 'DELETE',
652
664
  });
653
665
  if (!response.ok) {
@@ -656,7 +668,7 @@ class ControlPanelApi {
656
668
  }
657
669
 
658
670
  async getEntitlementsStatus(): Promise<EntitlementsStatus> {
659
- const response = await fetch(`${this.baseUrl}/api/entitlements/status`);
671
+ const response = await this._fetch(`${this.baseUrl}/api/entitlements/status`);
660
672
  if (!response.ok) {
661
673
  throw new Error(`Entitlements status request failed: ${response.statusText}`);
662
674
  }
@@ -668,7 +680,7 @@ class ControlPanelApi {
668
680
  // ==================
669
681
 
670
682
  async getHealth(): Promise<HealthResponse> {
671
- const response = await fetch(`${this.baseUrl}/api/health`);
683
+ const response = await this._fetch(`${this.baseUrl}/api/health`);
672
684
  if (!response.ok) {
673
685
  throw new Error(`Health check failed: ${response.statusText}`);
674
686
  }
@@ -676,7 +688,7 @@ class ControlPanelApi {
676
688
  }
677
689
 
678
690
  async getInfo(): Promise<InfoResponse> {
679
- const response = await fetch(`${this.baseUrl}/api/info`);
691
+ const response = await this._fetch(`${this.baseUrl}/api/info`);
680
692
  if (!response.ok) {
681
693
  throw new Error(`Info request failed: ${response.statusText}`);
682
694
  }
@@ -684,7 +696,7 @@ class ControlPanelApi {
684
696
  }
685
697
 
686
698
  async getDiagnostics(): Promise<DiagnosticsResponse> {
687
- const response = await fetch(`${this.baseUrl}/api/diagnostics`);
699
+ const response = await this._fetch(`${this.baseUrl}/api/diagnostics`);
688
700
  if (!response.ok) {
689
701
  throw new Error(`Diagnostics request failed: ${response.statusText}`);
690
702
  }
@@ -692,7 +704,7 @@ class ControlPanelApi {
692
704
  }
693
705
 
694
706
  async getConfig(): Promise<ConfigResponse> {
695
- const response = await fetch(`${this.baseUrl}/api/config`);
707
+ const response = await this._fetch(`${this.baseUrl}/api/config`);
696
708
  if (!response.ok) {
697
709
  throw new Error(`Config request failed: ${response.statusText}`);
698
710
  }
@@ -713,7 +725,7 @@ class ControlPanelApi {
713
725
  if (options.limit) params.set('limit', options.limit.toString());
714
726
  if (options.page) params.set('page', options.page.toString());
715
727
 
716
- const response = await fetch(`${this.baseUrl}/api/logs?${params}`);
728
+ const response = await this._fetch(`${this.baseUrl}/api/logs?${params}`);
717
729
  if (!response.ok) {
718
730
  throw new Error(`Logs request failed: ${response.statusText}`);
719
731
  }
@@ -721,7 +733,7 @@ class ControlPanelApi {
721
733
  }
722
734
 
723
735
  async getLogSources(): Promise<LogSource[]> {
724
- const response = await fetch(`${this.baseUrl}/api/logs/sources`);
736
+ const response = await this._fetch(`${this.baseUrl}/api/logs/sources`);
725
737
  if (!response.ok) {
726
738
  throw new Error(`Log sources request failed: ${response.statusText}`);
727
739
  }
@@ -734,7 +746,7 @@ class ControlPanelApi {
734
746
  // ==================
735
747
 
736
748
  async getPlugins(): Promise<PluginsResponse> {
737
- const response = await fetch(`${this.baseUrl}/api/plugins`);
749
+ const response = await this._fetch(`${this.baseUrl}/api/plugins`);
738
750
  if (!response.ok) {
739
751
  throw new Error(`Plugins request failed: ${response.statusText}`);
740
752
  }
@@ -742,7 +754,7 @@ class ControlPanelApi {
742
754
  }
743
755
 
744
756
  async getPluginDetail(id: string): Promise<PluginDetailResponse> {
745
- const response = await fetch(`${this.baseUrl}/api/plugins/${encodeURIComponent(id)}`);
757
+ const response = await this._fetch(`${this.baseUrl}/api/plugins/${encodeURIComponent(id)}`);
746
758
  if (!response.ok) {
747
759
  if (response.status === 404) {
748
760
  throw new Error(`Plugin not found: ${id}`);
@@ -757,7 +769,7 @@ class ControlPanelApi {
757
769
  // ==================
758
770
 
759
771
  async getUiContributions(): Promise<UiContributionsResponse> {
760
- const response = await fetch(`${this.baseUrl}/api/ui-contributions`);
772
+ const response = await this._fetch(`${this.baseUrl}/api/ui-contributions`);
761
773
  if (!response.ok) {
762
774
  throw new Error(`UI contributions request failed: ${response.statusText}`);
763
775
  }
@@ -769,7 +781,7 @@ class ControlPanelApi {
769
781
  // ==================
770
782
 
771
783
  async getAuthConfigStatus(): Promise<AuthConfigStatus> {
772
- const response = await fetch(`${this.baseUrl}/api/auth/config/status`);
784
+ const response = await this._fetch(`${this.baseUrl}/api/auth/config/status`);
773
785
  if (!response.ok) {
774
786
  // Return disabled state if endpoint not available
775
787
  if (response.status === 404) {
@@ -781,7 +793,7 @@ class ControlPanelApi {
781
793
  }
782
794
 
783
795
  async getAuthConfig(): Promise<AuthConfigStatus> {
784
- const response = await fetch(`${this.baseUrl}/api/auth/config`);
796
+ const response = await this._fetch(`${this.baseUrl}/api/auth/config`);
785
797
  if (!response.ok) {
786
798
  if (response.status === 404) {
787
799
  return { state: 'disabled', adapter: null };
@@ -795,7 +807,7 @@ class ControlPanelApi {
795
807
  * Update auth configuration (save to database for hot-reload)
796
808
  */
797
809
  async updateAuthConfig(request: UpdateAuthConfigRequest): Promise<{ success: boolean; message: string }> {
798
- const response = await fetch(`${this.baseUrl}/api/auth/config`, {
810
+ const response = await this._fetch(`${this.baseUrl}/api/auth/config`, {
799
811
  method: 'PUT',
800
812
  headers: { 'Content-Type': 'application/json' },
801
813
  body: JSON.stringify(request),
@@ -811,7 +823,7 @@ class ControlPanelApi {
811
823
  * Delete auth configuration (revert to environment variables)
812
824
  */
813
825
  async deleteAuthConfig(): Promise<{ success: boolean; message: string }> {
814
- const response = await fetch(`${this.baseUrl}/api/auth/config`, {
826
+ const response = await this._fetch(`${this.baseUrl}/api/auth/config`, {
815
827
  method: 'DELETE',
816
828
  });
817
829
  if (!response.ok) {
@@ -825,7 +837,7 @@ class ControlPanelApi {
825
837
  * Test auth provider connection without saving
826
838
  */
827
839
  async testAuthProvider(request: TestProviderRequest): Promise<TestProviderResponse> {
828
- const response = await fetch(`${this.baseUrl}/api/auth/test-provider`, {
840
+ const response = await this._fetch(`${this.baseUrl}/api/auth/test-provider`, {
829
841
  method: 'POST',
830
842
  headers: { 'Content-Type': 'application/json' },
831
843
  body: JSON.stringify(request),
@@ -841,7 +853,7 @@ class ControlPanelApi {
841
853
  * Test current auth provider connection (uses existing env/runtime config)
842
854
  */
843
855
  async testCurrentAuthProvider(): Promise<TestProviderResponse> {
844
- const response = await fetch(`${this.baseUrl}/api/auth/test-current`, {
856
+ const response = await this._fetch(`${this.baseUrl}/api/auth/test-current`, {
845
857
  method: 'POST',
846
858
  headers: { 'Content-Type': 'application/json' },
847
859
  });
@@ -857,7 +869,7 @@ class ControlPanelApi {
857
869
  // ==================
858
870
 
859
871
  async getRateLimitConfig(): Promise<RateLimitConfig> {
860
- const response = await fetch(`${this.baseUrl}/api/rate-limit/config`);
872
+ const response = await this._fetch(`${this.baseUrl}/api/rate-limit/config`);
861
873
  if (!response.ok) {
862
874
  throw new Error(`Rate limit config request failed: ${response.statusText}`);
863
875
  }
@@ -865,7 +877,7 @@ class ControlPanelApi {
865
877
  }
866
878
 
867
879
  async updateRateLimitConfig(updates: RateLimitConfigUpdateRequest): Promise<RateLimitConfigUpdateResponse> {
868
- const response = await fetch(`${this.baseUrl}/api/rate-limit/config`, {
880
+ const response = await this._fetch(`${this.baseUrl}/api/rate-limit/config`, {
869
881
  method: 'PUT',
870
882
  headers: { 'Content-Type': 'application/json' },
871
883
  body: JSON.stringify(updates),
@@ -882,7 +894,7 @@ class ControlPanelApi {
882
894
  // ==================
883
895
 
884
896
  async getNotificationsStats(): Promise<NotificationsStatsResponse> {
885
- const response = await fetch(`${this.baseUrl}/api/notifications/stats`);
897
+ const response = await this._fetch(`${this.baseUrl}/api/notifications/stats`);
886
898
  if (!response.ok) {
887
899
  throw new Error(`Notifications stats request failed: ${response.statusText}`);
888
900
  }
@@ -890,7 +902,7 @@ class ControlPanelApi {
890
902
  }
891
903
 
892
904
  async getNotificationsClients(): Promise<NotificationsClientsResponse> {
893
- const response = await fetch(`${this.baseUrl}/api/notifications/clients`);
905
+ const response = await this._fetch(`${this.baseUrl}/api/notifications/clients`);
894
906
  if (!response.ok) {
895
907
  throw new Error(`Notifications clients request failed: ${response.statusText}`);
896
908
  }
@@ -898,7 +910,7 @@ class ControlPanelApi {
898
910
  }
899
911
 
900
912
  async disconnectNotificationsClient(clientId: string): Promise<{ success: boolean }> {
901
- const response = await fetch(`${this.baseUrl}/api/notifications/clients/${encodeURIComponent(clientId)}`, {
913
+ const response = await this._fetch(`${this.baseUrl}/api/notifications/clients/${encodeURIComponent(clientId)}`, {
902
914
  method: 'DELETE',
903
915
  });
904
916
  if (!response.ok) {
@@ -909,7 +921,7 @@ class ControlPanelApi {
909
921
  }
910
922
 
911
923
  async forceNotificationsReconnect(): Promise<{ success: boolean; message: string }> {
912
- const response = await fetch(`${this.baseUrl}/api/notifications/reconnect`, {
924
+ const response = await this._fetch(`${this.baseUrl}/api/notifications/reconnect`, {
913
925
  method: 'POST',
914
926
  });
915
927
  if (!response.ok) {