@serve.zone/dcrouter 15.0.1 → 15.0.3

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 (117) hide show
  1. package/deno.json +1 -1
  2. package/dist_serve/bundle.js +768 -768
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/acme/classes.smartacme-lifecycle.d.ts +25 -0
  5. package/dist_ts/acme/classes.smartacme-lifecycle.js +144 -0
  6. package/dist_ts/acme/index.d.ts +1 -0
  7. package/dist_ts/acme/index.js +2 -1
  8. package/dist_ts/classes.dcrouter.d.ts +21 -139
  9. package/dist_ts/classes.dcrouter.js +71 -1585
  10. package/dist_ts/dns/classes.dns-server-runtime.d.ts +37 -0
  11. package/dist_ts/dns/classes.dns-server-runtime.js +449 -0
  12. package/dist_ts/dns/index.d.ts +1 -0
  13. package/dist_ts/dns/index.js +2 -1
  14. package/dist_ts/email/classes.accepted-email-spool.d.ts +55 -0
  15. package/dist_ts/email/classes.accepted-email-spool.js +345 -0
  16. package/dist_ts/email/classes.email-route-builder.d.ts +28 -0
  17. package/dist_ts/email/classes.email-route-builder.js +260 -0
  18. package/dist_ts/email/index.d.ts +2 -0
  19. package/dist_ts/email/index.js +3 -1
  20. package/dist_ts/opsserver/handlers/gatewayclient.handler.js +10 -8
  21. package/dist_ts/remoteingress/classes.hub-lifecycle.d.ts +27 -0
  22. package/dist_ts/remoteingress/classes.hub-lifecycle.js +241 -0
  23. package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +1 -2
  24. package/dist_ts/remoteingress/index.d.ts +1 -0
  25. package/dist_ts/remoteingress/index.js +2 -1
  26. package/dist_ts/security/classes.route-policy-augmenter.d.ts +22 -0
  27. package/dist_ts/security/classes.route-policy-augmenter.js +120 -0
  28. package/dist_ts/security/index.d.ts +1 -0
  29. package/dist_ts/security/index.js +2 -1
  30. package/dist_ts/vpn/classes.vpn-access-resolver.d.ts +34 -0
  31. package/dist_ts/vpn/classes.vpn-access-resolver.js +101 -0
  32. package/dist_ts/vpn/index.d.ts +1 -0
  33. package/dist_ts/vpn/index.js +2 -1
  34. package/dist_ts_migrations/index.js +92 -9
  35. package/dist_ts_web/00_commitinfo_data.js +1 -1
  36. package/dist_ts_web/appstate/acme.d.ts +17 -0
  37. package/dist_ts_web/appstate/acme.js +64 -0
  38. package/dist_ts_web/appstate/certificates.d.ts +37 -0
  39. package/dist_ts_web/appstate/certificates.js +107 -0
  40. package/dist_ts_web/appstate/config.d.ts +9 -0
  41. package/dist_ts_web/appstate/config.js +35 -0
  42. package/dist_ts_web/appstate/domains.d.ts +80 -0
  43. package/dist_ts_web/appstate/domains.js +324 -0
  44. package/dist_ts_web/appstate/email-domains.d.ts +25 -0
  45. package/dist_ts_web/appstate/email-domains.js +104 -0
  46. package/dist_ts_web/appstate/email-ops.d.ts +10 -0
  47. package/dist_ts_web/appstate/email-ops.js +40 -0
  48. package/dist_ts_web/appstate/login.d.ts +30 -0
  49. package/dist_ts_web/appstate/login.js +83 -0
  50. package/dist_ts_web/appstate/logs.d.ts +16 -0
  51. package/dist_ts_web/appstate/logs.js +27 -0
  52. package/dist_ts_web/appstate/network.d.ts +50 -0
  53. package/dist_ts_web/appstate/network.js +122 -0
  54. package/dist_ts_web/appstate/profiles-targets.d.ts +45 -0
  55. package/dist_ts_web/appstate/profiles-targets.js +173 -0
  56. package/dist_ts_web/appstate/remoteingress.d.ts +47 -0
  57. package/dist_ts_web/appstate/remoteingress.js +204 -0
  58. package/dist_ts_web/appstate/routes.d.ts +76 -0
  59. package/dist_ts_web/appstate/routes.js +316 -0
  60. package/dist_ts_web/appstate/runtime.d.ts +1 -0
  61. package/dist_ts_web/appstate/runtime.js +276 -0
  62. package/dist_ts_web/appstate/security.d.ts +29 -0
  63. package/dist_ts_web/appstate/security.js +167 -0
  64. package/dist_ts_web/appstate/shared.d.ts +3 -0
  65. package/dist_ts_web/appstate/shared.js +13 -0
  66. package/dist_ts_web/appstate/stats.d.ts +15 -0
  67. package/dist_ts_web/appstate/stats.js +59 -0
  68. package/dist_ts_web/appstate/target-profiles.d.ts +37 -0
  69. package/dist_ts_web/appstate/target-profiles.js +118 -0
  70. package/dist_ts_web/appstate/ui.d.ts +11 -0
  71. package/dist_ts_web/appstate/ui.js +55 -0
  72. package/dist_ts_web/appstate/users.d.ts +27 -0
  73. package/dist_ts_web/appstate/users.js +85 -0
  74. package/dist_ts_web/appstate/vpn.d.ts +44 -0
  75. package/dist_ts_web/appstate/vpn.js +148 -0
  76. package/dist_ts_web/appstate.d.ts +20 -568
  77. package/dist_ts_web/appstate.js +24 -2418
  78. package/package.json +1 -1
  79. package/ts/00_commitinfo_data.ts +1 -1
  80. package/ts/acme/classes.smartacme-lifecycle.ts +155 -0
  81. package/ts/acme/index.ts +1 -0
  82. package/ts/classes.dcrouter.ts +118 -1919
  83. package/ts/dns/classes.dns-server-runtime.ts +525 -0
  84. package/ts/dns/index.ts +1 -0
  85. package/ts/email/classes.accepted-email-spool.ts +434 -0
  86. package/ts/email/classes.email-route-builder.ts +312 -0
  87. package/ts/email/index.ts +2 -0
  88. package/ts/opsserver/handlers/gatewayclient.handler.ts +9 -7
  89. package/ts/remoteingress/classes.hub-lifecycle.ts +278 -0
  90. package/ts/remoteingress/classes.remoteingress-manager.ts +1 -1
  91. package/ts/remoteingress/index.ts +1 -0
  92. package/ts/security/classes.route-policy-augmenter.ts +140 -0
  93. package/ts/security/index.ts +1 -0
  94. package/ts/vpn/classes.vpn-access-resolver.ts +126 -0
  95. package/ts/vpn/index.ts +1 -0
  96. package/ts_web/00_commitinfo_data.ts +1 -1
  97. package/ts_web/appstate/acme.ts +93 -0
  98. package/ts_web/appstate/certificates.ts +159 -0
  99. package/ts_web/appstate/config.ts +49 -0
  100. package/ts_web/appstate/domains.ts +429 -0
  101. package/ts_web/appstate/email-domains.ts +155 -0
  102. package/ts_web/appstate/email-ops.ts +57 -0
  103. package/ts_web/appstate/login.ts +128 -0
  104. package/ts_web/appstate/logs.ts +50 -0
  105. package/ts_web/appstate/network.ts +161 -0
  106. package/ts_web/appstate/profiles-targets.ts +240 -0
  107. package/ts_web/appstate/remoteingress.ts +300 -0
  108. package/ts_web/appstate/routes.ts +447 -0
  109. package/ts_web/appstate/runtime.ts +308 -0
  110. package/ts_web/appstate/security.ts +229 -0
  111. package/ts_web/appstate/shared.ts +15 -0
  112. package/ts_web/appstate/stats.ts +79 -0
  113. package/ts_web/appstate/target-profiles.ts +164 -0
  114. package/ts_web/appstate/ui.ts +75 -0
  115. package/ts_web/appstate/users.ts +133 -0
  116. package/ts_web/appstate/vpn.ts +234 -0
  117. package/ts_web/appstate.ts +24 -3403
