@serve.zone/dcrouter 11.0.51 → 11.2.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.
Files changed (46) hide show
  1. package/dist_serve/bundle.js +1 -1
  2. package/dist_ts/00_commitinfo_data.js +2 -2
  3. package/dist_ts_apiclient/classes.apitoken.d.ts +41 -0
  4. package/dist_ts_apiclient/classes.apitoken.js +115 -0
  5. package/dist_ts_apiclient/classes.certificate.d.ts +57 -0
  6. package/dist_ts_apiclient/classes.certificate.js +69 -0
  7. package/dist_ts_apiclient/classes.config.d.ts +7 -0
  8. package/dist_ts_apiclient/classes.config.js +11 -0
  9. package/dist_ts_apiclient/classes.dcrouterapiclient.d.ts +41 -0
  10. package/dist_ts_apiclient/classes.dcrouterapiclient.js +81 -0
  11. package/dist_ts_apiclient/classes.email.d.ts +30 -0
  12. package/dist_ts_apiclient/classes.email.js +52 -0
  13. package/dist_ts_apiclient/classes.logs.d.ts +21 -0
  14. package/dist_ts_apiclient/classes.logs.js +14 -0
  15. package/dist_ts_apiclient/classes.radius.d.ts +59 -0
  16. package/dist_ts_apiclient/classes.radius.js +95 -0
  17. package/dist_ts_apiclient/classes.remoteingress.d.ts +54 -0
  18. package/dist_ts_apiclient/classes.remoteingress.js +136 -0
  19. package/dist_ts_apiclient/classes.route.d.ts +42 -0
  20. package/dist_ts_apiclient/classes.route.js +154 -0
  21. package/dist_ts_apiclient/classes.stats.d.ts +47 -0
  22. package/dist_ts_apiclient/classes.stats.js +38 -0
  23. package/dist_ts_apiclient/index.d.ts +10 -0
  24. package/dist_ts_apiclient/index.js +14 -0
  25. package/dist_ts_apiclient/plugins.d.ts +3 -0
  26. package/dist_ts_apiclient/plugins.js +5 -0
  27. package/dist_ts_web/00_commitinfo_data.js +2 -2
  28. package/package.json +7 -4
  29. package/readme.md +103 -7
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts_apiclient/classes.apitoken.ts +157 -0
  32. package/ts_apiclient/classes.certificate.ts +123 -0
  33. package/ts_apiclient/classes.config.ts +17 -0
  34. package/ts_apiclient/classes.dcrouterapiclient.ts +112 -0
  35. package/ts_apiclient/classes.email.ts +77 -0
  36. package/ts_apiclient/classes.logs.ts +37 -0
  37. package/ts_apiclient/classes.radius.ts +180 -0
  38. package/ts_apiclient/classes.remoteingress.ts +185 -0
  39. package/ts_apiclient/classes.route.ts +203 -0
  40. package/ts_apiclient/classes.stats.ts +111 -0
  41. package/ts_apiclient/index.ts +15 -0
  42. package/ts_apiclient/plugins.ts +8 -0
  43. package/ts_apiclient/readme.md +279 -0
  44. package/ts_apiclient/tspublish.json +3 -0
  45. package/ts_web/00_commitinfo_data.ts +1 -1
  46. package/ts_web/readme.md +7 -0
