cloudflare-ddns-sync 2.0.1 → 2.0.5
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/.eslintrc.json +229 -0
- package/CHANGELOG.md +135 -0
- package/README.md +12 -119
- package/dist/contracts/Callbacks.d.ts +3 -0
- package/dist/contracts/Callbacks.js +2 -0
- package/dist/contracts/DomainRecordList.d.ts +4 -0
- package/dist/contracts/DomainRecordList.js +2 -0
- package/dist/contracts/Record.d.ts +8 -0
- package/dist/contracts/Record.js +2 -0
- package/dist/contracts/ZoneMap.d.ts +1 -0
- package/dist/contracts/ZoneMap.js +2 -0
- package/dist/contracts/cloudflare/RecordData.d.ts +20 -0
- package/dist/contracts/cloudflare/RecordData.js +2 -0
- package/dist/contracts/cloudflare/ZoneData.d.ts +43 -0
- package/dist/contracts/cloudflare/ZoneData.js +2 -0
- package/dist/contracts/cloudflare/index.d.ts +2 -0
- package/dist/contracts/cloudflare/index.js +14 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.js +17 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +74 -0
- package/dist/lib/cloudflare-client.d.ts +25 -0
- package/dist/lib/cloudflare-client.js +231 -0
- package/dist/lib/cron.d.ts +5 -0
- package/dist/lib/cron.js +22 -0
- package/dist/lib/ip-utils.d.ts +9 -0
- package/dist/lib/ip-utils.js +82 -0
- package/package.json +24 -20
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"es2021": true,
|
|
4
|
+
"node": true
|
|
5
|
+
},
|
|
6
|
+
"extends": [
|
|
7
|
+
"eslint:recommended",
|
|
8
|
+
"plugin:@typescript-eslint/recommended"
|
|
9
|
+
],
|
|
10
|
+
"parser": "@typescript-eslint/parser",
|
|
11
|
+
"parserOptions": {
|
|
12
|
+
"ecmaVersion": "latest",
|
|
13
|
+
"sourceType": "module"
|
|
14
|
+
},
|
|
15
|
+
"plugins": [
|
|
16
|
+
"@typescript-eslint"
|
|
17
|
+
],
|
|
18
|
+
"rules": {
|
|
19
|
+
"@typescript-eslint/ban-types": "off",
|
|
20
|
+
"@typescript-eslint/indent": ["error", 2],
|
|
21
|
+
"@typescript-eslint/no-shadow": "error",
|
|
22
|
+
"accessor-pairs": "error",
|
|
23
|
+
"array-bracket-newline": "error",
|
|
24
|
+
"array-bracket-spacing": "error",
|
|
25
|
+
"array-callback-return": "error",
|
|
26
|
+
"array-element-newline": "error",
|
|
27
|
+
"arrow-body-style": "error",
|
|
28
|
+
"arrow-parens": "error",
|
|
29
|
+
"arrow-spacing": "error",
|
|
30
|
+
"block-scoped-var": "error",
|
|
31
|
+
"block-spacing": "error",
|
|
32
|
+
"brace-style": "error",
|
|
33
|
+
"camelcase": "warn",
|
|
34
|
+
"capitalized-comments": "off",
|
|
35
|
+
"class-methods-use-this": "off",
|
|
36
|
+
"comma-dangle": ["error", "always-multiline"],
|
|
37
|
+
"comma-spacing": "error",
|
|
38
|
+
"comma-style": "error",
|
|
39
|
+
"complexity": "error",
|
|
40
|
+
"computed-property-spacing": "error",
|
|
41
|
+
"consistent-return": "error",
|
|
42
|
+
"consistent-this": "error",
|
|
43
|
+
"curly": "error",
|
|
44
|
+
"default-case": "error",
|
|
45
|
+
"default-case-last": "error",
|
|
46
|
+
"default-param-last": "error",
|
|
47
|
+
"dot-location": ["error", "property"],
|
|
48
|
+
"dot-notation": "error",
|
|
49
|
+
"eol-last": "error",
|
|
50
|
+
"eqeqeq": "error",
|
|
51
|
+
"func-call-spacing": "error",
|
|
52
|
+
"func-name-matching": "error",
|
|
53
|
+
"func-names": "error",
|
|
54
|
+
"func-style": ["error", "declaration", {
|
|
55
|
+
"allowArrowFunctions": true
|
|
56
|
+
}],
|
|
57
|
+
"function-call-argument-newline": ["error", "consistent"],
|
|
58
|
+
"function-paren-newline": "error",
|
|
59
|
+
"generator-star-spacing": "error",
|
|
60
|
+
"grouped-accessor-pairs": "error",
|
|
61
|
+
"guard-for-in": "error",
|
|
62
|
+
"id-denylist": "error",
|
|
63
|
+
"id-length": "off",
|
|
64
|
+
"id-match": "error",
|
|
65
|
+
"implicit-arrow-linebreak": "error",
|
|
66
|
+
"indent": "off",
|
|
67
|
+
"init-declarations": "off",
|
|
68
|
+
"key-spacing": "error",
|
|
69
|
+
"keyword-spacing": "error",
|
|
70
|
+
"line-comment-position": "error",
|
|
71
|
+
"linebreak-style": "error",
|
|
72
|
+
"lines-around-comment": "off",
|
|
73
|
+
"lines-between-class-members": "off",
|
|
74
|
+
"max-classes-per-file": "error",
|
|
75
|
+
"max-depth": "error",
|
|
76
|
+
"max-len": ["error", 150],
|
|
77
|
+
"max-lines": ["error", 1000],
|
|
78
|
+
"max-lines-per-function": "off",
|
|
79
|
+
"max-nested-callbacks": "error",
|
|
80
|
+
"max-params": ["warn", {"max": 4}],
|
|
81
|
+
"max-statements": "off",
|
|
82
|
+
"max-statements-per-line": "off",
|
|
83
|
+
"multiline-comment-style": "off",
|
|
84
|
+
"multiline-ternary": "off",
|
|
85
|
+
"new-cap": "error",
|
|
86
|
+
"new-parens": "error",
|
|
87
|
+
"newline-per-chained-call": "error",
|
|
88
|
+
"no-alert": "error",
|
|
89
|
+
"no-array-constructor": "error",
|
|
90
|
+
"no-await-in-loop": "off",
|
|
91
|
+
"no-bitwise": "error",
|
|
92
|
+
"no-caller": "error",
|
|
93
|
+
"no-confusing-arrow": "error",
|
|
94
|
+
"no-console": "error",
|
|
95
|
+
"no-constructor-return": "error",
|
|
96
|
+
"no-continue": "error",
|
|
97
|
+
"no-div-regex": "error",
|
|
98
|
+
"no-duplicate-imports": "error",
|
|
99
|
+
"no-else-return": "error",
|
|
100
|
+
"no-empty-function": "error",
|
|
101
|
+
"no-eq-null": "error",
|
|
102
|
+
"no-eval": "error",
|
|
103
|
+
"no-extend-native": "error",
|
|
104
|
+
"no-extra-bind": "error",
|
|
105
|
+
"no-extra-label": "error",
|
|
106
|
+
"no-extra-parens": "off",
|
|
107
|
+
"no-floating-decimal": "error",
|
|
108
|
+
"no-implicit-coercion": "error",
|
|
109
|
+
"no-implicit-globals": "error",
|
|
110
|
+
"no-implied-eval": "error",
|
|
111
|
+
"no-inline-comments": "error",
|
|
112
|
+
"no-invalid-this": "error",
|
|
113
|
+
"no-iterator": "error",
|
|
114
|
+
"no-label-var": "error",
|
|
115
|
+
"no-labels": "error",
|
|
116
|
+
"no-lone-blocks": "error",
|
|
117
|
+
"no-lonely-if": "error",
|
|
118
|
+
"no-loop-func": "error",
|
|
119
|
+
"no-magic-numbers": "off",
|
|
120
|
+
"no-mixed-operators": "error",
|
|
121
|
+
"no-multi-assign": "error",
|
|
122
|
+
"no-multi-spaces": "error",
|
|
123
|
+
"no-multi-str": "error",
|
|
124
|
+
"no-multiple-empty-lines": "error",
|
|
125
|
+
"no-negated-condition": "error",
|
|
126
|
+
"no-nested-ternary": "error",
|
|
127
|
+
"no-new": "error",
|
|
128
|
+
"no-new-func": "error",
|
|
129
|
+
"no-new-object": "error",
|
|
130
|
+
"no-new-wrappers": "error",
|
|
131
|
+
"no-octal-escape": "error",
|
|
132
|
+
"no-param-reassign": "error",
|
|
133
|
+
"no-plusplus": "off",
|
|
134
|
+
"no-promise-executor-return": "error",
|
|
135
|
+
"no-proto": "error",
|
|
136
|
+
"no-restricted-exports": "error",
|
|
137
|
+
"no-restricted-globals": "error",
|
|
138
|
+
"no-restricted-imports": "error",
|
|
139
|
+
"no-restricted-properties": "error",
|
|
140
|
+
"no-restricted-syntax": "error",
|
|
141
|
+
"no-return-assign": "error",
|
|
142
|
+
"no-return-await": "error",
|
|
143
|
+
"no-script-url": "error",
|
|
144
|
+
"no-self-compare": "error",
|
|
145
|
+
"no-sequences": "error",
|
|
146
|
+
"no-shadow": "off",
|
|
147
|
+
"no-tabs": "error",
|
|
148
|
+
"no-template-curly-in-string": "error",
|
|
149
|
+
"no-ternary": "off",
|
|
150
|
+
"no-throw-literal": "error",
|
|
151
|
+
"no-trailing-spaces": "error",
|
|
152
|
+
"no-undef-init": "error",
|
|
153
|
+
"no-undefined": "off",
|
|
154
|
+
"no-underscore-dangle": "error",
|
|
155
|
+
"no-unmodified-loop-condition": "error",
|
|
156
|
+
"no-unneeded-ternary": "error",
|
|
157
|
+
"no-unreachable-loop": "error",
|
|
158
|
+
"no-unused-expressions": "error",
|
|
159
|
+
"no-unused-private-class-members": "error",
|
|
160
|
+
"no-use-before-define": "off",
|
|
161
|
+
"no-useless-call": "error",
|
|
162
|
+
"no-useless-computed-key": "error",
|
|
163
|
+
"no-useless-concat": "error",
|
|
164
|
+
"no-useless-constructor": "error",
|
|
165
|
+
"no-useless-rename": "error",
|
|
166
|
+
"no-useless-return": "error",
|
|
167
|
+
"no-var": "error",
|
|
168
|
+
"no-void": "error",
|
|
169
|
+
"no-warning-comments": "error",
|
|
170
|
+
"no-whitespace-before-property": "error",
|
|
171
|
+
"nonblock-statement-body-position": "error",
|
|
172
|
+
"object-curly-newline": "error",
|
|
173
|
+
"object-curly-spacing": "error",
|
|
174
|
+
"object-property-newline": "error",
|
|
175
|
+
"object-shorthand": ["error", "never"],
|
|
176
|
+
"one-var": ["error", "never"],
|
|
177
|
+
"operator-assignment": "error",
|
|
178
|
+
"operator-linebreak": ["error", "before"],
|
|
179
|
+
"padded-blocks": ["error", "never"],
|
|
180
|
+
"padding-line-between-statements": "error",
|
|
181
|
+
"prefer-arrow-callback": "error",
|
|
182
|
+
"prefer-const": "error",
|
|
183
|
+
"prefer-destructuring": "error",
|
|
184
|
+
"prefer-exponentiation-operator": "error",
|
|
185
|
+
"prefer-named-capture-group": "off",
|
|
186
|
+
"prefer-numeric-literals": "error",
|
|
187
|
+
"prefer-object-spread": "error",
|
|
188
|
+
"prefer-promise-reject-errors": "error",
|
|
189
|
+
"prefer-regex-literals": "error",
|
|
190
|
+
"prefer-rest-params": "error",
|
|
191
|
+
"prefer-spread": "error",
|
|
192
|
+
"prefer-template": "error",
|
|
193
|
+
"quote-props": ["error", "as-needed"],
|
|
194
|
+
"quotes": ["error", "single"],
|
|
195
|
+
"radix": "error",
|
|
196
|
+
"require-atomic-updates": "error",
|
|
197
|
+
"require-await": "error",
|
|
198
|
+
"require-unicode-regexp": "error",
|
|
199
|
+
"rest-spread-spacing": "error",
|
|
200
|
+
"semi": "error",
|
|
201
|
+
"semi-spacing": "error",
|
|
202
|
+
"semi-style": "error",
|
|
203
|
+
"sort-imports": ["error", {
|
|
204
|
+
"ignoreCase": false,
|
|
205
|
+
"ignoreDeclarationSort": false,
|
|
206
|
+
"ignoreMemberSort": false,
|
|
207
|
+
"allowSeparatedGroups": true
|
|
208
|
+
}],
|
|
209
|
+
"sort-keys": "off",
|
|
210
|
+
"sort-vars": "error",
|
|
211
|
+
"space-before-blocks": "error",
|
|
212
|
+
"space-before-function-paren": ["error", "never"],
|
|
213
|
+
"space-in-parens": "error",
|
|
214
|
+
"space-infix-ops": "error",
|
|
215
|
+
"space-unary-ops": "error",
|
|
216
|
+
"spaced-comment": "error",
|
|
217
|
+
"strict": "error",
|
|
218
|
+
"switch-colon-spacing": "error",
|
|
219
|
+
"symbol-description": "error",
|
|
220
|
+
"template-curly-spacing": "error",
|
|
221
|
+
"template-tag-spacing": "error",
|
|
222
|
+
"unicode-bom": "error",
|
|
223
|
+
"vars-on-top": "error",
|
|
224
|
+
"wrap-iife": "error",
|
|
225
|
+
"wrap-regex": "error",
|
|
226
|
+
"yield-star-spacing": "error",
|
|
227
|
+
"yoda": "error"
|
|
228
|
+
}
|
|
229
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v2
|
|
4
|
+
|
|
5
|
+
### v2.0.5
|
|
6
|
+
|
|
7
|
+
- 🚨 Replace tslint with eslint
|
|
8
|
+
- ⬆️ Update dependencies
|
|
9
|
+
|
|
10
|
+
### v2.0.4
|
|
11
|
+
|
|
12
|
+
- 📝 Update links in README
|
|
13
|
+
- ⬆️ Update dependencies
|
|
14
|
+
|
|
15
|
+
### v2.0.3
|
|
16
|
+
|
|
17
|
+
- ✨ **Add function to get ipv6 address**
|
|
18
|
+
- 🐛 **Fix syncing two records with the same name**
|
|
19
|
+
- 🐛 **Fix removing records if two records have the same name**
|
|
20
|
+
- ✏️ Fix typo in error messages
|
|
21
|
+
- 🐛 Make cloudflareClient private
|
|
22
|
+
- 📝 Update links in README
|
|
23
|
+
|
|
24
|
+
### v2.0.2
|
|
25
|
+
|
|
26
|
+
- 🐛 **Fix Publishing**
|
|
27
|
+
|
|
28
|
+
### v2.0.1
|
|
29
|
+
|
|
30
|
+
- ⬆️ Update Dependencies
|
|
31
|
+
|
|
32
|
+
### v2.0.0
|
|
33
|
+
|
|
34
|
+
- ♻️ **Rewrite Cloudflare-DDNS-Sync in Typescript**
|
|
35
|
+
|
|
36
|
+
## v1
|
|
37
|
+
|
|
38
|
+
### v1.5.4
|
|
39
|
+
|
|
40
|
+
- 📝 Update README
|
|
41
|
+
|
|
42
|
+
### v1.5.3
|
|
43
|
+
|
|
44
|
+
- 🐛 **Fix Stopping Sync On IP Change**
|
|
45
|
+
- 🐛 **Fix Crontime Converter For Hour**
|
|
46
|
+
- 🐛 **Fix Syncing With Crontime Without Setting an IP**
|
|
47
|
+
- ⚡️ **Small Performance Improvements**
|
|
48
|
+
- 💄 Improve Code Quality
|
|
49
|
+
- 💄 Extract Business Rules
|
|
50
|
+
|
|
51
|
+
### v1.5.2
|
|
52
|
+
|
|
53
|
+
- 🐛 Fix Wrong Using of Const
|
|
54
|
+
|
|
55
|
+
### v1.5.1
|
|
56
|
+
|
|
57
|
+
- 🐛 **Fix Bug When 'public-ip' Throws An Error**
|
|
58
|
+
- 💄 Improve Code Quality
|
|
59
|
+
|
|
60
|
+
### v1.5.0
|
|
61
|
+
|
|
62
|
+
- ✨ **Add Create Not Existing Records Functionality**
|
|
63
|
+
- ✨ **Add Fallback getIp Function**
|
|
64
|
+
|
|
65
|
+
### v1.4.0
|
|
66
|
+
|
|
67
|
+
- ✨ **Add Stop SyncOnIpChange Function**
|
|
68
|
+
|
|
69
|
+
### v1.3.4
|
|
70
|
+
|
|
71
|
+
- 🐛 **Fix ipChange Interval**
|
|
72
|
+
|
|
73
|
+
### v1.3.3
|
|
74
|
+
|
|
75
|
+
- 📝 **Add Changelog**
|
|
76
|
+
|
|
77
|
+
### v1.3.2
|
|
78
|
+
|
|
79
|
+
- ✨ **Add getRecordIps Function**
|
|
80
|
+
- ♻️ **Refactor syncOnIpChange**
|
|
81
|
+
- ✅ **Add Tests for Sync Functionality**
|
|
82
|
+
- 📝 **Improve README**
|
|
83
|
+
- 📝 **Add NPM Badge**
|
|
84
|
+
- 🔥 Remove Unnecessary Code
|
|
85
|
+
- 🚸 Update Description
|
|
86
|
+
- 🎨 Order Dependencies
|
|
87
|
+
- 🎨 Lint files
|
|
88
|
+
|
|
89
|
+
### v1.3.1
|
|
90
|
+
|
|
91
|
+
- 📝 **Fix README**
|
|
92
|
+
|
|
93
|
+
### v1.3.0
|
|
94
|
+
|
|
95
|
+
- ✨ **Add syncAtDate Function**
|
|
96
|
+
- 📝 **Improve README**
|
|
97
|
+
|
|
98
|
+
### v1.2.1
|
|
99
|
+
|
|
100
|
+
- 🚑 **Fix README**
|
|
101
|
+
|
|
102
|
+
### v1.2.0
|
|
103
|
+
|
|
104
|
+
- ✨ **Add onIpChange Function**
|
|
105
|
+
- 📝 **Adjust README**
|
|
106
|
+
- 🚚 Move Utils to Lib
|
|
107
|
+
- 🚚 Move IP Functions to Lib
|
|
108
|
+
- 🚚 Move Dependencies to devDependencies
|
|
109
|
+
- 🔧 Update Eslint Config
|
|
110
|
+
- 🎨 Fix Eslit Error
|
|
111
|
+
|
|
112
|
+
### v1.1.0
|
|
113
|
+
|
|
114
|
+
- ⚡️ **Improve Performance of First Sync**
|
|
115
|
+
- ✨ **Add Schedule Functionality**
|
|
116
|
+
- 🚸 **Improve Success Message of Sync Function**
|
|
117
|
+
- 📝 **Fix Typos in README**
|
|
118
|
+
- 📝 **Add Schedule Functions to README**
|
|
119
|
+
- ✅ Add Unittests
|
|
120
|
+
- ✨ Add Eslint
|
|
121
|
+
|
|
122
|
+
### v1.0.3
|
|
123
|
+
|
|
124
|
+
- 📝 **Fix Typo in README**
|
|
125
|
+
- 📄 Add LICENSE
|
|
126
|
+
|
|
127
|
+
### v1.0.2
|
|
128
|
+
|
|
129
|
+
- 📝 **Add "Get Your Cloudflare Api Key"-Section to README**
|
|
130
|
+
|
|
131
|
+
### v1.0.1
|
|
132
|
+
|
|
133
|
+
- ✨ **Add sync Function -> changes the IP of the configured DNS records on Cloudflare**
|
|
134
|
+
- ✨ **Add getIp Function -> returns the external IP**
|
|
135
|
+
- 📝 **Add README**
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Cloudflare-DDNS-Sync is a simple module that updates Cloudflare DNS records.
|
|
12
12
|
|
|
13
|
+
For a more detailed overview, have a look at the [Documentation](https://cddnss.knaup.dev/)
|
|
13
14
|
|
|
14
15
|
You may also have a look at the **official** [CLI version](https://www.npmjs.com/package/cloudflare-ddns-sync-cli) of Cloudflare-DDNS-Sync.
|
|
15
16
|
|
|
@@ -106,18 +107,19 @@ Cron expressions have the following syntax:
|
|
|
106
107
|
## Methods
|
|
107
108
|
|
|
108
109
|
- getIp(): Promise\<string\>
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
110
|
+
- getIpv6(): Promise\<string\>
|
|
111
|
+
- getRecordDataForDomain(domain: string): Promise\<Array\<[RecordData](https://cddnss.knaup.dev/types/recorddata)\>\>
|
|
112
|
+
- getRecordDataForDomains(domains: Array\<string\>): Promise\<[DomainRecordList](https://cddnss.knaup.dev/types/domainrecordlist)\>
|
|
113
|
+
- getRecordDataForRecord(record: [Record](https://cddnss.knaup.dev/types/record)): Promise\<[RecordData](https://cddnss.knaup.dev/types/recorddata)\>
|
|
114
|
+
- getRecordDataForRecords(records: Array\<[Record](https://cddnss.knaup.dev/types/record)\>): Promise\<Array\<[RecordData](https://cddnss.knaup.dev/types/recorddata)\>\>
|
|
115
|
+
- removeRecord(recordName: string, recordType?: string): Promise\<void\>
|
|
114
116
|
- stopSyncOnIpChange(changeListenerId: string): void
|
|
115
|
-
- syncByCronTime(cronExpression: string, records: Array\<[Record](https://
|
|
116
|
-
- syncOnIpChange(records: Array\<[Record](https://
|
|
117
|
-
- syncRecord(record: [Record](https://
|
|
118
|
-
- syncRecords(records: Array\<[Record](https://
|
|
117
|
+
- syncByCronTime(cronExpression: string, records: Array\<[Record](https://cddnss.knaup.dev/types/recorddata)\>, callback: [MultiSyncCallback](https://cddnss.knaup.dev/types/multisynccallback), ip?: string): [ScheduledTask](https://www.npmjs.com/package/node-cron#scheduledtask-methods)
|
|
118
|
+
- syncOnIpChange(records: Array\<[Record](https://cddnss.knaup.dev/types/record)\>, callback: multisynccallback): Promise\<string\>
|
|
119
|
+
- syncRecord(record: [Record](https://cddnss.knaup.dev/types/record), ip?: string): Promise\<[RecordData](https://cddnss.knaup.dev/types/recorddata)\>
|
|
120
|
+
- syncRecords(records: Array\<[Record](https://cddnss.knaup.dev/types/record)\>, ip?: string): Promise\<Array\<[RecordData](https://cddnss.knaup.dev/types/recorddata)\>\>
|
|
119
121
|
|
|
120
|
-
For a more detailed view, have a look at the [Documentation](https://
|
|
122
|
+
For a more detailed view, have a look at the [Documentation](https://cddnss.knaup.dev/)
|
|
121
123
|
|
|
122
124
|
## Get Your Cloudflare API Key
|
|
123
125
|
|
|
@@ -142,112 +144,3 @@ In order to run the tests there are two ways to do so
|
|
|
142
144
|
### Use `npm test` Only
|
|
143
145
|
|
|
144
146
|
- Run `npm test -- --email="your@email.com" --key="your_cloudflare_api_key" --domain="yourdomain.com"`
|
|
145
|
-
|
|
146
|
-
## Changelog
|
|
147
|
-
|
|
148
|
-
### v2.0.1
|
|
149
|
-
|
|
150
|
-
- ⬆️ Update Dependencies
|
|
151
|
-
|
|
152
|
-
### v2.0.0
|
|
153
|
-
|
|
154
|
-
- ♻️ **Rewrite Cloudflare-DDNS-Sync in Typescript**
|
|
155
|
-
|
|
156
|
-
### v1.5.4
|
|
157
|
-
|
|
158
|
-
- 📝 Update README
|
|
159
|
-
|
|
160
|
-
### v1.5.3
|
|
161
|
-
|
|
162
|
-
- 🐛 **Fix Stopping Sync On IP Change**
|
|
163
|
-
- 🐛 **Fix Crontime Converter For Hour**
|
|
164
|
-
- 🐛 **Fix Syncing With Crontime Without Setting an IP**
|
|
165
|
-
- ⚡️ **Small Performance Improvements**
|
|
166
|
-
- 💄 Improve Code Quality
|
|
167
|
-
- 💄 Extract Business Rules
|
|
168
|
-
|
|
169
|
-
### v1.5.2
|
|
170
|
-
|
|
171
|
-
- 🐛 Fix Wrong Using of Const
|
|
172
|
-
|
|
173
|
-
### v1.5.1
|
|
174
|
-
|
|
175
|
-
- 🐛 **Fix Bug When 'public-ip' Throws An Error**
|
|
176
|
-
- 💄 Improve Code Quality
|
|
177
|
-
|
|
178
|
-
### v1.5.0
|
|
179
|
-
|
|
180
|
-
- ✨ **Add Create Not Existing Records Functionality**
|
|
181
|
-
- ✨ **Add Fallback getIp Function**
|
|
182
|
-
|
|
183
|
-
### v1.4.0
|
|
184
|
-
|
|
185
|
-
- ✨ **Add Stop SyncOnIpChange Function**
|
|
186
|
-
|
|
187
|
-
### v1.3.4
|
|
188
|
-
|
|
189
|
-
- 🐛 **Fix ipChange Interval**
|
|
190
|
-
|
|
191
|
-
### v1.3.3
|
|
192
|
-
|
|
193
|
-
- 📝 **Add Changelog**
|
|
194
|
-
|
|
195
|
-
### v1.3.2
|
|
196
|
-
|
|
197
|
-
- ✨ **Add getRecordIps Function**
|
|
198
|
-
- ♻️ **Refactor syncOnIpChange**
|
|
199
|
-
- ✅ **Add Tests for Sync Functionality**
|
|
200
|
-
- 📝 **Improve README**
|
|
201
|
-
- 📝 **Add NPM Badge**
|
|
202
|
-
- 🔥 Remove Unnecessary Code
|
|
203
|
-
- 🚸 Update Description
|
|
204
|
-
- 🎨 Order Dependencies
|
|
205
|
-
- 🎨 Lint files
|
|
206
|
-
|
|
207
|
-
### v1.3.1
|
|
208
|
-
|
|
209
|
-
- 📝 **Fix README**
|
|
210
|
-
|
|
211
|
-
### v1.3.0
|
|
212
|
-
|
|
213
|
-
- ✨ **Add syncAtDate Function**
|
|
214
|
-
- 📝 **Improve README**
|
|
215
|
-
|
|
216
|
-
### v1.2.1
|
|
217
|
-
|
|
218
|
-
- 🚑 **Fix README**
|
|
219
|
-
|
|
220
|
-
### v1.2.0
|
|
221
|
-
|
|
222
|
-
- ✨ **Add onIpChange Function**
|
|
223
|
-
- 📝 **Adjust README**
|
|
224
|
-
- 🚚 Move Utils to Lib
|
|
225
|
-
- 🚚 Move IP Functions to Lib
|
|
226
|
-
- 🚚 Move Dependencies to devDependencies
|
|
227
|
-
- 🔧 Update Eslint Config
|
|
228
|
-
- 🎨 Fix Eslit Error
|
|
229
|
-
|
|
230
|
-
### v1.1.0
|
|
231
|
-
|
|
232
|
-
- ⚡️ **Improve Performance of First Sync**
|
|
233
|
-
- ✨ **Add Schedule Functionality**
|
|
234
|
-
- 🚸 **Improve Success Message of Sync Function**
|
|
235
|
-
- 📝 **Fix Typos in README**
|
|
236
|
-
- 📝 **Add Schedule Functions to README**
|
|
237
|
-
- ✅ Add Unittests
|
|
238
|
-
- ✨ Add Eslint
|
|
239
|
-
|
|
240
|
-
### v1.0.3
|
|
241
|
-
|
|
242
|
-
- 📝 **Fix Typo in README**
|
|
243
|
-
- 📄 Add LICENSE
|
|
244
|
-
|
|
245
|
-
### v1.0.2
|
|
246
|
-
|
|
247
|
-
- 📝 **Add "Get Your Cloudflare Api Key"-Section to README**
|
|
248
|
-
|
|
249
|
-
### v1.0.1
|
|
250
|
-
|
|
251
|
-
- ✨ **Add sync Function -> changes the IP of the configured DNS records on Cloudflare**
|
|
252
|
-
- ✨ **Add getIp Function -> returns the external IP**
|
|
253
|
-
- 📝 **Add README**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare type ZoneMap = Map<string, string>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare type RecordData = {
|
|
2
|
+
id: string;
|
|
3
|
+
type: string;
|
|
4
|
+
name: string;
|
|
5
|
+
content: string;
|
|
6
|
+
proxiable: boolean;
|
|
7
|
+
proxied: boolean;
|
|
8
|
+
ttl: number;
|
|
9
|
+
locked: boolean;
|
|
10
|
+
zone_id: string;
|
|
11
|
+
zone_name: string;
|
|
12
|
+
modified_on: string;
|
|
13
|
+
created_on: string;
|
|
14
|
+
meta: RecordMetaData;
|
|
15
|
+
};
|
|
16
|
+
export declare type RecordMetaData = {
|
|
17
|
+
auto_added: boolean;
|
|
18
|
+
managed_by_apps: boolean;
|
|
19
|
+
managed_by_argo_tunnel: boolean;
|
|
20
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare 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
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./RecordData"), exports);
|
|
14
|
+
__exportStar(require("./ZoneData"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./cloudflare/index"), exports);
|
|
14
|
+
__exportStar(require("./Callbacks"), exports);
|
|
15
|
+
__exportStar(require("./DomainRecordList"), exports);
|
|
16
|
+
__exportStar(require("./Record"), exports);
|
|
17
|
+
__exportStar(require("./ZoneMap"), exports);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ScheduledTask } from 'node-cron';
|
|
2
|
+
import { DomainRecordList, MultiSyncCallback, Record, RecordData } from './contracts/index';
|
|
3
|
+
export default class CloudflareDDNSSync {
|
|
4
|
+
private cloudflareClient;
|
|
5
|
+
constructor(email: string, authKey: string);
|
|
6
|
+
getIp(): Promise<string>;
|
|
7
|
+
getIpv6(): Promise<string>;
|
|
8
|
+
getRecordDataForDomain(domain: string): Promise<Array<RecordData>>;
|
|
9
|
+
getRecordDataForDomains(domains: Array<string>): Promise<DomainRecordList>;
|
|
10
|
+
getRecordDataForRecord(record: Record): Promise<RecordData>;
|
|
11
|
+
getRecordDataForRecords(records: Array<Record>): Promise<Array<RecordData>>;
|
|
12
|
+
removeRecord(recordName: string, recordType?: string): Promise<void>;
|
|
13
|
+
stopSyncOnIpChange(changeListenerId: string): void;
|
|
14
|
+
syncByCronTime(cronExpression: string, records: Array<Record>, callback: MultiSyncCallback, ip?: string): ScheduledTask;
|
|
15
|
+
syncOnIpChange(records: Array<Record>, callback: MultiSyncCallback): Promise<string>;
|
|
16
|
+
syncRecord(record: Record, ip?: string): Promise<RecordData>;
|
|
17
|
+
syncRecords(records: Array<Record>, ip?: string): Promise<Array<RecordData>>;
|
|
18
|
+
}
|
|
19
|
+
export * from './contracts/index';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
const cloudflare_client_1 = __importDefault(require("./lib/cloudflare-client"));
|
|
17
|
+
const cron_1 = __importDefault(require("./lib/cron"));
|
|
18
|
+
const ip_utils_1 = __importDefault(require("./lib/ip-utils"));
|
|
19
|
+
class CloudflareDDNSSync {
|
|
20
|
+
cloudflareClient;
|
|
21
|
+
constructor(email, authKey) {
|
|
22
|
+
this.cloudflareClient = new cloudflare_client_1.default(email, authKey);
|
|
23
|
+
}
|
|
24
|
+
getIp() {
|
|
25
|
+
return ip_utils_1.default.getIpv4();
|
|
26
|
+
}
|
|
27
|
+
getIpv6() {
|
|
28
|
+
return ip_utils_1.default.getIpv6();
|
|
29
|
+
}
|
|
30
|
+
getRecordDataForDomain(domain) {
|
|
31
|
+
return this.cloudflareClient.getRecordDataForDomain(domain);
|
|
32
|
+
}
|
|
33
|
+
getRecordDataForDomains(domains) {
|
|
34
|
+
return this.cloudflareClient.getRecordDataForDomains(domains);
|
|
35
|
+
}
|
|
36
|
+
getRecordDataForRecord(record) {
|
|
37
|
+
return this.cloudflareClient.getRecordDataForRecord(record);
|
|
38
|
+
}
|
|
39
|
+
getRecordDataForRecords(records) {
|
|
40
|
+
return this.cloudflareClient.getRecordDataForRecords(records);
|
|
41
|
+
}
|
|
42
|
+
removeRecord(recordName, recordType) {
|
|
43
|
+
return this.cloudflareClient.removeRecordByNameAndType(recordName, recordType);
|
|
44
|
+
}
|
|
45
|
+
stopSyncOnIpChange(changeListenerId) {
|
|
46
|
+
ip_utils_1.default.removeIpChangeListener(changeListenerId);
|
|
47
|
+
}
|
|
48
|
+
syncByCronTime(cronExpression, records, callback, ip) {
|
|
49
|
+
return cron_1.default.createCronJob(cronExpression, async () => {
|
|
50
|
+
const result = await this.syncRecords(records, ip);
|
|
51
|
+
callback(result);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async syncOnIpChange(records, callback) {
|
|
55
|
+
const changeListenerId = await ip_utils_1.default.addIpChangeListener(async (ip) => {
|
|
56
|
+
const result = await this.syncRecords(records, ip);
|
|
57
|
+
callback(result);
|
|
58
|
+
});
|
|
59
|
+
// Sync records to make sure the current ip is already set.
|
|
60
|
+
const currentIp = await ip_utils_1.default.getIpv4();
|
|
61
|
+
this.syncRecords(records, currentIp).then((syncedRecords) => {
|
|
62
|
+
callback(syncedRecords);
|
|
63
|
+
});
|
|
64
|
+
return changeListenerId;
|
|
65
|
+
}
|
|
66
|
+
syncRecord(record, ip) {
|
|
67
|
+
return this.cloudflareClient.syncRecord(record, ip);
|
|
68
|
+
}
|
|
69
|
+
syncRecords(records, ip) {
|
|
70
|
+
return this.cloudflareClient.syncRecords(records, ip);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.default = CloudflareDDNSSync;
|
|
74
|
+
__exportStar(require("./contracts/index"), exports);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DomainRecordList, Record, RecordData } from '../contracts';
|
|
2
|
+
export default class CloudflareClient {
|
|
3
|
+
private cloudflare;
|
|
4
|
+
private zoneMap;
|
|
5
|
+
constructor(email: string, authKey: string);
|
|
6
|
+
syncRecord(record: Record, ip?: string): Promise<RecordData>;
|
|
7
|
+
syncRecords(records: Array<Record>, ip?: string): Promise<Array<RecordData>>;
|
|
8
|
+
removeRecordByNameAndType(recordName: string, recordType?: string): Promise<void>;
|
|
9
|
+
getRecordDataForRecord(record: Record): Promise<RecordData>;
|
|
10
|
+
getRecordDataForRecords(records: Array<Record>): Promise<Array<RecordData>>;
|
|
11
|
+
getRecordDataForDomains(domains: Array<string>): Promise<DomainRecordList>;
|
|
12
|
+
getRecordDataForDomain(domain: string): Promise<Array<RecordData>>;
|
|
13
|
+
private createRecord;
|
|
14
|
+
private updateRecord;
|
|
15
|
+
private updateZoneMap;
|
|
16
|
+
private getRecordIdByNameAndType;
|
|
17
|
+
private getZoneIdByRecordName;
|
|
18
|
+
private getRecordByNameAndType;
|
|
19
|
+
private getRecordIdsForRecords;
|
|
20
|
+
private getRecordIdMapKey;
|
|
21
|
+
private getRecordsByDomain;
|
|
22
|
+
private getZoneIdByDomain;
|
|
23
|
+
private getDomainsFromRecords;
|
|
24
|
+
private getDomainByRecordName;
|
|
25
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const parse_domain_1 = require("parse-domain");
|
|
7
|
+
const cloudflare_1 = __importDefault(require("cloudflare"));
|
|
8
|
+
const ip_utils_1 = __importDefault(require("./ip-utils"));
|
|
9
|
+
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;
|
|
10
|
+
// eslint-disable-next-line max-len
|
|
11
|
+
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;
|
|
12
|
+
class CloudflareClient {
|
|
13
|
+
cloudflare;
|
|
14
|
+
zoneMap = new Map();
|
|
15
|
+
constructor(email, authKey) {
|
|
16
|
+
this.cloudflare = new cloudflare_1.default({
|
|
17
|
+
email: email,
|
|
18
|
+
key: authKey,
|
|
19
|
+
});
|
|
20
|
+
this.updateZoneMap();
|
|
21
|
+
}
|
|
22
|
+
async syncRecord(record, ip) {
|
|
23
|
+
const recordIds = await this.getRecordIdsForRecords([record]);
|
|
24
|
+
const ipToUse = ip ? ip : await ip_utils_1.default.getIpv4();
|
|
25
|
+
const zoneId = await this.getZoneIdByRecordName(record.name);
|
|
26
|
+
const recordId = recordIds.get(this.getRecordIdMapKey(record));
|
|
27
|
+
const recordExists = recordId !== undefined;
|
|
28
|
+
if (recordExists) {
|
|
29
|
+
const result = await this.updateRecord(zoneId, recordId, record, ipToUse);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
const result = await this.createRecord(zoneId, record, ipToUse);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
async syncRecords(records, ip) {
|
|
36
|
+
const recordIds = await this.getRecordIdsForRecords(records);
|
|
37
|
+
const ipToUse = ip ? ip : await ip_utils_1.default.getIpv4();
|
|
38
|
+
const resultPromises = records.map(async (record) => {
|
|
39
|
+
const zoneId = await this.getZoneIdByRecordName(record.name);
|
|
40
|
+
const recordId = recordIds.get(this.getRecordIdMapKey(record));
|
|
41
|
+
const recordExists = recordId !== undefined;
|
|
42
|
+
if (recordExists) {
|
|
43
|
+
const currentResult = await this.updateRecord(zoneId, recordId, record, ipToUse);
|
|
44
|
+
return currentResult;
|
|
45
|
+
}
|
|
46
|
+
const currentResult = await this.createRecord(zoneId, record, ipToUse);
|
|
47
|
+
return currentResult;
|
|
48
|
+
});
|
|
49
|
+
const results = await Promise.all(resultPromises);
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
async removeRecordByNameAndType(recordName, recordType) {
|
|
53
|
+
const recordTypeToUse = recordType ? recordType : 'A';
|
|
54
|
+
const zoneId = await this.getZoneIdByRecordName(recordName);
|
|
55
|
+
const recordId = await this.getRecordIdByNameAndType(recordName, recordTypeToUse);
|
|
56
|
+
return this.cloudflare.dnsRecords.del(zoneId, recordId);
|
|
57
|
+
}
|
|
58
|
+
async getRecordDataForRecord(record) {
|
|
59
|
+
const domain = this.getDomainByRecordName(record.name);
|
|
60
|
+
const recordDataForDomain = await this.getRecordsByDomain(domain);
|
|
61
|
+
const recordData = recordDataForDomain.find((singleRecordData) => record.name.toLowerCase() === singleRecordData.name.toLowerCase());
|
|
62
|
+
return recordData;
|
|
63
|
+
}
|
|
64
|
+
async getRecordDataForRecords(records) {
|
|
65
|
+
const domains = this.getDomainsFromRecords(records);
|
|
66
|
+
const recordDataPromises = domains.map(async (domain) => {
|
|
67
|
+
const recordDataForDomain = await this.getRecordsByDomain(domain);
|
|
68
|
+
const recordDataForDomainFilteredByRecords = recordDataForDomain
|
|
69
|
+
.filter((singleRecordData) => records
|
|
70
|
+
.some((record) => record.name.toLowerCase() === singleRecordData.name.toLowerCase()));
|
|
71
|
+
return recordDataForDomainFilteredByRecords;
|
|
72
|
+
});
|
|
73
|
+
const recordDataForDomains = await Promise.all(recordDataPromises);
|
|
74
|
+
const recordData = [].concat(...recordDataForDomains);
|
|
75
|
+
return recordData;
|
|
76
|
+
}
|
|
77
|
+
async getRecordDataForDomains(domains) {
|
|
78
|
+
const recordDataPromises = domains.map((domain) => this.getRecordDataForDomain(domain));
|
|
79
|
+
const recordDataForDomains = await Promise.all(recordDataPromises);
|
|
80
|
+
const recordData = {};
|
|
81
|
+
recordDataForDomains.forEach((recordDataForDomain, index) => {
|
|
82
|
+
recordData[domains[index]] = recordDataForDomain;
|
|
83
|
+
});
|
|
84
|
+
return recordData;
|
|
85
|
+
}
|
|
86
|
+
async getRecordDataForDomain(domain) {
|
|
87
|
+
const recordData = await this.getRecordsByDomain(domain);
|
|
88
|
+
return recordData;
|
|
89
|
+
}
|
|
90
|
+
async createRecord(zoneId, record, ip) {
|
|
91
|
+
const copyOfRecord = { ...record };
|
|
92
|
+
copyOfRecord.name = record.name.toLowerCase();
|
|
93
|
+
copyOfRecord.content = copyOfRecord.content ? copyOfRecord.content : ip;
|
|
94
|
+
copyOfRecord.type = copyOfRecord.type ? copyOfRecord.type : 'A';
|
|
95
|
+
if (!copyOfRecord.content) {
|
|
96
|
+
throw Error(`Could not create Record "${copyOfRecord.name}": Content is missing!`);
|
|
97
|
+
}
|
|
98
|
+
if (copyOfRecord.type === 'A') {
|
|
99
|
+
if (!copyOfRecord.content.match(ipv4Regex)) {
|
|
100
|
+
throw Error(`Could not create Record "${copyOfRecord.name}": '${copyOfRecord.content}' is not a valid ipv4!`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (copyOfRecord.type === 'AAAA') {
|
|
104
|
+
if (!copyOfRecord.content.match(ipv6Regex)) {
|
|
105
|
+
throw Error(`Could not create Record "${copyOfRecord.name}": '${copyOfRecord.content}' is not a valid ipv6!`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (copyOfRecord.type === 'CNAME') {
|
|
109
|
+
const parsedDomain = (0, parse_domain_1.parseDomain)((0, parse_domain_1.fromUrl)(copyOfRecord.content));
|
|
110
|
+
if (parsedDomain.type !== parse_domain_1.ParseResultType.Listed || !parsedDomain.domain) {
|
|
111
|
+
throw Error(`Could not create Record "${copyOfRecord.name}": '${copyOfRecord.content}' is not a valid domain name!`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const response = await this.cloudflare.dnsRecords.add(zoneId, copyOfRecord);
|
|
115
|
+
return response.result;
|
|
116
|
+
}
|
|
117
|
+
async updateRecord(zoneId, recordId, record, ip) {
|
|
118
|
+
const copyOfRecord = { ...record };
|
|
119
|
+
copyOfRecord.name = record.name.toLowerCase();
|
|
120
|
+
copyOfRecord.content = copyOfRecord.content ? copyOfRecord.content : ip;
|
|
121
|
+
copyOfRecord.type = copyOfRecord.type ? copyOfRecord.type : 'A';
|
|
122
|
+
if (!copyOfRecord.content) {
|
|
123
|
+
throw Error(`Could not update Record "${copyOfRecord.name}": Content is missing!`);
|
|
124
|
+
}
|
|
125
|
+
if (copyOfRecord.type === 'A') {
|
|
126
|
+
if (!copyOfRecord.content.match(ipv4Regex)) {
|
|
127
|
+
throw Error(`Could not update Record "${copyOfRecord.name}": '${copyOfRecord.content}' is not a valid ipv4!`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (copyOfRecord.type === 'AAAA') {
|
|
131
|
+
if (!copyOfRecord.content.match(ipv6Regex)) {
|
|
132
|
+
throw Error(`Could not update Record "${copyOfRecord.name}": '${copyOfRecord.content}' is not a valid ipv6!`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else if (copyOfRecord.type === 'CNAME') {
|
|
136
|
+
const parsedDomain = (0, parse_domain_1.parseDomain)((0, parse_domain_1.fromUrl)(copyOfRecord.content));
|
|
137
|
+
if (parsedDomain.type !== parse_domain_1.ParseResultType.Listed || !parsedDomain.domain) {
|
|
138
|
+
throw Error(`Could not update Record "${copyOfRecord.name}": '${copyOfRecord.content}' is not a valid domain name!`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const response = await this.cloudflare.dnsRecords.edit(zoneId, recordId, copyOfRecord);
|
|
142
|
+
return response.result;
|
|
143
|
+
}
|
|
144
|
+
async updateZoneMap() {
|
|
145
|
+
const response = await this.cloudflare.zones.browse();
|
|
146
|
+
const zones = response.result;
|
|
147
|
+
this.zoneMap = new Map();
|
|
148
|
+
for (const zone of zones) {
|
|
149
|
+
this.zoneMap.set(zone.name, zone.id);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async getRecordIdByNameAndType(recordName, recordType) {
|
|
153
|
+
const record = await this.getRecordByNameAndType(recordName, recordType);
|
|
154
|
+
return record.id;
|
|
155
|
+
}
|
|
156
|
+
getZoneIdByRecordName(recordName) {
|
|
157
|
+
const domain = this.getDomainByRecordName(recordName);
|
|
158
|
+
return this.getZoneIdByDomain(domain);
|
|
159
|
+
}
|
|
160
|
+
async getRecordByNameAndType(recordName, recordType) {
|
|
161
|
+
const domain = this.getDomainByRecordName(recordName);
|
|
162
|
+
const records = await this.getRecordsByDomain(domain);
|
|
163
|
+
const record = records.find((currentRecord) => currentRecord.name.toLowerCase() === recordName.toLowerCase()
|
|
164
|
+
&& currentRecord.type.toLowerCase() === recordType.toLowerCase());
|
|
165
|
+
const recordNotFound = record === undefined;
|
|
166
|
+
if (recordNotFound) {
|
|
167
|
+
throw new Error(`Record '${recordName}' not found.`);
|
|
168
|
+
}
|
|
169
|
+
return record;
|
|
170
|
+
}
|
|
171
|
+
async getRecordIdsForRecords(records) {
|
|
172
|
+
const recordIdMap = new Map();
|
|
173
|
+
const recordData = await this.getRecordDataForRecords(records);
|
|
174
|
+
for (const record of recordData) {
|
|
175
|
+
recordIdMap.set(this.getRecordIdMapKey(record), record.id);
|
|
176
|
+
}
|
|
177
|
+
return recordIdMap;
|
|
178
|
+
}
|
|
179
|
+
getRecordIdMapKey(record) {
|
|
180
|
+
const recordName = record.name.toLowerCase();
|
|
181
|
+
const recordType = record.type ? record.type.toLowerCase() : 'a';
|
|
182
|
+
return `"${recordName}"_"${recordType}"`;
|
|
183
|
+
}
|
|
184
|
+
async getRecordsByDomain(domain) {
|
|
185
|
+
const zoneId = await this.getZoneIdByDomain(domain);
|
|
186
|
+
const records = [];
|
|
187
|
+
let pageIndex = 1;
|
|
188
|
+
let allRecordsFound = false;
|
|
189
|
+
while (!allRecordsFound) {
|
|
190
|
+
const response = await this.cloudflare.dnsRecords.browse(zoneId, {
|
|
191
|
+
page: pageIndex,
|
|
192
|
+
per_page: 100,
|
|
193
|
+
});
|
|
194
|
+
records.push(...response.result);
|
|
195
|
+
allRecordsFound = response.result.length < 100;
|
|
196
|
+
pageIndex++;
|
|
197
|
+
}
|
|
198
|
+
return records;
|
|
199
|
+
}
|
|
200
|
+
async getZoneIdByDomain(domain) {
|
|
201
|
+
if (this.zoneMap.has(domain)) {
|
|
202
|
+
const zoneId = this.zoneMap.get(domain.toLowerCase());
|
|
203
|
+
return zoneId;
|
|
204
|
+
}
|
|
205
|
+
await this.updateZoneMap();
|
|
206
|
+
if (!this.zoneMap.has(domain)) {
|
|
207
|
+
throw new Error(`Could not find domain '${domain}'. Make sure the domain is set up for your cloudflare account.`);
|
|
208
|
+
}
|
|
209
|
+
const zoneId = this.zoneMap.get(domain.toLowerCase());
|
|
210
|
+
return zoneId;
|
|
211
|
+
}
|
|
212
|
+
getDomainsFromRecords(records) {
|
|
213
|
+
const domains = records
|
|
214
|
+
.map((record) => this.getDomainByRecordName(record.name))
|
|
215
|
+
.filter((domain, index, domainList) => domainList.indexOf(domain.toLowerCase()) === index);
|
|
216
|
+
return domains;
|
|
217
|
+
}
|
|
218
|
+
getDomainByRecordName(recordName) {
|
|
219
|
+
const parsedDomain = (0, parse_domain_1.parseDomain)((0, parse_domain_1.fromUrl)(recordName));
|
|
220
|
+
if (parsedDomain.type !== parse_domain_1.ParseResultType.Listed || !parsedDomain.domain) {
|
|
221
|
+
throw new Error(`Could not parse domain. '${JSON.stringify(recordName)}' is not a valid record name.`);
|
|
222
|
+
}
|
|
223
|
+
let domain = '';
|
|
224
|
+
domain += parsedDomain.domain;
|
|
225
|
+
for (const tld of parsedDomain.topLevelDomains) {
|
|
226
|
+
domain += `.${tld}`;
|
|
227
|
+
}
|
|
228
|
+
return domain.toLowerCase();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.default = CloudflareClient;
|
package/dist/lib/cron.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_cron_1 = __importDefault(require("node-cron"));
|
|
7
|
+
class Cron {
|
|
8
|
+
static createCronJob(cronExpression, callback) {
|
|
9
|
+
const cronExpressionIsInvalid = !this.isValid(cronExpression);
|
|
10
|
+
if (cronExpressionIsInvalid) {
|
|
11
|
+
// eslint-disable-next-line max-len
|
|
12
|
+
throw new Error(`'${cronExpression}' is not a valid cron expression.\nHere you can see how cron expressions work: https://cddnss.knaup.dev/cron-expression-syntax`);
|
|
13
|
+
}
|
|
14
|
+
return node_cron_1.default.schedule(cronExpression, () => {
|
|
15
|
+
callback();
|
|
16
|
+
}, undefined);
|
|
17
|
+
}
|
|
18
|
+
static isValid(cronExpression) {
|
|
19
|
+
return node_cron_1.default.validate(cronExpression);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.default = Cron;
|
|
@@ -0,0 +1,9 @@
|
|
|
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 getRandomId;
|
|
9
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
const wimIp = __importStar(require("what-is-my-ip-address"));
|
|
26
|
+
const public_ip_1 = __importDefault(require("public-ip"));
|
|
27
|
+
class IPUtils {
|
|
28
|
+
static ipPollingDelay = 10 * 1000;
|
|
29
|
+
static ipChangeEventListeners = new Map();
|
|
30
|
+
static async getIpv4() {
|
|
31
|
+
/* c8 ignore start*/
|
|
32
|
+
try {
|
|
33
|
+
return await public_ip_1.default.v4();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return wimIp.v4();
|
|
37
|
+
}
|
|
38
|
+
/* c8 ignore stop*/
|
|
39
|
+
}
|
|
40
|
+
static async getIpv6() {
|
|
41
|
+
/* c8 ignore start */
|
|
42
|
+
try {
|
|
43
|
+
return await public_ip_1.default.v6();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return wimIp.v6();
|
|
47
|
+
}
|
|
48
|
+
/* c8 ignore stop*/
|
|
49
|
+
}
|
|
50
|
+
static async addIpChangeListener(callback) {
|
|
51
|
+
const eventListenerId = this.getRandomId();
|
|
52
|
+
let previousIp = await this.getIpv4();
|
|
53
|
+
/* c8 ignore start */
|
|
54
|
+
const intervalId = setInterval(async () => {
|
|
55
|
+
const currentIp = await this.getIpv4();
|
|
56
|
+
const ipMustBeUpdated = currentIp !== previousIp;
|
|
57
|
+
if (ipMustBeUpdated) {
|
|
58
|
+
previousIp = currentIp;
|
|
59
|
+
callback(currentIp);
|
|
60
|
+
}
|
|
61
|
+
}, this.ipPollingDelay);
|
|
62
|
+
/* c8 ignore stop*/
|
|
63
|
+
this.ipChangeEventListeners.set(eventListenerId, intervalId);
|
|
64
|
+
return eventListenerId;
|
|
65
|
+
}
|
|
66
|
+
static removeIpChangeListener(eventListenerId) {
|
|
67
|
+
const eventListenerIntervalId = this.ipChangeEventListeners.get(eventListenerId);
|
|
68
|
+
clearInterval(eventListenerIntervalId);
|
|
69
|
+
IPUtils.ipChangeEventListeners.delete(eventListenerId);
|
|
70
|
+
}
|
|
71
|
+
static getRandomId() {
|
|
72
|
+
const beginningRandomString = Math.random().toString(36)
|
|
73
|
+
.substr(2);
|
|
74
|
+
const currentDateAsString = new Date().valueOf()
|
|
75
|
+
.toString(36);
|
|
76
|
+
const endingRandomString = Math.random().toString(36)
|
|
77
|
+
.substr(2);
|
|
78
|
+
const randomString = beginningRandomString + currentDateAsString + endingRandomString;
|
|
79
|
+
return randomString;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.default = IPUtils;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-ddns-sync",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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>",
|
|
@@ -13,37 +13,41 @@
|
|
|
13
13
|
"dyndns",
|
|
14
14
|
"sync"
|
|
15
15
|
],
|
|
16
|
+
"homepage": "https://cddnss.knaup.dev/",
|
|
16
17
|
"repository": {
|
|
17
18
|
"type": "git",
|
|
18
19
|
"url": "git://github.com:Steffen982/cloudflare-ddns-sync.git"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
22
|
"build": "tsc",
|
|
22
|
-
"lint": "
|
|
23
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
24
|
+
"lint:fix": "eslint --fix \"src/**/*.ts\"",
|
|
23
25
|
"test": "c8 mocha dist/tests/*.js --timeout 15000 --exit",
|
|
24
|
-
"test
|
|
25
|
-
"test
|
|
26
|
-
"test
|
|
26
|
+
"test:coverage": "npm run test:coverage-check && npm run test:coverage-report",
|
|
27
|
+
"test:coverage-check": "c8 check-coverage --lines 70 --functions 70 --branches 70",
|
|
28
|
+
"test:coverage-report": "c8 report"
|
|
27
29
|
},
|
|
28
30
|
"dependencies": {
|
|
29
|
-
"cloudflare": "2.
|
|
30
|
-
"node-cron": "
|
|
31
|
-
"parse-domain": "
|
|
32
|
-
"public-ip": "4.0.
|
|
31
|
+
"cloudflare": "2.9.1",
|
|
32
|
+
"node-cron": "3.0.0",
|
|
33
|
+
"parse-domain": "4.1.0",
|
|
34
|
+
"public-ip": "4.0.4",
|
|
33
35
|
"what-is-my-ip-address": "1.0.3"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
|
-
"@types/chai": "4.
|
|
37
|
-
"@types/minimist": "1.2.
|
|
38
|
-
"@types/mocha": "
|
|
39
|
-
"@types/node": "
|
|
40
|
-
"@types/node-cron": "
|
|
41
|
-
"
|
|
42
|
-
"
|
|
38
|
+
"@types/chai": "4.3.0",
|
|
39
|
+
"@types/minimist": "1.2.2",
|
|
40
|
+
"@types/mocha": "9.0.0",
|
|
41
|
+
"@types/node": "16.11.20",
|
|
42
|
+
"@types/node-cron": "3.0.1",
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "5.10.0",
|
|
44
|
+
"@typescript-eslint/parser": "5.10.0",
|
|
45
|
+
"c8": "7.11.0",
|
|
46
|
+
"chai": "4.3.4",
|
|
47
|
+
"eslint": "8.7.0",
|
|
43
48
|
"minimist": "1.2.5",
|
|
44
|
-
"mocha": "
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"typescript": "3.9.5"
|
|
49
|
+
"mocha": "9.1.4",
|
|
50
|
+
"tslint": "6.1.3",
|
|
51
|
+
"typescript": "4.5.4"
|
|
48
52
|
}
|
|
49
53
|
}
|