@@ -0,0 +1,429 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+ import { getActionContext } from './login.js';
5
+
6
+ // ============================================================================
7
+ // Domains State (DNS providers + domains + records)
8
+ // ============================================================================
9
+
10
+ export interface IDomainsState {
11
+ providers: interfaces.data.IDnsProviderPublic[];
12
+ domains: interfaces.data.IDomain[];
13
+ records: interfaces.data.IDnsRecord[];
14
+ /** id of the currently-selected domain in the DNS records subview. */
15
+ selectedDomainId: string | null;
16
+ isLoading: boolean;
17
+ error: string | null;
18
+ lastUpdated: number;
19
+ }
20
+
21
+ export const domainsStatePart = await appState.getStatePart<IDomainsState>(
22
+ 'domains',
23
+ {
24
+ providers: [],
25
+ domains: [],
26
+ records: [],
27
+ selectedDomainId: null,
28
+ isLoading: false,
29
+ error: null,
30
+ lastUpdated: 0,
31
+ },
32
+ 'soft',
33
+ );
34
+
35
+ export const fetchDomainsAndProvidersAction = domainsStatePart.createAction(
36
+ async (statePartArg): Promise<IDomainsState> => {
37
+ const context = getActionContext();
38
+ const currentState = statePartArg.getState()!;
39
+ if (!context.identity) return currentState;
40
+
41
+ try {
42
+ const providersRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
43
+ interfaces.requests.IReq_GetDnsProviders
44
+ >('/typedrequest', 'getDnsProviders');
45
+ const domainsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
46
+ interfaces.requests.IReq_GetDomains
47
+ >('/typedrequest', 'getDomains');
48
+
49
+ const [providersResponse, domainsResponse] = await Promise.all([
50
+ providersRequest.fire({ identity: context.identity }),
51
+ domainsRequest.fire({ identity: context.identity }),
52
+ ]);
53
+
54
+ return {
55
+ ...currentState,
56
+ providers: providersResponse.providers,
57
+ domains: domainsResponse.domains,
58
+ isLoading: false,
59
+ error: null,
60
+ lastUpdated: Date.now(),
61
+ };
62
+ } catch (error: unknown) {
63
+ return {
64
+ ...currentState,
65
+ isLoading: false,
66
+ error: error instanceof Error ? error.message : 'Failed to fetch domains/providers',
67
+ };
68
+ }
69
+ },
70
+ );
71
+
72
+ export const fetchDnsRecordsForDomainAction = domainsStatePart.createAction<{ domainId: string }>(
73
+ async (statePartArg, dataArg): Promise<IDomainsState> => {
74
+ const context = getActionContext();
75
+ const currentState = statePartArg.getState()!;
76
+ if (!context.identity) return currentState;
77
+
78
+ try {
79
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
80
+ interfaces.requests.IReq_GetDnsRecords
81
+ >('/typedrequest', 'getDnsRecords');
82
+ const response = await request.fire({
83
+ identity: context.identity,
84
+ domainId: dataArg.domainId,
85
+ });
86
+ return {
87
+ ...currentState,
88
+ records: response.records,
89
+ selectedDomainId: dataArg.domainId,
90
+ error: null,
91
+ };
92
+ } catch (error: unknown) {
93
+ return {
94
+ ...currentState,
95
+ error: error instanceof Error ? error.message : 'Failed to fetch DNS records',
96
+ };
97
+ }
98
+ },
99
+ );
100
+
101
+ export const createDnsProviderAction = domainsStatePart.createAction<{
102
+ name: string;
103
+ type: interfaces.data.TDnsProviderType;
104
+ credentials: interfaces.data.TDnsProviderCredentials;
105
+ }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
106
+ const context = getActionContext();
107
+ try {
108
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
109
+ interfaces.requests.IReq_CreateDnsProvider
110
+ >('/typedrequest', 'createDnsProvider');
111
+ const response = await request.fire({
112
+ identity: context.identity!,
113
+ name: dataArg.name,
114
+ type: dataArg.type,
115
+ credentials: dataArg.credentials,
116
+ });
117
+ if (!response.success) {
118
+ return {
119
+ ...statePartArg.getState()!,
120
+ error: response.message || 'Failed to create provider',
121
+ };
122
+ }
123
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
124
+ } catch (error: unknown) {
125
+ return {
126
+ ...statePartArg.getState()!,
127
+ error: error instanceof Error ? error.message : 'Failed to create provider',
128
+ };
129
+ }
130
+ });
131
+
132
+ export const updateDnsProviderAction = domainsStatePart.createAction<{
133
+ id: string;
134
+ name?: string;
135
+ credentials?: interfaces.data.TDnsProviderCredentials;
136
+ }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
137
+ const context = getActionContext();
138
+ try {
139
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
140
+ interfaces.requests.IReq_UpdateDnsProvider
141
+ >('/typedrequest', 'updateDnsProvider');
142
+ const response = await request.fire({
143
+ identity: context.identity!,
144
+ id: dataArg.id,
145
+ name: dataArg.name,
146
+ credentials: dataArg.credentials,
147
+ });
148
+ if (!response.success) {
149
+ return {
150
+ ...statePartArg.getState()!,
151
+ error: response.message || 'Failed to update provider',
152
+ };
153
+ }
154
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
155
+ } catch (error: unknown) {
156
+ return {
157
+ ...statePartArg.getState()!,
158
+ error: error instanceof Error ? error.message : 'Failed to update provider',
159
+ };
160
+ }
161
+ });
162
+
163
+ export const deleteDnsProviderAction = domainsStatePart.createAction<{ id: string; force?: boolean }>(
164
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
165
+ const context = getActionContext();
166
+ try {
167
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
168
+ interfaces.requests.IReq_DeleteDnsProvider
169
+ >('/typedrequest', 'deleteDnsProvider');
170
+ const response = await request.fire({
171
+ identity: context.identity!,
172
+ id: dataArg.id,
173
+ force: dataArg.force,
174
+ });
175
+ if (!response.success) {
176
+ return {
177
+ ...statePartArg.getState()!,
178
+ error: response.message || 'Failed to delete provider',
179
+ };
180
+ }
181
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
182
+ } catch (error: unknown) {
183
+ return {
184
+ ...statePartArg.getState()!,
185
+ error: error instanceof Error ? error.message : 'Failed to delete provider',
186
+ };
187
+ }
188
+ },
189
+ );
190
+
191
+ export const testDnsProviderAction = domainsStatePart.createAction<{ id: string }>(
192
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
193
+ const context = getActionContext();
194
+ try {
195
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
196
+ interfaces.requests.IReq_TestDnsProvider
197
+ >('/typedrequest', 'testDnsProvider');
198
+ await request.fire({ identity: context.identity!, id: dataArg.id });
199
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
200
+ } catch (error: unknown) {
201
+ return {
202
+ ...statePartArg.getState()!,
203
+ error: error instanceof Error ? error.message : 'Failed to test provider',
204
+ };
205
+ }
206
+ },
207
+ );
208
+
209
+ /** One-shot fetch for the import-domain modal. Does NOT modify state. */
210
+ export async function fetchProviderDomains(
211
+ providerId: string,
212
+ ): Promise<{ success: boolean; domains?: interfaces.data.IProviderDomainListing[]; message?: string }> {
213
+ const context = getActionContext();
214
+ if (!context.identity) return { success: false, message: 'Not authenticated' };
215
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
216
+ interfaces.requests.IReq_ListProviderDomains
217
+ >('/typedrequest', 'listProviderDomains');
218
+ return await request.fire({ identity: context.identity, providerId });
219
+ }
220
+
221
+ export const createDcrouterDomainAction = domainsStatePart.createAction<{
222
+ name: string;
223
+ description?: string;
224
+ }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
225
+ const context = getActionContext();
226
+ try {
227
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
228
+ interfaces.requests.IReq_CreateDomain
229
+ >('/typedrequest', 'createDomain');
230
+ const response = await request.fire({
231
+ identity: context.identity!,
232
+ name: dataArg.name,
233
+ description: dataArg.description,
234
+ });
235
+ if (!response.success) {
236
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to create domain' };
237
+ }
238
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
239
+ } catch (error: unknown) {
240
+ return {
241
+ ...statePartArg.getState()!,
242
+ error: error instanceof Error ? error.message : 'Failed to create domain',
243
+ };
244
+ }
245
+ });
246
+
247
+ export const importDomainsFromProviderAction = domainsStatePart.createAction<{
248
+ providerId: string;
249
+ domainNames: string[];
250
+ }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
251
+ const context = getActionContext();
252
+ try {
253
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
254
+ interfaces.requests.IReq_ImportDomain
255
+ >('/typedrequest', 'importDomain');
256
+ const response = await request.fire({
257
+ identity: context.identity!,
258
+ providerId: dataArg.providerId,
259
+ domainNames: dataArg.domainNames,
260
+ });
261
+ if (!response.success) {
262
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to import domains' };
263
+ }
264
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
265
+ } catch (error: unknown) {
266
+ return {
267
+ ...statePartArg.getState()!,
268
+ error: error instanceof Error ? error.message : 'Failed to import domains',
269
+ };
270
+ }
271
+ });
272
+
273
+ export const deleteDomainAction = domainsStatePart.createAction<{ id: string }>(
274
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
275
+ const context = getActionContext();
276
+ try {
277
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
278
+ interfaces.requests.IReq_DeleteDomain
279
+ >('/typedrequest', 'deleteDomain');
280
+ const response = await request.fire({ identity: context.identity!, id: dataArg.id });
281
+ if (!response.success) {
282
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to delete domain' };
283
+ }
284
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
285
+ } catch (error: unknown) {
286
+ return {
287
+ ...statePartArg.getState()!,
288
+ error: error instanceof Error ? error.message : 'Failed to delete domain',
289
+ };
290
+ }
291
+ },
292
+ );
293
+
294
+ export const syncDomainAction = domainsStatePart.createAction<{ id: string }>(
295
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
296
+ const context = getActionContext();
297
+ try {
298
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
299
+ interfaces.requests.IReq_SyncDomain
300
+ >('/typedrequest', 'syncDomain');
301
+ const response = await request.fire({ identity: context.identity!, id: dataArg.id });
302
+ if (!response.success) {
303
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to sync domain' };
304
+ }
305
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
306
+ } catch (error: unknown) {
307
+ return {
308
+ ...statePartArg.getState()!,
309
+ error: error instanceof Error ? error.message : 'Failed to sync domain',
310
+ };
311
+ }
312
+ },
313
+ );
314
+
315
+ export const migrateDomainAction = domainsStatePart.createAction<{
316
+ id: string;
317
+ targetSource: interfaces.data.TDomainSource;
318
+ targetProviderId?: string;
319
+ deleteExistingProviderRecords?: boolean;
320
+ }>(
321
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
322
+ const context = getActionContext();
323
+ try {
324
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
325
+ interfaces.requests.IReq_MigrateDomain
326
+ >('/typedrequest', 'migrateDomain');
327
+ const response = await request.fire({ identity: context.identity!, ...dataArg });
328
+ if (!response.success) {
329
+ return { ...statePartArg.getState()!, error: response.message || 'Migration failed' };
330
+ }
331
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
332
+ } catch (error: unknown) {
333
+ return {
334
+ ...statePartArg.getState()!,
335
+ error: error instanceof Error ? error.message : 'Migration failed',
336
+ };
337
+ }
338
+ },
339
+ );
340
+
341
+ export const createDnsRecordAction = domainsStatePart.createAction<{
342
+ domainId: string;
343
+ name: string;
344
+ type: interfaces.data.TDnsRecordType;
345
+ value: string;
346
+ ttl?: number;
347
+ proxied?: boolean;
348
+ }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
349
+ const context = getActionContext();
350
+ try {
351
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
352
+ interfaces.requests.IReq_CreateDnsRecord
353
+ >('/typedrequest', 'createDnsRecord');
354
+ const response = await request.fire({
355
+ identity: context.identity!,
356
+ domainId: dataArg.domainId,
357
+ name: dataArg.name,
358
+ type: dataArg.type,
359
+ value: dataArg.value,
360
+ ttl: dataArg.ttl,
361
+ proxied: dataArg.proxied,
362
+ });
363
+ if (!response.success) {
364
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to create record' };
365
+ }
366
+ return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
367
+ } catch (error: unknown) {
368
+ return {
369
+ ...statePartArg.getState()!,
370
+ error: error instanceof Error ? error.message : 'Failed to create record',
371
+ };
372
+ }
373
+ });
374
+
375
+ export const updateDnsRecordAction = domainsStatePart.createAction<{
376
+ id: string;
377
+ domainId: string;
378
+ name?: string;
379
+ value?: string;
380
+ ttl?: number;
381
+ proxied?: boolean;
382
+ }>(async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
383
+ const context = getActionContext();
384
+ try {
385
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
386
+ interfaces.requests.IReq_UpdateDnsRecord
387
+ >('/typedrequest', 'updateDnsRecord');
388
+ const response = await request.fire({
389
+ identity: context.identity!,
390
+ id: dataArg.id,
391
+ name: dataArg.name,
392
+ value: dataArg.value,
393
+ ttl: dataArg.ttl,
394
+ proxied: dataArg.proxied,
395
+ });
396
+ if (!response.success) {
397
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to update record' };
398
+ }
399
+ return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
400
+ } catch (error: unknown) {
401
+ return {
402
+ ...statePartArg.getState()!,
403
+ error: error instanceof Error ? error.message : 'Failed to update record',
404
+ };
405
+ }
406
+ });
407
+
408
+ export const deleteDnsRecordAction = domainsStatePart.createAction<{ id: string; domainId: string }>(
409
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
410
+ const context = getActionContext();
411
+ try {
412
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
413
+ interfaces.requests.IReq_DeleteDnsRecord
414
+ >('/typedrequest', 'deleteDnsRecord');
415
+ const response = await request.fire({ identity: context.identity!, id: dataArg.id });
416
+ if (!response.success) {
417
+ return { ...statePartArg.getState()!, error: response.message || 'Failed to delete record' };
418
+ }
419
+ return await actionContext!.dispatch(fetchDnsRecordsForDomainAction, { domainId: dataArg.domainId });
420
+ } catch (error: unknown) {
421
+ return {
422
+ ...statePartArg.getState()!,
423
+ error: error instanceof Error ? error.message : 'Failed to delete record',
424
+ };
425
+ }
426
+ },
427
+ );
428
+
429
+ // ============================================================================
@@ -0,0 +1,155 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+ import { getActionContext } from './login.js';
5
+
6
+
7
+ // ============================================================================
8
+ // Email Domains State
9
+ // ============================================================================
10
+
11
+ export interface IEmailDomainsState {
12
+ domains: interfaces.data.IEmailDomain[];
13
+ settings: interfaces.data.IEmailServerSettings | null;
14
+ isLoading: boolean;
15
+ lastUpdated: number;
16
+ }
17
+
18
+ export const emailDomainsStatePart = await appState.getStatePart<IEmailDomainsState>(
19
+ 'emailDomains',
20
+ {
21
+ domains: [],
22
+ settings: null,
23
+ isLoading: false,
24
+ lastUpdated: 0,
25
+ },
26
+ 'soft',
27
+ );
28
+
29
+ export const fetchEmailDomainsAction = emailDomainsStatePart.createAction(
30
+ async (statePartArg): Promise<IEmailDomainsState> => {
31
+ const context = getActionContext();
32
+ const currentState = statePartArg.getState()!;
33
+ if (!context.identity) return currentState;
34
+
35
+ try {
36
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
37
+ interfaces.requests.IReq_GetEmailDomains
38
+ >('/typedrequest', 'getEmailDomains');
39
+ const settingsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
40
+ interfaces.requests.IReq_GetEmailServerSettings
41
+ >('/typedrequest', 'getEmailServerSettings');
42
+ const response = await request.fire({ identity: context.identity });
43
+ const settingsResponse = await settingsRequest.fire({ identity: context.identity });
44
+ return {
45
+ ...currentState,
46
+ domains: response.domains,
47
+ settings: settingsResponse.settings,
48
+ isLoading: false,
49
+ lastUpdated: Date.now(),
50
+ };
51
+ } catch {
52
+ return { ...currentState, isLoading: false };
53
+ }
54
+ },
55
+ );
56
+
57
+ export const createEmailDomainAction = emailDomainsStatePart.createAction<{
58
+ linkedDomainId: string;
59
+ subdomain?: string;
60
+ dkimSelector?: string;
61
+ dkimKeySize?: number;
62
+ rotateKeys?: boolean;
63
+ rotationIntervalDays?: number;
64
+ }>(async (statePartArg, args, actionContext) => {
65
+ const context = getActionContext();
66
+ const currentState = statePartArg.getState()!;
67
+ try {
68
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
69
+ interfaces.requests.IReq_CreateEmailDomain
70
+ >('/typedrequest', 'createEmailDomain');
71
+ await request.fire({ identity: context.identity!, ...args });
72
+ return await actionContext!.dispatch(fetchEmailDomainsAction, null);
73
+ } catch {
74
+ return currentState;
75
+ }
76
+ });
77
+
78
+ export const updateEmailServerSettingsAction = emailDomainsStatePart.createAction<
79
+ interfaces.data.TEmailServerSettingsUpdate
80
+ >(async (statePartArg, settings, actionContext) => {
81
+ const context = getActionContext();
82
+ const currentState = statePartArg.getState()!;
83
+ try {
84
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
85
+ interfaces.requests.IReq_UpdateEmailServerSettings
86
+ >('/typedrequest', 'updateEmailServerSettings');
87
+ const response = await request.fire({ identity: context.identity!, settings });
88
+ if (!response.success) {
89
+ return currentState;
90
+ }
91
+ return await actionContext!.dispatch(fetchEmailDomainsAction, null);
92
+ } catch {
93
+ return currentState;
94
+ }
95
+ });
96
+
97
+ export const deleteEmailDomainAction = emailDomainsStatePart.createAction<string>(
98
+ async (statePartArg, id, actionContext) => {
99
+ const context = getActionContext();
100
+ const currentState = statePartArg.getState()!;
101
+ try {
102
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
103
+ interfaces.requests.IReq_DeleteEmailDomain
104
+ >('/typedrequest', 'deleteEmailDomain');
105
+ await request.fire({ identity: context.identity!, id });
106
+ return await actionContext!.dispatch(fetchEmailDomainsAction, null);
107
+ } catch {
108
+ return currentState;
109
+ }
110
+ },
111
+ );
112
+
113
+ export const validateEmailDomainAction = emailDomainsStatePart.createAction<string>(
114
+ async (statePartArg, id, actionContext) => {
115
+ const context = getActionContext();
116
+ const currentState = statePartArg.getState()!;
117
+ try {
118
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
119
+ interfaces.requests.IReq_ValidateEmailDomain
120
+ >('/typedrequest', 'validateEmailDomain');
121
+ await request.fire({ identity: context.identity!, id });
122
+ return await actionContext!.dispatch(fetchEmailDomainsAction, null);
123
+ } catch {
124
+ return currentState;
125
+ }
126
+ },
127
+ );
128
+
129
+ export const provisionEmailDomainDnsAction = emailDomainsStatePart.createAction<string>(
130
+ async (statePartArg, id, actionContext) => {
131
+ const context = getActionContext();
132
+ const currentState = statePartArg.getState()!;
133
+ try {
134
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
135
+ interfaces.requests.IReq_ProvisionEmailDomainDns
136
+ >('/typedrequest', 'provisionEmailDomainDns');
137
+ await request.fire({ identity: context.identity!, id });
138
+ return await actionContext!.dispatch(fetchEmailDomainsAction, null);
139
+ } catch {
140
+ return currentState;
141
+ }
142
+ },
143
+ );
144
+
145
+ // ============================================================================
146
+ // Email Domain Standalone Functions
147
+ // ============================================================================
148
+
149
+ export async function fetchEmailDomainDnsRecords(id: string) {
150
+ const context = getActionContext();
151
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
152
+ interfaces.requests.IReq_GetEmailDomainDnsRecords
153
+ >('/typedrequest', 'getEmailDomainDnsRecords');
154
+ return request.fire({ identity: context.identity!, id });
155
+ }
@@ -0,0 +1,57 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as interfaces from '../../ts_interfaces/index.js';
3
+ import { appState } from './shared.js';
4
+ import { getActionContext } from './login.js';
5
+
6
+ export interface IEmailOpsState {
7
+ emails: interfaces.requests.IEmail[];
8
+ isLoading: boolean;
9
+ error: string | null;
10
+ lastUpdated: number;
11
+ }
12
+
13
+ export const emailOpsStatePart = await appState.getStatePart<IEmailOpsState>(
14
+ 'emailOps',
15
+ {
16
+ emails: [],
17
+ isLoading: false,
18
+ error: null,
19
+ lastUpdated: 0,
20
+ },
21
+ 'soft'
22
+ );
23
+
24
+ // Email Operations Actions
25
+ // ============================================================================
26
+
27
+ // Fetch All Emails Action
28
+ export const fetchAllEmailsAction = emailOpsStatePart.createAction(async (statePartArg): Promise<IEmailOpsState> => {
29
+ const context = getActionContext();
30
+ const currentState = statePartArg.getState()!;
31
+ if (!context.identity) return currentState;
32
+
33
+ try {
34
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
35
+ interfaces.requests.IReq_GetAllEmails
36
+ >('/typedrequest', 'getAllEmails');
37
+
38
+ const response = await request.fire({
39
+ identity: context.identity,
40
+ });
41
+
42
+ return {
43
+ emails: response.emails,
44
+ isLoading: false,
45
+ error: null,
46
+ lastUpdated: Date.now(),
47
+ };
48
+ } catch (error) {
49
+ return {
50
+ ...currentState,
51
+ isLoading: false,
52
+ error: error instanceof Error ? error.message : 'Failed to fetch emails',
53
+ };
54
+ }
55
+ });
56
+
57
+ // ============================================================================