cloudflare-ddns-sync 4.0.0-beta.2 → 4.0.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.
- package/CHANGELOG.md +13 -0
- package/MIGRATION-V4.md +64 -0
- package/README.md +91 -117
- package/logo.png +0 -0
- package/package.json +18 -16
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -58
- package/dist/lib/cloudflare-client.d.ts +0 -25
- package/dist/lib/cloudflare-client.js +0 -216
- package/dist/lib/cron.d.ts +0 -5
- package/dist/lib/cron.js +0 -15
- package/dist/lib/ip-utils.d.ts +0 -9
- package/dist/lib/ip-utils.js +0 -49
- package/dist/types/Auth.d.ts +0 -2
- package/dist/types/Auth.js +0 -1
- package/dist/types/Callbacks.d.ts +0 -2
- package/dist/types/Callbacks.js +0 -1
- package/dist/types/CloudflareOptions.d.ts +0 -2
- package/dist/types/CloudflareOptions.js +0 -1
- package/dist/types/DomainRecordList.d.ts +0 -4
- package/dist/types/DomainRecordList.js +0 -1
- package/dist/types/Record.d.ts +0 -2
- package/dist/types/Record.js +0 -1
- package/dist/types/ZoneMap.d.ts +0 -1
- package/dist/types/ZoneMap.js +0 -1
- package/dist/types/cloudflare/RecordData.d.ts +0 -21
- package/dist/types/cloudflare/RecordData.js +0 -1
- package/dist/types/cloudflare/ZoneData.d.ts +0 -43
- package/dist/types/cloudflare/ZoneData.js +0 -1
- package/dist/types/cloudflare/index.d.ts +0 -2
- package/dist/types/cloudflare/index.js +0 -2
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v4
|
|
4
|
+
|
|
5
|
+
### v4.0.0
|
|
6
|
+
|
|
7
|
+
- ⬆️ Update dependencies, including Cloudflare 2 → 7, node-cron 3 → 4 and public-ip 6 → 8.
|
|
8
|
+
- ♻️ Preserve public auth fields, key/token precedence, record types, sync methods and DNS defaults while adapting to the new SDK.
|
|
9
|
+
- ♻️ Fetch all zone and DNS record pages; report zone-loading failures through the awaited operation.
|
|
10
|
+
- 🚨 Add a DDNS-oriented API with `createDdns()`, `sync()`, `schedule()`, `watch()`, `list()`, `remove()` and `ip()`
|
|
11
|
+
- 🚨 Allow records to be configured as hostnames; A and AAAA records automatically resolve the matching public IP
|
|
12
|
+
- 🚨 Return a dedicated controllable job from schedules and watchers
|
|
13
|
+
- 🐛 Respect the requested DNS type when listing records with the same hostname
|
|
14
|
+
- ♻️ Validate configuration before network access, make jobs safe to stop and add `sync({ipv4, ipv6})` for configured records
|
|
15
|
+
|
|
3
16
|
## v3
|
|
4
17
|
|
|
5
18
|
### 3.0.2
|
package/MIGRATION-V4.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Migrating from v3 to v4
|
|
2
|
+
|
|
3
|
+
v4 replaces the class-based API with `createDdns()` and consolidates related methods. Node.js 20 remains the minimum requirement.
|
|
4
|
+
|
|
5
|
+
## Initialization
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// v3
|
|
9
|
+
const ddns = new CloudflareDDNSSync({token});
|
|
10
|
+
|
|
11
|
+
// v4
|
|
12
|
+
const ddns = createDdns({token, records: ['home.example.com']});
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If `token` is omitted, v4 reads `CLOUDFLARE_API_TOKEN`. `email`/`key` and user service keys remain supported.
|
|
16
|
+
|
|
17
|
+
## Synchronizing
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// v3
|
|
21
|
+
await ddns.syncRecord({name: 'home.example.com'});
|
|
22
|
+
await ddns.syncRecords(records, '203.0.113.10');
|
|
23
|
+
|
|
24
|
+
// v4
|
|
25
|
+
await ddns.sync('home.example.com');
|
|
26
|
+
await ddns.sync(records, {ipv4: '203.0.113.10'});
|
|
27
|
+
await ddns.sync({ipv4: '203.0.113.10'}); // configured records
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`sync()` always returns an array. For a single record, use the first entry: `const [record] = await ddns.sync('home.example.com')`.
|
|
31
|
+
|
|
32
|
+
For configured records, pass fixed addresses directly as the first argument. The former placeholder form `sync(undefined, {ipv4})` remains supported for compatibility.
|
|
33
|
+
|
|
34
|
+
A and AAAA records automatically use the matching IP family. Non-IP records now require explicit `content`.
|
|
35
|
+
|
|
36
|
+
## Scheduling and watching
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// v3
|
|
40
|
+
const task = ddns.syncByCronTime('*/5 * * * *', records, callback);
|
|
41
|
+
const id = await ddns.syncOnIpChange(records, callback);
|
|
42
|
+
ddns.stopSyncOnIpChange(id);
|
|
43
|
+
|
|
44
|
+
// v4
|
|
45
|
+
const task = ddns.schedule('*/5 * * * *', {records, onSync: callback});
|
|
46
|
+
const watcher = await ddns.watch({records, onSync: callback});
|
|
47
|
+
await watcher.stop();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`schedule()` and `watch()` return a `SyncJob` with `run()`, `start()` and `stop()`. A v4 job replaces both the listener ID and the direct `node-cron` return value.
|
|
51
|
+
|
|
52
|
+
## Listing, removing and retrieving IPs
|
|
53
|
+
|
|
54
|
+
| v3 | v4 |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `getRecordDataForRecord(record)` | `list({records: record})` |
|
|
57
|
+
| `getRecordDataForRecords(records)` | `list({records})` |
|
|
58
|
+
| `getRecordDataForDomain(domain)` | `list({domains: domain})` |
|
|
59
|
+
| `getRecordDataForDomains(domains)` | `list({domains, groupBy: 'domain'})` |
|
|
60
|
+
| `removeRecord(name, type?)` | `remove(name)` oder `remove({name, type})` |
|
|
61
|
+
| `getIp()` | `ip()` |
|
|
62
|
+
| `getIpv6()` | `ip(6)` |
|
|
63
|
+
|
|
64
|
+
`list()` without filters uses the configured records. Without that configuration, a filter is required.
|
package/README.md
CHANGED
|
@@ -1,160 +1,134 @@
|
|
|
1
1
|
<table>
|
|
2
2
|
<tr>
|
|
3
|
-
<td width="25%"><img src="./logo.png" alt="
|
|
3
|
+
<td width="25%"><img src="./logo.png" alt="Cloudflare DDNS Sync logo" width="100%"></td>
|
|
4
4
|
<td><h1>Cloudflare DDNS Sync</h1></td>
|
|
5
5
|
</tr>
|
|
6
6
|
</table>
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
[](https://www.npmjs.com/package/cloudflare-ddns-sync)
|
|
10
|
-
[](https://www.npmjs.com/package/cloudflare-ddns-sync)
|
|
11
|
-
[](https://www.npmjs.com/package/cloudflare-ddns-sync-cli)
|
|
8
|
+
Cloudflare DDNS Sync synchronizes DNS records with the current public IP address. Version 4 provides a small, DDNS-oriented API.
|
|
12
9
|
|
|
13
|
-
##
|
|
10
|
+
## Voraussetzungen
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
- Node.js 20 or newer
|
|
13
|
+
- A Cloudflare API token with permission to read zones and edit DNS records
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
```sh
|
|
16
|
+
npm install cloudflare-ddns-sync
|
|
17
|
+
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## Einstieg
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Set `CLOUDFLARE_API_TOKEN` and configure the hostnames once:
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
```ts
|
|
24
|
+
import {createDdns} from 'cloudflare-ddns-sync';
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const ddns = createDdns({
|
|
27
|
+
records: ['home.example.com', {name: 'app.example.com', proxied: true}],
|
|
28
|
+
});
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
await ddns.sync();
|
|
31
|
+
```
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
A string represents an A record. Missing A and AAAA records are created; existing records with the same name and type are updated. A records automatically receive the public IPv4 address, and AAAA records receive the IPv6 address.
|
|
31
34
|
|
|
32
|
-
```
|
|
33
|
-
|
|
35
|
+
```ts
|
|
36
|
+
const ddns = createDdns({
|
|
37
|
+
token: process.env.CLOUDFLARE_API_TOKEN,
|
|
38
|
+
records: [
|
|
39
|
+
'home.example.com',
|
|
40
|
+
{name: 'home.example.com', type: 'AAAA'},
|
|
41
|
+
{name: 'alias.example.com', type: 'CNAME', content: 'home.example.com'},
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await ddns.sync();
|
|
34
46
|
```
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
Explicit `token`, `email`/`key` and user service keys remain supported. If no credentials are provided, only `CLOUDFLARE_API_TOKEN` is used.
|
|
37
49
|
|
|
38
|
-
##
|
|
50
|
+
## Synchronizing
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
```ts
|
|
53
|
+
await ddns.sync();
|
|
54
|
+
await ddns.sync('temporary.example.com');
|
|
55
|
+
await ddns.sync(['home.example.com', {name: 'vpn.example.com', proxied: true}]);
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
await ddns.sync({
|
|
58
|
+
ipv4: '203.0.113.10',
|
|
59
|
+
ipv6: '2001:db8::1',
|
|
60
|
+
});
|
|
61
|
+
```
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
const Cddnss = require('cloudflare-ddns-sync').default;
|
|
63
|
+
`sync()` always returns an array of updated DNS records. Explicit `content` on a record takes precedence. Record types other than A and AAAA require `content`.
|
|
47
64
|
|
|
48
|
-
|
|
49
|
-
const cddnss = new Cddnss({
|
|
50
|
-
email: 'your@email.com',
|
|
51
|
-
key: '<your-cloudflare-api-key>',
|
|
52
|
-
token: '<your-cloudflare-api-token>',
|
|
53
|
-
});
|
|
65
|
+
Use `plan()` to inspect the same operation without writing DNS records. Each entry has an `action` of `create`, `update` or `unchanged`. `sync()` leaves unchanged records untouched. If one or more writes fail, it throws `SyncError` with `succeeded` and `failed` entries.
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
name: 'test-1.domain.com',
|
|
58
|
-
type: 'A', // optional
|
|
59
|
-
proxied: true, // optional
|
|
60
|
-
ttl: 1, // optional
|
|
61
|
-
priority: 0, // optional
|
|
62
|
-
content: '1.2.3.4', // optional
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: 'test-2.domain.com',
|
|
66
|
-
},
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
cddnss.syncRecords(records).then((result) => {
|
|
70
|
-
console.log(result);
|
|
71
|
-
});
|
|
67
|
+
```ts
|
|
68
|
+
const changes = await ddns.plan();
|
|
72
69
|
```
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
## Scheduling and IP watching
|
|
75
72
|
|
|
76
|
-
```
|
|
77
|
-
|
|
73
|
+
```ts
|
|
74
|
+
const scheduled = ddns.schedule('*/5 * * * *');
|
|
75
|
+
await scheduled.stop();
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
email: 'your@email.com',
|
|
82
|
-
key: '<your-cloudflare-api-key>',
|
|
83
|
-
token: '<your-cloudflare-api-token>',
|
|
77
|
+
const watching = await ddns.watch({
|
|
78
|
+
onError: error => console.error(error),
|
|
84
79
|
});
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
{
|
|
88
|
-
name: 'test-1.yourdomain.com',
|
|
89
|
-
type: 'A', // optional
|
|
90
|
-
proxied: true, // optional
|
|
91
|
-
ttl: 1, // optional
|
|
92
|
-
priority: 0, // optional
|
|
93
|
-
content: '1.2.3.4', // optional
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'test-2.yourdomain.com',
|
|
97
|
-
},
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
cddnss.syncRecords(records).then((result: Array<RecordData>) => {
|
|
101
|
-
console.log(result);
|
|
102
|
-
});
|
|
81
|
+
await watching.stop();
|
|
103
82
|
```
|
|
104
83
|
|
|
105
|
-
|
|
84
|
+
`schedule()` starts immediately and synchronizes at the next cron interval. `watch()` synchronizes first and then watches only the required IP families; the default interval is ten seconds. Fixed addresses and records with explicit `content` are not polled. Both return a job with `run()`, `start()` and `stop()` methods. Errors from `run()` reject its promise; automatic runs call `onError` once.
|
|
106
85
|
|
|
107
|
-
|
|
86
|
+
Configuration errors are `DdnsError`s with `code: 'INVALID_CONFIG'`; errors while looking up a public address have `code: 'IP_UNAVAILABLE'`.
|
|
108
87
|
|
|
88
|
+
## Listing and removing records
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const records = await ddns.list({records: 'home.example.com'});
|
|
92
|
+
const ipv6Records = await ddns.list({records: {name: 'home.example.com', type: 'AAAA'}});
|
|
93
|
+
const domainRecords = await ddns.list({domains: 'example.com'});
|
|
94
|
+
const grouped = await ddns.list({domains: ['example.com', 'example.org'], groupBy: 'domain'});
|
|
95
|
+
|
|
96
|
+
await ddns.remove('home.example.com');
|
|
97
|
+
await ddns.remove({name: 'home.example.com', type: 'AAAA'});
|
|
109
98
|
```
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
└────────────── second (0-59) [optional]
|
|
99
|
+
|
|
100
|
+
`remove('name')` removes an A record. Use a record object for other types. `list()` without filters uses the records configured when creating the DDNS instance. A record filter with `type` returns only that DNS type.
|
|
101
|
+
|
|
102
|
+
## Public IP
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const ipv4 = await ddns.ip();
|
|
106
|
+
const ipv6 = await ddns.ip(6);
|
|
119
107
|
```
|
|
120
108
|
|
|
121
|
-
##
|
|
122
|
-
|
|
123
|
-
- getIp(): Promise\<string\>
|
|
124
|
-
- getIpv6(): Promise\<string\>
|
|
125
|
-
- getRecordDataForDomain(domain: string): Promise\<Array\<[RecordData](https://cddnss.knaup.pw/types/recorddata)\>\>
|
|
126
|
-
- getRecordDataForDomains(domains: Array\<string\>): Promise\<[DomainRecordList](https://cddnss.knaup.pw/types/domainrecordlist)\>
|
|
127
|
-
- getRecordDataForRecord(record: [Record](https://cddnss.knaup.pw/types/record)): Promise\<[RecordData](https://cddnss.knaup.pw/types/recorddata)\>
|
|
128
|
-
- getRecordDataForRecords(records: Array\<[Record](https://cddnss.knaup.pw/types/record)\>): Promise\<Array\<[RecordData](https://cddnss.knaup.pw/types/recorddata)\>\>
|
|
129
|
-
- removeRecord(recordName: string, recordType?: string): Promise\<void\>
|
|
130
|
-
- stopSyncOnIpChange(changeListenerId: string): void
|
|
131
|
-
- syncByCronTime(cronExpression: string, records: Array\<[Record](https://cddnss.knaup.pw/types/recorddata)\>, callback: [MultiSyncCallback](https://cddnss.knaup.pw/types/multisynccallback), ip?: string): [ScheduledTask](https://www.npmjs.com/package/node-cron#scheduledtask-methods)
|
|
132
|
-
- syncOnIpChange(records: Array\<[Record](https://cddnss.knaup.pw/types/record)\>, callback: multisynccallback): Promise\<string\>
|
|
133
|
-
- syncRecord(record: [Record](https://cddnss.knaup.pw/types/record), ip?: string): Promise\<[RecordData](https://cddnss.knaup.pw/types/recorddata)\>
|
|
134
|
-
- syncRecords(records: Array\<[Record](https://cddnss.knaup.pw/types/record)\>, ip?: string): Promise\<Array\<[RecordData](https://cddnss.knaup.pw/types/recorddata)\>\>
|
|
135
|
-
|
|
136
|
-
For a more detailed view, have a look at the [Documentation](https://cddnss.knaup.pw/)
|
|
137
|
-
|
|
138
|
-
## Get Your Cloudflare API Key
|
|
139
|
-
|
|
140
|
-
- Go to **[Cloudflare](https://www.cloudflare.com)**
|
|
141
|
-
- **Log In**
|
|
142
|
-
- In the upper right corner: **click on the user icon**
|
|
143
|
-
- Go to **"My Profile"**
|
|
144
|
-
- In the "API Tokens"-Section: **click on the "View"-Button of the Global Key**
|
|
145
|
-
- **Enter your password** and **fill the captcha**
|
|
146
|
-
- **Copy the API Key**
|
|
109
|
+
## Optional: eigene IP-Quelle und Zone
|
|
147
110
|
|
|
148
|
-
|
|
111
|
+
```ts
|
|
112
|
+
const ddns = createDdns({
|
|
113
|
+
zone: 'example.com',
|
|
114
|
+
records: ['@', 'home'],
|
|
115
|
+
resolveIp: family => readAddressFromRouter(family),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await ddns.close();
|
|
119
|
+
```
|
|
149
120
|
|
|
150
|
-
|
|
121
|
+
With `zone`, `@` means the zone apex and simple labels are expanded. `resolveIp` is used by both `sync()` and `watch()`.
|
|
151
122
|
|
|
152
|
-
|
|
123
|
+
## Migrating from v3
|
|
153
124
|
|
|
154
|
-
|
|
155
|
-
- Configure the email, cloudflare api key and the domain
|
|
156
|
-
- Run `npm test`
|
|
125
|
+
v4 is a breaking release. See [MIGRATION-V4.md](./MIGRATION-V4.md) for the complete mapping of the previous API.
|
|
157
126
|
|
|
158
|
-
|
|
127
|
+
## Tests
|
|
128
|
+
|
|
129
|
+
The integration tests create and remove records in a Cloudflare test zone:
|
|
159
130
|
|
|
160
|
-
|
|
131
|
+
```sh
|
|
132
|
+
npm run build
|
|
133
|
+
npm test -- --token="$CLOUDFLARE_API_TOKEN" --domain="example.com"
|
|
134
|
+
```
|
package/logo.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-ddns-sync",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
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>",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"url": "git://github.com:Steffen982/cloudflare-ddns-sync.git"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"
|
|
23
|
+
"clean": "node --input-type=module -e \"import {rm} from 'node:fs/promises'; await rm('dist', {recursive: true, force: true});\"",
|
|
24
|
+
"build": "npm run clean && tsc",
|
|
24
25
|
"lint": "prettier -c ./src",
|
|
25
26
|
"lint:fix": "prettier -w ./src",
|
|
26
27
|
"test": "c8 mocha dist/tests/*.js --timeout 15000 --exit",
|
|
@@ -29,23 +30,24 @@
|
|
|
29
30
|
"test:coverage-report": "c8 report"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"cloudflare": "
|
|
33
|
-
"node-cron": "
|
|
34
|
-
"parse-domain": "8.0
|
|
35
|
-
"public-ip": "
|
|
33
|
+
"cloudflare": "7.1.0",
|
|
34
|
+
"node-cron": "4.6.0",
|
|
35
|
+
"parse-domain": "8.4.0",
|
|
36
|
+
"public-ip": "8.0.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@types/chai": "
|
|
39
|
-
"@types/cloudflare": "^2.7.14",
|
|
39
|
+
"@types/chai": "5.2.3",
|
|
40
40
|
"@types/minimist": "1.2.5",
|
|
41
|
-
"@types/mocha": "10.0.
|
|
42
|
-
"@types/node": "
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"chai": "5.1.0",
|
|
41
|
+
"@types/mocha": "10.0.10",
|
|
42
|
+
"@types/node": "26.5.0",
|
|
43
|
+
"c8": "12.0.0",
|
|
44
|
+
"chai": "6.2.2",
|
|
46
45
|
"minimist": "1.2.8",
|
|
47
|
-
"mocha": "
|
|
48
|
-
"prettier": "
|
|
49
|
-
"typescript": "
|
|
46
|
+
"mocha": "12.0.0",
|
|
47
|
+
"prettier": "3.9.6",
|
|
48
|
+
"typescript": "7.0.2"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=20"
|
|
50
52
|
}
|
|
51
53
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Auth, MultiSyncCallback, Record } from './types/index.js';
|
|
2
|
-
export default class CloudflareDDNSSync {
|
|
3
|
-
private cloudflareClient;
|
|
4
|
-
constructor(auth: Auth);
|
|
5
|
-
getIp(): Promise<string>;
|
|
6
|
-
getIpv6(): Promise<string>;
|
|
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[]>;
|
|
11
|
-
removeRecord(recordName: string, recordType?: string): Promise<void>;
|
|
12
|
-
stopSyncOnIpChange(changeListenerId: string): void;
|
|
13
|
-
syncByCronTime(cronExpression: string, records: Array<Record>, callback: MultiSyncCallback, ip?: string): import("node-cron").ScheduledTask;
|
|
14
|
-
syncOnIpChange(records: Array<Record>, callback: MultiSyncCallback): Promise<string>;
|
|
15
|
-
syncRecord(record: Record, ip?: string): any;
|
|
16
|
-
syncRecords(records: Array<Record>, ip?: string): Promise<import("cloudflare/resources/dns/records.mjs").DNSRecord[]>;
|
|
17
|
-
}
|
|
18
|
-
export * from './types/index.js';
|
package/dist/index.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import CloudflareClient from './lib/cloudflare-client.js';
|
|
2
|
-
import Cron from './lib/cron.js';
|
|
3
|
-
import ipUtils from './lib/ip-utils.js';
|
|
4
|
-
export default class CloudflareDDNSSync {
|
|
5
|
-
cloudflareClient;
|
|
6
|
-
constructor(auth) {
|
|
7
|
-
this.cloudflareClient = new CloudflareClient(auth);
|
|
8
|
-
}
|
|
9
|
-
getIp() {
|
|
10
|
-
return ipUtils.getIpv4();
|
|
11
|
-
}
|
|
12
|
-
getIpv6() {
|
|
13
|
-
return ipUtils.getIpv6();
|
|
14
|
-
}
|
|
15
|
-
getRecordDataForDomain(domain) {
|
|
16
|
-
return this.cloudflareClient.getRecordsByDomain(domain);
|
|
17
|
-
}
|
|
18
|
-
getRecordDataForDomains(domains) {
|
|
19
|
-
return this.cloudflareClient.getRecordsByDomains(domains);
|
|
20
|
-
}
|
|
21
|
-
getRecordDataForRecord(record) {
|
|
22
|
-
return this.cloudflareClient.getRecordDataForRecord(record);
|
|
23
|
-
}
|
|
24
|
-
getRecordDataForRecords(records) {
|
|
25
|
-
return this.cloudflareClient.getRecordDataForRecords(records);
|
|
26
|
-
}
|
|
27
|
-
removeRecord(recordName, recordType) {
|
|
28
|
-
return this.cloudflareClient.removeRecordByNameAndType(recordName, recordType);
|
|
29
|
-
}
|
|
30
|
-
stopSyncOnIpChange(changeListenerId) {
|
|
31
|
-
ipUtils.removeIpChangeListener(changeListenerId);
|
|
32
|
-
}
|
|
33
|
-
syncByCronTime(cronExpression, records, callback, ip) {
|
|
34
|
-
return Cron.createCronJob(cronExpression, async () => {
|
|
35
|
-
const result = await this.syncRecords(records, ip);
|
|
36
|
-
callback(result);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
async syncOnIpChange(records, callback) {
|
|
40
|
-
const changeListenerId = await ipUtils.addIpChangeListener(async (ip) => {
|
|
41
|
-
const result = await this.syncRecords(records, ip);
|
|
42
|
-
callback(result);
|
|
43
|
-
});
|
|
44
|
-
// Sync records to make sure the current ip is already set.
|
|
45
|
-
const currentIp = await ipUtils.getIpv4();
|
|
46
|
-
this.syncRecords(records, currentIp).then((syncedRecords) => {
|
|
47
|
-
callback(syncedRecords);
|
|
48
|
-
});
|
|
49
|
-
return changeListenerId;
|
|
50
|
-
}
|
|
51
|
-
syncRecord(record, ip) {
|
|
52
|
-
return this.cloudflareClient.syncRecord(record, ip);
|
|
53
|
-
}
|
|
54
|
-
syncRecords(records, ip) {
|
|
55
|
-
return this.cloudflareClient.syncRecords(records, ip);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
export * from './types/index.js';
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import Cloudflare, { ClientOptions as CloudflareOptions } from 'cloudflare';
|
|
2
|
-
import { Record } from '../types/index.js';
|
|
3
|
-
export default class CloudflareClient {
|
|
4
|
-
private cloudflare;
|
|
5
|
-
private zoneMap;
|
|
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
|
-
removeRecordByNameAndType(recordName: string, recordType?: string): Promise<void>;
|
|
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
|
-
private createRecord;
|
|
15
|
-
private updateRecord;
|
|
16
|
-
private updateZoneMap;
|
|
17
|
-
private getRecordIdByNameAndType;
|
|
18
|
-
private getZoneIdByRecordName;
|
|
19
|
-
private getRecordByNameAndType;
|
|
20
|
-
private getRecordIdsForRecords;
|
|
21
|
-
private getRecordIdMapKey;
|
|
22
|
-
private getZoneIdByDomain;
|
|
23
|
-
private getDomainsFromRecords;
|
|
24
|
-
private getDomainByRecordName;
|
|
25
|
-
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { ParseResultType, fromUrl, parseDomain } from 'parse-domain';
|
|
2
|
-
import Cloudflare from 'cloudflare';
|
|
3
|
-
import IPUtils from './ip-utils.js';
|
|
4
|
-
const ipv4Regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/u;
|
|
5
|
-
const ipv6Regex = /^(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))$/u;
|
|
6
|
-
export default class CloudflareClient {
|
|
7
|
-
cloudflare;
|
|
8
|
-
zoneMap = new Map();
|
|
9
|
-
constructor(cloudflareOptions) {
|
|
10
|
-
this.cloudflare = new Cloudflare(cloudflareOptions);
|
|
11
|
-
this.updateZoneMap();
|
|
12
|
-
}
|
|
13
|
-
async syncRecord(record, ip) {
|
|
14
|
-
const recordIds = await this.getRecordIdsForRecords([record]);
|
|
15
|
-
const ipToUse = ip ? ip : await IPUtils.getIpv4();
|
|
16
|
-
const zoneId = await this.getZoneIdByRecordName(record.name);
|
|
17
|
-
const recordId = recordIds.get(this.getRecordIdMapKey(record));
|
|
18
|
-
const recordExists = recordId !== undefined;
|
|
19
|
-
if (recordExists) {
|
|
20
|
-
const result = await this.updateRecord(zoneId, recordId, record, ipToUse);
|
|
21
|
-
return result;
|
|
22
|
-
}
|
|
23
|
-
const result = await this.createRecord(zoneId, record, ipToUse);
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
async syncRecords(records, ip) {
|
|
27
|
-
const recordIds = await this.getRecordIdsForRecords(records);
|
|
28
|
-
const ipToUse = ip ? ip : await IPUtils.getIpv4();
|
|
29
|
-
const resultPromises = records.map(async (record) => {
|
|
30
|
-
const zoneId = await this.getZoneIdByRecordName(record.name);
|
|
31
|
-
const recordId = recordIds.get(this.getRecordIdMapKey(record));
|
|
32
|
-
const recordExists = recordId !== undefined;
|
|
33
|
-
if (recordExists) {
|
|
34
|
-
const currentResult = await this.updateRecord(zoneId, recordId, record, ipToUse);
|
|
35
|
-
return currentResult;
|
|
36
|
-
}
|
|
37
|
-
const currentResult = await this.createRecord(zoneId, record, ipToUse);
|
|
38
|
-
return currentResult;
|
|
39
|
-
});
|
|
40
|
-
const results = await Promise.all(resultPromises);
|
|
41
|
-
return results;
|
|
42
|
-
}
|
|
43
|
-
async removeRecordByNameAndType(recordName, recordType) {
|
|
44
|
-
const recordTypeToUse = recordType ? recordType : 'A';
|
|
45
|
-
const zoneId = await this.getZoneIdByRecordName(recordName);
|
|
46
|
-
const recordId = await this.getRecordIdByNameAndType(recordName, recordTypeToUse);
|
|
47
|
-
await this.cloudflare.dns.records.delete(recordId, { zone_id: zoneId });
|
|
48
|
-
}
|
|
49
|
-
async getRecordDataForRecord(record) {
|
|
50
|
-
const domain = this.getDomainByRecordName(record.name);
|
|
51
|
-
const recordDataForDomain = await this.getRecordsByDomain(domain);
|
|
52
|
-
const recordData = recordDataForDomain.find((singleRecordData) => record.name.toLowerCase() === singleRecordData.name.toLowerCase());
|
|
53
|
-
return recordData;
|
|
54
|
-
}
|
|
55
|
-
async getRecordDataForRecords(records) {
|
|
56
|
-
const domains = this.getDomainsFromRecords(records);
|
|
57
|
-
const recordDataPromises = domains.map(async (domain) => {
|
|
58
|
-
const recordDataForDomain = await this.getRecordsByDomain(domain);
|
|
59
|
-
const recordDataForDomainFilteredByRecords = recordDataForDomain.filter((singleRecordData) => records.some((record) => record.name.toLowerCase() === singleRecordData.name.toLowerCase()));
|
|
60
|
-
return recordDataForDomainFilteredByRecords;
|
|
61
|
-
});
|
|
62
|
-
const recordDataForDomains = await Promise.all(recordDataPromises);
|
|
63
|
-
const recordData = [].concat(...recordDataForDomains);
|
|
64
|
-
return recordData;
|
|
65
|
-
}
|
|
66
|
-
async getRecordsByDomains(domains) {
|
|
67
|
-
const recordDataPromises = domains.map((domain) => this.getRecordsByDomain(domain));
|
|
68
|
-
const recordDataForDomains = await Promise.all(recordDataPromises);
|
|
69
|
-
const recordData = {};
|
|
70
|
-
recordDataForDomains.forEach((recordDataForDomain, index) => {
|
|
71
|
-
recordData[domains[index]] = recordDataForDomain;
|
|
72
|
-
});
|
|
73
|
-
return recordData;
|
|
74
|
-
}
|
|
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,
|
|
91
|
-
};
|
|
92
|
-
if (!dnsRecordToCreate.content) {
|
|
93
|
-
throw Error(`Could not create Record "${dnsRecordToCreate.name}": Content is missing!`);
|
|
94
|
-
}
|
|
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!`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
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!`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else if (dnsRecordToCreate.type === 'CNAME') {
|
|
106
|
-
const parsedDomain = parseDomain(fromUrl(dnsRecordToCreate.content));
|
|
107
|
-
if (parsedDomain.type !== ParseResultType.Listed || !parsedDomain.domain) {
|
|
108
|
-
throw Error(`Could not create Record "${dnsRecordToCreate.name}": '${dnsRecordToCreate.content}' is not a valid domain name!`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return this.cloudflare.dns.records.create(dnsRecordToCreate);
|
|
112
|
-
}
|
|
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,
|
|
121
|
-
};
|
|
122
|
-
if (!updatedDnsRecord.content) {
|
|
123
|
-
throw Error(`Could not update Record "${updatedDnsRecord.name}": Content is missing!`);
|
|
124
|
-
}
|
|
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!`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
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!`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
else if (updatedDnsRecord.type === 'CNAME') {
|
|
136
|
-
const parsedDomain = parseDomain(fromUrl(updatedDnsRecord.content));
|
|
137
|
-
if (parsedDomain.type !== ParseResultType.Listed || !parsedDomain.domain) {
|
|
138
|
-
throw Error(`Could not update Record "${updatedDnsRecord.name}": '${updatedDnsRecord.content}' is not a valid domain name!`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return await this.cloudflare.dns.records.edit(recordId, updatedDnsRecord);
|
|
142
|
-
}
|
|
143
|
-
async updateZoneMap() {
|
|
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
|
-
}
|
|
152
|
-
this.zoneMap = new Map();
|
|
153
|
-
for (const zone of zones) {
|
|
154
|
-
this.zoneMap.set(zone.name, zone.id);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
async getRecordIdByNameAndType(recordName, recordType) {
|
|
158
|
-
const record = await this.getRecordByNameAndType(recordName, recordType);
|
|
159
|
-
return record.id;
|
|
160
|
-
}
|
|
161
|
-
getZoneIdByRecordName(recordName) {
|
|
162
|
-
const domain = this.getDomainByRecordName(recordName);
|
|
163
|
-
return this.getZoneIdByDomain(domain);
|
|
164
|
-
}
|
|
165
|
-
async getRecordByNameAndType(recordName, recordType) {
|
|
166
|
-
const domain = this.getDomainByRecordName(recordName);
|
|
167
|
-
const records = await this.getRecordsByDomain(domain);
|
|
168
|
-
const record = records.find((currentRecord) => currentRecord.name.toLowerCase() === recordName.toLowerCase() && currentRecord.type.toLowerCase() === recordType.toLowerCase());
|
|
169
|
-
const recordNotFound = record === undefined;
|
|
170
|
-
if (recordNotFound) {
|
|
171
|
-
throw new Error(`Record '${recordName}' not found.`);
|
|
172
|
-
}
|
|
173
|
-
return record;
|
|
174
|
-
}
|
|
175
|
-
async getRecordIdsForRecords(records) {
|
|
176
|
-
const recordIdMap = new Map();
|
|
177
|
-
const recordData = await this.getRecordDataForRecords(records);
|
|
178
|
-
for (const record of recordData) {
|
|
179
|
-
recordIdMap.set(this.getRecordIdMapKey(record), record.id);
|
|
180
|
-
}
|
|
181
|
-
return recordIdMap;
|
|
182
|
-
}
|
|
183
|
-
getRecordIdMapKey(record) {
|
|
184
|
-
const recordName = record.name.toLowerCase();
|
|
185
|
-
const recordType = record.type ? record.type.toLowerCase() : 'a';
|
|
186
|
-
return `"${recordName}"_"${recordType}"`;
|
|
187
|
-
}
|
|
188
|
-
async getZoneIdByDomain(domain) {
|
|
189
|
-
if (this.zoneMap.has(domain)) {
|
|
190
|
-
const zoneId = this.zoneMap.get(domain.toLowerCase());
|
|
191
|
-
return zoneId;
|
|
192
|
-
}
|
|
193
|
-
await this.updateZoneMap();
|
|
194
|
-
if (!this.zoneMap.has(domain)) {
|
|
195
|
-
throw new Error(`Could not find domain '${domain}'. Make sure the domain is set up for your cloudflare account.`);
|
|
196
|
-
}
|
|
197
|
-
const zoneId = this.zoneMap.get(domain.toLowerCase());
|
|
198
|
-
return zoneId;
|
|
199
|
-
}
|
|
200
|
-
getDomainsFromRecords(records) {
|
|
201
|
-
const domains = records.map((record) => this.getDomainByRecordName(record.name)).filter((domain, index, domainList) => domainList.indexOf(domain.toLowerCase()) === index);
|
|
202
|
-
return domains;
|
|
203
|
-
}
|
|
204
|
-
getDomainByRecordName(recordName) {
|
|
205
|
-
const parsedDomain = parseDomain(fromUrl(recordName));
|
|
206
|
-
if (parsedDomain.type !== ParseResultType.Listed || !parsedDomain.domain) {
|
|
207
|
-
throw new Error(`Could not parse domain. '${JSON.stringify(recordName)}' is not a valid record name.`);
|
|
208
|
-
}
|
|
209
|
-
let domain = '';
|
|
210
|
-
domain += parsedDomain.domain;
|
|
211
|
-
for (const tld of parsedDomain.topLevelDomains) {
|
|
212
|
-
domain += `.${tld}`;
|
|
213
|
-
}
|
|
214
|
-
return domain.toLowerCase();
|
|
215
|
-
}
|
|
216
|
-
}
|
package/dist/lib/cron.d.ts
DELETED
package/dist/lib/cron.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import cron from 'node-cron';
|
|
2
|
-
export default class Cron {
|
|
3
|
-
static createCronJob(cronExpression, callback) {
|
|
4
|
-
const cronExpressionIsInvalid = !this.isValid(cronExpression);
|
|
5
|
-
if (cronExpressionIsInvalid) {
|
|
6
|
-
throw new Error(`'${cronExpression}' is not a valid cron expression.\nHere you can see how cron expressions work: https://cddnss.knaup.pw/cron-expression-syntax`);
|
|
7
|
-
}
|
|
8
|
-
return cron.schedule(cronExpression, () => {
|
|
9
|
-
callback();
|
|
10
|
-
}, undefined);
|
|
11
|
-
}
|
|
12
|
-
static isValid(cronExpression) {
|
|
13
|
-
return cron.validate(cronExpression);
|
|
14
|
-
}
|
|
15
|
-
}
|
package/dist/lib/ip-utils.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export default class IPUtils {
|
|
2
|
-
private static readonly ipPollingDelay;
|
|
3
|
-
private static ipChangeEventListeners;
|
|
4
|
-
static getIpv4(): Promise<string>;
|
|
5
|
-
static getIpv6(): Promise<string>;
|
|
6
|
-
static addIpChangeListener(callback: Function): Promise<string>;
|
|
7
|
-
static removeIpChangeListener(eventListenerId: string): void;
|
|
8
|
-
private static getId;
|
|
9
|
-
}
|
package/dist/lib/ip-utils.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { publicIpv4, publicIpv6 } from 'public-ip';
|
|
2
|
-
export default class IPUtils {
|
|
3
|
-
static ipPollingDelay = 10 * 1000;
|
|
4
|
-
static ipChangeEventListeners = new Map();
|
|
5
|
-
static async getIpv4() {
|
|
6
|
-
/* c8 ignore start*/
|
|
7
|
-
try {
|
|
8
|
-
return await publicIpv4();
|
|
9
|
-
}
|
|
10
|
-
catch (error) {
|
|
11
|
-
return publicIpv4();
|
|
12
|
-
}
|
|
13
|
-
/* c8 ignore stop*/
|
|
14
|
-
}
|
|
15
|
-
static async getIpv6() {
|
|
16
|
-
/* c8 ignore start */
|
|
17
|
-
try {
|
|
18
|
-
return await publicIpv6();
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
return publicIpv6();
|
|
22
|
-
}
|
|
23
|
-
/* c8 ignore stop*/
|
|
24
|
-
}
|
|
25
|
-
static async addIpChangeListener(callback) {
|
|
26
|
-
const eventListenerId = this.getId();
|
|
27
|
-
let previousIp = await this.getIpv4();
|
|
28
|
-
/* c8 ignore start */
|
|
29
|
-
const intervalId = setInterval(async () => {
|
|
30
|
-
const currentIp = await this.getIpv4();
|
|
31
|
-
const ipMustBeUpdated = currentIp !== previousIp;
|
|
32
|
-
if (ipMustBeUpdated) {
|
|
33
|
-
previousIp = currentIp;
|
|
34
|
-
callback(currentIp);
|
|
35
|
-
}
|
|
36
|
-
}, this.ipPollingDelay);
|
|
37
|
-
/* c8 ignore stop*/
|
|
38
|
-
this.ipChangeEventListeners.set(eventListenerId, intervalId);
|
|
39
|
-
return eventListenerId;
|
|
40
|
-
}
|
|
41
|
-
static removeIpChangeListener(eventListenerId) {
|
|
42
|
-
const eventListenerIntervalId = this.ipChangeEventListeners.get(eventListenerId);
|
|
43
|
-
clearInterval(eventListenerIntervalId);
|
|
44
|
-
IPUtils.ipChangeEventListeners.delete(eventListenerId);
|
|
45
|
-
}
|
|
46
|
-
static getId() {
|
|
47
|
-
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
48
|
-
}
|
|
49
|
-
}
|
package/dist/types/Auth.d.ts
DELETED
package/dist/types/Auth.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/Callbacks.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/Record.d.ts
DELETED
package/dist/types/Record.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/ZoneMap.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type ZoneMap = Map<string, string>;
|
package/dist/types/ZoneMap.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { RecordTypes } from '../index.js';
|
|
2
|
-
export type RecordData = {
|
|
3
|
-
id: string;
|
|
4
|
-
type: RecordTypes;
|
|
5
|
-
name: string;
|
|
6
|
-
content: string;
|
|
7
|
-
proxiable: boolean;
|
|
8
|
-
proxied: boolean;
|
|
9
|
-
ttl: number;
|
|
10
|
-
locked: boolean;
|
|
11
|
-
zone_id: string;
|
|
12
|
-
zone_name: string;
|
|
13
|
-
modified_on: string;
|
|
14
|
-
created_on: string;
|
|
15
|
-
meta: RecordMetaData;
|
|
16
|
-
};
|
|
17
|
-
export type RecordMetaData = {
|
|
18
|
-
auto_added: boolean;
|
|
19
|
-
managed_by_apps: boolean;
|
|
20
|
-
managed_by_argo_tunnel: boolean;
|
|
21
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export type ZoneData = {
|
|
2
|
-
id: string;
|
|
3
|
-
name: string;
|
|
4
|
-
status: string;
|
|
5
|
-
paused: boolean;
|
|
6
|
-
type: string;
|
|
7
|
-
development_mode: number;
|
|
8
|
-
name_servers: Array<string>;
|
|
9
|
-
original_name_servers: Array<string>;
|
|
10
|
-
modified_on: Date;
|
|
11
|
-
created_on: Date;
|
|
12
|
-
activated_on: Date;
|
|
13
|
-
meta: {
|
|
14
|
-
step: number;
|
|
15
|
-
wildcard_proxiable: boolean;
|
|
16
|
-
custom_certificate_quota: number;
|
|
17
|
-
page_rule_quota: number;
|
|
18
|
-
phishing_detected: boolean;
|
|
19
|
-
multiple_railguns_allowed: boolean;
|
|
20
|
-
};
|
|
21
|
-
owner: {
|
|
22
|
-
id: string;
|
|
23
|
-
type: string;
|
|
24
|
-
email: string;
|
|
25
|
-
};
|
|
26
|
-
account: {
|
|
27
|
-
id: string;
|
|
28
|
-
name: string;
|
|
29
|
-
};
|
|
30
|
-
permissions: Array<string>;
|
|
31
|
-
plan: {
|
|
32
|
-
id: string;
|
|
33
|
-
name: string;
|
|
34
|
-
price: number;
|
|
35
|
-
currency: string;
|
|
36
|
-
frequency: string;
|
|
37
|
-
is_subscribed: boolean;
|
|
38
|
-
can_subscribe: boolean;
|
|
39
|
-
legacy_id: string;
|
|
40
|
-
legacy_discount: boolean;
|
|
41
|
-
externally_managed: boolean;
|
|
42
|
-
};
|
|
43
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/index.d.ts
DELETED
package/dist/types/index.js
DELETED