cloudflare-ddns-sync 3.0.2 → 4.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -9
- package/dist/index.js +2 -2
- package/dist/lib/cloudflare-client.d.ts +9 -10
- package/dist/lib/cloudflare-client.js +61 -68
- package/dist/types/Callbacks.d.ts +2 -3
- package/dist/types/CloudflareOptions.d.ts +2 -0
- package/dist/types/CloudflareOptions.js +1 -0
- package/dist/types/Record.d.ts +2 -10
- package/dist/types/index.d.ts +1 -3
- package/dist/types/index.js +1 -3
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Auth, DomainRecordList, MultiSyncCallback, Record, RecordData } from './types/index.js';
|
|
1
|
+
import { Auth, MultiSyncCallback, Record } from './types/index.js';
|
|
3
2
|
export default class CloudflareDDNSSync {
|
|
4
3
|
private cloudflareClient;
|
|
5
4
|
constructor(auth: Auth);
|
|
6
5
|
getIp(): Promise<string>;
|
|
7
6
|
getIpv6(): Promise<string>;
|
|
8
|
-
getRecordDataForDomain(domain: string): Promise<
|
|
9
|
-
getRecordDataForDomains(domains: Array<string>): Promise<
|
|
10
|
-
getRecordDataForRecord(record: Record): Promise<
|
|
11
|
-
getRecordDataForRecords(records: Array<Record>): Promise<
|
|
7
|
+
getRecordDataForDomain(domain: string): Promise<import("cloudflare/resources/dns/records.mjs").DNSRecord[]>;
|
|
8
|
+
getRecordDataForDomains(domains: Array<string>): Promise<{}>;
|
|
9
|
+
getRecordDataForRecord(record: Record): Promise<import("cloudflare/resources/dns/records.mjs").DNSRecord>;
|
|
10
|
+
getRecordDataForRecords(records: Array<Record>): Promise<any[]>;
|
|
12
11
|
removeRecord(recordName: string, recordType?: string): Promise<void>;
|
|
13
12
|
stopSyncOnIpChange(changeListenerId: string): void;
|
|
14
|
-
syncByCronTime(cronExpression: string, records: Array<Record>, callback: MultiSyncCallback, ip?: string): ScheduledTask;
|
|
13
|
+
syncByCronTime(cronExpression: string, records: Array<Record>, callback: MultiSyncCallback, ip?: string): import("node-cron").ScheduledTask;
|
|
15
14
|
syncOnIpChange(records: Array<Record>, callback: MultiSyncCallback): Promise<string>;
|
|
16
|
-
syncRecord(record: Record, ip?: string):
|
|
17
|
-
syncRecords(records: Array<Record>, ip?: string): Promise<
|
|
15
|
+
syncRecord(record: Record, ip?: string): any;
|
|
16
|
+
syncRecords(records: Array<Record>, ip?: string): Promise<import("cloudflare/resources/dns/records.mjs").DNSRecord[]>;
|
|
18
17
|
}
|
|
19
18
|
export * from './types/index.js';
|
package/dist/index.js
CHANGED
|
@@ -13,10 +13,10 @@ export default class CloudflareDDNSSync {
|
|
|
13
13
|
return ipUtils.getIpv6();
|
|
14
14
|
}
|
|
15
15
|
getRecordDataForDomain(domain) {
|
|
16
|
-
return this.cloudflareClient.
|
|
16
|
+
return this.cloudflareClient.getRecordsByDomain(domain);
|
|
17
17
|
}
|
|
18
18
|
getRecordDataForDomains(domains) {
|
|
19
|
-
return this.cloudflareClient.
|
|
19
|
+
return this.cloudflareClient.getRecordsByDomains(domains);
|
|
20
20
|
}
|
|
21
21
|
getRecordDataForRecord(record) {
|
|
22
22
|
return this.cloudflareClient.getRecordDataForRecord(record);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import Cloudflare from 'cloudflare';
|
|
2
|
-
import {
|
|
1
|
+
import Cloudflare, { ClientOptions as CloudflareOptions } from 'cloudflare';
|
|
2
|
+
import { Record } from '../types/index.js';
|
|
3
3
|
export default class CloudflareClient {
|
|
4
4
|
private cloudflare;
|
|
5
5
|
private zoneMap;
|
|
6
|
-
constructor(
|
|
7
|
-
syncRecord(record: Record, ip?: string): Promise<
|
|
8
|
-
syncRecords(records: Array<Record>, ip?: string): Promise<
|
|
6
|
+
constructor(cloudflareOptions: CloudflareOptions);
|
|
7
|
+
syncRecord(record: Record, ip?: string): Promise<Cloudflare.DNS.Records.DNSRecord>;
|
|
8
|
+
syncRecords(records: Array<Record>, ip?: string): Promise<Cloudflare.DNS.Records.DNSRecord[]>;
|
|
9
9
|
removeRecordByNameAndType(recordName: string, recordType?: string): Promise<void>;
|
|
10
|
-
getRecordDataForRecord(record: Record): Promise<
|
|
11
|
-
getRecordDataForRecords(records: Array<Record>): Promise<
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
getRecordDataForRecord(record: Record): Promise<Cloudflare.DNS.Records.DNSRecord>;
|
|
11
|
+
getRecordDataForRecords(records: Array<Record>): Promise<any[]>;
|
|
12
|
+
getRecordsByDomains(domains: Array<string>): Promise<{}>;
|
|
13
|
+
getRecordsByDomain(domain: string): Promise<Cloudflare.DNS.Records.DNSRecord[]>;
|
|
14
14
|
private createRecord;
|
|
15
15
|
private updateRecord;
|
|
16
16
|
private updateZoneMap;
|
|
@@ -19,7 +19,6 @@ export default class CloudflareClient {
|
|
|
19
19
|
private getRecordByNameAndType;
|
|
20
20
|
private getRecordIdsForRecords;
|
|
21
21
|
private getRecordIdMapKey;
|
|
22
|
-
private getRecordsByDomain;
|
|
23
22
|
private getZoneIdByDomain;
|
|
24
23
|
private getDomainsFromRecords;
|
|
25
24
|
private getDomainByRecordName;
|
|
@@ -6,8 +6,8 @@ const ipv6Regex = /^(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:
|
|
|
6
6
|
export default class CloudflareClient {
|
|
7
7
|
cloudflare;
|
|
8
8
|
zoneMap = new Map();
|
|
9
|
-
constructor(
|
|
10
|
-
this.cloudflare = new Cloudflare(
|
|
9
|
+
constructor(cloudflareOptions) {
|
|
10
|
+
this.cloudflare = new Cloudflare(cloudflareOptions);
|
|
11
11
|
this.updateZoneMap();
|
|
12
12
|
}
|
|
13
13
|
async syncRecord(record, ip) {
|
|
@@ -44,7 +44,7 @@ export default class CloudflareClient {
|
|
|
44
44
|
const recordTypeToUse = recordType ? recordType : 'A';
|
|
45
45
|
const zoneId = await this.getZoneIdByRecordName(recordName);
|
|
46
46
|
const recordId = await this.getRecordIdByNameAndType(recordName, recordTypeToUse);
|
|
47
|
-
await this.cloudflare.
|
|
47
|
+
await this.cloudflare.dns.records.delete(recordId, { zone_id: zoneId });
|
|
48
48
|
}
|
|
49
49
|
async getRecordDataForRecord(record) {
|
|
50
50
|
const domain = this.getDomainByRecordName(record.name);
|
|
@@ -63,8 +63,8 @@ export default class CloudflareClient {
|
|
|
63
63
|
const recordData = [].concat(...recordDataForDomains);
|
|
64
64
|
return recordData;
|
|
65
65
|
}
|
|
66
|
-
async
|
|
67
|
-
const recordDataPromises = domains.map((domain) => this.
|
|
66
|
+
async getRecordsByDomains(domains) {
|
|
67
|
+
const recordDataPromises = domains.map((domain) => this.getRecordsByDomain(domain));
|
|
68
68
|
const recordDataForDomains = await Promise.all(recordDataPromises);
|
|
69
69
|
const recordData = {};
|
|
70
70
|
recordDataForDomains.forEach((recordDataForDomain, index) => {
|
|
@@ -72,73 +72,83 @@ export default class CloudflareClient {
|
|
|
72
72
|
});
|
|
73
73
|
return recordData;
|
|
74
74
|
}
|
|
75
|
-
async
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
async getRecordsByDomain(domain) {
|
|
76
|
+
const zoneId = await this.getZoneIdByDomain(domain);
|
|
77
|
+
const allRecords = [];
|
|
78
|
+
for await (const recordListResponse of this.cloudflare.dns.records.list({ zone_id: zoneId })) {
|
|
79
|
+
allRecords.push(recordListResponse);
|
|
80
|
+
}
|
|
81
|
+
return allRecords;
|
|
82
|
+
}
|
|
83
|
+
async createRecord(zoneId, recordData, ip) {
|
|
84
|
+
const dnsRecordToCreate = {
|
|
85
|
+
...recordData,
|
|
86
|
+
name: recordData.name.toLowerCase(),
|
|
87
|
+
content: (recordData.content ? recordData.content : ip),
|
|
88
|
+
type: (recordData.type ? recordData.type : 'A'),
|
|
89
|
+
ttl: recordData.ttl ? recordData.ttl : 1,
|
|
90
|
+
zone_id: zoneId,
|
|
86
91
|
};
|
|
87
|
-
if (!
|
|
88
|
-
throw Error(`Could not create Record "${
|
|
92
|
+
if (!dnsRecordToCreate.content) {
|
|
93
|
+
throw Error(`Could not create Record "${dnsRecordToCreate.name}": Content is missing!`);
|
|
89
94
|
}
|
|
90
|
-
if (
|
|
91
|
-
if (!
|
|
92
|
-
throw Error(`Could not create Record "${
|
|
95
|
+
if (dnsRecordToCreate.type === 'A') {
|
|
96
|
+
if (!dnsRecordToCreate.content.match(ipv4Regex)) {
|
|
97
|
+
throw Error(`Could not create Record "${dnsRecordToCreate.name}": '${dnsRecordToCreate.content}' is not a valid ipv4!`);
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
|
-
else if (
|
|
96
|
-
if (!
|
|
97
|
-
throw Error(`Could not create Record "${
|
|
100
|
+
else if (dnsRecordToCreate.type === 'AAAA') {
|
|
101
|
+
if (!dnsRecordToCreate.content.match(ipv6Regex)) {
|
|
102
|
+
throw Error(`Could not create Record "${dnsRecordToCreate.name}": '${dnsRecordToCreate.content}' is not a valid ipv6!`);
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
|
-
else if (
|
|
101
|
-
const parsedDomain = parseDomain(fromUrl(
|
|
105
|
+
else if (dnsRecordToCreate.type === 'CNAME') {
|
|
106
|
+
const parsedDomain = parseDomain(fromUrl(dnsRecordToCreate.content));
|
|
102
107
|
if (parsedDomain.type !== ParseResultType.Listed || !parsedDomain.domain) {
|
|
103
|
-
throw Error(`Could not create Record "${
|
|
108
|
+
throw Error(`Could not create Record "${dnsRecordToCreate.name}": '${dnsRecordToCreate.content}' is not a valid domain name!`);
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
|
-
|
|
107
|
-
return response.result;
|
|
111
|
+
return this.cloudflare.dns.records.create(dnsRecordToCreate);
|
|
108
112
|
}
|
|
109
|
-
async updateRecord(zoneId, recordId,
|
|
110
|
-
const
|
|
111
|
-
...
|
|
112
|
-
name:
|
|
113
|
-
content:
|
|
114
|
-
type:
|
|
115
|
-
ttl:
|
|
113
|
+
async updateRecord(zoneId, recordId, recordData, ip) {
|
|
114
|
+
const updatedDnsRecord = {
|
|
115
|
+
...recordData,
|
|
116
|
+
name: recordData.name.toLowerCase(),
|
|
117
|
+
content: (recordData.content ? recordData.content : ip),
|
|
118
|
+
type: (recordData.type ? recordData.type : 'A'),
|
|
119
|
+
ttl: recordData.ttl ? recordData.ttl : 1,
|
|
120
|
+
zone_id: zoneId,
|
|
116
121
|
};
|
|
117
|
-
if (!
|
|
118
|
-
throw Error(`Could not update Record "${
|
|
122
|
+
if (!updatedDnsRecord.content) {
|
|
123
|
+
throw Error(`Could not update Record "${updatedDnsRecord.name}": Content is missing!`);
|
|
119
124
|
}
|
|
120
|
-
if (
|
|
121
|
-
if (!
|
|
122
|
-
throw Error(`Could not update Record "${
|
|
125
|
+
if (updatedDnsRecord.type === 'A') {
|
|
126
|
+
if (!updatedDnsRecord.content.match(ipv4Regex)) {
|
|
127
|
+
throw Error(`Could not update Record "${updatedDnsRecord.name}": '${updatedDnsRecord.content}' is not a valid ipv4!`);
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
|
-
else if (
|
|
126
|
-
if (!
|
|
127
|
-
throw Error(`Could not update Record "${
|
|
130
|
+
else if (updatedDnsRecord.type === 'AAAA') {
|
|
131
|
+
if (!updatedDnsRecord.content.match(ipv6Regex)) {
|
|
132
|
+
throw Error(`Could not update Record "${updatedDnsRecord.name}": '${updatedDnsRecord.content}' is not a valid ipv6!`);
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
|
-
else if (
|
|
131
|
-
const parsedDomain = parseDomain(fromUrl(
|
|
135
|
+
else if (updatedDnsRecord.type === 'CNAME') {
|
|
136
|
+
const parsedDomain = parseDomain(fromUrl(updatedDnsRecord.content));
|
|
132
137
|
if (parsedDomain.type !== ParseResultType.Listed || !parsedDomain.domain) {
|
|
133
|
-
throw Error(`Could not update Record "${
|
|
138
|
+
throw Error(`Could not update Record "${updatedDnsRecord.name}": '${updatedDnsRecord.content}' is not a valid domain name!`);
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
|
-
|
|
137
|
-
return response.result;
|
|
141
|
+
return await this.cloudflare.dns.records.edit(recordId, updatedDnsRecord);
|
|
138
142
|
}
|
|
139
143
|
async updateZoneMap() {
|
|
140
|
-
const response =
|
|
141
|
-
|
|
144
|
+
const response = await this.cloudflare.zones.list();
|
|
145
|
+
let zones = response.result;
|
|
146
|
+
let nextPageExists = response.hasNextPage();
|
|
147
|
+
while (nextPageExists) {
|
|
148
|
+
const nextPageResponse = await response.getNextPage();
|
|
149
|
+
zones = zones.concat(nextPageResponse.result);
|
|
150
|
+
nextPageExists = nextPageResponse.hasNextPage();
|
|
151
|
+
}
|
|
142
152
|
this.zoneMap = new Map();
|
|
143
153
|
for (const zone of zones) {
|
|
144
154
|
this.zoneMap.set(zone.name, zone.id);
|
|
@@ -175,23 +185,6 @@ export default class CloudflareClient {
|
|
|
175
185
|
const recordType = record.type ? record.type.toLowerCase() : 'a';
|
|
176
186
|
return `"${recordName}"_"${recordType}"`;
|
|
177
187
|
}
|
|
178
|
-
async getRecordsByDomain(domain) {
|
|
179
|
-
const zoneId = await this.getZoneIdByDomain(domain);
|
|
180
|
-
const records = [];
|
|
181
|
-
let pageIndex = 1;
|
|
182
|
-
let allRecordsFound = false;
|
|
183
|
-
const recordsPerPage = 5000;
|
|
184
|
-
while (!allRecordsFound) {
|
|
185
|
-
const response = (await this.cloudflare.dnsRecords.browse(zoneId, {
|
|
186
|
-
page: pageIndex,
|
|
187
|
-
per_page: recordsPerPage,
|
|
188
|
-
}));
|
|
189
|
-
records.push(...response.result);
|
|
190
|
-
allRecordsFound = response.result.length < recordsPerPage;
|
|
191
|
-
pageIndex++;
|
|
192
|
-
}
|
|
193
|
-
return records;
|
|
194
|
-
}
|
|
195
188
|
async getZoneIdByDomain(domain) {
|
|
196
189
|
if (this.zoneMap.has(domain)) {
|
|
197
190
|
const zoneId = this.zoneMap.get(domain.toLowerCase());
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
export type
|
|
3
|
-
export type MultiSyncCallback = (syncResult: Array<RecordData>) => void;
|
|
1
|
+
import Cloudflare from 'cloudflare';
|
|
2
|
+
export type MultiSyncCallback = (syncResult: Cloudflare.DNS.DNSRecord[]) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/Record.d.ts
CHANGED
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type
|
|
3
|
-
export type Record = {
|
|
4
|
-
name: string;
|
|
5
|
-
type?: RecordTypes;
|
|
6
|
-
proxied?: boolean;
|
|
7
|
-
ttl?: number;
|
|
8
|
-
priority?: number;
|
|
9
|
-
content?: string;
|
|
10
|
-
};
|
|
1
|
+
import { Cloudflare } from 'cloudflare';
|
|
2
|
+
export type Record = Cloudflare.DNS.DNSRecord;
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-ddns-sync",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-beta.2",
|
|
4
4
|
"description": "A simple module to update DNS records on Cloudflare whenever you want",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Steffen Knaup <SteffenKnaup@hotmail.de>",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"test:coverage-report": "c8 report"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"cloudflare": "
|
|
32
|
+
"cloudflare": "3.0.0",
|
|
33
33
|
"node-cron": "3.0.3",
|
|
34
34
|
"parse-domain": "8.0.2",
|
|
35
35
|
"public-ip": "6.0.2"
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"@types/cloudflare": "^2.7.14",
|
|
40
40
|
"@types/minimist": "1.2.5",
|
|
41
41
|
"@types/mocha": "10.0.6",
|
|
42
|
-
"@types/node": "20.
|
|
42
|
+
"@types/node": "20.12.7",
|
|
43
43
|
"@types/node-cron": "3.0.11",
|
|
44
44
|
"c8": "9.1.0",
|
|
45
45
|
"chai": "5.1.0",
|
|
46
46
|
"minimist": "1.2.8",
|
|
47
|
-
"mocha": "10.
|
|
47
|
+
"mocha": "10.4.0",
|
|
48
48
|
"prettier": "^3.2.5",
|
|
49
|
-
"typescript": "5.4.
|
|
49
|
+
"typescript": "5.4.5"
|
|
50
50
|
}
|
|
51
51
|
}
|