@@ -0,0 +1,77 @@
1
+ import * as interfaces from '../ts_interfaces/index.js';
2
+ import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
3
+
4
+ export class Email {
5
+ private clientRef: DcRouterApiClient;
6
+
7
+ // Data from IEmail
8
+ public id: string;
9
+ public direction: interfaces.requests.TEmailDirection;
10
+ public status: interfaces.requests.TEmailStatus;
11
+ public from: string;
12
+ public to: string;
13
+ public subject: string;
14
+ public timestamp: string;
15
+ public messageId: string;
16
+ public size: string;
17
+
18
+ constructor(clientRef: DcRouterApiClient, data: interfaces.requests.IEmail) {
19
+ this.clientRef = clientRef;
20
+ this.id = data.id;
21
+ this.direction = data.direction;
22
+ this.status = data.status;
23
+ this.from = data.from;
24
+ this.to = data.to;
25
+ this.subject = data.subject;
26
+ this.timestamp = data.timestamp;
27
+ this.messageId = data.messageId;
28
+ this.size = data.size;
29
+ }
30
+
31
+ public async getDetail(): Promise<interfaces.requests.IEmailDetail | null> {
32
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetEmailDetail>(
33
+ 'getEmailDetail',
34
+ this.clientRef.buildRequestPayload({ emailId: this.id }) as any,
35
+ );
36
+ return response.email;
37
+ }
38
+
39
+ public async resend(): Promise<{ success: boolean; newQueueId?: string }> {
40
+ const response = await this.clientRef.request<interfaces.requests.IReq_ResendEmail>(
41
+ 'resendEmail',
42
+ this.clientRef.buildRequestPayload({ emailId: this.id }) as any,
43
+ );
44
+ return response;
45
+ }
46
+ }
47
+
48
+ export class EmailManager {
49
+ private clientRef: DcRouterApiClient;
50
+
51
+ constructor(clientRef: DcRouterApiClient) {
52
+ this.clientRef = clientRef;
53
+ }
54
+
55
+ public async list(): Promise<Email[]> {
56
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetAllEmails>(
57
+ 'getAllEmails',
58
+ this.clientRef.buildRequestPayload() as any,
59
+ );
60
+ return response.emails.map((e) => new Email(this.clientRef, e));
61
+ }
62
+
63
+ public async getDetail(emailId: string): Promise<interfaces.requests.IEmailDetail | null> {
64
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetEmailDetail>(
65
+ 'getEmailDetail',
66
+ this.clientRef.buildRequestPayload({ emailId }) as any,
67
+ );
68
+ return response.email;
69
+ }
70
+
71
+ public async resend(emailId: string): Promise<{ success: boolean; newQueueId?: string }> {
72
+ return this.clientRef.request<interfaces.requests.IReq_ResendEmail>(
73
+ 'resendEmail',
74
+ this.clientRef.buildRequestPayload({ emailId }) as any,
75
+ );
76
+ }
77
+ }
@@ -0,0 +1,37 @@
1
+ import * as interfaces from '../ts_interfaces/index.js';
2
+ import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
3
+
4
+ export class LogManager {
5
+ private clientRef: DcRouterApiClient;
6
+
7
+ constructor(clientRef: DcRouterApiClient) {
8
+ this.clientRef = clientRef;
9
+ }
10
+
11
+ public async getRecent(options?: {
12
+ level?: 'debug' | 'info' | 'warn' | 'error';
13
+ category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
14
+ limit?: number;
15
+ offset?: number;
16
+ search?: string;
17
+ timeRange?: string;
18
+ }): Promise<interfaces.requests.IReq_GetRecentLogs['response']> {
19
+ return this.clientRef.request<interfaces.requests.IReq_GetRecentLogs>(
20
+ 'getRecentLogs',
21
+ this.clientRef.buildRequestPayload(options || {}) as any,
22
+ );
23
+ }
24
+
25
+ public async getStream(options?: {
26
+ follow?: boolean;
27
+ filters?: {
28
+ level?: string[];
29
+ category?: string[];
30
+ };
31
+ }): Promise<interfaces.requests.IReq_GetLogStream['response']> {
32
+ return this.clientRef.request<interfaces.requests.IReq_GetLogStream>(
33
+ 'getLogStream',
34
+ this.clientRef.buildRequestPayload(options || {}) as any,
35
+ );
36
+ }
37
+ }
@@ -0,0 +1,180 @@
1
+ import * as interfaces from '../ts_interfaces/index.js';
2
+ import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
3
+
4
+ // =====================
5
+ // Sub-managers
6
+ // =====================
7
+
8
+ export class RadiusClientManager {
9
+ private clientRef: DcRouterApiClient;
10
+
11
+ constructor(clientRef: DcRouterApiClient) {
12
+ this.clientRef = clientRef;
13
+ }
14
+
15
+ public async list(): Promise<Array<{
16
+ name: string;
17
+ ipRange: string;
18
+ description?: string;
19
+ enabled: boolean;
20
+ }>> {
21
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetRadiusClients>(
22
+ 'getRadiusClients',
23
+ this.clientRef.buildRequestPayload() as any,
24
+ );
25
+ return response.clients;
26
+ }
27
+
28
+ public async set(client: {
29
+ name: string;
30
+ ipRange: string;
31
+ secret: string;
32
+ description?: string;
33
+ enabled: boolean;
34
+ }): Promise<void> {
35
+ const response = await this.clientRef.request<interfaces.requests.IReq_SetRadiusClient>(
36
+ 'setRadiusClient',
37
+ this.clientRef.buildRequestPayload({ client }) as any,
38
+ );
39
+ if (!response.success) {
40
+ throw new Error(response.message || 'Failed to set RADIUS client');
41
+ }
42
+ }
43
+
44
+ public async remove(name: string): Promise<void> {
45
+ const response = await this.clientRef.request<interfaces.requests.IReq_RemoveRadiusClient>(
46
+ 'removeRadiusClient',
47
+ this.clientRef.buildRequestPayload({ name }) as any,
48
+ );
49
+ if (!response.success) {
50
+ throw new Error(response.message || 'Failed to remove RADIUS client');
51
+ }
52
+ }
53
+ }
54
+
55
+ export class RadiusVlanManager {
56
+ private clientRef: DcRouterApiClient;
57
+
58
+ constructor(clientRef: DcRouterApiClient) {
59
+ this.clientRef = clientRef;
60
+ }
61
+
62
+ public async list(): Promise<interfaces.requests.IReq_GetVlanMappings['response']> {
63
+ return this.clientRef.request<interfaces.requests.IReq_GetVlanMappings>(
64
+ 'getVlanMappings',
65
+ this.clientRef.buildRequestPayload() as any,
66
+ );
67
+ }
68
+
69
+ public async set(mapping: {
70
+ mac: string;
71
+ vlan: number;
72
+ description?: string;
73
+ enabled: boolean;
74
+ }): Promise<void> {
75
+ const response = await this.clientRef.request<interfaces.requests.IReq_SetVlanMapping>(
76
+ 'setVlanMapping',
77
+ this.clientRef.buildRequestPayload({ mapping }) as any,
78
+ );
79
+ if (!response.success) {
80
+ throw new Error(response.message || 'Failed to set VLAN mapping');
81
+ }
82
+ }
83
+
84
+ public async remove(mac: string): Promise<void> {
85
+ const response = await this.clientRef.request<interfaces.requests.IReq_RemoveVlanMapping>(
86
+ 'removeVlanMapping',
87
+ this.clientRef.buildRequestPayload({ mac }) as any,
88
+ );
89
+ if (!response.success) {
90
+ throw new Error(response.message || 'Failed to remove VLAN mapping');
91
+ }
92
+ }
93
+
94
+ public async updateConfig(options: {
95
+ defaultVlan?: number;
96
+ allowUnknownMacs?: boolean;
97
+ }): Promise<{ defaultVlan: number; allowUnknownMacs: boolean }> {
98
+ const response = await this.clientRef.request<interfaces.requests.IReq_UpdateVlanConfig>(
99
+ 'updateVlanConfig',
100
+ this.clientRef.buildRequestPayload(options) as any,
101
+ );
102
+ if (!response.success) {
103
+ throw new Error('Failed to update VLAN config');
104
+ }
105
+ return response.config;
106
+ }
107
+
108
+ public async testAssignment(mac: string): Promise<interfaces.requests.IReq_TestVlanAssignment['response']> {
109
+ return this.clientRef.request<interfaces.requests.IReq_TestVlanAssignment>(
110
+ 'testVlanAssignment',
111
+ this.clientRef.buildRequestPayload({ mac }) as any,
112
+ );
113
+ }
114
+ }
115
+
116
+ export class RadiusSessionManager {
117
+ private clientRef: DcRouterApiClient;
118
+
119
+ constructor(clientRef: DcRouterApiClient) {
120
+ this.clientRef = clientRef;
121
+ }
122
+
123
+ public async list(filter?: {
124
+ username?: string;
125
+ nasIpAddress?: string;
126
+ vlanId?: number;
127
+ }): Promise<interfaces.requests.IReq_GetRadiusSessions['response']> {
128
+ return this.clientRef.request<interfaces.requests.IReq_GetRadiusSessions>(
129
+ 'getRadiusSessions',
130
+ this.clientRef.buildRequestPayload({ filter }) as any,
131
+ );
132
+ }
133
+
134
+ public async disconnect(sessionId: string, reason?: string): Promise<void> {
135
+ const response = await this.clientRef.request<interfaces.requests.IReq_DisconnectRadiusSession>(
136
+ 'disconnectRadiusSession',
137
+ this.clientRef.buildRequestPayload({ sessionId, reason }) as any,
138
+ );
139
+ if (!response.success) {
140
+ throw new Error(response.message || 'Failed to disconnect session');
141
+ }
142
+ }
143
+ }
144
+
145
+ // =====================
146
+ // Main RADIUS Manager
147
+ // =====================
148
+
149
+ export class RadiusManager {
150
+ private clientRef: DcRouterApiClient;
151
+
152
+ public clients: RadiusClientManager;
153
+ public vlans: RadiusVlanManager;
154
+ public sessions: RadiusSessionManager;
155
+
156
+ constructor(clientRef: DcRouterApiClient) {
157
+ this.clientRef = clientRef;
158
+ this.clients = new RadiusClientManager(clientRef);
159
+ this.vlans = new RadiusVlanManager(clientRef);
160
+ this.sessions = new RadiusSessionManager(clientRef);
161
+ }
162
+
163
+ public async getAccountingSummary(
164
+ startTime: number,
165
+ endTime: number,
166
+ ): Promise<interfaces.requests.IReq_GetRadiusAccountingSummary['response']['summary']> {
167
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetRadiusAccountingSummary>(
168
+ 'getRadiusAccountingSummary',
169
+ this.clientRef.buildRequestPayload({ startTime, endTime }) as any,
170
+ );
171
+ return response.summary;
172
+ }
173
+
174
+ public async getStatistics(): Promise<interfaces.requests.IReq_GetRadiusStatistics['response']> {
175
+ return this.clientRef.request<interfaces.requests.IReq_GetRadiusStatistics>(
176
+ 'getRadiusStatistics',
177
+ this.clientRef.buildRequestPayload() as any,
178
+ );
179
+ }
180
+ }
@@ -0,0 +1,185 @@
1
+ import * as interfaces from '../ts_interfaces/index.js';
2
+ import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
3
+
4
+ export class RemoteIngress {
5
+ private clientRef: DcRouterApiClient;
6
+
7
+ // Data from IRemoteIngress
8
+ public id: string;
9
+ public name: string;
10
+ public secret: string;
11
+ public listenPorts: number[];
12
+ public enabled: boolean;
13
+ public autoDerivePorts: boolean;
14
+ public tags?: string[];
15
+ public createdAt: number;
16
+ public updatedAt: number;
17
+ public effectiveListenPorts?: number[];
18
+ public manualPorts?: number[];
19
+ public derivedPorts?: number[];
20
+
21
+ constructor(clientRef: DcRouterApiClient, data: interfaces.data.IRemoteIngress) {
22
+ this.clientRef = clientRef;
23
+ this.id = data.id;
24
+ this.name = data.name;
25
+ this.secret = data.secret;
26
+ this.listenPorts = data.listenPorts;
27
+ this.enabled = data.enabled;
28
+ this.autoDerivePorts = data.autoDerivePorts;
29
+ this.tags = data.tags;
30
+ this.createdAt = data.createdAt;
31
+ this.updatedAt = data.updatedAt;
32
+ this.effectiveListenPorts = data.effectiveListenPorts;
33
+ this.manualPorts = data.manualPorts;
34
+ this.derivedPorts = data.derivedPorts;
35
+ }
36
+
37
+ public async update(changes: {
38
+ name?: string;
39
+ listenPorts?: number[];
40
+ autoDerivePorts?: boolean;
41
+ enabled?: boolean;
42
+ tags?: string[];
43
+ }): Promise<void> {
44
+ const response = await this.clientRef.request<interfaces.requests.IReq_UpdateRemoteIngress>(
45
+ 'updateRemoteIngress',
46
+ this.clientRef.buildRequestPayload({ id: this.id, ...changes }) as any,
47
+ );
48
+ if (!response.success) {
49
+ throw new Error('Failed to update remote ingress');
50
+ }
51
+ // Update local state from response
52
+ const edge = response.edge;
53
+ this.name = edge.name;
54
+ this.listenPorts = edge.listenPorts;
55
+ this.enabled = edge.enabled;
56
+ this.autoDerivePorts = edge.autoDerivePorts;
57
+ this.tags = edge.tags;
58
+ this.updatedAt = edge.updatedAt;
59
+ this.effectiveListenPorts = edge.effectiveListenPorts;
60
+ this.manualPorts = edge.manualPorts;
61
+ this.derivedPorts = edge.derivedPorts;
62
+ }
63
+
64
+ public async delete(): Promise<void> {
65
+ const response = await this.clientRef.request<interfaces.requests.IReq_DeleteRemoteIngress>(
66
+ 'deleteRemoteIngress',
67
+ this.clientRef.buildRequestPayload({ id: this.id }) as any,
68
+ );
69
+ if (!response.success) {
70
+ throw new Error(response.message || 'Failed to delete remote ingress');
71
+ }
72
+ }
73
+
74
+ public async regenerateSecret(): Promise<string> {
75
+ const response = await this.clientRef.request<interfaces.requests.IReq_RegenerateRemoteIngressSecret>(
76
+ 'regenerateRemoteIngressSecret',
77
+ this.clientRef.buildRequestPayload({ id: this.id }) as any,
78
+ );
79
+ if (!response.success) {
80
+ throw new Error('Failed to regenerate secret');
81
+ }
82
+ this.secret = response.secret;
83
+ return response.secret;
84
+ }
85
+
86
+ public async getConnectionToken(hubHost?: string): Promise<string | undefined> {
87
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngressConnectionToken>(
88
+ 'getRemoteIngressConnectionToken',
89
+ this.clientRef.buildRequestPayload({ edgeId: this.id, hubHost }) as any,
90
+ );
91
+ if (!response.success) {
92
+ throw new Error(response.message || 'Failed to get connection token');
93
+ }
94
+ return response.token;
95
+ }
96
+ }
97
+
98
+ export class RemoteIngressBuilder {
99
+ private clientRef: DcRouterApiClient;
100
+ private edgeName: string = '';
101
+ private edgeListenPorts?: number[];
102
+ private edgeAutoDerivePorts?: boolean;
103
+ private edgeTags?: string[];
104
+
105
+ constructor(clientRef: DcRouterApiClient) {
106
+ this.clientRef = clientRef;
107
+ }
108
+
109
+ public setName(name: string): this {
110
+ this.edgeName = name;
111
+ return this;
112
+ }
113
+
114
+ public setListenPorts(ports: number[]): this {
115
+ this.edgeListenPorts = ports;
116
+ return this;
117
+ }
118
+
119
+ public setAutoDerivePorts(auto: boolean): this {
120
+ this.edgeAutoDerivePorts = auto;
121
+ return this;
122
+ }
123
+
124
+ public setTags(tags: string[]): this {
125
+ this.edgeTags = tags;
126
+ return this;
127
+ }
128
+
129
+ public async save(): Promise<RemoteIngress> {
130
+ const response = await this.clientRef.request<interfaces.requests.IReq_CreateRemoteIngress>(
131
+ 'createRemoteIngress',
132
+ this.clientRef.buildRequestPayload({
133
+ name: this.edgeName,
134
+ listenPorts: this.edgeListenPorts,
135
+ autoDerivePorts: this.edgeAutoDerivePorts,
136
+ tags: this.edgeTags,
137
+ }) as any,
138
+ );
139
+ if (!response.success) {
140
+ throw new Error('Failed to create remote ingress');
141
+ }
142
+ return new RemoteIngress(this.clientRef, response.edge);
143
+ }
144
+ }
145
+
146
+ export class RemoteIngressManager {
147
+ private clientRef: DcRouterApiClient;
148
+
149
+ constructor(clientRef: DcRouterApiClient) {
150
+ this.clientRef = clientRef;
151
+ }
152
+
153
+ public async list(): Promise<RemoteIngress[]> {
154
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngresses>(
155
+ 'getRemoteIngresses',
156
+ this.clientRef.buildRequestPayload() as any,
157
+ );
158
+ return response.edges.map((e) => new RemoteIngress(this.clientRef, e));
159
+ }
160
+
161
+ public async getStatuses(): Promise<interfaces.data.IRemoteIngressStatus[]> {
162
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngressStatus>(
163
+ 'getRemoteIngressStatus',
164
+ this.clientRef.buildRequestPayload() as any,
165
+ );
166
+ return response.statuses;
167
+ }
168
+
169
+ public async create(options: {
170
+ name: string;
171
+ listenPorts?: number[];
172
+ autoDerivePorts?: boolean;
173
+ tags?: string[];
174
+ }): Promise<RemoteIngress> {
175
+ const builder = this.build().setName(options.name);
176
+ if (options.listenPorts) builder.setListenPorts(options.listenPorts);
177
+ if (options.autoDerivePorts !== undefined) builder.setAutoDerivePorts(options.autoDerivePorts);
178
+ if (options.tags) builder.setTags(options.tags);
179
+ return builder.save();
180
+ }
181
+
182
+ public build(): RemoteIngressBuilder {
183
+ return new RemoteIngressBuilder(this.clientRef);
184
+ }
185
+ }
@@ -0,0 +1,203 @@
1
+ import * as interfaces from '../ts_interfaces/index.js';
2
+ import type { IRouteConfig } from '@push.rocks/smartproxy';
3
+ import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';
4
+
5
+ export class Route {
6
+ private clientRef: DcRouterApiClient;
7
+
8
+ // Data from IMergedRoute
9
+ public routeConfig: IRouteConfig;
10
+ public source: 'hardcoded' | 'programmatic';
11
+ public enabled: boolean;
12
+ public overridden: boolean;
13
+ public storedRouteId?: string;
14
+ public createdAt?: number;
15
+ public updatedAt?: number;
16
+
17
+ // Convenience accessors
18
+ public get name(): string {
19
+ return this.routeConfig.name || '';
20
+ }
21
+
22
+ constructor(clientRef: DcRouterApiClient, data: interfaces.data.IMergedRoute) {
23
+ this.clientRef = clientRef;
24
+ this.routeConfig = data.route;
25
+ this.source = data.source;
26
+ this.enabled = data.enabled;
27
+ this.overridden = data.overridden;
28
+ this.storedRouteId = data.storedRouteId;
29
+ this.createdAt = data.createdAt;
30
+ this.updatedAt = data.updatedAt;
31
+ }
32
+
33
+ public async update(changes: Partial<IRouteConfig>): Promise<void> {
34
+ if (!this.storedRouteId) {
35
+ throw new Error('Cannot update a hardcoded route. Use setOverride() instead.');
36
+ }
37
+ const response = await this.clientRef.request<interfaces.requests.IReq_UpdateRoute>(
38
+ 'updateRoute',
39
+ this.clientRef.buildRequestPayload({ id: this.storedRouteId, route: changes }) as any,
40
+ );
41
+ if (!response.success) {
42
+ throw new Error(response.message || 'Failed to update route');
43
+ }
44
+ }
45
+
46
+ public async delete(): Promise<void> {
47
+ if (!this.storedRouteId) {
48
+ throw new Error('Cannot delete a hardcoded route. Use setOverride() instead.');
49
+ }
50
+ const response = await this.clientRef.request<interfaces.requests.IReq_DeleteRoute>(
51
+ 'deleteRoute',
52
+ this.clientRef.buildRequestPayload({ id: this.storedRouteId }) as any,
53
+ );
54
+ if (!response.success) {
55
+ throw new Error(response.message || 'Failed to delete route');
56
+ }
57
+ }
58
+
59
+ public async toggle(enabled: boolean): Promise<void> {
60
+ if (!this.storedRouteId) {
61
+ throw new Error('Cannot toggle a hardcoded route. Use setOverride() instead.');
62
+ }
63
+ const response = await this.clientRef.request<interfaces.requests.IReq_ToggleRoute>(
64
+ 'toggleRoute',
65
+ this.clientRef.buildRequestPayload({ id: this.storedRouteId, enabled }) as any,
66
+ );
67
+ if (!response.success) {
68
+ throw new Error(response.message || 'Failed to toggle route');
69
+ }
70
+ this.enabled = enabled;
71
+ }
72
+
73
+ public async setOverride(enabled: boolean): Promise<void> {
74
+ const response = await this.clientRef.request<interfaces.requests.IReq_SetRouteOverride>(
75
+ 'setRouteOverride',
76
+ this.clientRef.buildRequestPayload({ routeName: this.name, enabled }) as any,
77
+ );
78
+ if (!response.success) {
79
+ throw new Error(response.message || 'Failed to set route override');
80
+ }
81
+ this.overridden = true;
82
+ this.enabled = enabled;
83
+ }
84
+
85
+ public async removeOverride(): Promise<void> {
86
+ const response = await this.clientRef.request<interfaces.requests.IReq_RemoveRouteOverride>(
87
+ 'removeRouteOverride',
88
+ this.clientRef.buildRequestPayload({ routeName: this.name }) as any,
89
+ );
90
+ if (!response.success) {
91
+ throw new Error(response.message || 'Failed to remove route override');
92
+ }
93
+ this.overridden = false;
94
+ }
95
+ }
96
+
97
+ export class RouteBuilder {
98
+ private clientRef: DcRouterApiClient;
99
+ private routeConfig: Partial<IRouteConfig> = {};
100
+ private isEnabled: boolean = true;
101
+
102
+ constructor(clientRef: DcRouterApiClient) {
103
+ this.clientRef = clientRef;
104
+ }
105
+
106
+ public setName(name: string): this {
107
+ this.routeConfig.name = name;
108
+ return this;
109
+ }
110
+
111
+ public setMatch(match: IRouteConfig['match']): this {
112
+ this.routeConfig.match = match;
113
+ return this;
114
+ }
115
+
116
+ public setAction(action: IRouteConfig['action']): this {
117
+ this.routeConfig.action = action;
118
+ return this;
119
+ }
120
+
121
+ public setTls(tls: IRouteConfig['action']['tls']): this {
122
+ if (!this.routeConfig.action) {
123
+ this.routeConfig.action = { type: 'forward' } as IRouteConfig['action'];
124
+ }
125
+ this.routeConfig.action!.tls = tls;
126
+ return this;
127
+ }
128
+
129
+ public setEnabled(enabled: boolean): this {
130
+ this.isEnabled = enabled;
131
+ return this;
132
+ }
133
+
134
+ public async save(): Promise<Route> {
135
+ const response = await this.clientRef.request<interfaces.requests.IReq_CreateRoute>(
136
+ 'createRoute',
137
+ this.clientRef.buildRequestPayload({
138
+ route: this.routeConfig as IRouteConfig,
139
+ enabled: this.isEnabled,
140
+ }) as any,
141
+ );
142
+ if (!response.success) {
143
+ throw new Error(response.message || 'Failed to create route');
144
+ }
145
+
146
+ // Return a Route instance by re-fetching the list
147
+ // The created route is programmatic, so we find it by storedRouteId
148
+ const { routes } = await new RouteManager(this.clientRef).list();
149
+ const created = routes.find((r) => r.storedRouteId === response.storedRouteId);
150
+ if (created) {
151
+ return created;
152
+ }
153
+
154
+ // Fallback: construct from known data
155
+ return new Route(this.clientRef, {
156
+ route: this.routeConfig as IRouteConfig,
157
+ source: 'programmatic',
158
+ enabled: this.isEnabled,
159
+ overridden: false,
160
+ storedRouteId: response.storedRouteId,
161
+ });
162
+ }
163
+ }
164
+
165
+ export class RouteManager {
166
+ private clientRef: DcRouterApiClient;
167
+
168
+ constructor(clientRef: DcRouterApiClient) {
169
+ this.clientRef = clientRef;
170
+ }
171
+
172
+ public async list(): Promise<{ routes: Route[]; warnings: interfaces.data.IRouteWarning[] }> {
173
+ const response = await this.clientRef.request<interfaces.requests.IReq_GetMergedRoutes>(
174
+ 'getMergedRoutes',
175
+ this.clientRef.buildRequestPayload() as any,
176
+ );
177
+ return {
178
+ routes: response.routes.map((r) => new Route(this.clientRef, r)),
179
+ warnings: response.warnings,
180
+ };
181
+ }
182
+
183
+ public async create(routeConfig: IRouteConfig, enabled?: boolean): Promise<Route> {
184
+ const response = await this.clientRef.request<interfaces.requests.IReq_CreateRoute>(
185
+ 'createRoute',
186
+ this.clientRef.buildRequestPayload({ route: routeConfig, enabled: enabled ?? true }) as any,
187
+ );
188
+ if (!response.success) {
189
+ throw new Error(response.message || 'Failed to create route');
190
+ }
191
+ return new Route(this.clientRef, {
192
+ route: routeConfig,
193
+ source: 'programmatic',
194
+ enabled: enabled ?? true,
195
+ overridden: false,
196
+ storedRouteId: response.storedRouteId,
197
+ });
198
+ }
199
+
200
+ public build(): RouteBuilder {
201
+ return new RouteBuilder(this.clientRef);
202
+ }
203
+ }