@serve.zone/dcrouter 11.12.3 → 11.13.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 (50) hide show
  1. package/dist_serve/bundle.js +705 -548
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.d.ts +30 -0
  4. package/dist_ts/classes.dcrouter.js +104 -5
  5. package/dist_ts/config/classes.route-config-manager.d.ts +2 -1
  6. package/dist_ts/config/classes.route-config-manager.js +21 -5
  7. package/dist_ts/opsserver/classes.opsserver.d.ts +1 -0
  8. package/dist_ts/opsserver/classes.opsserver.js +3 -1
  9. package/dist_ts/opsserver/handlers/index.d.ts +1 -0
  10. package/dist_ts/opsserver/handlers/index.js +2 -1
  11. package/dist_ts/opsserver/handlers/vpn.handler.d.ts +6 -0
  12. package/dist_ts/opsserver/handlers/vpn.handler.js +199 -0
  13. package/dist_ts/plugins.d.ts +2 -1
  14. package/dist_ts/plugins.js +3 -2
  15. package/dist_ts/vpn/classes.vpn-manager.d.ts +113 -0
  16. package/dist_ts/vpn/classes.vpn-manager.js +297 -0
  17. package/dist_ts/vpn/index.d.ts +1 -0
  18. package/dist_ts/vpn/index.js +2 -0
  19. package/dist_ts_interfaces/data/index.d.ts +1 -0
  20. package/dist_ts_interfaces/data/index.js +2 -1
  21. package/dist_ts_interfaces/data/remoteingress.d.ts +10 -1
  22. package/dist_ts_interfaces/data/vpn.d.ts +43 -0
  23. package/dist_ts_interfaces/data/vpn.js +2 -0
  24. package/dist_ts_interfaces/requests/index.d.ts +1 -0
  25. package/dist_ts_interfaces/requests/index.js +2 -1
  26. package/dist_ts_interfaces/requests/vpn.d.ts +135 -0
  27. package/dist_ts_interfaces/requests/vpn.js +3 -0
  28. package/dist_ts_web/00_commitinfo_data.js +1 -1
  29. package/dist_ts_web/appstate.d.ts +22 -0
  30. package/dist_ts_web/appstate.js +111 -1
  31. package/dist_ts_web/elements/index.d.ts +1 -0
  32. package/dist_ts_web/elements/index.js +2 -1
  33. package/dist_ts_web/elements/ops-dashboard.js +7 -1
  34. package/dist_ts_web/elements/ops-view-vpn.d.ts +14 -0
  35. package/dist_ts_web/elements/ops-view-vpn.js +369 -0
  36. package/package.json +4 -3
  37. package/ts/00_commitinfo_data.ts +1 -1
  38. package/ts/classes.dcrouter.ts +137 -3
  39. package/ts/config/classes.route-config-manager.ts +20 -3
  40. package/ts/opsserver/classes.opsserver.ts +2 -0
  41. package/ts/opsserver/handlers/index.ts +2 -1
  42. package/ts/opsserver/handlers/vpn.handler.ts +257 -0
  43. package/ts/plugins.ts +2 -1
  44. package/ts/vpn/classes.vpn-manager.ts +378 -0
  45. package/ts/vpn/index.ts +1 -0
  46. package/ts_web/00_commitinfo_data.ts +1 -1
  47. package/ts_web/appstate.ts +164 -0
  48. package/ts_web/elements/index.ts +1 -0
  49. package/ts_web/elements/ops-dashboard.ts +6 -0
  50. package/ts_web/elements/ops-view-vpn.ts +330 -0
@@ -7,6 +7,7 @@ import type {
7
7
  IMergedRoute,
8
8
  IRouteWarning,
9
9
  } from '../../ts_interfaces/data/route-management.js';
10
+ import type { IDcRouterRouteConfig } from '../../ts_interfaces/data/remoteingress.js';
10
11
  import { type IHttp3Config, augmentRouteWithHttp3 } from '../http3/index.js';
11
12
 
12
13
  const ROUTES_PREFIX = '/config-api/routes/';
@@ -22,6 +23,7 @@ export class RouteConfigManager {
22
23
  private getHardcodedRoutes: () => plugins.smartproxy.IRouteConfig[],
23
24
  private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
24
25
  private getHttp3Config?: () => IHttp3Config | undefined,
26
+ private getVpnSubnet?: () => string | undefined,
25
27
  ) {}
