@serve.zone/dcrouter 13.5.0 → 13.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_serve/bundle.js +1705 -1365
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +2 -5
- package/dist_ts/classes.dcrouter.js +41 -10
- package/dist_ts/db/documents/classes.dns-provider.doc.d.ts +22 -0
- package/dist_ts/db/documents/classes.dns-provider.doc.js +134 -0
- package/dist_ts/db/documents/classes.dns-record.doc.d.ts +21 -0
- package/dist_ts/db/documents/classes.dns-record.doc.js +143 -0
- package/dist_ts/db/documents/classes.domain.doc.d.ts +22 -0
- package/dist_ts/db/documents/classes.domain.doc.js +146 -0
- package/dist_ts/db/documents/index.d.ts +3 -0
- package/dist_ts/db/documents/index.js +5 -1
- package/dist_ts/dns/index.d.ts +2 -0
- package/dist_ts/dns/index.js +3 -0
- package/dist_ts/dns/manager.dns.d.ts +227 -0
- package/dist_ts/dns/manager.dns.js +747 -0
- package/dist_ts/dns/providers/cloudflare.provider.d.ts +21 -0
- package/dist_ts/dns/providers/cloudflare.provider.js +106 -0
- package/dist_ts/dns/providers/factory.d.ts +23 -0
- package/dist_ts/dns/providers/factory.js +38 -0
- package/dist_ts/dns/providers/index.d.ts +3 -0
- package/dist_ts/dns/providers/index.js +4 -0
- package/dist_ts/dns/providers/interfaces.d.ts +54 -0
- package/dist_ts/dns/providers/interfaces.js +2 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +3 -0
- package/dist_ts/opsserver/classes.opsserver.js +7 -1
- package/dist_ts/opsserver/handlers/config.handler.js +11 -2
- package/dist_ts/opsserver/handlers/dns-provider.handler.d.ts +16 -0
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +119 -0
- package/dist_ts/opsserver/handlers/dns-record.handler.d.ts +13 -0
- package/dist_ts/opsserver/handlers/dns-record.handler.js +98 -0
- package/dist_ts/opsserver/handlers/domain.handler.d.ts +13 -0
- package/dist_ts/opsserver/handlers/domain.handler.js +124 -0
- package/dist_ts/opsserver/handlers/index.d.ts +3 -0
- package/dist_ts/opsserver/handlers/index.js +4 -1
- package/dist_ts_interfaces/data/dns-provider.d.ts +112 -0
- package/dist_ts_interfaces/data/dns-provider.js +27 -0
- package/dist_ts_interfaces/data/dns-record.d.ts +40 -0
- package/dist_ts_interfaces/data/dns-record.js +2 -0
- package/dist_ts_interfaces/data/domain.d.ts +34 -0
- package/dist_ts_interfaces/data/domain.js +2 -0
- package/dist_ts_interfaces/data/index.d.ts +3 -0
- package/dist_ts_interfaces/data/index.js +4 -1
- package/dist_ts_interfaces/data/route-management.d.ts +1 -1
- package/dist_ts_interfaces/requests/dns-providers.d.ts +117 -0
- package/dist_ts_interfaces/requests/dns-providers.js +2 -0
- package/dist_ts_interfaces/requests/dns-records.d.ts +89 -0
- package/dist_ts_interfaces/requests/dns-records.js +2 -0
- package/dist_ts_interfaces/requests/domains.d.ts +118 -0
- package/dist_ts_interfaces/requests/domains.js +2 -0
- package/dist_ts_interfaces/requests/index.d.ts +3 -0
- package/dist_ts_interfaces/requests/index.js +4 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +72 -0
- package/dist_ts_web/appstate.js +308 -6
- package/dist_ts_web/elements/access/ops-view-apitokens.js +1 -1
- package/dist_ts_web/elements/access/ops-view-users.js +1 -1
- package/dist_ts_web/elements/domains/dns-provider-form.d.ts +58 -0
- package/dist_ts_web/elements/domains/dns-provider-form.js +268 -0
- package/dist_ts_web/elements/domains/index.d.ts +5 -0
- package/dist_ts_web/elements/domains/index.js +6 -0
- package/dist_ts_web/elements/{ops-view-certificates.d.ts → domains/ops-view-certificates.d.ts} +1 -1
- package/dist_ts_web/elements/{ops-view-certificates.js → domains/ops-view-certificates.js} +5 -5
- package/dist_ts_web/elements/domains/ops-view-dns.d.ts +17 -0
- package/dist_ts_web/elements/domains/ops-view-dns.js +304 -0
- package/dist_ts_web/elements/domains/ops-view-domains.d.ts +18 -0
- package/dist_ts_web/elements/domains/ops-view-domains.js +361 -0
- package/dist_ts_web/elements/domains/ops-view-providers.d.ts +21 -0
- package/dist_ts_web/elements/domains/ops-view-providers.js +316 -0
- package/dist_ts_web/elements/email/ops-view-email-security.js +1 -1
- package/dist_ts_web/elements/email/ops-view-emails.js +1 -1
- package/dist_ts_web/elements/index.d.ts +1 -1
- package/dist_ts_web/elements/index.js +2 -2
- package/dist_ts_web/elements/network/ops-view-network-activity.js +1 -1
- package/dist_ts_web/elements/network/ops-view-networktargets.js +1 -1
- package/dist_ts_web/elements/network/ops-view-remoteingress.js +1 -1
- package/dist_ts_web/elements/network/ops-view-routes.js +1 -1
- package/dist_ts_web/elements/network/ops-view-sourceprofiles.js +1 -1
- package/dist_ts_web/elements/network/ops-view-targetprofiles.js +1 -1
- package/dist_ts_web/elements/network/ops-view-vpn.js +1 -1
- package/dist_ts_web/elements/ops-dashboard.js +14 -5
- package/dist_ts_web/elements/ops-view-logs.js +1 -1
- package/dist_ts_web/elements/overview/ops-view-config.js +3 -3
- package/dist_ts_web/elements/overview/ops-view-overview.js +1 -1
- package/dist_ts_web/elements/security/ops-view-security-authentication.js +1 -1
- package/dist_ts_web/elements/security/ops-view-security-blocked.js +1 -1
- package/dist_ts_web/elements/security/ops-view-security-overview.js +1 -1
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +4 -2
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +46 -17
- package/ts/db/documents/classes.dns-provider.doc.ts +63 -0
- package/ts/db/documents/classes.dns-record.doc.ts +62 -0
- package/ts/db/documents/classes.domain.doc.ts +66 -0
- package/ts/db/documents/index.ts +5 -0
- package/ts/dns/index.ts +2 -0
- package/ts/dns/manager.dns.ts +869 -0
- package/ts/dns/providers/cloudflare.provider.ts +131 -0
- package/ts/dns/providers/factory.ts +48 -0
- package/ts/dns/providers/index.ts +3 -0
- package/ts/dns/providers/interfaces.ts +67 -0
- package/ts/opsserver/classes.opsserver.ts +6 -0
- package/ts/opsserver/handlers/config.handler.ts +10 -1
- package/ts/opsserver/handlers/dns-provider.handler.ts +159 -0
- package/ts/opsserver/handlers/dns-record.handler.ts +127 -0
- package/ts/opsserver/handlers/domain.handler.ts +161 -0
- package/ts/opsserver/handlers/index.ts +4 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +403 -5
- package/ts_web/elements/access/ops-view-apitokens.ts +1 -1
- package/ts_web/elements/access/ops-view-users.ts +1 -1
- package/ts_web/elements/domains/dns-provider-form.ts +216 -0
- package/ts_web/elements/domains/index.ts +5 -0
- package/ts_web/elements/{ops-view-certificates.ts → domains/ops-view-certificates.ts} +4 -4
- package/ts_web/elements/domains/ops-view-dns.ts +273 -0
- package/ts_web/elements/domains/ops-view-domains.ts +335 -0
- package/ts_web/elements/domains/ops-view-providers.ts +284 -0
- package/ts_web/elements/email/ops-view-email-security.ts +1 -1
- package/ts_web/elements/email/ops-view-emails.ts +1 -1
- package/ts_web/elements/index.ts +1 -1
- package/ts_web/elements/network/ops-view-network-activity.ts +1 -1
- package/ts_web/elements/network/ops-view-networktargets.ts +1 -1
- package/ts_web/elements/network/ops-view-remoteingress.ts +1 -1
- package/ts_web/elements/network/ops-view-routes.ts +1 -1
- package/ts_web/elements/network/ops-view-sourceprofiles.ts +1 -1
- package/ts_web/elements/network/ops-view-targetprofiles.ts +1 -1
- package/ts_web/elements/network/ops-view-vpn.ts +1 -1
- package/ts_web/elements/ops-dashboard.ts +14 -4
- package/ts_web/elements/ops-view-logs.ts +1 -1
- package/ts_web/elements/overview/ops-view-config.ts +2 -2
- package/ts_web/elements/overview/ops-view-overview.ts +1 -1
- package/ts_web/elements/security/ops-view-security-authentication.ts +1 -1
- package/ts_web/elements/security/ops-view-security-blocked.ts +1 -1
- package/ts_web/elements/security/ops-view-security-overview.ts +1 -1
- package/ts_web/router.ts +3 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
import type {
|
|
4
|
+
IDnsProviderClient,
|
|
5
|
+
IConnectionTestResult,
|
|
6
|
+
IProviderRecord,
|
|
7
|
+
IProviderRecordInput,
|
|
8
|
+
} from './interfaces.js';
|
|
9
|
+
import type { IProviderDomainListing } from '../../../ts_interfaces/data/dns-provider.js';
|
|
10
|
+
import type { TDnsRecordType } from '../../../ts_interfaces/data/dns-record.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Cloudflare implementation of IDnsProviderClient.
|
|
14
|
+
*
|
|
15
|
+
* Wraps `@apiclient.xyz/cloudflare`. Records at Cloudflare are addressed by
|
|
16
|
+
* an internal record id, which we surface as `providerRecordId` so the rest
|
|
17
|
+
* of the system can issue updates and deletes without ambiguity (Cloudflare
|
|
18
|
+
* can have multiple records of the same name+type).
|
|
19
|
+
*/
|
|
20
|
+
export class CloudflareDnsProvider implements IDnsProviderClient {
|
|
21
|
+
private cfAccount: plugins.cloudflare.CloudflareAccount;
|
|
22
|
+
|
|
23
|
+
constructor(apiToken: string) {
|
|
24
|
+
if (!apiToken) {
|
|
25
|
+
throw new Error('CloudflareDnsProvider: apiToken is required');
|
|
26
|
+
}
|
|
27
|
+
this.cfAccount = new plugins.cloudflare.CloudflareAccount(apiToken);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async testConnection(): Promise<IConnectionTestResult> {
|
|
31
|
+
try {
|
|
32
|
+
// Listing zones is the lightest-weight call that proves the token works.
|
|
33
|
+
await this.cfAccount.zoneManager.listZones();
|
|
34
|
+
return { ok: true };
|
|
35
|
+
} catch (err: unknown) {
|
|
36
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
37
|
+
logger.log('warn', `CloudflareDnsProvider testConnection failed: ${message}`);
|
|
38
|
+
return { ok: false, error: message };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async listDomains(): Promise<IProviderDomainListing[]> {
|
|
43
|
+
const zones = await this.cfAccount.zoneManager.listZones();
|
|
44
|
+
return zones.map((zone) => ({
|
|
45
|
+
name: zone.name,
|
|
46
|
+
externalId: zone.id,
|
|
47
|
+
nameservers: zone.name_servers ?? [],
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public async listRecords(domain: string): Promise<IProviderRecord[]> {
|
|
52
|
+
const records = await this.cfAccount.recordManager.listRecords(domain);
|
|
53
|
+
return records
|
|
54
|
+
.filter((r) => this.isSupportedType(r.type))
|
|
55
|
+
.map((r) => ({
|
|
56
|
+
providerRecordId: r.id,
|
|
57
|
+
name: r.name,
|
|
58
|
+
type: r.type as TDnsRecordType,
|
|
59
|
+
value: r.content,
|
|
60
|
+
ttl: r.ttl,
|
|
61
|
+
proxied: r.proxied,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async createRecord(
|
|
66
|
+
domain: string,
|
|
67
|
+
record: IProviderRecordInput,
|
|
68
|
+
): Promise<IProviderRecord> {
|
|
69
|
+
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain);
|
|
70
|
+
const apiRecord: any = {
|
|
71
|
+
zone_id: zoneId,
|
|
72
|
+
type: record.type,
|
|
73
|
+
name: record.name,
|
|
74
|
+
content: record.value,
|
|
75
|
+
ttl: record.ttl ?? 1, // 1 = automatic
|
|
76
|
+
};
|
|
77
|
+
if (record.proxied !== undefined) {
|
|
78
|
+
apiRecord.proxied = record.proxied;
|
|
79
|
+
}
|
|
80
|
+
const created = await (this.cfAccount as any).apiAccount.dns.records.create(apiRecord);
|
|
81
|
+
return {
|
|
82
|
+
providerRecordId: created.id,
|
|
83
|
+
name: created.name,
|
|
84
|
+
type: created.type as TDnsRecordType,
|
|
85
|
+
value: created.content,
|
|
86
|
+
ttl: created.ttl,
|
|
87
|
+
proxied: created.proxied,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async updateRecord(
|
|
92
|
+
domain: string,
|
|
93
|
+
providerRecordId: string,
|
|
94
|
+
record: IProviderRecordInput,
|
|
95
|
+
): Promise<IProviderRecord> {
|
|
96
|
+
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain);
|
|
97
|
+
const apiRecord: any = {
|
|
98
|
+
zone_id: zoneId,
|
|
99
|
+
type: record.type,
|
|
100
|
+
name: record.name,
|
|
101
|
+
content: record.value,
|
|
102
|
+
ttl: record.ttl ?? 1,
|
|
103
|
+
};
|
|
104
|
+
if (record.proxied !== undefined) {
|
|
105
|
+
apiRecord.proxied = record.proxied;
|
|
106
|
+
}
|
|
107
|
+
const updated = await (this.cfAccount as any).apiAccount.dns.records.edit(
|
|
108
|
+
providerRecordId,
|
|
109
|
+
apiRecord,
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
providerRecordId: updated.id,
|
|
113
|
+
name: updated.name,
|
|
114
|
+
type: updated.type as TDnsRecordType,
|
|
115
|
+
value: updated.content,
|
|
116
|
+
ttl: updated.ttl,
|
|
117
|
+
proxied: updated.proxied,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public async deleteRecord(domain: string, providerRecordId: string): Promise<void> {
|
|
122
|
+
const zoneId = await this.cfAccount.zoneManager.getZoneId(domain);
|
|
123
|
+
await (this.cfAccount as any).apiAccount.dns.records.delete(providerRecordId, {
|
|
124
|
+
zone_id: zoneId,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private isSupportedType(type: string): boolean {
|
|
129
|
+
return ['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'NS', 'SOA', 'CAA'].includes(type);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { IDnsProviderClient } from './interfaces.js';
|
|
2
|
+
import type {
|
|
3
|
+
TDnsProviderType,
|
|
4
|
+
TDnsProviderCredentials,
|
|
5
|
+
} from '../../../ts_interfaces/data/dns-provider.js';
|
|
6
|
+
import { CloudflareDnsProvider } from './cloudflare.provider.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Instantiate a runtime DNS provider client from a stored DnsProviderDoc.
|
|
10
|
+
*
|
|
11
|
+
* @throws if the provider type is not supported.
|
|
12
|
+
*
|
|
13
|
+
* ## Adding a new provider (e.g. Route53)
|
|
14
|
+
*
|
|
15
|
+
* 1. **Type union** — extend `TDnsProviderType` in
|
|
16
|
+
* `ts_interfaces/data/dns-provider.ts` (e.g. `'cloudflare' | 'route53'`).
|
|
17
|
+
* 2. **Credentials interface** — add `IRoute53Credentials` and append it to
|
|
18
|
+
* the `TDnsProviderCredentials` discriminated union.
|
|
19
|
+
* 3. **Descriptor** — append a new entry to `dnsProviderTypeDescriptors` so
|
|
20
|
+
* the OpsServer UI picks up the new type and renders the right credential
|
|
21
|
+
* form fields automatically.
|
|
22
|
+
* 4. **Provider class** — create `ts/dns/providers/route53.provider.ts`
|
|
23
|
+
* implementing `IDnsProviderClient`.
|
|
24
|
+
* 5. **Factory case** — add a new `case 'route53':` below. The
|
|
25
|
+
* `_exhaustive: never` line will fail to compile until you do.
|
|
26
|
+
* 6. **Index** — re-export the new class from `ts/dns/providers/index.ts`.
|
|
27
|
+
*/
|
|
28
|
+
export function createDnsProvider(
|
|
29
|
+
type: TDnsProviderType,
|
|
30
|
+
credentials: TDnsProviderCredentials,
|
|
31
|
+
): IDnsProviderClient {
|
|
32
|
+
switch (type) {
|
|
33
|
+
case 'cloudflare': {
|
|
34
|
+
if (credentials.type !== 'cloudflare') {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`createDnsProvider: type mismatch — provider type is 'cloudflare' but credentials.type is '${credentials.type}'`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return new CloudflareDnsProvider(credentials.apiToken);
|
|
40
|
+
}
|
|
41
|
+
default: {
|
|
42
|
+
// If you see a TypeScript error here after extending TDnsProviderType,
|
|
43
|
+
// add a `case` for the new type above. The `never` enforces exhaustiveness.
|
|
44
|
+
const _exhaustive: never = type;
|
|
45
|
+
throw new Error(`createDnsProvider: unsupported provider type: ${_exhaustive}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { TDnsRecordType } from '../../../ts_interfaces/data/dns-record.js';
|
|
2
|
+
import type { IProviderDomainListing } from '../../../ts_interfaces/data/dns-provider.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A DNS record as seen at a provider's API. The `providerRecordId` field
|
|
6
|
+
* is the provider's internal identifier, used for subsequent updates and
|
|
7
|
+
* deletes (since providers can have multiple records of the same name+type).
|
|
8
|
+
*/
|
|
9
|
+
export interface IProviderRecord {
|
|
10
|
+
providerRecordId: string;
|
|
11
|
+
name: string;
|
|
12
|
+
type: TDnsRecordType;
|
|
13
|
+
value: string;
|
|
14
|
+
ttl: number;
|
|
15
|
+
proxied?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Input shape for creating / updating a DNS record at a provider.
|
|
20
|
+
*/
|
|
21
|
+
export interface IProviderRecordInput {
|
|
22
|
+
name: string;
|
|
23
|
+
type: TDnsRecordType;
|
|
24
|
+
value: string;
|
|
25
|
+
ttl?: number;
|
|
26
|
+
proxied?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Outcome of a connection test against a provider's API.
|
|
31
|
+
*/
|
|
32
|
+
export interface IConnectionTestResult {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Pluggable DNS provider client interface. One implementation per provider type
|
|
39
|
+
* (Cloudflare, Route53, …). Implementations live in ts/dns/providers/ and are
|
|
40
|
+
* instantiated by `createDnsProvider()` in factory.ts.
|
|
41
|
+
*
|
|
42
|
+
* NOT a smartdata interface — this is the *runtime* client. The persisted
|
|
43
|
+
* representation is in `IDnsProvider` (ts_interfaces/data/dns-provider.ts).
|
|
44
|
+
*/
|
|
45
|
+
export interface IDnsProviderClient {
|
|
46
|
+
/** Lightweight check that credentials are valid and the API is reachable. */
|
|
47
|
+
testConnection(): Promise<IConnectionTestResult>;
|
|
48
|
+
|
|
49
|
+
/** List all DNS zones visible to this provider account. */
|
|
50
|
+
listDomains(): Promise<IProviderDomainListing[]>;
|
|
51
|
+
|
|
52
|
+
/** List all DNS records for a zone (FQDN). */
|
|
53
|
+
listRecords(domain: string): Promise<IProviderRecord[]>;
|
|
54
|
+
|
|
55
|
+
/** Create a new DNS record at the provider; returns the created record (with id). */
|
|
56
|
+
createRecord(domain: string, record: IProviderRecordInput): Promise<IProviderRecord>;
|
|
57
|
+
|
|
58
|
+
/** Update an existing record by provider id; returns the updated record. */
|
|
59
|
+
updateRecord(
|
|
60
|
+
domain: string,
|
|
61
|
+
providerRecordId: string,
|
|
62
|
+
record: IProviderRecordInput,
|
|
63
|
+
): Promise<IProviderRecord>;
|
|
64
|
+
|
|
65
|
+
/** Delete a record by provider id. */
|
|
66
|
+
deleteRecord(domain: string, providerRecordId: string): Promise<void>;
|
|
67
|
+
}
|
|
@@ -33,6 +33,9 @@ export class OpsServer {
|
|
|
33
33
|
private targetProfileHandler!: handlers.TargetProfileHandler;
|
|
34
34
|
private networkTargetHandler!: handlers.NetworkTargetHandler;
|
|
35
35
|
private usersHandler!: handlers.UsersHandler;
|
|
36
|
+
private dnsProviderHandler!: handlers.DnsProviderHandler;
|
|
37
|
+
private domainHandler!: handlers.DomainHandler;
|
|
38
|
+
private dnsRecordHandler!: handlers.DnsRecordHandler;
|
|
36
39
|
|
|
37
40
|
constructor(dcRouterRefArg: DcRouter) {
|
|
38
41
|
this.dcRouterRef = dcRouterRefArg;
|
|
@@ -96,6 +99,9 @@ export class OpsServer {
|
|
|
96
99
|
this.targetProfileHandler = new handlers.TargetProfileHandler(this);
|
|
97
100
|
this.networkTargetHandler = new handlers.NetworkTargetHandler(this);
|
|
98
101
|
this.usersHandler = new handlers.UsersHandler(this);
|
|
102
|
+
this.dnsProviderHandler = new handlers.DnsProviderHandler(this);
|
|
103
|
+
this.domainHandler = new handlers.DomainHandler(this);
|
|
104
|
+
this.dnsRecordHandler = new handlers.DnsRecordHandler(this);
|
|
99
105
|
|
|
100
106
|
console.log('✅ OpsServer TypedRequest handlers initialized');
|
|
101
107
|
}
|
|
@@ -123,6 +123,15 @@ export class ConfigHandler {
|
|
|
123
123
|
ttl: r.ttl,
|
|
124
124
|
}));
|
|
125
125
|
|
|
126
|
+
// dnsChallenge: true when at least one DnsProviderDoc exists in the DB
|
|
127
|
+
// (replaces the legacy `dnsChallenge.cloudflareApiKey` constructor field).
|
|
128
|
+
let dnsChallengeEnabled = false;
|
|
129
|
+
try {
|
|
130
|
+
dnsChallengeEnabled = (await dcRouter.dnsManager?.hasAcmeCapableProvider()) ?? false;
|
|
131
|
+
} catch {
|
|
132
|
+
dnsChallengeEnabled = false;
|
|
133
|
+
}
|
|
134
|
+
|
|
126
135
|
const dns: interfaces.requests.IConfigData['dns'] = {
|
|
127
136
|
enabled: !!dcRouter.dnsServer,
|
|
128
137
|
port: 53,
|
|
@@ -130,7 +139,7 @@ export class ConfigHandler {
|
|
|
130
139
|
scopes: opts.dnsScopes || [],
|
|
131
140
|
recordCount: dnsRecords.length,
|
|
132
141
|
records: dnsRecords,
|
|
133
|
-
dnsChallenge:
|
|
142
|
+
dnsChallenge: dnsChallengeEnabled,
|
|
134
143
|
};
|
|
135
144
|
|
|
136
145
|
// --- TLS ---
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* CRUD + connection-test handlers for DnsProviderDoc.
|
|
7
|
+
*
|
|
8
|
+
* Auth: same dual-mode pattern as TargetProfileHandler — admin JWT or
|
|
9
|
+
* API token with the appropriate `dns-providers:read|write` scope.
|
|
10
|
+
*/
|
|
11
|
+
export class DnsProviderHandler {
|
|
12
|
+
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
13
|
+
|
|
14
|
+
constructor(private opsServerRef: OpsServer) {
|
|
15
|
+
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
16
|
+
this.registerHandlers();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private async requireAuth(
|
|
20
|
+
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
|
21
|
+
requiredScope?: interfaces.data.TApiTokenScope,
|
|
22
|
+
): Promise<string> {
|
|
23
|
+
if (request.identity?.jwt) {
|
|
24
|
+
try {
|
|
25
|
+
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
|
26
|
+
identity: request.identity,
|
|
27
|
+
});
|
|
28
|
+
if (isAdmin) return request.identity.userId;
|
|
29
|
+
} catch { /* fall through */ }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (request.apiToken) {
|
|
33
|
+
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
34
|
+
if (tokenManager) {
|
|
35
|
+
const token = await tokenManager.validateToken(request.apiToken);
|
|
36
|
+
if (token) {
|
|
37
|
+
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
38
|
+
return token.createdBy;
|
|
39
|
+
}
|
|
40
|
+
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private registerHandlers(): void {
|
|
49
|
+
// Get all providers
|
|
50
|
+
this.typedrouter.addTypedHandler(
|
|
51
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsProviders>(
|
|
52
|
+
'getDnsProviders',
|
|
53
|
+
async (dataArg) => {
|
|
54
|
+
await this.requireAuth(dataArg, 'dns-providers:read');
|
|
55
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
56
|
+
if (!dnsManager) return { providers: [] };
|
|
57
|
+
return { providers: await dnsManager.listProviders() };
|
|
58
|
+
},
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Get single provider
|
|
63
|
+
this.typedrouter.addTypedHandler(
|
|
64
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsProvider>(
|
|
65
|
+
'getDnsProvider',
|
|
66
|
+
async (dataArg) => {
|
|
67
|
+
await this.requireAuth(dataArg, 'dns-providers:read');
|
|
68
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
69
|
+
if (!dnsManager) return { provider: null };
|
|
70
|
+
return { provider: await dnsManager.getProvider(dataArg.id) };
|
|
71
|
+
},
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Create provider
|
|
76
|
+
this.typedrouter.addTypedHandler(
|
|
77
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateDnsProvider>(
|
|
78
|
+
'createDnsProvider',
|
|
79
|
+
async (dataArg) => {
|
|
80
|
+
const userId = await this.requireAuth(dataArg, 'dns-providers:write');
|
|
81
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
82
|
+
if (!dnsManager) {
|
|
83
|
+
return { success: false, message: 'DnsManager not initialized (DB disabled?)' };
|
|
84
|
+
}
|
|
85
|
+
const id = await dnsManager.createProvider({
|
|
86
|
+
name: dataArg.name,
|
|
87
|
+
type: dataArg.type,
|
|
88
|
+
credentials: dataArg.credentials,
|
|
89
|
+
createdBy: userId,
|
|
90
|
+
});
|
|
91
|
+
return { success: true, id };
|
|
92
|
+
},
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Update provider
|
|
97
|
+
this.typedrouter.addTypedHandler(
|
|
98
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateDnsProvider>(
|
|
99
|
+
'updateDnsProvider',
|
|
100
|
+
async (dataArg) => {
|
|
101
|
+
await this.requireAuth(dataArg, 'dns-providers:write');
|
|
102
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
103
|
+
if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
|
|
104
|
+
const ok = await dnsManager.updateProvider(dataArg.id, {
|
|
105
|
+
name: dataArg.name,
|
|
106
|
+
credentials: dataArg.credentials,
|
|
107
|
+
});
|
|
108
|
+
return ok ? { success: true } : { success: false, message: 'Provider not found' };
|
|
109
|
+
},
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Delete provider
|
|
114
|
+
this.typedrouter.addTypedHandler(
|
|
115
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteDnsProvider>(
|
|
116
|
+
'deleteDnsProvider',
|
|
117
|
+
async (dataArg) => {
|
|
118
|
+
await this.requireAuth(dataArg, 'dns-providers:write');
|
|
119
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
120
|
+
if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
|
|
121
|
+
return await dnsManager.deleteProvider(dataArg.id, dataArg.force ?? false);
|
|
122
|
+
},
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Test provider connection
|
|
127
|
+
this.typedrouter.addTypedHandler(
|
|
128
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_TestDnsProvider>(
|
|
129
|
+
'testDnsProvider',
|
|
130
|
+
async (dataArg) => {
|
|
131
|
+
await this.requireAuth(dataArg, 'dns-providers:read');
|
|
132
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
133
|
+
if (!dnsManager) {
|
|
134
|
+
return { ok: false, error: 'DnsManager not initialized', testedAt: Date.now() };
|
|
135
|
+
}
|
|
136
|
+
return await dnsManager.testProvider(dataArg.id);
|
|
137
|
+
},
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// List domains visible to a provider's account (without importing them)
|
|
142
|
+
this.typedrouter.addTypedHandler(
|
|
143
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListProviderDomains>(
|
|
144
|
+
'listProviderDomains',
|
|
145
|
+
async (dataArg) => {
|
|
146
|
+
await this.requireAuth(dataArg, 'dns-providers:read');
|
|
147
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
148
|
+
if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
|
|
149
|
+
try {
|
|
150
|
+
const domains = await dnsManager.listProviderDomains(dataArg.providerId);
|
|
151
|
+
return { success: true, domains };
|
|
152
|
+
} catch (err: unknown) {
|
|
153
|
+
return { success: false, message: (err as Error).message };
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* CRUD handlers for DnsRecordDoc.
|
|
7
|
+
*/
|
|
8
|
+
export class DnsRecordHandler {
|
|
9
|
+
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
10
|
+
|
|
11
|
+
constructor(private opsServerRef: OpsServer) {
|
|
12
|
+
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
|
13
|
+
this.registerHandlers();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private async requireAuth(
|
|
17
|
+
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
|
18
|
+
requiredScope?: interfaces.data.TApiTokenScope,
|
|
19
|
+
): Promise<string> {
|
|
20
|
+
if (request.identity?.jwt) {
|
|
21
|
+
try {
|
|
22
|
+
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
|
23
|
+
identity: request.identity,
|
|
24
|
+
});
|
|
25
|
+
if (isAdmin) return request.identity.userId;
|
|
26
|
+
} catch { /* fall through */ }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (request.apiToken) {
|
|
30
|
+
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
31
|
+
if (tokenManager) {
|
|
32
|
+
const token = await tokenManager.validateToken(request.apiToken);
|
|
33
|
+
if (token) {
|
|
34
|
+
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
35
|
+
return token.createdBy;
|
|
36
|
+
}
|
|
37
|
+
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private registerHandlers(): void {
|
|
46
|
+
// Get records by domain
|
|
47
|
+
this.typedrouter.addTypedHandler(
|
|
48
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsRecords>(
|
|
49
|
+
'getDnsRecords',
|
|
50
|
+
async (dataArg) => {
|
|
51
|
+
await this.requireAuth(dataArg, 'dns-records:read');
|
|
52
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
53
|
+
if (!dnsManager) return { records: [] };
|
|
54
|
+
const docs = await dnsManager.listRecordsForDomain(dataArg.domainId);
|
|
55
|
+
return { records: docs.map((d) => dnsManager.toPublicRecord(d)) };
|
|
56
|
+
},
|
|
57
|
+
),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Get single record
|
|
61
|
+
this.typedrouter.addTypedHandler(
|
|
62
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsRecord>(
|
|
63
|
+
'getDnsRecord',
|
|
64
|
+
async (dataArg) => {
|
|
65
|
+
await this.requireAuth(dataArg, 'dns-records:read');
|
|
66
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
67
|
+
if (!dnsManager) return { record: null };
|
|
68
|
+
const doc = await dnsManager.getRecord(dataArg.id);
|
|
69
|
+
return { record: doc ? dnsManager.toPublicRecord(doc) : null };
|
|
70
|
+
},
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Create record
|
|
75
|
+
this.typedrouter.addTypedHandler(
|
|
76
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateDnsRecord>(
|
|
77
|
+
'createDnsRecord',
|
|
78
|
+
async (dataArg) => {
|
|
79
|
+
const userId = await this.requireAuth(dataArg, 'dns-records:write');
|
|
80
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
81
|
+
if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
|
|
82
|
+
return await dnsManager.createRecord({
|
|
83
|
+
domainId: dataArg.domainId,
|
|
84
|
+
name: dataArg.name,
|
|
85
|
+
type: dataArg.type,
|
|
86
|
+
value: dataArg.value,
|
|
87
|
+
ttl: dataArg.ttl,
|
|
88
|
+
proxied: dataArg.proxied,
|
|
89
|
+
createdBy: userId,
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Update record
|
|
96
|
+
this.typedrouter.addTypedHandler(
|
|
97
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateDnsRecord>(
|
|
98
|
+
'updateDnsRecord',
|
|
99
|
+
async (dataArg) => {
|
|
100
|
+
await this.requireAuth(dataArg, 'dns-records:write');
|
|
101
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
102
|
+
if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
|
|
103
|
+
return await dnsManager.updateRecord({
|
|
104
|
+
id: dataArg.id,
|
|
105
|
+
name: dataArg.name,
|
|
106
|
+
value: dataArg.value,
|
|
107
|
+
ttl: dataArg.ttl,
|
|
108
|
+
proxied: dataArg.proxied,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Delete record
|
|
115
|
+
this.typedrouter.addTypedHandler(
|
|
116
|
+
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteDnsRecord>(
|
|
117
|
+
'deleteDnsRecord',
|
|
118
|
+
async (dataArg) => {
|
|
119
|
+
await this.requireAuth(dataArg, 'dns-records:write');
|
|
120
|
+
const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
|
|
121
|
+
if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
|
|
122
|
+
return await dnsManager.deleteRecord(dataArg.id);
|
|
123
|
+
},
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|