26
28
 
27
29
  /**
@@ -262,13 +264,28 @@ export class RouteConfigManager {
262
264
 
263
265
  // Add enabled programmatic routes (with HTTP/3 augmentation if enabled)
264
266
  const http3Config = this.getHttp3Config?.();
267
+ const vpnSubnet = this.getVpnSubnet?.();
265
268
  for (const stored of this.storedRoutes.values()) {
266
269
  if (stored.enabled) {
270
+ let route = stored.route;
267
271
  if (http3Config && http3Config.enabled !== false) {
268
- enabledRoutes.push(augmentRouteWithHttp3(stored.route, { enabled: true, ...http3Config }));
269
- } else {
270
- enabledRoutes.push(stored.route);
272
+ route = augmentRouteWithHttp3(route, { enabled: true, ...http3Config });
271
273
  }
274
+ // Inject VPN security for programmatic routes with vpn.required
275
+ if (vpnSubnet) {
276
+ const dcRoute = route as IDcRouterRouteConfig;
277
+ if (dcRoute.vpn?.required) {
278
+ const existing = route.security?.ipAllowList || [];
279
+ route = {
280
+ ...route,
281
+ security: {
282
+ ...route.security,
283
+ ipAllowList: [...existing, vpnSubnet],
284
+ },
285
+ };
286
+ }
287
+ }
288
+ enabledRoutes.push(route);
272
289
  }
273
290
  }
274
291
 
@@ -28,6 +28,7 @@ export class OpsServer {
28
28
  private remoteIngressHandler!: handlers.RemoteIngressHandler;
29
29
  private routeManagementHandler!: handlers.RouteManagementHandler;
30
30
  private apiTokenHandler!: handlers.ApiTokenHandler;
31
+ private vpnHandler!: handlers.VpnHandler;
31
32
 
32
33
  constructor(dcRouterRefArg: DcRouter) {
33
34
  this.dcRouterRef = dcRouterRefArg;
@@ -86,6 +87,7 @@ export class OpsServer {
86
87
  this.remoteIngressHandler = new handlers.RemoteIngressHandler(this);
87
88
  this.routeManagementHandler = new handlers.RouteManagementHandler(this);
88
89
  this.apiTokenHandler = new handlers.ApiTokenHandler(this);
90
+ this.vpnHandler = new handlers.VpnHandler(this);
89
91
 
90
92
  console.log('✅ OpsServer TypedRequest handlers initialized');
91
93
  }
@@ -8,4 +8,5 @@ export * from './email-ops.handler.js';
8
8
  export * from './certificate.handler.js';
9
9
  export * from './remoteingress.handler.js';
10
10
  export * from './route-management.handler.js';
11
- export * from './api-token.handler.js';
11
+ export * from './api-token.handler.js';
12
+ export * from './vpn.handler.js';
@@ -0,0 +1,257 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { OpsServer } from '../classes.opsserver.js';
3
+ import * as interfaces from '../../../ts_interfaces/index.js';
4
+
5
+ export class VpnHandler {
6
+ constructor(private opsServerRef: OpsServer) {
7
+ this.registerHandlers();
8
+ }
9
+
10
+ private registerHandlers(): void {
11
+ const viewRouter = this.opsServerRef.viewRouter;
12
+ const adminRouter = this.opsServerRef.adminRouter;
13
+
14
+ // ---- Read endpoints (viewRouter — valid identity required via middleware) ----
15
+
16
+ // Get all registered VPN clients
17
+ viewRouter.addTypedHandler(
18
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnClients>(
19
+ 'getVpnClients',
20
+ async (dataArg, toolsArg) => {
21
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
22
+ if (!manager) {
23
+ return { clients: [] };
24
+ }
25
+ const clients = manager.listClients().map((c) => ({
26
+ clientId: c.clientId,
27
+ enabled: c.enabled,
28
+ tags: c.tags,
29
+ description: c.description,
30
+ assignedIp: c.assignedIp,
31
+ createdAt: c.createdAt,
32
+ updatedAt: c.updatedAt,
33
+ expiresAt: c.expiresAt,
34
+ }));
35
+ return { clients };
36
+ },
37
+ ),
38
+ );
39
+
40
+ // Get VPN server status
41
+ viewRouter.addTypedHandler(
42
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnStatus>(
43
+ 'getVpnStatus',
44
+ async (dataArg, toolsArg) => {
45
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
46
+ const vpnConfig = this.opsServerRef.dcRouterRef.options.vpnConfig;
47
+ if (!manager) {
48
+ return {
49
+ status: {
50
+ running: false,
51
+ forwardingMode: 'socket' as const,
52
+ subnet: vpnConfig?.subnet || '10.8.0.0/24',
53
+ wgListenPort: vpnConfig?.wgListenPort ?? 51820,
54
+ serverPublicKeys: null,
55
+ registeredClients: 0,
56
+ connectedClients: 0,
57
+ },
58
+ };
59
+ }
60
+
61
+ const connected = await manager.getConnectedClients();
62
+ return {
63
+ status: {
64
+ running: manager.running,
65
+ forwardingMode: manager.forwardingMode,
66
+ subnet: manager.getSubnet(),
67
+ wgListenPort: vpnConfig?.wgListenPort ?? 51820,
68
+ serverPublicKeys: manager.getServerPublicKeys(),
69
+ registeredClients: manager.listClients().length,
70
+ connectedClients: connected.length,
71
+ },
72
+ };
73
+ },
74
+ ),
75
+ );
76
+
77
+ // ---- Write endpoints (adminRouter — admin identity required via middleware) ----
78
+
79
+ // Create a new VPN client
80
+ adminRouter.addTypedHandler(
81
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateVpnClient>(
82
+ 'createVpnClient',
83
+ async (dataArg, toolsArg) => {
84
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
85
+ if (!manager) {
86
+ return { success: false, message: 'VPN not configured' };
87
+ }
88
+
89
+ try {
90
+ const bundle = await manager.createClient({
91
+ clientId: dataArg.clientId,
92
+ tags: dataArg.tags,
93
+ description: dataArg.description,
94
+ });
95
+
96
+ return {
97
+ success: true,
98
+ client: {
99
+ clientId: bundle.entry.clientId,
100
+ enabled: bundle.entry.enabled ?? true,
101
+ tags: bundle.entry.tags,
102
+ description: bundle.entry.description,
103
+ assignedIp: bundle.entry.assignedIp,
104
+ createdAt: Date.now(),
105
+ updatedAt: Date.now(),
106
+ expiresAt: bundle.entry.expiresAt,
107
+ },
108
+ wireguardConfig: bundle.wireguardConfig,
109
+ };
110
+ } catch (err: unknown) {
111
+ return { success: false, message: (err as Error).message };
112
+ }
113
+ },
114
+ ),
115
+ );
116
+
117
+ // Delete a VPN client
118
+ adminRouter.addTypedHandler(
119
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteVpnClient>(
120
+ 'deleteVpnClient',
121
+ async (dataArg, toolsArg) => {
122
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
123
+ if (!manager) {
124
+ return { success: false, message: 'VPN not configured' };
125
+ }
126
+
127
+ try {
128
+ await manager.removeClient(dataArg.clientId);
129
+ return { success: true };
130
+ } catch (err: unknown) {
131
+ return { success: false, message: (err as Error).message };
132
+ }
133
+ },
134
+ ),
135
+ );
136
+
137
+ // Enable a VPN client
138
+ adminRouter.addTypedHandler(
139
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_EnableVpnClient>(
140
+ 'enableVpnClient',
141
+ async (dataArg, toolsArg) => {
142
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
143
+ if (!manager) {
144
+ return { success: false, message: 'VPN not configured' };
145
+ }
146
+
147
+ try {
148
+ await manager.enableClient(dataArg.clientId);
149
+ return { success: true };
150
+ } catch (err: unknown) {
151
+ return { success: false, message: (err as Error).message };
152
+ }
153
+ },
154
+ ),
155
+ );
156
+
157
+ // Disable a VPN client
158
+ adminRouter.addTypedHandler(
159
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DisableVpnClient>(
160
+ 'disableVpnClient',
161
+ async (dataArg, toolsArg) => {
162
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
163
+ if (!manager) {
164
+ return { success: false, message: 'VPN not configured' };
165
+ }
166
+
167
+ try {
168
+ await manager.disableClient(dataArg.clientId);
169
+ return { success: true };
170
+ } catch (err: unknown) {
171
+ return { success: false, message: (err as Error).message };
172
+ }
173
+ },
174
+ ),
175
+ );
176
+
177
+ // Rotate a VPN client's keys
178
+ adminRouter.addTypedHandler(
179
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RotateVpnClientKey>(
180
+ 'rotateVpnClientKey',
181
+ async (dataArg, toolsArg) => {
182
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
183
+ if (!manager) {
184
+ return { success: false, message: 'VPN not configured' };
185
+ }
186
+
187
+ try {
188
+ const bundle = await manager.rotateClientKey(dataArg.clientId);
189
+ return {
190
+ success: true,
191
+ wireguardConfig: bundle.wireguardConfig,
192
+ };
193
+ } catch (err: unknown) {
194
+ return { success: false, message: (err as Error).message };
195
+ }
196
+ },
197
+ ),
198
+ );
199
+
200
+ // Export a VPN client config
201
+ adminRouter.addTypedHandler(
202
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ExportVpnClientConfig>(
203
+ 'exportVpnClientConfig',
204
+ async (dataArg, toolsArg) => {
205
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
206
+ if (!manager) {
207
+ return { success: false, message: 'VPN not configured' };
208
+ }
209
+
210
+ try {
211
+ const config = await manager.exportClientConfig(dataArg.clientId, dataArg.format);
212
+ return { success: true, config };
213
+ } catch (err: unknown) {
214
+ return { success: false, message: (err as Error).message };
215
+ }
216
+ },
217
+ ),
218
+ );
219
+
220
+ // Get telemetry for a specific VPN client
221
+ viewRouter.addTypedHandler(
222
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnClientTelemetry>(
223
+ 'getVpnClientTelemetry',
224
+ async (dataArg, toolsArg) => {
225
+ const manager = this.opsServerRef.dcRouterRef.vpnManager;
226
+ if (!manager) {
227
+ return { success: false, message: 'VPN not configured' };
228
+ }
229
+
230
+ try {
231
+ const telemetry = await manager.getClientTelemetry(dataArg.clientId);
232
+ if (!telemetry) {
233
+ return { success: false, message: 'Client not found or not connected' };
234
+ }
235
+ return {
236
+ success: true,
237
+ telemetry: {
238
+ clientId: telemetry.clientId,
239
+ assignedIp: telemetry.assignedIp,
240
+ bytesSent: telemetry.bytesSent,
241
+ bytesReceived: telemetry.bytesReceived,
242
+ packetsDropped: telemetry.packetsDropped,
243
+ bytesDropped: telemetry.bytesDropped,
244
+ lastKeepaliveAt: telemetry.lastKeepaliveAt,
245
+ keepalivesReceived: telemetry.keepalivesReceived,
246
+ rateLimitBytesPerSec: telemetry.rateLimitBytesPerSec,
247
+ burstBytes: telemetry.burstBytes,
248
+ },
249
+ };
250
+ } catch (err: unknown) {
251
+ return { success: false, message: (err as Error).message };
252
+ }
253
+ },
254
+ ),
255
+ );
256
+ }
257
+ }
package/ts/plugins.ts CHANGED
@@ -58,13 +58,14 @@ import * as smartnetwork from '@push.rocks/smartnetwork';
58
58
  import * as smartpath from '@push.rocks/smartpath';
59
59
  import * as smartproxy from '@push.rocks/smartproxy';
60
60
  import * as smartpromise from '@push.rocks/smartpromise';
61
+ import * as smartvpn from '@push.rocks/smartvpn';
61
62
  import * as smartradius from '@push.rocks/smartradius';
62
63
  import * as smartrequest from '@push.rocks/smartrequest';
63
64
  import * as smartrx from '@push.rocks/smartrx';
64
65
  import * as smartunique from '@push.rocks/smartunique';
65
66
  import * as taskbuffer from '@push.rocks/taskbuffer';
66
67
 
67
- export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfs, smartguard, smartjwt, smartlog, smartmetrics, smartdb, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, taskbuffer };
68
+ export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfs, smartguard, smartjwt, smartlog, smartmetrics, smartdb, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, smartvpn, taskbuffer };
68
69
 
69
70
  // Define SmartLog types for use in error handling
70
71
  export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug';