mirror of
https://github.com/qdm12/ddns-updater.git
synced 2026-04-23 17:42:32 -04:00
Compare commits
569 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b53164a8fc | ||
|
|
df18c42301 | ||
|
|
2a474a292b | ||
|
|
e8af269660 | ||
|
|
d93186d0da | ||
|
|
88b151e320 | ||
|
|
a49deb85bb | ||
|
|
86f3737081 | ||
|
|
e11f7263e5 | ||
|
|
133c9b8c3c | ||
|
|
5ea242b0d7 | ||
|
|
1b21f542cc | ||
|
|
7555b7ae39 | ||
|
|
dece29d46a | ||
|
|
02a0ddd7c1 | ||
|
|
a3a89a112c | ||
|
|
34a7f28905 | ||
|
|
39bfd2e0d8 | ||
|
|
a55ea6eb4d | ||
|
|
2081578060 | ||
|
|
83aa2fd8fb | ||
|
|
9bafa5a4ce | ||
|
|
fe811a53bc | ||
|
|
509f5da273 | ||
|
|
982de66c2c | ||
|
|
c5ff9ec308 | ||
|
|
7c4045c538 | ||
|
|
730e2dda36 | ||
|
|
7ff11a705c | ||
|
|
ec46ffa140 | ||
|
|
486b4a1201 | ||
|
|
d0562e2a8c | ||
|
|
246e3f1b70 | ||
|
|
9f7ebbd99e | ||
|
|
7e47fe314f | ||
|
|
4fb8e92b9e | ||
|
|
189ad1f71d | ||
|
|
48b40e2dcc | ||
|
|
347d144b58 | ||
|
|
18af1bbde7 | ||
|
|
40f8009e01 | ||
|
|
f04b830420 | ||
|
|
3ab32f83c5 | ||
|
|
61e282f959 | ||
|
|
f2adf3fc20 | ||
|
|
01d3cd1383 | ||
|
|
2455055d20 | ||
|
|
7e2b4f568f | ||
|
|
d898c654c9 | ||
|
|
c70265a6d3 | ||
|
|
487873ed57 | ||
|
|
b148344096 | ||
|
|
4c91ef0ef8 | ||
|
|
18a5493345 | ||
|
|
de2e675ed2 | ||
|
|
177a9a2d76 | ||
|
|
de142bbd30 | ||
|
|
eed96495bd | ||
|
|
43891cf8a6 | ||
|
|
aa0b136a7b | ||
|
|
3dbdc339bc | ||
|
|
10cfe67060 | ||
|
|
212841c651 | ||
|
|
b2a446b8c1 | ||
|
|
cdaf05e4ab | ||
|
|
95bcce2809 | ||
|
|
3e63897769 | ||
|
|
20ac110753 | ||
|
|
78f30614b1 | ||
|
|
03154c35f8 | ||
|
|
be6679273f | ||
|
|
6b61b8c6b8 | ||
|
|
b9227d5f9d | ||
|
|
395686bb47 | ||
|
|
edb5cd85b1 | ||
|
|
71191fa707 | ||
|
|
89564b763b | ||
|
|
6198b48a68 | ||
|
|
9ba1633300 | ||
|
|
6c3490f5c5 | ||
|
|
56370f9ef2 | ||
|
|
8d66333415 | ||
|
|
a0b9ed5dcb | ||
|
|
b3a992c8f4 | ||
|
|
c085cdae2c | ||
|
|
8b15e35575 | ||
|
|
e28b86f9c0 | ||
|
|
a6f6f48df3 | ||
|
|
c5041c27e2 | ||
|
|
1138de5446 | ||
|
|
9a62043ff6 | ||
|
|
c23da1518e | ||
|
|
5c9d04eae4 | ||
|
|
80670048ed | ||
|
|
d221522e02 | ||
|
|
9caf1bef53 | ||
|
|
46e1edb8f2 | ||
|
|
bb231bc736 | ||
|
|
949dcd9a30 | ||
|
|
bad113b290 | ||
|
|
691ed320fd | ||
|
|
8b5da2c580 | ||
|
|
05d566c807 | ||
|
|
4c7c794494 | ||
|
|
777a5e224f | ||
|
|
2451609cdd | ||
|
|
75f342c9ee | ||
|
|
03f5d0b372 | ||
|
|
5e4bd1611a | ||
|
|
e177826ae8 | ||
|
|
6a683f5161 | ||
|
|
2f44cb2afd | ||
|
|
8747f4bd01 | ||
|
|
74077dffac | ||
|
|
118132f238 | ||
|
|
94e008dc19 | ||
|
|
4254600bcf | ||
|
|
e10d778e5f | ||
|
|
c16287e48a | ||
|
|
0674864e54 | ||
|
|
039396b68c | ||
|
|
e46f2f2965 | ||
|
|
4ccd53da4d | ||
|
|
4653a484ef | ||
|
|
71139938f0 | ||
|
|
b3b4dad0a2 | ||
|
|
d5515cfb2c | ||
|
|
ab22578c9e | ||
|
|
623cb536e1 | ||
|
|
13035511bb | ||
|
|
e019e371ea | ||
|
|
c14324153c | ||
|
|
0c77340887 | ||
|
|
9787957b94 | ||
|
|
3a6262ef2c | ||
|
|
6b9ed56b18 | ||
|
|
8c1b3e556c | ||
|
|
1627254667 | ||
|
|
a8edd5ccc3 | ||
|
|
4cdc052577 | ||
|
|
bc272e079e | ||
|
|
c7dbbcbaa0 | ||
|
|
8f456977be | ||
|
|
425da967a2 | ||
|
|
1cd57d655e | ||
|
|
d04c67c7ab | ||
|
|
918df24488 | ||
|
|
6d70ca078c | ||
|
|
9f0ca6ceaa | ||
|
|
7ad0d8dc57 | ||
|
|
5b800cf278 | ||
|
|
1d6053e528 | ||
|
|
c345a788e3 | ||
|
|
158fed7c51 | ||
|
|
07d7645d78 | ||
|
|
55d8c0d703 | ||
|
|
db07ed3759 | ||
|
|
dbd2f79760 | ||
|
|
da4791e2db | ||
|
|
32fafeca95 | ||
|
|
711b1acfc7 | ||
|
|
339f5001e1 | ||
|
|
d37f05766b | ||
|
|
ca85596e19 | ||
|
|
cf184070b8 | ||
|
|
012a6dddcd | ||
|
|
d3b689d0ef | ||
|
|
a1a9fc0b62 | ||
|
|
2f88d44af7 | ||
|
|
5d74d11835 | ||
|
|
2f2bef3d6c | ||
|
|
3494cfbcbb | ||
|
|
b7986dd6b9 | ||
|
|
3644e5cc21 | ||
|
|
2f252a3ec1 | ||
|
|
270014ccda | ||
|
|
b70a91582c | ||
|
|
5b384cbf18 | ||
|
|
ff3b661d66 | ||
|
|
b3134c7770 | ||
|
|
b9bbbf26d2 | ||
|
|
dfd352b817 | ||
|
|
a84a1d664a | ||
|
|
25472f43c3 | ||
|
|
aa4a1f3813 | ||
|
|
76afd8361e | ||
|
|
cc995a79c8 | ||
|
|
776206eec8 | ||
|
|
c1bf7a49c1 | ||
|
|
987138dfc1 | ||
|
|
85780dc6e1 | ||
|
|
130ab008b5 | ||
|
|
20792e9460 | ||
|
|
8e802d45ae | ||
|
|
2b02ac154e | ||
|
|
8e09cd6342 | ||
|
|
542e89536c | ||
|
|
093e8154f3 | ||
|
|
b131c3d90b | ||
|
|
d3541da812 | ||
|
|
937a249ffa | ||
|
|
11575ee82a | ||
|
|
ae4ab39421 | ||
|
|
3e6336fcbc | ||
|
|
6d4bb626aa | ||
|
|
9b142c99dc | ||
|
|
7d627c8581 | ||
|
|
b72711b0de | ||
|
|
8f34c766c5 | ||
|
|
ae2bcd55c8 | ||
|
|
dcc66fb857 | ||
|
|
b31d848d96 | ||
|
|
99d670f7f9 | ||
|
|
a5cc9da33c | ||
|
|
877531c3d9 | ||
|
|
bfdae74925 | ||
|
|
c09e01c6c7 | ||
|
|
6a6b1a8ebb | ||
|
|
346f4aa7f7 | ||
|
|
5e0e3f4702 | ||
|
|
d009ef3d3e | ||
|
|
1706443ecb | ||
|
|
ec4411e12d | ||
|
|
1697697b81 | ||
|
|
cb3075ea32 | ||
|
|
4499d87e05 | ||
|
|
e676983dba | ||
|
|
5d550980c1 | ||
|
|
983f41743d | ||
|
|
7bbc860225 | ||
|
|
9835d5a5d7 | ||
|
|
463ad6ac93 | ||
|
|
133956f082 | ||
|
|
7aa14c8737 | ||
|
|
845770f441 | ||
|
|
8f709d7c90 | ||
|
|
2af4a1bf82 | ||
|
|
0bb717c55a | ||
|
|
d07ce88aa3 | ||
|
|
860820087f | ||
|
|
e793c4926b | ||
|
|
c338c28ce3 | ||
|
|
aa12ccc0b9 | ||
|
|
cfdc1f3dbe | ||
|
|
85b64cbeeb | ||
|
|
1c76c84bc3 | ||
|
|
2afec6c0ac | ||
|
|
7b573266e9 | ||
|
|
3107e00048 | ||
|
|
1c80aaa3ec | ||
|
|
3935b3640b | ||
|
|
0247d5ca0a | ||
|
|
c494662976 | ||
|
|
6f71982a7e | ||
|
|
a018b75609 | ||
|
|
e3ed94ff43 | ||
|
|
2aaf02537a | ||
|
|
f7db1ac7cf | ||
|
|
ebd852fcc1 | ||
|
|
443daad0fa | ||
|
|
b6e8f699a6 | ||
|
|
8d282cd6d9 | ||
|
|
ed0a0d07ff | ||
|
|
875cd027d7 | ||
|
|
dc2b7aaa0b | ||
|
|
816406dc41 | ||
|
|
621c3a13be | ||
|
|
bad0d3aeda | ||
|
|
25b8e02acc | ||
|
|
9b1c98ce49 | ||
|
|
9e6b7e5b99 | ||
|
|
00a9653e95 | ||
|
|
b771b5c9b5 | ||
|
|
67b83d402a | ||
|
|
fc500f7443 | ||
|
|
2658f609da | ||
|
|
f60f7212c3 | ||
|
|
eee8485543 | ||
|
|
5c166a6ab6 | ||
|
|
417a26282e | ||
|
|
8839db93dc | ||
|
|
7eee3fcccf | ||
|
|
8888694b9a | ||
|
|
b3cae52145 | ||
|
|
005c0c8f04 | ||
|
|
bc54ba5aca | ||
|
|
481321b5d3 | ||
|
|
115ce8faac | ||
|
|
e665684fa4 | ||
|
|
74168ad4ab | ||
|
|
7f0e858fa0 | ||
|
|
41de082ffc | ||
|
|
6521103359 | ||
|
|
af9d45702d | ||
|
|
f8bb927de6 | ||
|
|
0c561d4378 | ||
|
|
12c46e7635 | ||
|
|
c51a41e1a4 | ||
|
|
1ff838e0ae | ||
|
|
010634db28 | ||
|
|
ce792ac753 | ||
|
|
59d8791b8d | ||
|
|
c8ba7edcb7 | ||
|
|
8096944623 | ||
|
|
869b010853 | ||
|
|
46054fb631 | ||
|
|
28b2e121da | ||
|
|
37f473160c | ||
|
|
b1e71b7a77 | ||
|
|
d02fe17d55 | ||
|
|
f9f5d590c3 | ||
|
|
80d0755bfb | ||
|
|
3d619566c1 | ||
|
|
68b0dbf5f1 | ||
|
|
1a9cf0e0a6 | ||
|
|
706dcc7ba2 | ||
|
|
ec1ee7803b | ||
|
|
8dc39453e1 | ||
|
|
3e638326ed | ||
|
|
015d3fca55 | ||
|
|
0a41ff5b3b | ||
|
|
ab6908b8cc | ||
|
|
e100656d6f | ||
|
|
d36384744b | ||
|
|
1eaa51d871 | ||
|
|
343ab1cf96 | ||
|
|
bb807330bb | ||
|
|
f132a82c0a | ||
|
|
f16945e128 | ||
|
|
e97ced5608 | ||
|
|
f98f0874a0 | ||
|
|
e50b38b331 | ||
|
|
9d5c48dbf3 | ||
|
|
91741a5aad | ||
|
|
ff5767aa38 | ||
|
|
7ed63a036e | ||
|
|
cdae4fcced | ||
|
|
44c77ef848 | ||
|
|
45881d137b | ||
|
|
1c982f7d8d | ||
|
|
658fe5be2a | ||
|
|
679a1d1b70 | ||
|
|
ee495bbfea | ||
|
|
ba2e77798a | ||
|
|
301e4d6cc4 | ||
|
|
496781ac34 | ||
|
|
e7558b7e2b | ||
|
|
daa5edb56f | ||
|
|
6669b39d45 | ||
|
|
e635b19af6 | ||
|
|
c7c5468e5f | ||
|
|
13ccb4ffd9 | ||
|
|
7fac178b7d | ||
|
|
5c0b2012b7 | ||
|
|
a20cc6e1c2 | ||
|
|
5c3e407272 | ||
|
|
b92274347a | ||
|
|
7b3b6609fd | ||
|
|
6f75aa1457 | ||
|
|
27456b628d | ||
|
|
0c98229588 | ||
|
|
320d91d8e3 | ||
|
|
828373da7f | ||
|
|
336bf057ab | ||
|
|
5a353c1b66 | ||
|
|
a5e49eb866 | ||
|
|
e4bb82d316 | ||
|
|
3fdda01509 | ||
|
|
330fae1469 | ||
|
|
1033711ab4 | ||
|
|
f2b56afda7 | ||
|
|
c26b3fc0d3 | ||
|
|
99f83f3f12 | ||
|
|
9f6e9750c3 | ||
|
|
05473044a6 | ||
|
|
e5188906bf | ||
|
|
6e65c4f3a5 | ||
|
|
06b6288e58 | ||
|
|
4ac2bd7933 | ||
|
|
31374c3634 | ||
|
|
c98f8d6052 | ||
|
|
07cdce1fa2 | ||
|
|
4a6020558f | ||
|
|
09eb9e706d | ||
|
|
5455207852 | ||
|
|
feaac82c11 | ||
|
|
baacd052ce | ||
|
|
c12b7e50d7 | ||
|
|
954dffd3a7 | ||
|
|
204be2072e | ||
|
|
bfa6883f37 | ||
|
|
6513e5869a | ||
|
|
81b336fe07 | ||
|
|
83488e2020 | ||
|
|
4922b1db0b | ||
|
|
0f127bd58d | ||
|
|
9b5e0e1187 | ||
|
|
9d7244fade | ||
|
|
936bf4386e | ||
|
|
5676673e9e | ||
|
|
6a9ba7ecb4 | ||
|
|
2949112ad1 | ||
|
|
6d9568c7e5 | ||
|
|
0241e32e92 | ||
|
|
a9cb4ec63b | ||
|
|
3e4d359140 | ||
|
|
ac67611b06 | ||
|
|
aebe5988f1 | ||
|
|
b1eea74db1 | ||
|
|
6d018d920d | ||
|
|
88f98aebe4 | ||
|
|
9a4a268926 | ||
|
|
3e6fb5ead4 | ||
|
|
330d65dc8f | ||
|
|
baabc80b07 | ||
|
|
d3d6725021 | ||
|
|
e4fd4592fd | ||
|
|
dfcd97f6c1 | ||
|
|
619f626ae8 | ||
|
|
4b51c8b35b | ||
|
|
05b2535fee | ||
|
|
ec072d2862 | ||
|
|
57a928df92 | ||
|
|
62808c6d1c | ||
|
|
61ead94870 | ||
|
|
10b6b79c70 | ||
|
|
625b687b77 | ||
|
|
4028e8ade2 | ||
|
|
9ee74a3e75 | ||
|
|
35c8928bd4 | ||
|
|
d31318d1fe | ||
|
|
e41a39008e | ||
|
|
97ca03544a | ||
|
|
8631b4e8d5 | ||
|
|
a6e5053800 | ||
|
|
5f107ffc2c | ||
|
|
c39df2796f | ||
|
|
790ff8b78b | ||
|
|
39faaa771f | ||
|
|
a6f72d97bc | ||
|
|
5fff4f4a56 | ||
|
|
41d848f9a7 | ||
|
|
e42feb933f | ||
|
|
e7ebfc4b9a | ||
|
|
23043b73d9 | ||
|
|
4fb7526ce7 | ||
|
|
43d405cfd8 | ||
|
|
b0493e9c1c | ||
|
|
28509cfb36 | ||
|
|
522cc7c9c6 | ||
|
|
519b9c4380 | ||
|
|
464a557adb | ||
|
|
5ae4668296 | ||
|
|
934c67d710 | ||
|
|
9e6d456fc9 | ||
|
|
15c6ad4b01 | ||
|
|
3339c838fa | ||
|
|
18751973c1 | ||
|
|
49b779813a | ||
|
|
f779a2159e | ||
|
|
43f47fdee0 | ||
|
|
0e3703b8f2 | ||
|
|
4a2a6d83d5 | ||
|
|
1aebdef825 | ||
|
|
8819e5f030 | ||
|
|
228fc507a7 | ||
|
|
eaa7200734 | ||
|
|
bfbd68f9d5 | ||
|
|
83a9a2d999 | ||
|
|
8a18aec420 | ||
|
|
3fd82ab89f | ||
|
|
2165b69137 | ||
|
|
3ad972b718 | ||
|
|
e197478638 | ||
|
|
3b0fae84e5 | ||
|
|
46b1d649f0 | ||
|
|
bd4b87032f | ||
|
|
59bffe99ba | ||
|
|
d8de9d25ad | ||
|
|
ba8cd00326 | ||
|
|
9671407502 | ||
|
|
3a3c892385 | ||
|
|
cd37ab1646 | ||
|
|
7c815924ff | ||
|
|
675c4ae64e | ||
|
|
71b9b92288 | ||
|
|
23ec8d7907 | ||
|
|
89711cd76d | ||
|
|
f09ca2dd0a | ||
|
|
251112697a | ||
|
|
7fcd0f902b | ||
|
|
88474c8a3a | ||
|
|
5674102019 | ||
|
|
41a64bbd55 | ||
|
|
f793454476 | ||
|
|
bcbf0938c1 | ||
|
|
1561babd76 | ||
|
|
45c684232d | ||
|
|
d725c7c856 | ||
|
|
bc081ef3c1 | ||
|
|
a688255270 | ||
|
|
0655f1a080 | ||
|
|
b3a7669d2a | ||
|
|
f455c2a880 | ||
|
|
584597d5cb | ||
|
|
1f9eb467ca | ||
|
|
b1a69e028e | ||
|
|
39c3732202 | ||
|
|
b2634a15fa | ||
|
|
5b426afc57 | ||
|
|
f5b76f214e | ||
|
|
e4f0c0d561 | ||
|
|
0f28807240 | ||
|
|
d1fbbbe9d4 | ||
|
|
ae7f10448d | ||
|
|
01334ad91f | ||
|
|
fa15abb781 | ||
|
|
87ffce17b1 | ||
|
|
7382c5bb92 | ||
|
|
5b657dfa92 | ||
|
|
ecda99a217 | ||
|
|
7fccf2c979 | ||
|
|
14cd7b0a47 | ||
|
|
d0f03c8ae1 | ||
|
|
7acc9b931a | ||
|
|
ea215dec6b | ||
|
|
87f06eeb28 | ||
|
|
963da5aab7 | ||
|
|
9e73f99ee1 | ||
|
|
01f4044404 | ||
|
|
904c59d6ac | ||
|
|
242c47bbed | ||
|
|
00a1d25847 | ||
|
|
8b327f81c7 | ||
|
|
b29d6bb9d2 | ||
|
|
cdbebc2323 | ||
|
|
335c82b4be | ||
|
|
28e2d00198 | ||
|
|
c584f9b3d5 | ||
|
|
51dd14cb66 | ||
|
|
41e61ddaa6 | ||
|
|
ee69feaaea | ||
|
|
9252bca505 | ||
|
|
8f8d187a32 | ||
|
|
d837acd3a2 | ||
|
|
ad57321ea3 | ||
|
|
e20a9b8634 | ||
|
|
5847e9fe8f | ||
|
|
e608a016db | ||
|
|
050b34a9bd | ||
|
|
96cf980b36 | ||
|
|
3fc3b115f8 | ||
|
|
6a3789f78c | ||
|
|
cd6d46e146 | ||
|
|
6fdbaa1b97 | ||
|
|
4684dfc9a3 | ||
|
|
9b094fff7a | ||
|
|
a4af81dddb | ||
|
|
e735ef335d | ||
|
|
3ece0c967e | ||
|
|
523bbee5b8 | ||
|
|
86f559c0b3 | ||
|
|
4be365574d | ||
|
|
10a952f7b9 | ||
|
|
3606f7a461 | ||
|
|
ae24ab8d56 | ||
|
|
bc1a5b206f | ||
|
|
2dceab7452 | ||
|
|
1e74dc6179 |
@@ -1 +1 @@
|
||||
FROM qmcgaw/godevcontainer
|
||||
FROM ghcr.io/qdm12/godevcontainer:v0.22.0-alpine
|
||||
|
||||
@@ -2,60 +2,52 @@
|
||||
|
||||
Development container that can be used with VSCode.
|
||||
|
||||
It works on Linux, Windows and OSX.
|
||||
It works on Linux, Windows (WSL2) and OSX.
|
||||
|
||||
## Requirements
|
||||
|
||||
- [VS code](https://code.visualstudio.com/download) installed
|
||||
- [VS code remote containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed
|
||||
- [VS code dev containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed
|
||||
- [Docker](https://www.docker.com/products/docker-desktop) installed and running
|
||||
- If you don't use Linux or WSL 2, share your home directory `~/` and the directory of your project with Docker Desktop
|
||||
- [Docker Compose](https://docs.docker.com/compose/install/) installed
|
||||
- Ensure your host has the following and that they are accessible by Docker:
|
||||
- `~/.ssh` directory
|
||||
- `~/.gitconfig` file (can be empty)
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create the following files and directory on your host if you don't have them:
|
||||
|
||||
```sh
|
||||
touch ~/.gitconfig ~/.zsh_history
|
||||
mkdir -p ~/.ssh
|
||||
```
|
||||
|
||||
1. **For OSX hosts**: ensure the project directory and your home directory `~` are accessible by Docker.
|
||||
1. Open the command palette in Visual Studio Code (CTRL+SHIFT+P).
|
||||
1. Select `Remote-Containers: Open Folder in Container...` and choose the project directory.
|
||||
1. For Docker running on Windows HyperV, if you want to use SSH keys, bind mount them at `/tmp/.ssh` by changing the `volumes` section in the [docker-compose.yml](docker-compose.yml).
|
||||
1. Select `Dev-Containers: Open Folder in Container...` and choose the project directory.
|
||||
|
||||
## Customization
|
||||
|
||||
### Customize the image
|
||||
For any customization to take effect, you should "rebuild and reopen":
|
||||
|
||||
You can make changes to the [Dockerfile](Dockerfile) and then rebuild the image. For example, your Dockerfile could be:
|
||||
1. Open the command palette in Visual Studio Code (CTRL+SHIFT+P)
|
||||
2. Select `Dev-Containers: Rebuild Container`
|
||||
|
||||
```Dockerfile
|
||||
FROM qmcgaw/godevcontainer
|
||||
USER root
|
||||
RUN apk add curl
|
||||
USER vscode
|
||||
```
|
||||
Changes you can make are notably:
|
||||
|
||||
Note that you may need to use `USER root` to build as root, and then change back to `USER vscode`.
|
||||
- Changes to the Docker image in [Dockerfile](Dockerfile)
|
||||
- Changes to VSCode **settings** and **extensions** in [devcontainer.json](devcontainer.json).
|
||||
- Change the entrypoint script by adding a bind mount in [devcontainer.json](devcontainer.json) of a shell script to `/root/.welcome.sh` to replace the [current welcome script](https://github.com/qdm12/godevcontainer/blob/master/shell/.welcome.sh). For example:
|
||||
|
||||
To rebuild the image, either:
|
||||
```json
|
||||
// Welcome script
|
||||
{
|
||||
"source": "/yourpath/.welcome.sh",
|
||||
"target": "/root/.welcome.sh",
|
||||
"type": "bind"
|
||||
},
|
||||
```
|
||||
|
||||
- With VSCode through the command palette, select `Remote-Containers: Rebuild and reopen in container`
|
||||
- With a terminal, go to this directory and `docker-compose build`
|
||||
|
||||
### Customize VS code settings
|
||||
|
||||
You can customize **settings** and **extensions** in the [devcontainer.json](devcontainer.json) definition file.
|
||||
|
||||
### Entrypoint script
|
||||
|
||||
You can bind mount a shell script to `/home/vscode/.welcome.sh` to replace the [current welcome script](shell/.welcome.sh).
|
||||
|
||||
### Publish a port
|
||||
|
||||
To access a port from your host to your development container, publish a port in [docker-compose.yml](docker-compose.yml).
|
||||
|
||||
### Run other services
|
||||
|
||||
1. Modify [docker-compose.yml](docker-compose.yml) to launch other services at the same time as this development container, such as a test database:
|
||||
- Change the `vscode` service container configuration either in [docker-compose.yml](docker-compose.yml) or in [devcontainer.json](devcontainer.json).
|
||||
- Add other services in [docker-compose.yml](docker-compose.yml) to run together with the development VSCode service container. For example to add a test database:
|
||||
|
||||
```yml
|
||||
database:
|
||||
@@ -65,5 +57,4 @@ To access a port from your host to your development container, publish a port in
|
||||
POSTGRES_PASSWORD: password
|
||||
```
|
||||
|
||||
1. In [devcontainer.json](devcontainer.json), change the line `"runServices": ["vscode"],` to `"runServices": ["vscode", "database"],`.
|
||||
1. In the VS code command palette, rebuild the container.
|
||||
- More options are documented in the [devcontainer.json reference](https://containers.dev/implementors/json_reference/).
|
||||
|
||||
@@ -1,79 +1,114 @@
|
||||
{
|
||||
// See https://containers.dev/implementors/json_reference/
|
||||
// User defined settings
|
||||
"containerEnv": {
|
||||
"TZ": ""
|
||||
},
|
||||
// Fixed settings
|
||||
"name": "ddns-dev",
|
||||
"dockerComposeFile": ["docker-compose.yml"],
|
||||
"postCreateCommand": "~/.windows.sh && go mod download && go mod tidy",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"overrideCommand": true,
|
||||
"service": "vscode",
|
||||
"runServices": ["vscode"],
|
||||
"shutdownAction": "stopCompose",
|
||||
"postCreateCommand": "source ~/.windows.sh && go mod download && go mod tidy",
|
||||
"workspaceFolder": "/workspace",
|
||||
// "overrideCommand": "",
|
||||
"extensions": [
|
||||
"golang.go",
|
||||
"eamodio.gitlens", // IDE Git information
|
||||
"davidanson.vscode-markdownlint",
|
||||
"ms-azuretools.vscode-docker", // Docker integration and linting
|
||||
"shardulm94.trailing-spaces", // Show trailing spaces
|
||||
"Gruntfuggly.todo-tree", // Highlights TODO comments
|
||||
"bierner.emojisense", // Emoji sense for markdown
|
||||
"stkb.rewrap", // rewrap comments after n characters on one line
|
||||
"vscode-icons-team.vscode-icons", // Better file extension icons
|
||||
"github.vscode-pull-request-github", // Github interaction
|
||||
"redhat.vscode-yaml", // Kubernetes, Drone syntax highlighting
|
||||
"bajdzis.vscode-database", // Supports connections to mysql or postgres, over SSL, socked
|
||||
"IBM.output-colorizer", // Colorize your output/test logs
|
||||
// "mohsen1.prettify-json", // Prettify JSON data
|
||||
// "zxh404.vscode-proto3", // Supports Proto syntax
|
||||
// "jrebocho.vscode-random", // Generates random values
|
||||
// "alefragnani.Bookmarks", // Manage bookmarks
|
||||
// "quicktype.quicktype", // Paste JSON as code
|
||||
// "spikespaz.vscode-smoothtype", // smooth cursor animation
|
||||
"mounts": [
|
||||
// Source code
|
||||
{
|
||||
"source": "../",
|
||||
"target": "/workspace",
|
||||
"type": "bind"
|
||||
},
|
||||
// Zsh commands history persistence
|
||||
{
|
||||
"source": "${localEnv:HOME}/.zsh_history",
|
||||
"target": "/root/.zsh_history",
|
||||
"type": "bind"
|
||||
},
|
||||
// Git configuration file
|
||||
{
|
||||
"source": "${localEnv:HOME}/.gitconfig",
|
||||
"target": "/root/.gitconfig",
|
||||
"type": "bind"
|
||||
},
|
||||
// SSH directory for Linux, OSX and WSL
|
||||
// On Linux and OSX, a symlink /mnt/ssh <-> ~/.ssh is
|
||||
// created in the container. On Windows, files are copied
|
||||
// from /mnt/ssh to ~/.ssh to fix permissions.
|
||||
{
|
||||
"source": "${localEnv:HOME}/.ssh",
|
||||
"target": "/mnt/ssh",
|
||||
"type": "bind"
|
||||
},
|
||||
// Docker socket to access the host Docker server
|
||||
{
|
||||
"source": "/var/run/docker.sock",
|
||||
"target": "/var/run/docker.sock",
|
||||
"type": "bind"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
"remote.extensionKind": {
|
||||
"ms-azuretools.vscode-docker": "workspace"
|
||||
},
|
||||
"editor.codeActionsOnSaveTimeout": 3000,
|
||||
"go.useLanguageServer": true,
|
||||
"[go]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true,
|
||||
},
|
||||
// Optional: Disable snippets, as they conflict with completion ranking.
|
||||
"editor.snippetSuggestions": "none"
|
||||
},
|
||||
"[go.mod]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true,
|
||||
},
|
||||
},
|
||||
"gopls": {
|
||||
"usePlaceholders": false,
|
||||
"staticcheck": true
|
||||
},
|
||||
"go.autocompleteUnimportedPackages": true,
|
||||
"go.gotoSymbol.includeImports": true,
|
||||
"go.gotoSymbol.includeGoroot": true,
|
||||
"go.lintTool": "golangci-lint",
|
||||
"go.buildOnSave": "workspace",
|
||||
"go.lintOnSave": "workspace",
|
||||
"go.vetOnSave": "workspace",
|
||||
"editor.formatOnSave": true,
|
||||
"go.toolsEnvVars": {
|
||||
"GOFLAGS": "-tags=",
|
||||
"CGO_ENABLED": 1 // for the race detector
|
||||
},
|
||||
"gopls.env": {
|
||||
"GOFLAGS": "-tags="
|
||||
},
|
||||
"go.testEnvVars": {
|
||||
"": "",
|
||||
},
|
||||
"go.testFlags": ["-v", "-race"],
|
||||
"go.testTimeout": "10s",
|
||||
"go.coverOnSingleTest": true,
|
||||
"go.coverOnSingleTestFile": true,
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"golang.go",
|
||||
"eamodio.gitlens", // IDE Git information
|
||||
"davidanson.vscode-markdownlint",
|
||||
"ms-azuretools.vscode-docker", // Docker integration and linting
|
||||
"shardulm94.trailing-spaces", // Show trailing spaces
|
||||
"Gruntfuggly.todo-tree", // Highlights TODO comments
|
||||
"bierner.emojisense", // Emoji sense for markdown
|
||||
"stkb.rewrap", // rewrap comments after n characters on one line
|
||||
"vscode-icons-team.vscode-icons", // Better file extension icons
|
||||
"github.vscode-pull-request-github", // Github interaction
|
||||
"redhat.vscode-yaml", // Kubernetes, Drone syntax highlighting
|
||||
"bajdzis.vscode-database", // Supports connections to mysql or postgres, over SSL, socked
|
||||
"IBM.output-colorizer", // Colorize your output/test logs
|
||||
"github.copilot" // AI code completion
|
||||
// "mohsen1.prettify-json", // Prettify JSON data
|
||||
// "zxh404.vscode-proto3", // Supports Proto syntax
|
||||
// "jrebocho.vscode-random", // Generates random values
|
||||
// "alefragnani.Bookmarks", // Manage bookmarks
|
||||
// "quicktype.quicktype", // Paste JSON as code
|
||||
// "spikespaz.vscode-smoothtype", // smooth cursor animation
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
"editor.formatOnSave": true,
|
||||
"go.buildTags": "",
|
||||
"go.toolsEnvVars": {
|
||||
"CGO_ENABLED": "0"
|
||||
},
|
||||
"go.useLanguageServer": true,
|
||||
"go.testEnvVars": {
|
||||
"CGO_ENABLED": "1"
|
||||
},
|
||||
"go.testFlags": [
|
||||
"-v",
|
||||
"-race"
|
||||
],
|
||||
"go.testTimeout": "10s",
|
||||
"go.coverOnSingleTest": true,
|
||||
"go.coverOnSingleTestFile": true,
|
||||
"go.coverOnTestPackage": true,
|
||||
"go.lintTool": "golangci-lint",
|
||||
"go.lintOnSave": "package",
|
||||
"[go]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "always"
|
||||
}
|
||||
},
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
"usePlaceholders": false,
|
||||
"staticcheck": true,
|
||||
"ui.diagnostic.analyses": {
|
||||
"ST1000": false
|
||||
},
|
||||
"vulncheck": "Imports"
|
||||
},
|
||||
"remote.extensionKind": {
|
||||
"ms-azuretools.vscode-docker": "workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,3 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
vscode:
|
||||
build: .
|
||||
image: godevcontainer
|
||||
volumes:
|
||||
- ../:/workspace
|
||||
# Docker
|
||||
- ~/.docker:/root/.docker:z
|
||||
# Docker socket to access Docker server
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# SSH directory for Linux, OSX and WSL
|
||||
- ~/.ssh:/root/.ssh:z
|
||||
# For Windows without WSL, a copy will be made
|
||||
# from /tmp/.ssh to ~/.ssh to fix permissions
|
||||
# - ~/.ssh:/tmp/.ssh:ro
|
||||
# Shell history persistence
|
||||
- ~/.zsh_history:/root/.zsh_history:z
|
||||
# Git config
|
||||
- ~/.gitconfig:/root/.gitconfig:z
|
||||
# Kubernetes
|
||||
- ~/.kube:/root/.kube:z
|
||||
environment:
|
||||
- TZ=
|
||||
cap_add:
|
||||
# For debugging with dlv
|
||||
- SYS_PTRACE
|
||||
security_opt:
|
||||
# For debugging with dlv
|
||||
- seccomp:unconfined
|
||||
entrypoint: zsh -c "while sleep 1000; do :; done"
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
.vscode
|
||||
docs
|
||||
readme
|
||||
!readme/*.go
|
||||
.gitignore
|
||||
config.json
|
||||
docker-compose.yml
|
||||
LICENSE
|
||||
README.md
|
||||
ui/favicon.svg
|
||||
|
||||
81
.github/CONTRIBUTING.md
vendored
81
.github/CONTRIBUTING.md
vendored
@@ -1,17 +1,92 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [open source license of this project](../LICENSE).
|
||||
## Table of content
|
||||
|
||||
1. [Submitting a pull request](#submitting-a-pull-request)
|
||||
1. [Development setup](#development-setup)
|
||||
1. [Commands available](#commands-available)
|
||||
1. [Add a new DNS provider](#add-a-new-dns-provider)
|
||||
1. [License](#license)
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
1. [Fork](https://github.com/qdm12/ddns-updater/fork) and clone the repository
|
||||
1. Create a new branch `git checkout -b my-branch-name`
|
||||
1. Modify the code
|
||||
1. Ensure the docker build succeeds `docker build .`
|
||||
1. Commit your modifications
|
||||
1. Push to your fork and [submit a pull request](https://github.com/qdm12/ddns-updater/compare)
|
||||
|
||||
## Resources
|
||||
Additional resources:
|
||||
|
||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
|
||||
## Development setup
|
||||
|
||||
### Using VSCode and Docker
|
||||
|
||||
That should be easier and better than a local setup, although it might use more memory if you're not on Linux.
|
||||
|
||||
1. Install [Docker](https://docs.docker.com/install/)
|
||||
- On Windows, share a drive with Docker Desktop and have the project on that partition
|
||||
- On OSX, share your project directory with Docker Desktop
|
||||
1. With [Visual Studio Code](https://code.visualstudio.com/download), install the [dev containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
1. In Visual Studio Code, press on `F1` and select `Dev Containers: Open Folder in Container...`
|
||||
1. Your dev environment is ready to go!... and it's running in a container :+1:
|
||||
|
||||
### Locally
|
||||
|
||||
Install [Go](https://golang.org/dl/), [Docker](https://www.docker.com/products/docker-desktop) and [Git](https://git-scm.com/downloads); then:
|
||||
|
||||
```sh
|
||||
go mod download
|
||||
```
|
||||
|
||||
And finally install [golangci-lint](https://github.com/golangci/golangci-lint#install).
|
||||
|
||||
You might want to use an editor such as [Visual Studio Code](https://code.visualstudio.com/download) with the [Go extension](https://code.visualstudio.com/docs/languages/go).
|
||||
|
||||
## Commands available
|
||||
|
||||
- Test the code: `go test ./...`
|
||||
- Lint the code `golangci-lint run`
|
||||
- Build the program: `go build -o app cmd/ddns-updater/main.go`
|
||||
- Build the Docker image (tests and lint included): `docker build -t ghcr.io/qdm12/ddns-updater .`
|
||||
- Run the Docker container: `docker run -it --rm -v /yourpath/data:/updater/data ghcr.io/qdm12/ddns-updater`
|
||||
|
||||
## Add a new DNS provider
|
||||
|
||||
An "example" DNS provider is present in the code, you can simply copy paste it modify it to your needs.
|
||||
In more detailed steps:
|
||||
|
||||
1. Copy the directory [`internal/provider/providers/example`](../internal/provider/providers/example) to `internal/provider/providers/yourprovider` where `yourprovider` is the name of the DNS provider you want to add, in a single word without spaces, dashes or underscores.
|
||||
1. Modify the `internal/provider/providers/yourprovider/provider.go` file to fit the requirements of your DNS provider. There are many `// TODO` comments you can follow and **need to remove** when done.
|
||||
1. Add the provider name constant to the `ProviderChoices` function in [`internal/provider/constants/providers.go`](../internal/provider/constants/providers.go). For example:
|
||||
|
||||
```go
|
||||
func ProviderChoices() []models.Provider {
|
||||
return []models.Provider{
|
||||
// ...
|
||||
Example,
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Add a case for your provider in the `switch` statement in the `New` function in [`internal/provider/provider.go`](../internal/provider/provider.go). For example:
|
||||
|
||||
```go
|
||||
case constants.Example:
|
||||
return example.New(data, domain, owner, ipVersion, ipv6Suffix)
|
||||
```
|
||||
|
||||
1. Copy the file [`docs/example.md`](../docs/example.md) to `docs/yourprovider.md` and modify it to fit the configuration and domain setup of your DNS provider. There are a few `<!-- ... -->` comments indicating what to change, please **remove them** when done.
|
||||
1. In the [README.md](../README.md):
|
||||
1. Add your provider name to the list of providers supported `- Your provider`
|
||||
1. Add your provider name and link to its document to the second list: `- [Your provider](docs/yourprovider.md)`
|
||||
1. Make sure to run the actual program (in Docker or directly) and check it updates your DNS records as expected, of course 😉 You can do this by setting a record to `127.0.0.1` manually and then run the updater to see if the update succeeds.
|
||||
1. Profit 🎉 Don't forget to [open a pull request](https://github.com/qdm12/ddns-updater/compare)
|
||||
|
||||
## License
|
||||
|
||||
Contributions are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [open source license of this project](../LICENSE).
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/bug.md
vendored
5
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -1,9 +1,8 @@
|
||||
---
|
||||
name: Bug
|
||||
about: Report a bug
|
||||
title: 'Bug: ...'
|
||||
labels: ":bug: bug"
|
||||
assignees: qdm12
|
||||
title: 'Bug: FILL THIS TEXT OR ISSUE WILL BE CLOSED'
|
||||
labels:
|
||||
|
||||
---
|
||||
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/feature_request.md
vendored
5
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,9 +1,8 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature to add to this project
|
||||
title: 'Feature request: ...'
|
||||
labels: ":bulb: feature request"
|
||||
assignees: qdm12
|
||||
title: 'Feature request: FILL THIS TEXT OR ISSUE WILL BE CLOSED'
|
||||
labels:
|
||||
|
||||
---
|
||||
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/help.md
vendored
5
.github/ISSUE_TEMPLATE/help.md
vendored
@@ -1,9 +1,8 @@
|
||||
---
|
||||
name: Help
|
||||
about: Ask for help
|
||||
title: 'Help: ...'
|
||||
labels: ":pray: help wanted"
|
||||
assignees:
|
||||
title: 'Help: FILL THIS TEXT OR ISSUE WILL BE CLOSED'
|
||||
labels:
|
||||
|
||||
---
|
||||
|
||||
|
||||
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: docker
|
||||
directory: /
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: "daily"
|
||||
113
.github/labels.yml
vendored
113
.github/labels.yml
vendored
@@ -1,51 +1,62 @@
|
||||
- name: ":robot: bot"
|
||||
color: "69cde9"
|
||||
description: ""
|
||||
- name: ":bug: bug"
|
||||
color: "b60205"
|
||||
description: ""
|
||||
- name: ":game_die: dependencies"
|
||||
color: "0366d6"
|
||||
description: ""
|
||||
- name: ":memo: documentation"
|
||||
color: "c5def5"
|
||||
description: ""
|
||||
- name: ":busts_in_silhouette: duplicate"
|
||||
color: "cccccc"
|
||||
description: ""
|
||||
- name: ":sparkles: enhancement"
|
||||
color: "0054ca"
|
||||
description: ""
|
||||
- name: ":bulb: feature request"
|
||||
color: "0e8a16"
|
||||
description: ""
|
||||
- name: ":mega: feedback"
|
||||
color: "03a9f4"
|
||||
description: ""
|
||||
- name: ":rocket: future maybe"
|
||||
color: "fef2c0"
|
||||
description: ""
|
||||
- name: ":hatching_chick: good first issue"
|
||||
color: "7057ff"
|
||||
description: ""
|
||||
- name: ":pray: help wanted"
|
||||
color: "4caf50"
|
||||
description: ""
|
||||
- name: ":hand: hold"
|
||||
color: "24292f"
|
||||
description: ""
|
||||
- name: ":no_entry_sign: invalid"
|
||||
color: "e6e6e6"
|
||||
description: ""
|
||||
- name: ":interrobang: maybe bug"
|
||||
color: "ff5722"
|
||||
description: ""
|
||||
- name: ":thinking: needs more info"
|
||||
color: "795548"
|
||||
description: ""
|
||||
- name: ":question: question"
|
||||
color: "3f51b5"
|
||||
description: ""
|
||||
- name: ":coffin: wontfix"
|
||||
color: "ffffff"
|
||||
description: ""
|
||||
- name: "Status: 🗯️ Waiting for feedback"
|
||||
color: "f7d692"
|
||||
- name: "Status: 🔴 Blocked"
|
||||
color: "f7d692"
|
||||
description: "Blocked by another issue or pull request"
|
||||
- name: "Status: 🔒 After next release"
|
||||
color: "f7d692"
|
||||
description: "Will be done after the next release"
|
||||
|
||||
- name: "Closed: ⚰️ Inactive"
|
||||
color: "959a9c"
|
||||
description: "No answer was received for weeks"
|
||||
- name: "Closed: 👥 Duplicate"
|
||||
color: "959a9c"
|
||||
description: "Issue duplicates an existing issue"
|
||||
- name: "Closed: 🗑️ Bad issue"
|
||||
color: "959a9c"
|
||||
|
||||
- name: "Priority: 🚨 Urgent"
|
||||
color: "03adfc"
|
||||
- name: "Priority: 💤 Low priority"
|
||||
color: "03adfc"
|
||||
|
||||
- name: "Complexity: ☣️ Hard to do"
|
||||
color: "ff9efc"
|
||||
- name: "Complexity: 🟩 Easy to do"
|
||||
color: "ff9efc"
|
||||
|
||||
- name: "Category: Config problem 📝"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Healthcheck 🩺"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Documentation ✒️"
|
||||
description: "A problem with the readme or in the docs/ directory"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Maintenance ⛓️"
|
||||
description: "Anything related to code or other maintenance"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Good idea 🎯"
|
||||
description: "This is a good idea, judged by the maintainers"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Motivated! 🙌"
|
||||
description: "Your pumpness makes me pumped! The issue or PR shows great motivation!"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Foolproof settings 👼"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Label missing ❗"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Provider update ♻️"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Shoutrrr 📢"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: IP fetching 📥"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Database 🗃️"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: New provider 🆕"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Web UI 🖱️"
|
||||
color: "ffc7ea"
|
||||
- name: "Category: Wildcard 🃏"
|
||||
color: "ffc7ea"
|
||||
|
||||
35
.github/workflows/build-skip.yml
vendored
Normal file
35
.github/workflows/build-skip.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: No trigger file paths
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- .github/workflows/build.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- .github/workflows/build.yml
|
||||
- cmd/**
|
||||
- internal/**
|
||||
- pkg/**
|
||||
- .dockerignore
|
||||
- .golangci.yml
|
||||
- Dockerfile
|
||||
- go.mod
|
||||
- go.sum
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: No trigger path triggered for required verify workflow.
|
||||
run: exit 0
|
||||
141
.github/workflows/build.yml
vendored
141
.github/workflows/build.yml
vendored
@@ -1,6 +1,11 @@
|
||||
name: CI
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- .github/workflows/build.yml
|
||||
- cmd/**
|
||||
@@ -26,16 +31,26 @@ on:
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: reviewdog/action-misspell@v1
|
||||
with:
|
||||
locale: "US"
|
||||
level: error
|
||||
exclude: |
|
||||
*.md
|
||||
|
||||
- name: Linting
|
||||
run: docker build --target lint .
|
||||
|
||||
- name: Go mod tidy check
|
||||
run: docker build --target tidy .
|
||||
- name: Mocks check
|
||||
run: docker build --target mocks .
|
||||
|
||||
- name: Build test image
|
||||
run: docker build --target test -t test-container .
|
||||
@@ -47,55 +62,105 @@ jobs:
|
||||
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
|
||||
test-container
|
||||
|
||||
# We run this here to use the caching of the previous steps
|
||||
- name: Build final image
|
||||
run: docker build .
|
||||
run: docker build -t final-image .
|
||||
|
||||
codeql:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: go
|
||||
- uses: github/codeql-action/autobuild@v4
|
||||
- uses: github/codeql-action/analyze@v4
|
||||
|
||||
publish:
|
||||
needs: [verify]
|
||||
if: github.event_name == 'push'
|
||||
if: |
|
||||
github.repository == 'qdm12/ddns-updater' &&
|
||||
(
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'release' ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]')
|
||||
)
|
||||
needs: [verify, codeql]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
packages: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0 # for gorelease last step
|
||||
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
# extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
flavor: |
|
||||
latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
images: |
|
||||
ghcr.io/qdm12/ddns-updater
|
||||
qmcgaw/ddns-updater
|
||||
tags: |
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{major}}.{{minor}}.{{patch}}
|
||||
type=semver,pattern=v{{major}}.{{minor}}.{{patch}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern=v{{major}}.{{minor}}
|
||||
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
|
||||
type=semver,pattern=v{{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
|
||||
- uses: docker/login-action@v1
|
||||
- uses: docker/setup-qemu-action@v4
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Set variables
|
||||
id: vars
|
||||
run: |
|
||||
BRANCH=${GITHUB_REF#refs/heads/}
|
||||
TAG=${GITHUB_REF#refs/tags/}
|
||||
echo ::set-output name=commit::$(git rev-parse --short HEAD)
|
||||
echo ::set-output name=build_date::$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
if [ "$TAG" != "$GITHUB_REF" ]; then
|
||||
echo ::set-output name=version::$TAG
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le,linux/riscv64
|
||||
elif [ "$BRANCH" = "master" ]; then
|
||||
echo ::set-output name=version::latest
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le,linux/riscv64
|
||||
else
|
||||
echo ::set-output name=version::$BRANCH
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
|
||||
fi
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Short commit
|
||||
id: shortcommit
|
||||
run: echo "::set-output name=value::$(git rev-parse --short HEAD)"
|
||||
|
||||
- name: Build and push final image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
platforms: ${{ steps.vars.outputs.platforms }}
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le,linux/riscv64
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
BUILD_DATE=${{ steps.vars.outputs.build_date }}
|
||||
COMMIT=${{ steps.vars.outputs.commit }}
|
||||
VERSION=${{ steps.vars.outputs.version }}
|
||||
tags: qmcgaw/ddns-updater:${{ steps.vars.outputs.version }}
|
||||
CREATED=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
|
||||
COMMIT=${{ steps.shortcommit.outputs.value }}
|
||||
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
push: true
|
||||
|
||||
- if: github.event.ref == 'refs/heads/master'
|
||||
name: Microbadger hook
|
||||
run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/ddns-updater/t2fcZxog8ce_kJYJ61JjkYwHF5s=
|
||||
continue-on-error: true
|
||||
- if: github.event_name == 'release'
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- if: github.event_name == 'release'
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --config .github/workflows/configs/.goreleaser.yaml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
30
.github/workflows/configs/.goreleaser.yaml
vendored
Normal file
30
.github/workflows/configs/.goreleaser.yaml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
- main: ./cmd/ddns-updater/main.go
|
||||
flags:
|
||||
- -trimpath
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
# See https://goreleaser.com/customization/build/
|
||||
- linux_amd64
|
||||
- linux_386
|
||||
- linux_arm64
|
||||
- linux_arm_7
|
||||
- linux_arm_6
|
||||
- linux_arm_5
|
||||
- darwin_amd64
|
||||
- darwin_arm64
|
||||
- windows_amd64
|
||||
- windows_386
|
||||
- windows_arm64
|
||||
archives:
|
||||
- format: binary
|
||||
checksum:
|
||||
name_template: "checksums.txt"
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
49
.github/workflows/configs/mlc-config.json
vendored
Normal file
49
.github/workflows/configs/mlc-config.json
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"ignorePatterns": [
|
||||
{
|
||||
"pattern": "^http://localhost"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://api(6|64).ipify.org$"
|
||||
},
|
||||
{
|
||||
"pattern": "^http://ip1.dynupdate6.no-ip.com$"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://ap.www.namecheap.com/Domains/DomainControlPanel/example.com/advancedns$"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://www.godaddy.com"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://www.namecheap.com"
|
||||
},
|
||||
{
|
||||
"pattern": "https://www.linode.com/docs/products/tools/api/guides/manage-api-tokens/"
|
||||
},
|
||||
{
|
||||
"pattern": "https://(ip|ipv|v)6.+"
|
||||
},
|
||||
{
|
||||
"pattern": "https://github.com/qdm12/ddns-updater/pkgs/container/ddns-updater"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://www.duckdns.org/$"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://my.vultr.com/settings/#settingsapi$"
|
||||
},
|
||||
{
|
||||
"pattern": "^https://www.namesilo.com"
|
||||
}
|
||||
],
|
||||
"timeout": "20s",
|
||||
"retryOn429": false,
|
||||
"fallbackRetryDelay": "30s",
|
||||
"aliveStatusCodes": [
|
||||
200,
|
||||
206,
|
||||
403,
|
||||
429
|
||||
]
|
||||
}
|
||||
19
.github/workflows/dockerhub-description.yml
vendored
19
.github/workflows/dockerhub-description.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: Docker Hub description
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- README.md
|
||||
- .github/workflows/dockerhub-description.yml
|
||||
jobs:
|
||||
dockerHubDescription:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v2.1.0
|
||||
env:
|
||||
DOCKERHUB_USERNAME: qmcgaw
|
||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
DOCKERHUB_REPOSITORY: qmcgaw/ddns-updater
|
||||
8
.github/workflows/labels.yml
vendored
8
.github/workflows/labels.yml
vendored
@@ -10,9 +10,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
- name: Labeler
|
||||
if: success()
|
||||
uses: crazy-max/ghaction-github-labeler@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: crazy-max/ghaction-github-labeler@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
21
.github/workflows/markdown-skip.yml
vendored
Normal file
21
.github/workflows/markdown-skip.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Markdown
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- .github/workflows/markdown.yml
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- .github/workflows/markdown.yml
|
||||
|
||||
jobs:
|
||||
markdown:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: No trigger path triggered for required markdown workflow.
|
||||
run: exit 0
|
||||
48
.github/workflows/markdown.yml
vendored
Normal file
48
.github/workflows/markdown.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Markdown
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "**.md"
|
||||
- .github/workflows/markdown.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.md"
|
||||
- .github/workflows/markdown.yml
|
||||
|
||||
jobs:
|
||||
markdown:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: DavidAnson/markdownlint-cli2-action@v23
|
||||
with:
|
||||
globs: "**.md"
|
||||
config: .markdownlint.json
|
||||
|
||||
- uses: reviewdog/action-misspell@v1
|
||||
with:
|
||||
locale: "US"
|
||||
level: error
|
||||
pattern: |
|
||||
*.md
|
||||
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
use-quiet-mode: yes
|
||||
config-file: .github/workflows/configs/mlc-config.json
|
||||
|
||||
- uses: peter-evans/dockerhub-description@v5
|
||||
if: github.repository == 'qdm12/ddns-updater' && github.event_name == 'push'
|
||||
with:
|
||||
username: qmcgaw
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
repository: qmcgaw/ddns-updater
|
||||
short-description: Container to update DNS records periodically with WebUI for many DNS providers
|
||||
readme-filepath: README.md
|
||||
enable-url-completion: true
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -0,0 +1 @@
|
||||
/data
|
||||
124
.golangci.yml
124
.golangci.yml
@@ -1,74 +1,124 @@
|
||||
linters-settings:
|
||||
maligned:
|
||||
suggest-new: true
|
||||
misspell:
|
||||
locale: US
|
||||
version: "2"
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: cmd/updater/main.go
|
||||
text: "mnd: Magic number: 4, in <argument> detected"
|
||||
linters:
|
||||
- gomnd
|
||||
- path: cmd/updater/main.go
|
||||
text: "mnd: Magic number: 2, in <argument> detected"
|
||||
linters:
|
||||
- gomnd
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofumpt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
settings:
|
||||
misspell:
|
||||
locale: US
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- containedctx
|
||||
- dupl
|
||||
- err113
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- contextcheck
|
||||
text: Function `exitHealthchecksio` should pass the context parameter
|
||||
- linters:
|
||||
- lll
|
||||
source: See https://
|
||||
- linters:
|
||||
- revive
|
||||
text: "var-naming: avoid meaningless package names"
|
||||
path: internal\/provider\/utils\/
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
enable:
|
||||
# - cyclop
|
||||
- asasalint
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- canonicalheader
|
||||
- containedctx
|
||||
- contextcheck
|
||||
- copyloopvar
|
||||
- decorder
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- dupword
|
||||
- durationcheck
|
||||
- err113
|
||||
- errchkjson
|
||||
- errname
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- gci
|
||||
- fatcontext
|
||||
- forcetypeassert
|
||||
- gocheckcompilerdirectives
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gochecksumtype
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godoclint
|
||||
- godot
|
||||
- goheader
|
||||
- goimports
|
||||
- gomnd
|
||||
- gomoddirectives
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
# - goerr113 # TODO
|
||||
- gosimple
|
||||
- govet
|
||||
- gosmopolitan
|
||||
- grouper
|
||||
- importas
|
||||
- ineffassign
|
||||
- inamedparam
|
||||
- interfacebloat
|
||||
- intrange
|
||||
- iotamixing
|
||||
- ireturn
|
||||
- lll
|
||||
- maintidx
|
||||
- makezero
|
||||
- mirror
|
||||
- misspell
|
||||
- mnd
|
||||
- modernize
|
||||
- musttag
|
||||
- nakedret
|
||||
- nestif
|
||||
- nilerr
|
||||
- nilnil
|
||||
- noctx
|
||||
- nolintlint
|
||||
- nosprintfhostport
|
||||
- paralleltest
|
||||
- perfsprint
|
||||
- prealloc
|
||||
- predeclared
|
||||
- promlinter
|
||||
- protogetter
|
||||
- reassign
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- exportloopref
|
||||
- sloglint
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- tagalign
|
||||
- thelper
|
||||
- tparallel
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
- whitespace
|
||||
|
||||
run:
|
||||
skip-dirs:
|
||||
- .devcontainer
|
||||
- .github
|
||||
- zerologlint
|
||||
|
||||
8
.markdownlint.json
Normal file
8
.markdownlint.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"MD013": false,
|
||||
"MD033": {
|
||||
"allowed_elements": [
|
||||
"img"
|
||||
]
|
||||
}
|
||||
}
|
||||
81
Dockerfile
81
Dockerfile
@@ -1,24 +1,22 @@
|
||||
ARG BUILDPLATFORM=linux/amd64
|
||||
ARG ALPINE_VERSION=3.13
|
||||
ARG GO_VERSION=1.16
|
||||
ARG XCPUTRANSLATE_VERSION=v0.6.0
|
||||
ARG GOLANGCI_LINT_VERSION=v1.41.1
|
||||
ARG ALPINE_VERSION=3.23
|
||||
ARG GO_VERSION=1.26
|
||||
ARG XCPUTRANSLATE_VERSION=v0.9.0
|
||||
ARG GOLANGCI_LINT_VERSION=v2.11.4
|
||||
ARG MOCKGEN_VERSION=v0.6.0
|
||||
|
||||
FROM --platform=${BUILDPLATFORM} qmcgaw/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||
FROM --platform=${BUILDPLATFORM} qmcgaw/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint
|
||||
|
||||
FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS alpine
|
||||
RUN apk --update add ca-certificates
|
||||
RUN mkdir /tmp/data && \
|
||||
chown 1000 /tmp/data && \
|
||||
chmod 700 /tmp/data
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/qdm12/xcputranslate:${XCPUTRANSLATE_VERSION} AS xcputranslate
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/qdm12/binpot:golangci-lint-${GOLANGCI_LINT_VERSION} AS golangci-lint
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/qdm12/binpot:mockgen-${MOCKGEN_VERSION} AS mockgen
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
WORKDIR /tmp/gobuild
|
||||
ENV CGO_ENABLED=0
|
||||
RUN apk --update add git g++
|
||||
# Note: findutils needed to have xargs support `-d` flag for mocks stage.
|
||||
RUN apk --update add git g++ findutils
|
||||
COPY --from=xcputranslate /xcputranslate /usr/local/bin/xcputranslate
|
||||
COPY --from=golangci-lint /bin /go/bin/golangci-lint
|
||||
COPY --from=mockgen /bin /go/bin/mockgen
|
||||
# Copy repository code and install Go dependencies
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
@@ -31,41 +29,49 @@ FROM --platform=$BUILDPLATFORM base AS test
|
||||
# - we set CGO_ENABLED=1 to have it enabled
|
||||
# - we installed g++ to support the race detector
|
||||
ENV CGO_ENABLED=1
|
||||
COPY readme/ ./readme/
|
||||
COPY README.md ./README.md
|
||||
ENTRYPOINT go test -race -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS lint
|
||||
COPY .golangci.yml ./
|
||||
RUN golangci-lint run --timeout=10m
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS tidy
|
||||
FROM --platform=${BUILDPLATFORM} base AS mocks
|
||||
RUN git init && \
|
||||
git config user.email ci@localhost && \
|
||||
git config user.name ci && \
|
||||
git add -A && git commit -m ci && \
|
||||
sed -i '/\/\/ indirect/d' go.mod && \
|
||||
go mod tidy && \
|
||||
git diff --exit-code -- go.mod
|
||||
git config core.fileMode false && \
|
||||
git add -A && \
|
||||
git commit -m "snapshot" && \
|
||||
grep -lr -E '^// Code generated by MockGen\. DO NOT EDIT\.$' . | xargs -r -d '\n' rm && \
|
||||
go generate -run "mockgen" ./... && \
|
||||
git diff --exit-code && \
|
||||
rm -rf .git/
|
||||
|
||||
FROM --platform=$BUILDPLATFORM base AS build
|
||||
RUN mkdir -p /tmp/data
|
||||
ARG VERSION=unknown
|
||||
ARG BUILD_DATE="an unknown date"
|
||||
ARG CREATED="an unknown date"
|
||||
ARG COMMIT=unknown
|
||||
ARG TARGETPLATFORM
|
||||
RUN GOARCH="$(xcputranslate translate -targetplatform ${TARGETPLATFORM} -field arch)" \
|
||||
GOARM="$(xcputranslate translate -targetplatform ${TARGETPLATFORM} -field arm)" \
|
||||
go build -trimpath -ldflags="-s -w \
|
||||
-X 'main.version=$VERSION' \
|
||||
-X 'main.buildDate=$BUILD_DATE' \
|
||||
-X 'main.date=$CREATED' \
|
||||
-X 'main.commit=$COMMIT' \
|
||||
" -o app cmd/updater/main.go
|
||||
" -o app cmd/ddns-updater/main.go
|
||||
|
||||
FROM scratch
|
||||
COPY --from=alpine --chown=1000 /tmp/data /updater/data/
|
||||
COPY --from=alpine --chown=1000 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
EXPOSE 8000
|
||||
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=2 CMD ["/updater/app", "healthcheck"]
|
||||
USER 1000
|
||||
ENTRYPOINT ["/updater/app"]
|
||||
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=2 CMD ["/updater/ddns-updater", "healthcheck"]
|
||||
ARG UID=1000
|
||||
ARG GID=1000
|
||||
USER ${UID}:${GID}
|
||||
WORKDIR /updater
|
||||
ENTRYPOINT ["/updater/ddns-updater"]
|
||||
COPY --from=build --chown=${UID}:${GID} /tmp/data /updater/data
|
||||
ENV \
|
||||
# Core
|
||||
CONFIG= \
|
||||
@@ -79,31 +85,38 @@ ENV \
|
||||
PUBLICIP_DNS_TIMEOUT=3s \
|
||||
HTTP_TIMEOUT=10s \
|
||||
DATADIR=/updater/data \
|
||||
|
||||
CONFIG_FILEPATH=/updater/data/config.json \
|
||||
RESOLVER_ADDRESS= \
|
||||
RESOLVER_TIMEOUT=5s \
|
||||
# Web UI
|
||||
LISTENING_PORT=8000 \
|
||||
SERVER_ENABLED=yes \
|
||||
LISTENING_ADDRESS=:8000 \
|
||||
ROOT_URL=/ \
|
||||
|
||||
# Backup
|
||||
BACKUP_PERIOD=0 \
|
||||
BACKUP_DIRECTORY=/updater/data \
|
||||
|
||||
# Other
|
||||
LOG_LEVEL=info \
|
||||
LOG_CALLER=hidden \
|
||||
SHOUTRRR_ADDRESSES= \
|
||||
TZ=
|
||||
SHOUTRRR_DEFAULT_TITLE="DDNS Updater" \
|
||||
TZ= \
|
||||
# UMASK left empty so it dynamically defaults to the OS current umask
|
||||
UMASK= \
|
||||
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
|
||||
HEALTH_HEALTHCHECKSIO_BASE_URL=https://hc-ping.com \
|
||||
HEALTH_HEALTHCHECKSIO_UUID=
|
||||
ARG VERSION=unknown
|
||||
ARG BUILD_DATE="an unknown date"
|
||||
ARG CREATED="an unknown date"
|
||||
ARG COMMIT=unknown
|
||||
LABEL \
|
||||
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
|
||||
org.opencontainers.image.version=$VERSION \
|
||||
org.opencontainers.image.created=$BUILD_DATE \
|
||||
org.opencontainers.image.created=$CREATED \
|
||||
org.opencontainers.image.revision=$COMMIT \
|
||||
org.opencontainers.image.url="https://github.com/qdm12/ddns-updater" \
|
||||
org.opencontainers.image.documentation="https://github.com/qdm12/ddns-updater" \
|
||||
org.opencontainers.image.source="https://github.com/qdm12/ddns-updater" \
|
||||
org.opencontainers.image.title="ddns-updater" \
|
||||
org.opencontainers.image.description="Universal DNS updater with WebUI"
|
||||
COPY --from=build --chown=1000 /tmp/gobuild/app /updater/app
|
||||
COPY --from=build --chown=${UID}:${GID} /tmp/gobuild/app /updater/ddns-updater
|
||||
|
||||
740
README.md
740
README.md
@@ -1,296 +1,444 @@
|
||||
# Lightweight universal DDNS Updater with Docker and web UI
|
||||
|
||||
*Light container updating DNS A and/or AAAA records periodically for multiple DNS providers*
|
||||
|
||||
<img height="200" alt="DDNS Updater logo" src="https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/ddnsgopher.svg?sanitize=true">
|
||||
|
||||
[](https://github.com/qdm12/ddns-updater/actions?query=workflow%3A%22Buildx+latest%22)
|
||||
[](https://hub.docker.com/r/qmcgaw/ddns-updater)
|
||||
[](https://hub.docker.com/r/qmcgaw/ddns-updater)
|
||||
[](https://microbadger.com/images/qmcgaw/ddns-updater)
|
||||
[](https://microbadger.com/images/qmcgaw/ddns-updater)
|
||||
|
||||
[](https://join.slack.com/t/qdm12/shared_invite/enQtODMwMDQyMTAxMjY1LTU1YjE1MTVhNTBmNTViNzJiZmQwZWRmMDhhZjEyNjVhZGM4YmIxOTMxOTYzN2U0N2U2YjQ2MDk3YmYxN2NiNTc)
|
||||
[](https://github.com/qdm12/ddns-updater/issues)
|
||||
[](https://github.com/qdm12/ddns-updater/issues)
|
||||
[](https://github.com/qdm12/ddns-updater/issues)
|
||||
|
||||
## Features
|
||||
|
||||
- Updates periodically A records for different DNS providers:
|
||||
- Cloudflare
|
||||
- DD24
|
||||
- DDNSS.de
|
||||
- DigitalOcean
|
||||
- DonDominio
|
||||
- DNSOMatic
|
||||
- DNSPod
|
||||
- Dreamhost
|
||||
- DuckDNS
|
||||
- DynDNS
|
||||
- FreeDNS
|
||||
- Gandi
|
||||
- GoDaddy
|
||||
- Google
|
||||
- He.net
|
||||
- Infomaniak
|
||||
- Linode
|
||||
- LuaDNS
|
||||
- Namecheap
|
||||
- NoIP
|
||||
- Njalla
|
||||
- OpenDNS
|
||||
- OVH
|
||||
- Selfhost.de
|
||||
- Spdyn
|
||||
- Strato.de
|
||||
- Variomedia.de
|
||||
- **Want more?** [Create an issue for it](https://github.com/qdm12/ddns-updater/issues/new/choose)!
|
||||
- Web User interface
|
||||
|
||||

|
||||
|
||||
- 14MB Docker image based on a Go static binary in a Scratch Docker image with ca-certificates and timezone data
|
||||
- Persistence with a JSON file *updates.json* to store old IP addresses with change times for each record
|
||||
- Docker healthcheck verifying the DNS resolution of your domains
|
||||
- Highly configurable
|
||||
- Send notifications with [**Shoutrrr**](https://containrrr.dev/shoutrrr/services/overview/) using `SHOUTRRR_ADDRESSES`
|
||||
- Compatible with `amd64`, `386`, `arm64`, `armv7`, `armv6`, `s390x`, `ppc64le`, `riscv64` CPU architectures.
|
||||
|
||||
## Setup
|
||||
|
||||
The program reads the configuration from a JSON object, either from a file or from an environment variable.
|
||||
|
||||
1. Create a directory of your choice, say *data* with a file named **config.json** inside:
|
||||
|
||||
```sh
|
||||
mkdir data
|
||||
touch data/config.json
|
||||
# Owned by user ID of Docker container (1000)
|
||||
chown -R 1000 data
|
||||
# all access (for creating json database file data/updates.json)
|
||||
chmod 700 data
|
||||
# read access only
|
||||
chmod 400 data/config.json
|
||||
```
|
||||
|
||||
*(You could change the user ID, for example with `1001`, by running the container with `--user=1001`)*
|
||||
|
||||
1. Write a JSON configuration in *data/config.json*, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "namecheap",
|
||||
"domain": "example.com",
|
||||
"host": "@",
|
||||
"password": "e5322165c1d74692bfa6d807100c0310"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can find more information in the [configuration section](#configuration) to customize it.
|
||||
|
||||
1. Run the container with
|
||||
|
||||
```sh
|
||||
docker run -d -p 8000:8000/tcp -v "$(pwd)"/data:/updater/data qmcgaw/ddns-updater
|
||||
```
|
||||
|
||||
1. ⚠️ If you use IPv6, you might need to set `-e IPV6_PREFIX=/64` (`/64` is your prefix, depending on your ISP)
|
||||
1. (Optional) You can also set your JSON configuration as a single environment variable line (i.e. `{"settings": [{"provider": "namecheap", ...}]}`), which takes precedence over config.json. Note however that if you don't bind mount the `/updater/data` directory, there won't be a persistent database file `/updater/updates.json` but it will still work.
|
||||
|
||||
### Next steps
|
||||
|
||||
You can also use [docker-compose.yml](https://github.com/qdm12/ddns-updater/blob/master/docker-compose.yml) with:
|
||||
|
||||
```sh
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
You can update the image with `docker pull qmcgaw/ddns-updater`. Other [Docker image tags are available](https://hub.docker.com/repository/docker/qmcgaw/ddns-updater/tags).
|
||||
|
||||
## Configuration
|
||||
|
||||
Start by having the following content in *config.json*, or in your `CONFIG` environment variable:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "",
|
||||
},
|
||||
{
|
||||
"provider": "",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For each setting, you need to fill in parameters.
|
||||
Check the documentation for your DNS provider:
|
||||
|
||||
- [Cloudflare](https://github.com/qdm12/ddns-updater/blob/master/docs/cloudflare.md)
|
||||
- [DDNSS.de](https://github.com/qdm12/ddns-updater/blob/master/docs/ddnss.de.md)
|
||||
- [DigitalOcean](https://github.com/qdm12/ddns-updater/blob/master/docs/digitalocean.md)
|
||||
- [DD24](https://github.com/qdm12/ddns-updater/blob/master/docs/domaindiscount24.md)
|
||||
- [DonDominio](https://github.com/qdm12/ddns-updater/blob/master/docs/dondominio.md)
|
||||
- [DNSOMatic](https://github.com/qdm12/ddns-updater/blob/master/docs/dnsomatic.md)
|
||||
- [DNSPod](https://github.com/qdm12/ddns-updater/blob/master/docs/dnspod.md)
|
||||
- [Dreamhost](https://github.com/qdm12/ddns-updater/blob/master/docs/dreamhost.md)
|
||||
- [DuckDNS](https://github.com/qdm12/ddns-updater/blob/master/docs/duckdns.md)
|
||||
- [DynDNS](https://github.com/qdm12/ddns-updater/blob/master/docs/dyndns.md)
|
||||
- [DynV6](https://github.com/qdm12/ddns-updater/blob/master/docs/dynv6.md)
|
||||
- [FreeDNS](https://github.com/qdm12/ddns-updater/blob/master/docs/freedns.md)
|
||||
- [Gandi](https://github.com/qdm12/ddns-updater/blob/master/docs/gandi.md)
|
||||
- [GoDaddy](https://github.com/qdm12/ddns-updater/blob/master/docs/godaddy.md)
|
||||
- [Google](https://github.com/qdm12/ddns-updater/blob/master/docs/google.md)
|
||||
- [He.net](https://github.com/qdm12/ddns-updater/blob/master/docs/he.net.md)
|
||||
- [Infomaniak](https://github.com/qdm12/ddns-updater/blob/master/docs/infomaniak.md)
|
||||
- [Linode](https://github.com/qdm12/ddns-updater/blob/master/docs/linode.md)
|
||||
- [LuaDNS](https://github.com/qdm12/ddns-updater/blob/master/docs/luadns.md)
|
||||
- [Namecheap](https://github.com/qdm12/ddns-updater/blob/master/docs/namecheap.md)
|
||||
- [NoIP](https://github.com/qdm12/ddns-updater/blob/master/docs/noip.md)
|
||||
- [Njalla](https://github.com/qdm12/ddns-updater/blob/master/docs/njalla.md)
|
||||
- [OpenDNS](https://github.com/qdm12/ddns-updater/blob/master/docs/opendns.md)
|
||||
- [OVH](https://github.com/qdm12/ddns-updater/blob/master/docs/ovh.md)
|
||||
- [Selfhost.de](https://github.com/qdm12/ddns-updater/blob/master/docs/selfhost.de.md)
|
||||
- [Spdyn](https://github.com/qdm12/ddns-updater/blob/master/docs/spdyn.md)
|
||||
- [Strato.de](https://github.com/qdm12/ddns-updater/blob/master/docs/strato.md)
|
||||
- [Variomedia.de](https://github.com/qdm12/ddns-updater/blob/master/docs/variomedia.md)
|
||||
|
||||
Note that:
|
||||
|
||||
- you can specify multiple hosts for the same domain using a comma separated list. For example with `"host": "@,subdomain1,subdomain2",`.
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Environment variable | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `CONFIG` | | One line JSON object containing the entire config (takes precendence over config.json file) if specified |
|
||||
| `PERIOD` | `5m` | Default period of IP address check, following [this format](https://golang.org/pkg/time/#ParseDuration) |
|
||||
| `IPV6_PREFIX` | `/128` | IPv6 prefix used to mask your public IPv6 address and your record IPv6 address. Ranges from `/0` to `/128` depending on your ISP. |
|
||||
| `PUBLICIP_FETCHERS` | `all` | Comma separated fetcher types to obtain the public IP address from `http` and `dns` |
|
||||
| `PUBLICIP_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (ipv4 or ipv6). See the [Public IP section](#Public-IP) |
|
||||
| `PUBLICIPV4_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv4 address only. See the [Public IP section](#Public-IP) |
|
||||
| `PUBLICIPV6_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv6 address only. See the [Public IP section](#Public-IP) |
|
||||
| `PUBLICIP_DNS_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (IPv4 and/or IPv6). See the [Public IP section](#Public-IP) |
|
||||
| `PUBLICIP_DNS_TIMEOUT` | `3s` | Public IP DNS query timeout |
|
||||
| `UPDATE_COOLDOWN_PERIOD` | `5m` | Duration to cooldown between updates for each record. This is useful to avoid being rate limited or banned. |
|
||||
| `HTTP_TIMEOUT` | `10s` | Timeout for all HTTP requests |
|
||||
| `LISTENING_PORT` | `8000` | Internal TCP listening port for the web UI |
|
||||
| `ROOT_URL` | `/` | URL path to append to all paths to the webUI (i.e. `/ddns` for accessing `https://example.com/ddns` through a proxy) |
|
||||
| `HEALTH_SERVER_ADDRESS` | `127.0.0.1:9999` | Health server listening address |
|
||||
| `DATADIR` | `/updater/data` | Directory to read and write data files from internally |
|
||||
| `BACKUP_PERIOD` | `0` | Set to a period (i.e. `72h15m`) to enable zip backups of data/config.json and data/updates.json in a zip file |
|
||||
| `BACKUP_DIRECTORY` | `/updater/data` | Directory to write backup zip files to if `BACKUP_PERIOD` is not `0`. |
|
||||
| `LOG_LEVEL` | `info` | Level of logging, `debug`, `info`, `warning` or `error` |
|
||||
| `LOG_CALLER` | `hidden` | Show caller per log line, `hidden` or `short` |
|
||||
| `SHOUTRRR_ADDRESSES` | | (optional) Comma separated list of [Shoutrrr addresses](https://containrrr.dev/shoutrrr/services/overview/) (notification services) |
|
||||
| `TZ` | | Timezone to have accurate times, i.e. `America/Montreal` |
|
||||
|
||||
#### Public IP
|
||||
|
||||
By default, all public IP fetching types are used and cycled (over DNS and over HTTPs).
|
||||
|
||||
On top of that, for each fetching method, all echo services available are cycled on each request.
|
||||
|
||||
This allows you not to be blocked for making too many requests.
|
||||
|
||||
You can otherwise customize it with the following:
|
||||
|
||||
- `PUBLICIP_HTTP_PROVIDERS` gets your public IPv4 or IPv6 address. It can be one or more of the following:
|
||||
- `opendns` using [https://diagnostic.opendns.com/myip](https://diagnostic.opendns.com/myip)
|
||||
- `ifconfig` using [https://ifconfig.io/ip](https://ifconfig.io/ip)
|
||||
- `ipinfo` using [https://ipinfo.io/ip](https://ipinfo.io/ip)
|
||||
- `ddnss` using [https://ddnss.de/meineip.php](https://ddnss.de/meineip.php)
|
||||
- `google` using [https://domains.google.com/checkip](https://domains.google.com/checkip)
|
||||
- You can also specify an HTTPS URL such as `https://ipinfo.io/ip`
|
||||
- `PUBLICIPV4_HTTP_PROVIDERS` gets your public IPv4 address only. It can be one or more of the following:
|
||||
- `ipify` using [https://api.ipify.org](https://api.ipify.org)
|
||||
- `noip` using [http://ip1.dynupdate.no-ip.com](http://ip1.dynupdate.no-ip.com)
|
||||
- You can also specify an HTTPS URL such as `https://ipinfo.io/ip`
|
||||
- `PUBLICIPV6_HTTP_PROVIDERS` gets your public IPv6 address only. It can be one or more of the following:
|
||||
- `ipify` using [https://api6.ipify.org](https://api6.ipify.org)
|
||||
- `noip` using [http://ip1.dynupdate6.no-ip.com](http://ip1.dynupdate6.no-ip.com)
|
||||
- You can also specify an HTTPS URL such as `https://ipinfo.io/ip`
|
||||
- `PUBLICIP_DNS_PROVIDERS` gets your public IPv4 address only or IPv6 address only or one of them (see #136). It can be one or more of the following:
|
||||
- `google`
|
||||
- `cloudflare`
|
||||
|
||||
### Host firewall
|
||||
|
||||
If you have a host firewall in place, this container needs the following ports:
|
||||
|
||||
- TCP 443 outbound for outbound HTTPS
|
||||
- UDP 53 outbound for outbound DNS resolution
|
||||
- TCP 8000 inbound (or other) for the WebUI
|
||||
|
||||
## Architecture
|
||||
|
||||
At program start and every period (5 minutes by default):
|
||||
|
||||
1. Fetch your public IP address
|
||||
1. For each record:
|
||||
1. DNS resolve it to obtain its current IP address(es)
|
||||
- If the resolution fails, update the record with your public IP address by calling the DNS provider API and finish
|
||||
1. Check if your public IP address is within the resolved IP addresses
|
||||
- Yes: skip the update
|
||||
- No: update the record with your public IP address by calling the DNS provider API
|
||||
|
||||
💡 We do DNS resolution every period so it detects a change made to the record manually, for example on the DNS provider web UI
|
||||
💡 As DNS resolutions are essentially free and without rate limiting, these are great to avoid getting banned for too many requests.
|
||||
|
||||
### Special case: Cloudflare
|
||||
|
||||
For Cloudflare records with the `proxied` option, the following is done.
|
||||
|
||||
At program start and every period (5 minutes by default), for each record:
|
||||
|
||||
1. Fetch your public IP address
|
||||
1. For each record:
|
||||
1. Check the last IP address (persisted in `updates.json`) for that record
|
||||
- If it doesn't exist, update the record with your public IP address by calling the DNS provider API and finish
|
||||
1. Check if your public IP address matches the last IP address you updated the record with
|
||||
- Yes: skip the update
|
||||
- No: update the record with your public IP address by calling the DNS provider API
|
||||
|
||||
This is the only way as doing a DNS resolution on the record will give the IP address of a Cloudflare server instead of your server.
|
||||
|
||||
⚠️ This has the disadvantage that if the record is changed manually, the program will not detect it.
|
||||
We could do an API call to get the record IP address every period, but that would get you banned especially with a low period duration.
|
||||
|
||||
## Testing
|
||||
|
||||
- The automated healthcheck verifies all your records are up to date [using DNS lookups](https://github.com/qdm12/ddns-updater/blob/master/internal/healthcheck/healthcheck.go#L15)
|
||||
- You can also manually check, by:
|
||||
1. Going to your DNS management webpage
|
||||
1. Setting your record to `127.0.0.1`
|
||||
1. Run the container
|
||||
1. Refresh the DNS management webpage and verify the update happened
|
||||
|
||||
## Development and contributing
|
||||
|
||||
- [Contribute with code](https://github.com/qdm12/ddns-updater/blob/master/docs/contributing.md)
|
||||
- [Github workflows to know what's building](https://github.com/qdm12/ddns-updater/actions)
|
||||
- [List of issues and feature requests](https://github.com/qdm12/ddns-updater/issues)
|
||||
- [Kanban board](https://github.com/qdm12/ddns-updater/projects/1)
|
||||
|
||||
## License
|
||||
|
||||
This repository is under an [MIT license](https://github.com/qdm12/ddns-updater/master/license)
|
||||
|
||||
## Used in external projects
|
||||
|
||||
- [Starttoaster/docker-traefik](https://github.com/Starttoaster/docker-traefik#home-networks-extra-credit-dynamic-dns)
|
||||
|
||||
## Support
|
||||
|
||||
Sponsor me on [Github](https://github.com/sponsors/qdm12) or donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||
|
||||
[](https://github.com/sponsors/qdm12)
|
||||
[](https://www.paypal.me/qmcgaw)
|
||||
|
||||
Many thanks to J. Famiglietti for supporting me financially 🥇👍
|
||||
# Lightweight universal DDNS Updater program
|
||||
|
||||
Program to keep DNS A and/or AAAA records updated for multiple DNS providers
|
||||
|
||||
<img height="200" alt="DDNS Updater logo" src="https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/ddnsgopher.svg">
|
||||
|
||||
[](https://github.com/qdm12/ddns-updater/actions/workflows/build.yml)
|
||||
|
||||

|
||||

|
||||
[](https://hub.docker.com/r/qmcgaw/ddns-updater/tags?page=1&ordering=last_updated)
|
||||

|
||||

|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/ddns-updater/tags)
|
||||
|
||||
[](https://github.com/qdm12/ddns-updater/commits/main)
|
||||
[](https://github.com/qdm12/ddns-updater/graphs/contributors)
|
||||
[](https://github.com/qdm12/ddns-updater/pulls?q=is%3Apr+is%3Aclosed)
|
||||
[](https://github.com/qdm12/ddns-updater/issues)
|
||||
[](https://github.com/qdm12/ddns-updater/issues?q=is%3Aissue+is%3Aclosed)
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
[](LICENSE)
|
||||

|
||||
|
||||
## Versioned documentation
|
||||
|
||||
This readme and the [docs/](docs/) directory are **versioned** to match the program version:
|
||||
|
||||
| Version | Readme link | Docs link |
|
||||
| --- | --- | --- |
|
||||
| Latest | [README](https://github.com/qdm12/ddns-updater/blob/master/README.md) | [docs/](https://github.com/qdm12/ddns-updater/tree/master/docs) |
|
||||
| `v2.8` | [README](https://github.com/qdm12/ddns-updater/blob/v2.8.0/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.8.0/docs) |
|
||||
| `v2.7` | [README](https://github.com/qdm12/ddns-updater/blob/v2.7.1/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.7.1/docs) |
|
||||
| `v2.6` | [README](https://github.com/qdm12/ddns-updater/blob/v2.6.1/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.6.1/docs) |
|
||||
| `v2.5` | [README](https://github.com/qdm12/ddns-updater/blob/v2.5.0/README.md) | [docs/](https://github.com/qdm12/ddns-updater/blob/v2.5.0/docs) |
|
||||
|
||||
## Features
|
||||
|
||||
- Available as a Docker image [`ghcr.io/qdm12/ddns-updater`]((https://github.com/qdm12/ddns-updater/pkgs/container/ddns-updater)) and [`qmcgaw/ddns-updater`](https://hub.docker.com/r/qmcgaw/ddns-updater)
|
||||
- Available as [zero-dependency binaries for Linux, Windows and MacOS](https://github.com/qdm12/ddns-updater/releases)
|
||||
- 🆕 Available in the AUR as [`ddns-updater`](https://aur.archlinux.org/packages/ddns-updater) - see [#808](https://github.com/qdm12/ddns-updater/discussions/808)
|
||||
- Updates periodically A records for different DNS providers:
|
||||
- Aliyun
|
||||
- AllInkl
|
||||
- ChangeIP
|
||||
- Cloudflare
|
||||
- DD24
|
||||
- DDNSS.de
|
||||
- deSEC
|
||||
- DigitalOcean
|
||||
- Domeneshop
|
||||
- DonDominio
|
||||
- DNSOMatic
|
||||
- DNSPod
|
||||
- Dreamhost
|
||||
- DuckDNS
|
||||
- DynDNS
|
||||
- Dynu
|
||||
- DynV6
|
||||
- EasyDNS
|
||||
- FreeDNS
|
||||
- Gandi
|
||||
- GCP
|
||||
- GoDaddy
|
||||
- GoIP.de
|
||||
- He.net
|
||||
- Hetzner (legacy API)
|
||||
- Hetzner Cloud
|
||||
- Infomaniak
|
||||
- INWX
|
||||
- Ionos
|
||||
- ipv64
|
||||
- Linode
|
||||
- Loopia
|
||||
- LuaDNS
|
||||
- Myaddr
|
||||
- Name.com
|
||||
- Namecheap
|
||||
- NameSilo
|
||||
- Netcup
|
||||
- NoIP
|
||||
- Now-DNS
|
||||
- Njalla
|
||||
- OpenDNS
|
||||
- OVH
|
||||
- Porkbun
|
||||
- Route53
|
||||
- Scaleway
|
||||
- Selfhost.de
|
||||
- Servercow.de
|
||||
- Spdyn
|
||||
- Strato.de
|
||||
- Variomedia.de
|
||||
- Vultr
|
||||
- Zoneedit
|
||||
- **Want more?** [Create an issue for it](https://github.com/qdm12/ddns-updater/issues/new/choose)!
|
||||
- Web user interface (Desktop)
|
||||
|
||||

|
||||
|
||||
- Web user interface (Mobile)
|
||||
|
||||

|
||||
|
||||
- Send notifications with [**Shoutrrr**](https://containrrr.dev/shoutrrr/v0.8/services/overview/) using `SHOUTRRR_ADDRESSES`
|
||||
- Container (Docker/K8s) specific features:
|
||||
- Lightweight 12MB Docker image based on the Scratch Docker image
|
||||
- Docker healthcheck verifying the DNS resolution of your domains
|
||||
- Images compatible with `amd64`, `386`, `arm64`, `armv7`, `armv6`, `s390x`, `ppc64le`, `riscv64` CPU architectures
|
||||
- Persistence with a JSON file *updates.json* to store old IP addresses with change times for each record
|
||||
|
||||
## Setup
|
||||
|
||||
### Binary programs
|
||||
|
||||
1. Download the pre-built program for your platform from the assets of a release in the [releases page](https://github.com/qdm12/ddns-updater/releases). You can alternatively download, build and install the latest version of the program by installing [Go](https://golang.org/doc/install) and then run `go install github.com/qdm12/ddns-updater/cmd/ddns-updater@latest`.
|
||||
1. For Linux and MacOS, make the program executable with `chmod +x ddns-updater`.
|
||||
1. In the directory where the program is saved, create a directory `data`.
|
||||
1. Write a JSON configuration in `data/config.json`, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "namecheap",
|
||||
"domain": "sub.example.com",
|
||||
"password": "e5322165c1d74692bfa6d807100c0310"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can find more information in the [configuration section](#configuration) to customize it.
|
||||
1. Run the program with `./ddns-updater` (`./ddns-updater.exe` on Windows) or by double-clicking on it.
|
||||
1. The following is **optional**.
|
||||
- You can customize the program behavior using either [environment variables](#environment-variables) or flags. For flags, there is a flag corresponding to each environment variable, where it's all lowercase and underscores are replaced with dashes. For example the environment variable `LOG_LEVEL` translates into `--log-level`.
|
||||
|
||||
### Container
|
||||
|
||||
[➡️ Qnap guide by @Araminta](https://github.com/qdm12/ddns-updater/issues/708)
|
||||
|
||||
1. Create a directory, for example, *data* which is:
|
||||
- owned by user id `1000`, which is the built-in user ID of the ddns-updater container
|
||||
- has user read+write+execute permissions
|
||||
|
||||
```sh
|
||||
mkdir data
|
||||
chown 1000 data
|
||||
chmod u+r+w+x data
|
||||
```
|
||||
|
||||
If you want to use another user ID, [build the image yourself](#build-the-image) with `--build-arg UID=<your-uid>`. You could also just run the container as root with `--user="0"` but this is not advised security wise.
|
||||
|
||||
1. Similarly, create a *data/config.json* file which is:
|
||||
- owned by user id `1000`
|
||||
- has user read permissions
|
||||
|
||||
```sh
|
||||
touch data/config.json
|
||||
chmod u+r data/config.json
|
||||
```
|
||||
|
||||
1. Edit *data/config.json*, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "namecheap",
|
||||
"domain": "sub.example.com",
|
||||
"password": "e5322165c1d74692bfa6d807100c0310"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can find more information in the [configuration section](#configuration) to customize it.
|
||||
|
||||
1. Run the container with
|
||||
|
||||
```sh
|
||||
docker run -d -p 8000:8000/tcp -v "$(pwd)"/data:/updater/data ghcr.io/qdm12/ddns-updater
|
||||
```
|
||||
|
||||
1. The following is **optional**.
|
||||
- You can customize the program behavior using [environment variables](#environment-variables)
|
||||
- You can use [docker-compose.yml](docker-compose.yml) with `docker-compose up -d`
|
||||
- **Kubernetes**: check out the [k8s directory](k8s) for an installation guide and examples.
|
||||
- Other [Docker image tags are available](https://github.com/qdm12/ddns-updater/pkgs/container/ddns-updater)
|
||||
- You can update the image with `docker pull ghcr.io/qdm12/ddns-updater`
|
||||
- You can set your JSON configuration as a single environment variable line (i.e. `{"settings": [{"provider": "namecheap", ...}]}`), which takes precedence over config.json. Note however that if you don't bind mount the `/updater/data` directory, there won't be a persistent database file `/updater/updates.json` but it will still work.
|
||||
|
||||
## Configuration
|
||||
|
||||
Start by having the following content in *config.json*, or in your `CONFIG` environment variable:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "",
|
||||
},
|
||||
{
|
||||
"provider": "",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For each setting, you need to fill in parameters.
|
||||
Check the documentation for your DNS provider:
|
||||
|
||||
- [Aliyun](docs/aliyun.md)
|
||||
- [Allinkl](docs/allinkl.md)
|
||||
- [ChangeIP](docs/changeip.md)
|
||||
- [Cloudflare](docs/cloudflare.md)
|
||||
- [DD24](docs/dd24.md)
|
||||
- [DDNSS.de](docs/ddnss.de.md)
|
||||
- [deSEC](docs/desec.md)
|
||||
- [DigitalOcean](docs/digitalocean.md)
|
||||
- [Domeneshop](docs/domeneshop.md)
|
||||
- [DonDominio](docs/dondominio.md)
|
||||
- [DNSOMatic](docs/dnsomatic.md)
|
||||
- [DNSPod](docs/dnspod.md)
|
||||
- [Dreamhost](docs/dreamhost.md)
|
||||
- [DuckDNS](docs/duckdns.md)
|
||||
- [DynDNS](docs/dyndns.md)
|
||||
- [Dynu](docs/dynu.md)
|
||||
- [DynV6](docs/dynv6.md)
|
||||
- [EasyDNS](docs/easydns.md)
|
||||
- [FreeDNS](docs/freedns.md)
|
||||
- [Gandi](docs/gandi.md)
|
||||
- [GCP](docs/gcp.md)
|
||||
- [GoDaddy](docs/godaddy.md)
|
||||
- [GoIP.de](docs/goip.md)
|
||||
- [He.net](docs/he.net.md)
|
||||
- [Hetzner](docs/hetzner.md)
|
||||
- [HetznerCloud](docs/hetznercloud.md)
|
||||
- [Infomaniak](docs/infomaniak.md)
|
||||
- [INWX](docs/inwx.md)
|
||||
- [Ionos](docs/ionos.md)
|
||||
- [IPv64](docs/ipv64.md)
|
||||
- [Linode](docs/linode.md)
|
||||
- [Loopia](docs/loopia.md)
|
||||
- [LuaDNS](docs/luadns.md)
|
||||
- [Myaddr](docs/myaddr.md)
|
||||
- [Name.com](docs/name.com.md)
|
||||
- [Namecheap](docs/namecheap.md)
|
||||
- [NameSilo](docs/namesilo.md)
|
||||
- [Netcup](docs/netcup.md)
|
||||
- [NoIP](docs/noip.md)
|
||||
- [Now-DNS](docs/nowdns.md)
|
||||
- [Njalla](docs/njalla.md)
|
||||
- [OpenDNS](docs/opendns.md)
|
||||
- [OVH](docs/ovh.md)
|
||||
- [Porkbun](docs/porkbun.md)
|
||||
- [Route53](docs/route53.md)
|
||||
- [Scaleway](docs/scaleway.md)
|
||||
- [Selfhost.de](docs/selfhost.de.md)
|
||||
- [Servercow.de](docs/servercow.md)
|
||||
- [Spdyn](docs/spdyn.md)
|
||||
- [Strato.de](docs/strato.md)
|
||||
- [Variomedia.de](docs/variomedia.md)
|
||||
- [Vultr](docs/vultr.md)
|
||||
- [Zoneedit](docs/zoneedit.md)
|
||||
- [Custom](docs/custom.md)
|
||||
|
||||
Note that:
|
||||
|
||||
- you can specify multiple owners/hosts for the same domain using a comma separated list. For example with `"domain": "example.com,sub.example.com,sub2.example.com",`.
|
||||
⚠️ this is a bit different for DuckDNS and GoIP, see their respective documentation.
|
||||
|
||||
### Environment variables
|
||||
|
||||
🆕 There are now flags equivalent for each variable below, for example `--log-level`.
|
||||
|
||||
| Environment variable | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `CONFIG` | | One line JSON object containing the entire config (takes precedence over config.json file) if specified |
|
||||
| `PERIOD` | `5m` | Default period of IP address check, following [this format](https://golang.org/pkg/time/#ParseDuration) |
|
||||
| `PUBLICIP_FETCHERS` | `all` | Comma separated fetcher types to obtain the public IP address from `http` and `dns` |
|
||||
| `PUBLICIP_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (ipv4 or ipv6). See the [Public IP section](#public-ip) |
|
||||
| `PUBLICIPV4_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv4 address only. See the [Public IP section](#public-ip) |
|
||||
| `PUBLICIPV6_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv6 address only. See the [Public IP section](#public-ip) |
|
||||
| `PUBLICIP_DNS_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (IPv4 and/or IPv6). See the [Public IP section](#public-ip) |
|
||||
| `PUBLICIP_DNS_TIMEOUT` | `3s` | Public IP DNS query timeout |
|
||||
| `UPDATE_COOLDOWN_PERIOD` | `5m` | Duration to cooldown between updates for each record. This is useful to avoid being rate limited or banned. |
|
||||
| `HTTP_TIMEOUT` | `10s` | Timeout for all HTTP requests |
|
||||
| `SERVER_ENABLED` | `yes` | Enable the web server and web UI |
|
||||
| `LISTENING_ADDRESS` | `:8000` | Internal TCP listening port for the web UI |
|
||||
| `ROOT_URL` | `/` | URL path to append to all paths to the webUI (i.e. `/ddns` for accessing `https://example.com/ddns` through a proxy) |
|
||||
| `HEALTH_SERVER_ADDRESS` | `127.0.0.1:9999` | Health server listening address |
|
||||
| `HEALTH_HEALTHCHECKSIO_BASE_URL` | `https://hc-ping.com` | Base URL for the [healthchecks.io](https://healthchecks.io) server |
|
||||
| `HEALTH_HEALTHCHECKSIO_UUID` | | UUID to idenfity with the [healthchecks.io](https://healthchecks.io) server |
|
||||
| `DATADIR` | `/updater/data` | Directory to read and write data files from internally |
|
||||
| `CONFIG_FILEPATH` | `/updater/data/config.json` | Path to the JSON configuration file |
|
||||
| `BACKUP_PERIOD` | `0` | Set to a period (i.e. `72h15m`) to enable zip backups of data/config.json and data/updates.json in a zip file |
|
||||
| `BACKUP_DIRECTORY` | `/updater/data` | Directory to write backup zip files to if `BACKUP_PERIOD` is not `0`. |
|
||||
| `RESOLVER_ADDRESS` | Your network DNS | A plaintext DNS address to use to resolve your domain names defined in your settings only. For example it can be `1.1.1.1:53`. This is useful for split dns, see [#389](https://github.com/qdm12/ddns-updater/issues/389) |
|
||||
| `LOG_LEVEL` | `info` | Level of logging, `debug`, `info`, `warning` or `error` |
|
||||
| `LOG_CALLER` | `hidden` | Show caller per log line, `hidden` or `short` |
|
||||
| `SHOUTRRR_ADDRESSES` | | (optional) Comma separated list of [Shoutrrr addresses](https://containrrr.dev/shoutrrr/v0.8/services/overview/) (notification services) |
|
||||
| `SHOUTRRR_DEFAULT_TITLE` | `DDNS Updater` | Default title for Shoutrrr notifications |
|
||||
| `TZ` | | Timezone to have accurate times, i.e. `America/Montreal` |
|
||||
| `UMASK` | System current umask | Umask to set for the program in octal, i.e. `0022` |
|
||||
|
||||
#### Public IP
|
||||
|
||||
By default, all public IP fetching types are used and cycled (over DNS and over HTTPs).
|
||||
|
||||
On top of that, for each fetching method, all echo services available are cycled on each request.
|
||||
|
||||
This allows you not to be blocked for making too many requests.
|
||||
|
||||
You can otherwise customize it with the following:
|
||||
|
||||
- `PUBLICIP_HTTP_PROVIDERS` gets your public IPv4 or IPv6 address. It can be one or more of the following:
|
||||
- `ipify` using [https://api64.ipify.org](https://api64.ipify.org)
|
||||
- `ifconfig` using [https://ifconfig.io/ip](https://ifconfig.io/ip)
|
||||
- `ipinfo` using [https://ipinfo.io/ip](https://ipinfo.io/ip)
|
||||
- `spdyn` using [https://checkip.spdyn.de](https://checkip.spdyn.de/)
|
||||
- `ipleak` using [https://ipleak.net/json](https://ipleak.net/json)
|
||||
- `icanhazip` using [https://icanhazip.com](https://icanhazip.com)
|
||||
- `ident` using [https://ident.me](https://ident.me)
|
||||
- `nnev` using [https://ip.nnev.de](https://ip.nnev.de)
|
||||
- `wtfismyip` using [https://wtfismyip.com/text](https://wtfismyip.com/text)
|
||||
- `seeip` using [https://api.seeip.org](https://api.seeip.org)
|
||||
- `changeip` using [https://ip.changeip.com](https://ip.changeip.com)
|
||||
- You can also specify an HTTPS URL with prefix `url:` for example `url:https://ipinfo.io/ip`
|
||||
- `PUBLICIPV4_HTTP_PROVIDERS` gets your public IPv4 address only. It can be one or more of the following:
|
||||
- `ipleak` using [https://ipv4.ipleak.net/json](https://ipv4.ipleak.net/json)
|
||||
- `ipify` using [https://api.ipify.org](https://api.ipify.org)
|
||||
- `icanhazip` using [https://ipv4.icanhazip.com](https://ipv4.icanhazip.com)
|
||||
- `ident` using [https://v4.ident.me](https://v4.ident.me)
|
||||
- `nnev` using [https://ip4.nnev.de](https://ip4.nnev.de)
|
||||
- `wtfismyip` using [https://ipv4.wtfismyip.com/text](https://ipv4.wtfismyip.com/text)
|
||||
- `seeip` using [https://ipv4.seeip.org](https://ipv4.seeip.org)
|
||||
- You can also specify an HTTPS URL with prefix `url:` for example `url:https://ipinfo.io/ip`
|
||||
- `PUBLICIPV6_HTTP_PROVIDERS` gets your public IPv6 address only. It can be one or more of the following:
|
||||
- `ipleak` using [https://ipv6.ipleak.net/json](https://ipv6.ipleak.net/json)
|
||||
- `ipify` using [https://api6.ipify.org](https://api6.ipify.org)
|
||||
- `icanhazip` using [https://ipv6.icanhazip.com](https://ipv6.icanhazip.com)
|
||||
- `ident` using [https://v6.ident.me](https://v6.ident.me)
|
||||
- `nnev` using [https://ip6.nnev.de](https://ip6.nnev.de)
|
||||
- `wtfismyip` using [https://ipv6.wtfismyip.com/text](https://ipv6.wtfismyip.com/text)
|
||||
- `seeip` using [https://ipv6.seeip.org](https://ipv6.seeip.org)
|
||||
- You can also specify an HTTPS URL with prefix `url:` for example `url:https://ipinfo.io/ip`
|
||||
- `PUBLICIP_DNS_PROVIDERS` gets your public IPv4 address only or IPv6 address only or one of them (see [#136](https://github.com/qdm12/ddns-updater/issues/136)). It can be one or more of the following:
|
||||
- `cloudflare`
|
||||
- `opendns`
|
||||
|
||||
### Host firewall
|
||||
|
||||
If you have a host firewall in place, this container needs the following ports:
|
||||
|
||||
- TCP 443 outbound for outbound HTTPS
|
||||
- UDP 53 outbound for outbound DNS resolution
|
||||
- TCP 8000 inbound (or other) for the WebUI
|
||||
|
||||
## Architecture
|
||||
|
||||
At program start and every period (5 minutes by default):
|
||||
|
||||
1. Fetch your public IP address
|
||||
1. For each record:
|
||||
1. DNS resolve it to obtain its current IP address(es)
|
||||
- If the resolution fails, update the record with your public IP address by calling the DNS provider API and finish
|
||||
1. Check if your public IP address is within the resolved IP addresses
|
||||
- Yes: skip the update
|
||||
- No: update the record with your public IP address by calling the DNS provider API
|
||||
|
||||
💡 We do DNS resolution every period so it detects a change made to the record manually, for example on the DNS provider web UI
|
||||
💡 As DNS resolutions are essentially free and without rate limiting, these are great to avoid getting banned for too many requests.
|
||||
|
||||
### Special case: Cloudflare
|
||||
|
||||
For Cloudflare records with the `proxied` option, the following is done.
|
||||
|
||||
At program start and every period (5 minutes by default), for each record:
|
||||
|
||||
1. Fetch your public IP address
|
||||
1. For each record:
|
||||
1. Check the last IP address (persisted in `updates.json`) for that record
|
||||
- If it doesn't exist, update the record with your public IP address by calling the DNS provider API and finish
|
||||
1. Check if your public IP address matches the last IP address you updated the record with
|
||||
- Yes: skip the update
|
||||
- No: update the record with your public IP address by calling the DNS provider API
|
||||
|
||||
This is the only way as doing a DNS resolution on the record will give the IP address of a Cloudflare server instead of your server.
|
||||
|
||||
⚠️ This has the disadvantage that if the record is changed manually, the program will not detect it.
|
||||
We could do an API call to get the record IP address every period, but that would get you banned especially with a low period duration.
|
||||
|
||||
## Testing
|
||||
|
||||
- The automated healthcheck verifies all your records are up to date [using DNS lookups](internal/health/check.go#L42)
|
||||
- You can also manually check, by:
|
||||
1. Going to your DNS management webpage
|
||||
1. Setting your record to `127.0.0.1`
|
||||
1. Run the container
|
||||
1. Refresh the DNS management webpage and verify the update happened
|
||||
|
||||
## Build the image
|
||||
|
||||
You can build the image yourself with:
|
||||
|
||||
```sh
|
||||
docker build -t ghcr.io/qdm12/ddns-updater https://github.com/qdm12/ddns-updater.git
|
||||
```
|
||||
|
||||
You can use optional build arguments with `--build-arg KEY=VALUE` from the table below:
|
||||
|
||||
| Build argument | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `UID` | `1000` | User ID running the container |
|
||||
| `GID` | `1000` | User group ID running the container |
|
||||
| `VERSION` | `unknown` | Version of the program and Docker image |
|
||||
| `CREATED` | `an unknown date` | Build date of the program and Docker image |
|
||||
| `COMMIT` | `unknown` | Commit hash of the program and Docker image |
|
||||
|
||||
## Development and contributing
|
||||
|
||||
- [Contribute with code](.github/CONTRIBUTING.md)
|
||||
- [Github workflows to know what's building](https://github.com/qdm12/ddns-updater/actions)
|
||||
- [List of issues and feature requests](https://github.com/qdm12/ddns-updater/issues)
|
||||
|
||||
## License
|
||||
|
||||
This repository is under an [MIT license](LICENSE)
|
||||
|
||||
## Used in external projects
|
||||
|
||||
- [Starttoaster/docker-traefik](https://github.com/Starttoaster/docker-traefik#home-networks-extra-credit-dynamic-dns)
|
||||
|
||||
## Support
|
||||
|
||||
Sponsor me on [Github](https://github.com/sponsors/qdm12) or donate to [paypal.me/qmcgaw](https://www.paypal.me/qmcgaw)
|
||||
|
||||
Many thanks to J. Famiglietti for supporting me financially 🥇👍
|
||||
|
||||
380
cmd/ddns-updater/main.go
Normal file
380
cmd/ddns-updater/main.go
Normal file
@@ -0,0 +1,380 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
_ "github.com/breml/rootcerts"
|
||||
"github.com/qdm12/ddns-updater/internal/backup"
|
||||
"github.com/qdm12/ddns-updater/internal/config"
|
||||
"github.com/qdm12/ddns-updater/internal/data"
|
||||
"github.com/qdm12/ddns-updater/internal/health"
|
||||
"github.com/qdm12/ddns-updater/internal/healthchecksio"
|
||||
"github.com/qdm12/ddns-updater/internal/models"
|
||||
"github.com/qdm12/ddns-updater/internal/noop"
|
||||
jsonparams "github.com/qdm12/ddns-updater/internal/params"
|
||||
persistence "github.com/qdm12/ddns-updater/internal/persistence/json"
|
||||
"github.com/qdm12/ddns-updater/internal/provider"
|
||||
recordslib "github.com/qdm12/ddns-updater/internal/records"
|
||||
"github.com/qdm12/ddns-updater/internal/resolver"
|
||||
"github.com/qdm12/ddns-updater/internal/server"
|
||||
"github.com/qdm12/ddns-updater/internal/shoutrrr"
|
||||
"github.com/qdm12/ddns-updater/internal/system"
|
||||
"github.com/qdm12/ddns-updater/internal/update"
|
||||
"github.com/qdm12/ddns-updater/pkg/publicip"
|
||||
"github.com/qdm12/goservices"
|
||||
"github.com/qdm12/gosettings/reader"
|
||||
"github.com/qdm12/gosplash"
|
||||
"github.com/qdm12/log"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
version = "unknown"
|
||||
commit = "unknown"
|
||||
date = "an unknown date"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buildInfo := models.BuildInformation{
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
Created: date,
|
||||
}
|
||||
logger := log.New()
|
||||
|
||||
reader := reader.New(reader.Settings{
|
||||
HandleDeprecatedKey: func(source, oldKey, newKey string) {
|
||||
logger.Warnf("%q key %s is deprecated, please use %q instead",
|
||||
source, oldKey, newKey)
|
||||
},
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
errorCh := make(chan error)
|
||||
go func() {
|
||||
errorCh <- _main(ctx, reader, os.Args, logger, buildInfo, time.Now)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stop()
|
||||
logger.Warn("Caught OS signal, shutting down")
|
||||
case err := <-errorCh:
|
||||
stop()
|
||||
close(errorCh)
|
||||
if err == nil { // expected exit such as healthcheck
|
||||
os.Exit(0)
|
||||
}
|
||||
logger.Error(err.Error())
|
||||
cancel()
|
||||
}
|
||||
|
||||
const shutdownGracePeriod = 5 * time.Second
|
||||
timer := time.NewTimer(shutdownGracePeriod)
|
||||
select {
|
||||
case err := <-errorCh:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
logger.Info("Shutdown successful")
|
||||
case <-timer.C:
|
||||
logger.Warn("Shutdown timed out")
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func _main(ctx context.Context, reader *reader.Reader, args []string, logger log.LoggerInterface,
|
||||
buildInfo models.BuildInformation, timeNow func() time.Time,
|
||||
) (err error) {
|
||||
if len(args) > 1 {
|
||||
switch args[1] {
|
||||
case "version", "-version", "--version":
|
||||
fmt.Println(buildInfo.VersionString())
|
||||
return nil
|
||||
case "healthcheck":
|
||||
// Running the program in a separate instance through the Docker
|
||||
// built-in healthcheck, in an ephemeral fashion to query the
|
||||
// long running instance of the program about its status
|
||||
|
||||
var healthSettings config.Health
|
||||
healthSettings.Read(reader)
|
||||
healthSettings.SetDefaults()
|
||||
err = healthSettings.Validate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("health settings: %w", err)
|
||||
}
|
||||
|
||||
client := health.NewClient()
|
||||
return client.Query(ctx, *healthSettings.ServerAddress)
|
||||
}
|
||||
}
|
||||
|
||||
printSplash(buildInfo)
|
||||
|
||||
config, err := readConfig(reader, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *config.Paths.Umask > 0 {
|
||||
system.SetUmask(*config.Paths.Umask)
|
||||
}
|
||||
|
||||
shoutrrrSettings := shoutrrr.Settings{
|
||||
Addresses: config.Shoutrrr.Addresses,
|
||||
DefaultTitle: config.Shoutrrr.DefaultTitle,
|
||||
Logger: logger.New(log.SetComponent("shoutrrr")),
|
||||
}
|
||||
shoutrrrClient, err := shoutrrr.New(shoutrrrSettings)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up Shoutrrr: %w", err)
|
||||
}
|
||||
|
||||
persistentDB, err := persistence.NewDatabase(*config.Paths.DataDir)
|
||||
if err != nil {
|
||||
shoutrrrClient.Notify(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
jsonReader := jsonparams.NewReader(logger)
|
||||
providers, warnings, err := jsonReader.JSONProviders(*config.Paths.Config)
|
||||
for _, w := range warnings {
|
||||
logger.Warn(w)
|
||||
shoutrrrClient.Notify(w)
|
||||
}
|
||||
if err != nil {
|
||||
shoutrrrClient.Notify(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
logProvidersCount(len(providers), logger)
|
||||
|
||||
client := &http.Client{Timeout: config.Client.Timeout}
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
err = health.CheckHTTP(ctx, client)
|
||||
if err != nil {
|
||||
logger.Warn(err.Error())
|
||||
}
|
||||
|
||||
records, err := readRecords(providers, persistentDB, logger, shoutrrrClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading records: %w", err)
|
||||
}
|
||||
|
||||
db := data.NewDatabase(records, persistentDB)
|
||||
|
||||
httpSettings := publicip.HTTPSettings{
|
||||
Enabled: *config.PubIP.HTTPEnabled,
|
||||
Client: client,
|
||||
Options: config.PubIP.ToHTTPOptions(),
|
||||
}
|
||||
dnsSettings := publicip.DNSSettings{
|
||||
Enabled: *config.PubIP.DNSEnabled,
|
||||
Options: config.PubIP.ToDNSPOptions(),
|
||||
}
|
||||
|
||||
ipGetter, err := publicip.NewFetcher(dnsSettings, httpSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resolverSettings := resolver.Settings{
|
||||
Address: config.Resolver.Address,
|
||||
Timeout: config.Resolver.Timeout,
|
||||
}
|
||||
resolver, err := resolver.New(resolverSettings)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating resolver: %w", err)
|
||||
}
|
||||
|
||||
hioClient := healthchecksio.New(client, config.Health.HealthchecksioBaseURL,
|
||||
*config.Health.HealthchecksioUUID)
|
||||
|
||||
updater := update.NewUpdater(db, client, shoutrrrClient, logger, timeNow)
|
||||
updaterService := update.NewService(db, updater, ipGetter, config.Update.Period,
|
||||
config.Update.Cooldown, logger, resolver, timeNow, hioClient)
|
||||
|
||||
healthServer, err := createHealthServer(db, resolver, logger, *config.Health.ServerAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating health server: %w", err)
|
||||
}
|
||||
|
||||
server, err := createServer(ctx, config.Server, logger, db, updaterService)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating server: %w", err)
|
||||
}
|
||||
|
||||
var backupService goservices.Service
|
||||
backupLogger := logger.New(log.SetComponent("backup"))
|
||||
backupService = backup.New(*config.Backup.Period, *config.Paths.DataDir,
|
||||
*config.Backup.Directory, backupLogger)
|
||||
backupService, err = goservices.NewRestarter(goservices.RestarterSettings{Service: backupService})
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating backup restarter: %w", err)
|
||||
}
|
||||
|
||||
servicesSequence, err := goservices.NewSequence(goservices.SequenceSettings{
|
||||
ServicesStart: []goservices.Service{db, updaterService, healthServer, server, backupService},
|
||||
ServicesStop: []goservices.Service{server, healthServer, updaterService, backupService, db},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating services sequence: %w", err)
|
||||
}
|
||||
|
||||
runError, startErr := servicesSequence.Start(ctx)
|
||||
if startErr != nil {
|
||||
return fmt.Errorf("starting services: %w", startErr)
|
||||
}
|
||||
|
||||
// note: errors are logged within the goroutine,
|
||||
// no need to collect the resulting errors.
|
||||
go updaterService.ForceUpdate(ctx)
|
||||
|
||||
shoutrrrClient.Notify("Launched with " + strconv.Itoa(len(records)) + " records to watch")
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case err = <-runError:
|
||||
exitHealthchecksio(hioClient, logger, healthchecksio.Exit1)
|
||||
shoutrrrClient.Notify(err.Error())
|
||||
return fmt.Errorf("exiting due to critical error: %w", err)
|
||||
}
|
||||
|
||||
err = servicesSequence.Stop()
|
||||
if err != nil {
|
||||
exitHealthchecksio(hioClient, logger, healthchecksio.Exit1)
|
||||
shoutrrrClient.Notify(err.Error())
|
||||
return fmt.Errorf("stopping failed: %w", err)
|
||||
}
|
||||
|
||||
exitHealthchecksio(hioClient, logger, healthchecksio.Exit0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func printSplash(buildInfo models.BuildInformation) {
|
||||
announcementExp, err := time.Parse(time.RFC3339, "2024-10-15T00:00:00Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
splashSettings := gosplash.Settings{
|
||||
User: "qdm12",
|
||||
Repository: "ddns-updater",
|
||||
Emails: []string{"quentin.mcgaw@gmail.com"},
|
||||
Version: buildInfo.Version,
|
||||
Commit: buildInfo.Commit,
|
||||
Created: buildInfo.Created,
|
||||
Announcement: "Public IP http provider GOOGLE is no longer working",
|
||||
AnnounceExp: announcementExp,
|
||||
// Sponsor information
|
||||
PaypalUser: "qmcgaw",
|
||||
GithubSponsor: "qdm12",
|
||||
}
|
||||
for _, line := range gosplash.MakeLines(splashSettings) {
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
func readConfig(reader *reader.Reader, logger log.LoggerInterface) (
|
||||
config config.Config, err error,
|
||||
) {
|
||||
err = config.Read(reader, logger)
|
||||
if err != nil {
|
||||
return config, fmt.Errorf("reading settings: %w", err)
|
||||
}
|
||||
config.SetDefaults()
|
||||
err = config.Validate()
|
||||
if err != nil {
|
||||
return config, fmt.Errorf("settings validation: %w", err)
|
||||
}
|
||||
|
||||
logger.Patch(config.Logger.ToOptions()...)
|
||||
logger.Info(config.String())
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func logProvidersCount(providersCount int, logger log.LeveledLogger) {
|
||||
switch providersCount {
|
||||
case 0:
|
||||
logger.Warn("Found no setting to update record")
|
||||
case 1:
|
||||
logger.Info("Found single setting to update record")
|
||||
default:
|
||||
logger.Info("Found " + strconv.Itoa(providersCount) + " settings to update records")
|
||||
}
|
||||
}
|
||||
|
||||
func readRecords(providers []provider.Provider, persistentDB *persistence.Database,
|
||||
logger log.LoggerInterface, shoutrrrClient *shoutrrr.Client) (
|
||||
records []recordslib.Record, err error,
|
||||
) {
|
||||
records = make([]recordslib.Record, len(providers))
|
||||
for i, provider := range providers {
|
||||
logger.Info("Reading history from database: domain " +
|
||||
provider.Domain() + " owner " + provider.Owner() +
|
||||
" " + provider.IPVersion().String())
|
||||
events, err := persistentDB.GetEvents(provider.Domain(),
|
||||
provider.Owner(), provider.IPVersion())
|
||||
if err != nil {
|
||||
shoutrrrClient.Notify(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
records[i] = recordslib.New(provider, events)
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func exitHealthchecksio(hioClient *healthchecksio.Client,
|
||||
logger log.LoggerInterface, state healthchecksio.State,
|
||||
) {
|
||||
const timeout = 3 * time.Second
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
err := hioClient.Ping(ctx, state)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:ireturn
|
||||
func createHealthServer(db health.AllSelecter, resolver health.LookupIPer,
|
||||
logger log.LoggerInterface, serverAddress string) (
|
||||
healthServer goservices.Service, err error,
|
||||
) {
|
||||
if serverAddress == "" {
|
||||
return noop.New("healthcheck server"), nil
|
||||
}
|
||||
isHealthy := health.MakeIsHealthy(db, resolver)
|
||||
healthLogger := logger.New(log.SetComponent("healthcheck server"))
|
||||
return health.NewServer(serverAddress, healthLogger, isHealthy)
|
||||
}
|
||||
|
||||
//nolint:ireturn
|
||||
func createServer(ctx context.Context, config config.Server,
|
||||
logger log.LoggerInterface, db server.Database,
|
||||
updaterService server.UpdateForcer) (
|
||||
service goservices.Service, err error,
|
||||
) {
|
||||
if !*config.Enabled {
|
||||
return noop.New("server"), nil
|
||||
}
|
||||
serverLogger := logger.New(log.SetComponent("http server"))
|
||||
return server.New(ctx, config.ListeningAddress, config.RootURL,
|
||||
db, serverLogger, updaterService)
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/containrrr/shoutrrr"
|
||||
"github.com/qdm12/ddns-updater/internal/backup"
|
||||
"github.com/qdm12/ddns-updater/internal/config"
|
||||
"github.com/qdm12/ddns-updater/internal/data"
|
||||
"github.com/qdm12/ddns-updater/internal/health"
|
||||
"github.com/qdm12/ddns-updater/internal/models"
|
||||
jsonparams "github.com/qdm12/ddns-updater/internal/params"
|
||||
"github.com/qdm12/ddns-updater/internal/persistence"
|
||||
recordslib "github.com/qdm12/ddns-updater/internal/records"
|
||||
"github.com/qdm12/ddns-updater/internal/server"
|
||||
"github.com/qdm12/ddns-updater/internal/splash"
|
||||
"github.com/qdm12/ddns-updater/internal/update"
|
||||
"github.com/qdm12/ddns-updater/pkg/publicip"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/network/connectivity"
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/goshutdown"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
version = "unknown"
|
||||
commit = "unknown"
|
||||
buildDate = "an unknown date"
|
||||
)
|
||||
|
||||
func main() {
|
||||
buildInfo := models.BuildInformation{
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
BuildDate: buildDate,
|
||||
}
|
||||
env := params.NewEnv()
|
||||
logger := logging.NewParent(logging.Settings{Writer: os.Stdout})
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
errorCh := make(chan error)
|
||||
go func() {
|
||||
errorCh <- _main(ctx, env, os.Args, logger, buildInfo, time.Now)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stop()
|
||||
logger.Warn("Caught OS signal, shutting down")
|
||||
case err := <-errorCh:
|
||||
stop()
|
||||
close(errorCh)
|
||||
if err == nil { // expected exit such as healthcheck
|
||||
os.Exit(0)
|
||||
}
|
||||
logger.Error(err)
|
||||
cancel()
|
||||
}
|
||||
|
||||
const shutdownGracePeriod = 5 * time.Second
|
||||
timer := time.NewTimer(shutdownGracePeriod)
|
||||
select {
|
||||
case err := <-errorCh:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
logger.Info("Shutdown successful")
|
||||
case <-timer.C:
|
||||
logger.Warn("Shutdown timed out")
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func _main(ctx context.Context, env params.Env, args []string, logger logging.ParentLogger,
|
||||
buildInfo models.BuildInformation, timeNow func() time.Time) (err error) {
|
||||
if health.IsClientMode(args) {
|
||||
// Running the program in a separate instance through the Docker
|
||||
// built-in healthcheck, in an ephemeral fashion to query the
|
||||
// long running instance of the program about its status
|
||||
client := health.NewClient()
|
||||
var healthConfig config.Health
|
||||
_, err := healthConfig.Get(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Query(ctx, healthConfig.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println(splash.Splash(buildInfo))
|
||||
|
||||
var config config.Config
|
||||
warnings, err := config.Get(env)
|
||||
for _, warning := range warnings {
|
||||
logger.Warn(warning)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup logger
|
||||
loggerSettings := logging.Settings{
|
||||
Level: config.Logger.Level,
|
||||
Caller: config.Logger.Caller}
|
||||
logger = logging.NewParent(loggerSettings)
|
||||
|
||||
sender, err := shoutrrr.CreateSender(config.Shoutrrr.Addresses...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
notify := func(message string) {
|
||||
errs := sender.Send(message, &config.Shoutrrr.Params)
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
persistentDB, err := persistence.NewJSON(config.Paths.DataDir)
|
||||
if err != nil {
|
||||
notify(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
jsonReader := jsonparams.NewReader(logger)
|
||||
settings, warnings, err := jsonReader.JSONSettings(config.Paths.JSON, logger)
|
||||
for _, w := range warnings {
|
||||
logger.Warn(w)
|
||||
notify(w)
|
||||
}
|
||||
if err != nil {
|
||||
notify(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
L := len(settings)
|
||||
switch L {
|
||||
case 0:
|
||||
logger.Warn("Found no setting to update record")
|
||||
case 1:
|
||||
logger.Info("Found single setting to update record")
|
||||
default:
|
||||
logger.Info("Found %d settings to update records", len(settings))
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: config.Client.Timeout}
|
||||
|
||||
connectivity := connectivity.NewConnectivity(net.DefaultResolver, client)
|
||||
for _, err := range connectivity.Checks(ctx, "github.com") {
|
||||
logger.Warn(err)
|
||||
}
|
||||
|
||||
records := make([]recordslib.Record, len(settings))
|
||||
for i, s := range settings {
|
||||
logger.Info("Reading history from database: domain %s host %s", s.Domain(), s.Host())
|
||||
events, err := persistentDB.GetEvents(s.Domain(), s.Host())
|
||||
if err != nil {
|
||||
notify(err.Error())
|
||||
return err
|
||||
}
|
||||
records[i] = recordslib.New(s, events)
|
||||
}
|
||||
|
||||
defer client.CloseIdleConnections()
|
||||
db := data.NewDatabase(records, persistentDB)
|
||||
defer func() {
|
||||
if err := db.Close(); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
config.PubIP.HTTPSettings.Client = client
|
||||
|
||||
ipGetter, err := publicip.NewFetcher(config.PubIP.DNSSettings, config.PubIP.HTTPSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updater := update.NewUpdater(db, client, notify, logger)
|
||||
runner := update.NewRunner(db, updater, ipGetter, config.Update.Period,
|
||||
config.IPv6.Mask, config.Update.Cooldown, logger, timeNow)
|
||||
|
||||
runnerHandler, runnerCtx, runnerDone := goshutdown.NewGoRoutineHandler(
|
||||
"runner", goshutdown.GoRoutineSettings{})
|
||||
go runner.Run(runnerCtx, runnerDone)
|
||||
|
||||
// note: errors are logged within the goroutine,
|
||||
// no need to collect the resulting errors.
|
||||
go runner.ForceUpdate(ctx)
|
||||
|
||||
isHealthy := health.MakeIsHealthy(db, net.LookupIP, logger)
|
||||
healthServer := health.NewServer(config.Health.ServerAddress,
|
||||
logger.NewChild(logging.Settings{Prefix: "healthcheck server: "}),
|
||||
isHealthy)
|
||||
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
|
||||
"health server", goshutdown.GoRoutineSettings{})
|
||||
go healthServer.Run(healthServerCtx, healthServerDone)
|
||||
|
||||
address := ":" + strconv.Itoa(int(config.Server.Port))
|
||||
serverLogger := logger.NewChild(logging.Settings{Prefix: "http server: "})
|
||||
server := server.New(ctx, address, config.Server.RootURL, db, serverLogger, runner)
|
||||
serverHandler, serverCtx, serverDone := goshutdown.NewGoRoutineHandler(
|
||||
"server", goshutdown.GoRoutineSettings{})
|
||||
go server.Run(serverCtx, serverDone)
|
||||
notify("Launched with " + strconv.Itoa(len(records)) + " records to watch")
|
||||
|
||||
backupHandler, backupCtx, backupDone := goshutdown.NewGoRoutineHandler(
|
||||
"backup", goshutdown.GoRoutineSettings{})
|
||||
go backupRunLoop(backupCtx, backupDone, config.Backup.Period, config.Paths.DataDir, config.Backup.Directory,
|
||||
logger.NewChild(logging.Settings{Prefix: "backup: "}), timeNow)
|
||||
|
||||
shutdownGroup := goshutdown.NewGroupHandler("", goshutdown.GroupSettings{})
|
||||
shutdownGroup.Add(runnerHandler, healthServerHandler, serverHandler, backupHandler)
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
if err := shutdownGroup.Shutdown(context.Background()); err != nil {
|
||||
notify(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func backupRunLoop(ctx context.Context, done chan<- struct{}, backupPeriod time.Duration,
|
||||
dataDir, outputDir string, logger logging.Logger, timeNow func() time.Time) {
|
||||
defer close(done)
|
||||
if backupPeriod == 0 {
|
||||
logger.Info("disabled")
|
||||
return
|
||||
}
|
||||
logger.Info("each %s; writing zip files to directory %s", backupPeriod, outputDir)
|
||||
ziper := backup.NewZiper()
|
||||
timer := time.NewTimer(backupPeriod)
|
||||
for {
|
||||
fileName := "ddns-updater-backup-" + strconv.Itoa(int(timeNow().UnixNano())) + ".zip"
|
||||
zipFilepath := filepath.Join(outputDir, fileName)
|
||||
if err := ziper.ZipFiles(
|
||||
zipFilepath,
|
||||
filepath.Join(dataDir, "updates.json"),
|
||||
filepath.Join(dataDir, "config.json"),
|
||||
); err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
select {
|
||||
case <-timer.C:
|
||||
timer.Reset(backupPeriod)
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
{
|
||||
"provider": "namecheap",
|
||||
"domain": "example.com",
|
||||
"host": "@",
|
||||
"password": "e5322165c1d74692bfa6d807100c0310"
|
||||
},
|
||||
{
|
||||
@@ -13,8 +12,7 @@
|
||||
},
|
||||
{
|
||||
"provider": "godaddy",
|
||||
"domain": "example.org",
|
||||
"host": "subdomain",
|
||||
"domain": "subdomain.example.org",
|
||||
"key": "aaaaaaaaaaaaaaaa",
|
||||
"secret": "aaaaaaaaaaaaaaaa"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
ddns-updater:
|
||||
image: qmcgaw/ddns-updater
|
||||
image: ghcr.io/qdm12/ddns-updater
|
||||
container_name: ddns-updater
|
||||
network_mode: bridge
|
||||
ports:
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
- HTTP_TIMEOUT=10s
|
||||
|
||||
# Web UI
|
||||
- LISTENING_PORT=8000
|
||||
- LISTENING_ADDRESS=:8000
|
||||
- ROOT_URL=/
|
||||
|
||||
# Backup
|
||||
|
||||
33
docs/aliyun.md
Normal file
33
docs/aliyun.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Aliyun
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "aliyun",
|
||||
"domain": "domain.com",
|
||||
"access_key_id": "your access_key_id",
|
||||
"access_secret": "your access_secret",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"access_key_id"`
|
||||
- `"access_secret"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
33
docs/allinkl.md
Normal file
33
docs/allinkl.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# All-Inkl
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "allinkl",
|
||||
"domain": "sub.domain.com",
|
||||
"username": "dynXXXXXXX",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"` username (usually starts with dyn followed by numbers)
|
||||
- `"password"` password in plain text
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
33
docs/changeip.md
Normal file
33
docs/changeip.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# ChangeIP
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "changeip",
|
||||
"domain": "sub.domain.com",
|
||||
"username": "dynXXXXXXX",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
@@ -11,10 +11,10 @@
|
||||
"provider": "cloudflare",
|
||||
"zone_identifier": "some id",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"ttl": 600,
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -22,36 +22,19 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"zone_identifier"` is the Zone ID of your site
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
|
||||
- `"zone_identifier"` is the Zone ID of your site, from the domain overview page written as *Zone ID*
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
See [this issue comment for context](https://github.com/qdm12/ddns-updater/issues/243#issuecomment-928313949). This is left as is for compatibility.
|
||||
- `"ttl"` integer value for record TTL in seconds (specify 1 for automatic)
|
||||
- One of the following:
|
||||
- Email `"email"` and Global API Key `"key"`
|
||||
- User service key `"user_service_key"`
|
||||
- API Token `"token"`, configured with DNS edit permissions for your DNS name's zone
|
||||
- One of the following ([how to find API keys](https://developers.cloudflare.com/fundamentals/api/get-started/)):
|
||||
- Email `"email"` and Global API Key `"key"`
|
||||
- User service key `"user_service_key"`
|
||||
- API Token `"token"`, configured with DNS edit permissions for your DNS name's zone
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"proxied"` can be set to `true` to use the proxy services of Cloudflare
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), and defaults to `ipv4 or ipv6`
|
||||
|
||||
## Domain setup
|
||||
|
||||
1. Make sure you have `curl` installed
|
||||
1. Obtain your API key from Cloudflare website ([see this](https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-))
|
||||
1. Obtain your zone identifier for your domain name, from the domain's overview page written as *Zone ID*
|
||||
1. Find your **identifier** in the `id` field with
|
||||
|
||||
```sh
|
||||
ZONEID=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
EMAIL=example@example.com
|
||||
APIKEY=aaaaaaaaaaaaaaaaaa
|
||||
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONEID/dns_records" \
|
||||
-H "X-Auth-Email: $EMAIL" \
|
||||
-H "X-Auth-Key: $APIKEY"
|
||||
```
|
||||
|
||||
You can now fill in the necessary parameters in *config.json*
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
Special thanks to @Starttoaster for helping out with the [documentation](https://gist.github.com/Starttoaster/07d568c2a99ad7631dd776688c988326) and testing.
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
## Table of content
|
||||
|
||||
1. [Setup](#Setup)
|
||||
1. [Commands available](#Commands-available)
|
||||
1. [Guidelines](#Guidelines)
|
||||
|
||||
## Setup
|
||||
|
||||
### Using VSCode and Docker
|
||||
|
||||
That should be easier and better than a local setup, although it might use more memory if you're not on Linux.
|
||||
|
||||
1. Install [Docker](https://docs.docker.com/install/)
|
||||
- On Windows, share a drive with Docker Desktop and have the project on that partition
|
||||
- On OSX, share your project directory with Docker Desktop
|
||||
1. With [Visual Studio Code](https://code.visualstudio.com/download), install the [remote containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||
1. In Visual Studio Code, press on `F1` and select `Remote-Containers: Open Folder in Container...`
|
||||
1. Your dev environment is ready to go!... and it's running in a container :+1:
|
||||
|
||||
### Locally
|
||||
|
||||
Install [Go](https://golang.org/dl/), [Docker](https://www.docker.com/products/docker-desktop) and [Git](https://git-scm.com/downloads); then:
|
||||
|
||||
```sh
|
||||
go mod download
|
||||
```
|
||||
|
||||
And finally install [golangci-lint](https://github.com/golangci/golangci-lint#install).
|
||||
|
||||
You might want to use an editor such as [Visual Studio Code](https://code.visualstudio.com/download) with the [Go extension](https://code.visualstudio.com/docs/languages/go). Working settings are already in [.vscode/settings.json](../.vscode/settings.json).
|
||||
|
||||
## Build and Run
|
||||
|
||||
```sh
|
||||
go build -o app cmd/updater/main.go
|
||||
./app
|
||||
```
|
||||
|
||||
## Commands available
|
||||
|
||||
- Test the code: `go test ./...`
|
||||
- Lint the code `golangci-lint run`
|
||||
- Build the Docker image (tests and lint included): `docker build -t qmcgaw/ddns-updater .`
|
||||
- Run the Docker container: `docker run -it --rm -v /yourpath/data:/updater/data qmcgaw/ddns-updater`
|
||||
|
||||
## Guidelines
|
||||
|
||||
The Go code is in the Go file [cmd/updater/main.go](](../cmd/updater/main.go) and the [internal directory](](../internal), you might want to start reading the main.go file.
|
||||
|
||||
See the [Contributing document](](../.github/CONTRIBUTING.md) for more information on how to contribute to this repository.
|
||||
40
docs/custom.md
Normal file
40
docs/custom.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Custom provider
|
||||
|
||||
The custom provider allows to configure a URL with a few additional parameters to update your records.
|
||||
|
||||
For now it sends an HTTP GET request to the URL given with some additional parameters.
|
||||
Feel free to open issues to extend its configuration options.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "custom",
|
||||
"domain": "example.com",
|
||||
"url": "https://example.com/update?domain=example.com&host=@&username=username&client_key=client_key",
|
||||
"ipv4key": "ipv4",
|
||||
"ipv6key": "ipv6",
|
||||
"success_regex": "good",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"url"` is the URL to update your records and should contain all the information EXCEPT the IP address to update
|
||||
- `"ipv4key"` is the URL query parameter name for the IPv4 address, for example `ipv4` will be added to the URL with `&ipv4=1.2.3.4`. In the rare case you do not wish to send an IPv4 query parameter to the registrar API when updating your A (IPv4) record, leave it to `"ipv4key": ""`.
|
||||
- `"ipv6key"` is the URL query parameter name for the IPv6 address, for example `ipv6` will be added to the URL with `&ipv6=::aaff`. In the rare case you do not wish to send an IPv6 query parameter to the registrar API when updating your AAAA (IPv6) record, leave it to `"ipv6key": ""`.
|
||||
- `"success_regex"` is a regular expression to match the response from the server to determine if the update was successful. You can use [regex101.com](https://regex101.com/) to find the regular expression you want. For example `good` would match any response containing the word "good".
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
10
docs/dd24.md
10
docs/dd24.md
@@ -10,9 +10,9 @@
|
||||
{
|
||||
"provider": "dd24",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,10 +20,10 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"password"` is your password
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
"settings": [
|
||||
{
|
||||
"provider": "ddnss",
|
||||
"provider_ip": true,
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "user",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4"
|
||||
"dual_stack": false,
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -22,13 +22,16 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"dual_stack"` can be set to `true` **if you have turn on dual stack for your record** to update both IPv4 and IPv6 addresses. More precisely:
|
||||
- if it is `false`, the updates are done using the `ip` parameter and only one IP address can be set (ipv4 or ipv6, whichever is last sent).
|
||||
- if it is `true`, the updates are done using the `ip` and `ip6` parameters, for IPv4 and IPv6 respectively, and both can be set on the same record
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
33
docs/desec.md
Normal file
33
docs/desec.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# deSEC
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "desec",
|
||||
"domain": "sub.dedyn.io",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"token"` is your token that you can create at [desec.io/tokens](https://desec.io/tokens)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
[desec.io/domains](https://desec.io/domains)
|
||||
@@ -10,9 +10,9 @@
|
||||
{
|
||||
"provider": "digitalocean",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,12 +20,12 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"token"` is your token that you can create [here](https://cloud.digitalocean.com/settings/applications)
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"token"` is your token that you can create at [cloud.digitalocean.com/settings/applications](https://cloud.digitalocean.com/settings/applications)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
{
|
||||
"provider": "dnsomatic",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4"
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,13 +21,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
{
|
||||
"provider": "dnspod",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,12 +20,12 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
31
docs/domeneshop.md
Normal file
31
docs/domeneshop.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Domeneshop.no
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "domeneshop",
|
||||
"domain": "domain.com,seconddomain.com",
|
||||
"token": "token",
|
||||
"secret": "secret",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`)
|
||||
- `"token"` See [api.domeneshop.no/docs/](https://api.domeneshop.no/docs/) for instructions on how to generate credentials.
|
||||
- `"secret"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -10,10 +10,10 @@
|
||||
{
|
||||
"provider": "dondominio",
|
||||
"domain": "domain.com",
|
||||
"name": "something",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4"
|
||||
"key": "key",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,13 +21,15 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"name"` is the name server associated with the domain
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
See [dondominio.dev/en/dondns/docs/api/#before-start](https://dondominio.dev/en/dondns/docs/api/#before-start)
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"provider": "dreamhost",
|
||||
"domain": "domain.com",
|
||||
"key": "key",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -24,6 +25,8 @@
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
"settings": [
|
||||
{
|
||||
"provider": "duckdns",
|
||||
"host": "host",
|
||||
"domain": "sub.duckdns.org",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,16 +20,19 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"host"` is your host, for example `subdomain` for `subdomain.duckdns.org`
|
||||
- `"domain"` is the domain to update. The [eTLD](https://developer.mozilla.org/en-US/docs/Glossary/eTLD) must be `duckdns.org`. For example:
|
||||
- for the root owner/host `@`, it would be `mydomain.duckdns.org`
|
||||
- for the owner/host `sub`, it would be `sub.mydomain.duckdns.org`
|
||||
- for multiple domains, it can be `sub1.mydomain.duckdns.org,sub2.mydomain.duckdns.org` BUT it cannot be `a.duckdns.org,b.duckdns.org`, since the effective domains would be `a.duckdns.org` and `b.duckdns.org`
|
||||
- `"token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
[](https://duckdns.org)
|
||||
[](https://www.duckdns.org/)
|
||||
|
||||
*See the [duckdns website](https://duckdns.org)*
|
||||
*See the [duckdns website](https://www.duckdns.org/)*
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
{
|
||||
"provider": "dyn",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"client_key": "client_key",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -22,14 +21,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
- `"client_key"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
35
docs/dynu.md
Normal file
35
docs/dynu.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Dynu
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "dynu",
|
||||
"domain": "domain.com",
|
||||
"group": "group",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"`
|
||||
- `"password"` could be plain text or password in MD5 or SHA256 format (There's also an option for setting a password for IP Update only)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"group"` specify the Group for which you want to set the IP (will update any domains and subdomains in the same group)
|
||||
|
||||
## Domain setup
|
||||
@@ -10,10 +10,9 @@
|
||||
{
|
||||
"provider": "dynv6",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,13 +20,12 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"token"` that you can obtain [here](https://dynv6.com/keys#token)
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"token"` that you can obtain from [dynv6.com/keys#token](https://dynv6.com/keys#token)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
33
docs/easydns.md
Normal file
33
docs/easydns.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# EasyDNS
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "easydns",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"`
|
||||
- `"token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
41
docs/example.md
Normal file
41
docs/example.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Example.com
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
<!-- UPDATE THIS JSON EXAMPLE -->
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "example",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
<!-- UPDATE THIS IF NEEDED -->
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
<!-- UPDATE THIS IF NEEDED -->
|
||||
|
||||
## Domain setup
|
||||
|
||||
<!-- FILL THIS UP WITH A FEW NUMBERED STEPS -->
|
||||
@@ -9,10 +9,10 @@
|
||||
"settings": [
|
||||
{
|
||||
"provider": "freedns",
|
||||
"domain": "domain.com",
|
||||
"host": "host",
|
||||
"domain": "sub.domain.com",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,12 +20,15 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host (subdomain)
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"token"` is the randomized update token you use to update your record
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
This integration uses FreeDNS's v2 dynamic dns interface, which is not shown by default when you select `Dynamic DNS` from the side menu.
|
||||
Instead you must go to [freedns.afraid.org/dynamic/v2/](https://freedns.afraid.org/dynamic/v2/) and enable dynamic DNS for the subdomains you wish and you will then see a url like `https://sync.afraid.org/u/token/` for each enabled subdomain.
|
||||
|
||||
@@ -12,10 +12,10 @@ This provider uses Gandi v5 API
|
||||
{
|
||||
"provider": "gandi",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"key": "key",
|
||||
"personal_access_token": "token",
|
||||
"ttl": 3600,
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,15 +23,15 @@ This provider uses Gandi v5 API
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` which can be a subdomain, `@` or a wildcard `*`
|
||||
- `"key"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"personal_access_token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"ttl"` default is `3600`
|
||||
|
||||
## Domain setup
|
||||
|
||||
[Gandi Documentation Website](https://docs.gandi.net/en/domain_names/advanced_users/api.html#gandi-s-api)
|
||||
[Gandi Documentation Website](https://docs.gandi.net/en/rest_api/index.html)
|
||||
|
||||
37
docs/gcp.md
Normal file
37
docs/gcp.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# GCP
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "gcp",
|
||||
"project": "my-project-id",
|
||||
"zone": "zone",
|
||||
"credentials": {
|
||||
"type": "service_account",
|
||||
"project_id": "my-project-id",
|
||||
// ...
|
||||
},
|
||||
"domain": "domain.com",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"project"` is the id of your Google Cloud project
|
||||
- `"zone"` is the zone, that your DNS record is located in
|
||||
- `"credentials"` is the JSON credentials for your Google Cloud project. This is usually downloaded as a JSON file, which you can copy paste the content as the value of the `"credentials"` key. More information on how to get it is available at [cloud.google.com/docs/authentication/getting-started](https://cloud.google.com/docs/authentication/getting-started). Please ensure your service account has all necessary permissions to create/update/list/get DNS records within your project.
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -10,10 +10,10 @@
|
||||
{
|
||||
"provider": "godaddy",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"key": "key",
|
||||
"secret": "secret",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,18 +21,18 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"key"`
|
||||
- `"secret"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
[](https://godaddy.com)
|
||||
[](https://www.godaddy.com/en-ie)
|
||||
|
||||
1. Login to [https://developer.godaddy.com/keys](https://developer.godaddy.com/keys/) with your account credentials.
|
||||
|
||||
|
||||
36
docs/goip.md
Normal file
36
docs/goip.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# GoIP.de
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "goip",
|
||||
"domain": "sub.mydomain.goip.de",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "",
|
||||
"ipv6_suffix": ""
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. For example, for the owner/host `sub`, it would be `sub.goip.de`. The [eTLD](https://developer.mozilla.org/en-US/docs/Glossary/eTLD) must be `goip.de` or `goip.it`.
|
||||
- `"domain"` is the domain to update. The [eTLD](https://developer.mozilla.org/en-US/docs/Glossary/eTLD) must be `goip.de` or `goip.it`. For example:
|
||||
- for the root owner/host `@`, it would be `mydomain.goip.de`
|
||||
- for the owner/host `sub`, it would be `sub.mydomain.goip.de`
|
||||
- for multiple domains, it can be `sub1.mydomain.goip.de,sub2.mydomain.goip.de` BUT it cannot be `a.goip.de,b.goip.de`, since the effective domains would be `a.goip.de` and `b.goip.de`
|
||||
- `"username"` is your goip.de username listed under "Routers"
|
||||
- `"password"` is your router account password
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -1,42 +0,0 @@
|
||||
# Google
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "godaddy",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
|
||||
## Domain setup
|
||||
|
||||
Thanks to [@gauravspatel](https://github.com/gauravspatel) for #124
|
||||
|
||||
1. Enable dynamic DNS in the *synthetic records* section of DNS management.
|
||||
1. The username and password is generated once you create the dynamic DNS entry.
|
||||
|
||||
### Wildcard entries
|
||||
|
||||
If you want to create a **wildcard entry**, you have to create a custom **CNAME** record with key `"*"` and value `"@"`.
|
||||
@@ -10,9 +10,9 @@
|
||||
{
|
||||
"provider": "he",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,12 +20,12 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"` (untested)
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard. (untested)
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
36
docs/hetzner.md
Normal file
36
docs/hetzner.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Hetzner
|
||||
|
||||
⚠️ You should use [Hetzner Cloud](./hetznercloud.md) with the new Hetzner Cloud console instead, given this legacy Hetzner API is going to be shutdown soon.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "hetzner",
|
||||
"zone_identifier": "some id",
|
||||
"domain": "domain.com",
|
||||
"ttl": 600,
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"zone_identifier"` is the Zone ID of your site, from the domain overview page written as *Zone ID*
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"ttl"` optional integer value corresponding to a number of seconds
|
||||
- One of the following ([how to find API keys](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token)):
|
||||
- API Token `"token"`, configured with DNS edit permissions for your DNS name's zone
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
32
docs/hetznercloud.md
Normal file
32
docs/hetznercloud.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Hetzner Cloud
|
||||
|
||||
This provider uses the Hetzner Cloud API `https://api.hetzner.cloud/v1/` which is different from the legacy Hetzner DNS API.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "hetznercloud",
|
||||
"domain": "example.com",
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"token"` is your API token configured with DNS write permissions for your DNS zone, see [the Authentication section](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"ttl"` time to live for the DNS record in seconds. It is only used to add a record to the rrset, and is not used to update an existing record. If left empty, it defaults to the existing zone TTL.
|
||||
@@ -10,10 +10,10 @@
|
||||
{
|
||||
"provider": "infomaniak",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,14 +21,17 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"` for dyndns (**not** your infomaniak admin username!)
|
||||
- `"password"` for dyndns (**not** your infomaniak admin password!)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
Follow [this guide](https://www.infomaniak.com/en/support/faq/2357/getting-started-guide-dyndns-with-an-infomaniak-domain) to set up your subdomain including `username` and `password` for use in the configuration. **do not use your infomaniak admin username and password in the configuration!**
|
||||
|
||||
If you only plan on using IPv4, add your current IPv4 Address. If you only plan on using IPv6, add your current IPv6 Address. If you plan to use dual-stack (IPv4 and IPv6) addresses, it does not matter what ip-address you put in the dialog.
|
||||
|
||||
35
docs/inwx.md
Normal file
35
docs/inwx.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# INWX
|
||||
|
||||
## Configuration
|
||||
|
||||
You must configure a [DynDNS Account in INWX](https://www.inwx.com/en/offer/dyndns) and use the configured account and hostname.
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "inwx",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain configured for the DynDNS Account. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
29
docs/ionos.md
Normal file
29
docs/ionos.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Ionos
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "ionos",
|
||||
"domain": "domain.com",
|
||||
"api_key": "api_key",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"api_key"` is your API key, consisting of two parts — a prefix and the key itself — and can be obtained by [creating an API key](https://www.ionos.com/help/domains/configuring-your-ip-address/set-up-dynamic-dns-with-company-name/#c181598). It must follow the format `<prefix>.<key>`.
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
31
docs/ipv64.md
Normal file
31
docs/ipv64.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# IPv64
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "ipv64",
|
||||
"domain": "domain.com",
|
||||
"key": "key",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"key"` that you can obtain at [ipv64.net/account](https://ipv64.net/account)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
@@ -10,9 +10,9 @@
|
||||
{
|
||||
"provider": "linode",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,15 +20,15 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
1. Create a personal access token with `domains` set, with read and write privileges, ideally that never expires. You can refer to [@AnujRNair's comment](https://github.com/qdm12/ddns-updater/pull/144#discussion_r559292678) and to [Linode's guide](https://www.linode.com/docs/products/tools/cloud-manager/guides/cloud-api-keys).
|
||||
1. Create a personal access token with `domains` set, with read and write privileges, ideally that never expires. You can refer to [@AnujRNair's comment](https://github.com/qdm12/ddns-updater/pull/144#discussion_r559292678) and to [Linode's guide](https://www.linode.com/docs/products/tools/api/guides/manage-api-tokens/).
|
||||
1. The program will create the A or AAAA record for you if it doesn't exist already.
|
||||
|
||||
31
docs/loopia.md
Normal file
31
docs/loopia.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Loopia
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "loopia",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`). It cannot be a wildcard domain.
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -10,10 +10,10 @@
|
||||
{
|
||||
"provider": "luadns",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"email": "email",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,14 +21,14 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"email"`
|
||||
- `"token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
|
||||
33
docs/myaddr.md
Normal file
33
docs/myaddr.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# [Myaddr](https://myaddr.tools/)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "myaddr",
|
||||
"domain": "your-name.myaddr.tools",
|
||||
"key": "key",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` - the **single** domain to update; note the `key` below updates all records and subdomains for this domain. It should be `your-name.myaddr.tools`.
|
||||
- `"key"` - the private key corresponding to the domain to update
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
Claim a subdomain at [myaddr.tools](https://myaddr.tools/)
|
||||
35
docs/name.com.md
Normal file
35
docs/name.com.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Name.com
|
||||
|
||||
<img src="../readme/name.svg" alt="drawing" width="25%"/>
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "name.com",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"token": "token",
|
||||
"ttl": 300,
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"` is your account username
|
||||
- `"token"` which you can obtain from [www.name.com/account/settings/api](https://www.name.com/account/settings/api)
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ttl"` is the time this record can be cached for in seconds. Name.com allows a minimum TTL of 300, or 5 minutes. Name.com defaults to 300 if not provided.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -10,9 +10,7 @@
|
||||
{
|
||||
"provider": "namecheap",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"password": "password",
|
||||
"provider_ip": true
|
||||
"password": "password"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,38 +18,35 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
|
||||
Note that Namecheap only supports ipv4 addresses for now.
|
||||
|
||||
## Domain setup
|
||||
|
||||
[](https://www.namecheap.com)
|
||||
[](https://www.namecheap.com/)
|
||||
|
||||
1. Create a Namecheap account and buy a domain name - *example.com* as an example
|
||||
1. Login to Namecheap at [https://www.namecheap.com/myaccount/login.aspx](https://www.namecheap.com/myaccount/login.aspx)
|
||||
1. Login to Namecheap at [https://www.namecheap.com/myaccount/login/](https://www.namecheap.com/myaccount/login/)
|
||||
|
||||
For **each domain name** you want to add, replace *example.com* in the following link with your domain name and go to [https://ap.www.namecheap.com/Domains/DomainControlPanel/**example.com**/advancedns](https://ap.www.namecheap.com/Domains/DomainControlPanel/example.com/advancedns)
|
||||
|
||||
1. For each host you want to add (if you don't know, create one record with the host set to `*`):
|
||||
1. In the *HOST RECORDS* section, click on *ADD NEW RECORD*
|
||||
|
||||

|
||||

|
||||
|
||||
1. Select the following settings and create the *A + Dynamic DNS Record*:
|
||||
|
||||

|
||||

|
||||
|
||||
1. Scroll down and turn on the switch for *DYNAMIC DNS*
|
||||
|
||||

|
||||

|
||||
|
||||
1. The Dynamic DNS Password will appear, which is `0e4512a9c45a4fe88313bcc2234bf547` in this example.
|
||||
|
||||

|
||||

|
||||
|
||||
51
docs/namesilo.md
Normal file
51
docs/namesilo.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# NameSilo
|
||||
|
||||
[](https://www.namesilo.com)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "namesilo",
|
||||
"domain": "sub.example.com",
|
||||
"key": "71dZaE8c2Aa926Ca2E8c1",
|
||||
"ttl": 7207,
|
||||
"ip_version": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"key"` is the NameSilo API Key obtained using the domain setup instructions below. For example: `71dZaE8c2Aa926Ca2E8c1`.
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"ttl"` is the record's Time to Live (TTL), which defaults to `7207` seconds. It must be numeric, less than `2592001`, and greater than or equal to `3600`. TTL values of `3603` or `7207` may be subject to NameSilo's [Automatic TTL Adjustments](https://www.namesilo.com/support/v2/articles/domain-manager/dns-manager#auto_ttl).
|
||||
|
||||
## Domain setup
|
||||
|
||||
1. Login to the [Namesilo API Manager](https://www.namesilo.com/account/api-manager) with your account credentials.
|
||||
1. Generate an API key. The generated API key will look similar to `71dZaE8c2Aa926Ca2E8c1`.
|
||||
- (do _not_ check the "Generate key for read-only access" box)
|
||||
|
||||
[](https://www.namesilo.com/account/api-manager)
|
||||
[](https://www.namesilo.com/account/api-manager)
|
||||
|
||||
## Testing
|
||||
|
||||
1. Go to the [NameSilo Domain Manager](https://www.namesilo.com/account_domains.php).
|
||||
1. Choose "Manage DNS for this domain" (the globe icon) for the domain you wish to test.
|
||||
[](https://www.namesilo.com/account_domains.php)
|
||||
|
||||
1. Change the IP address of the host to `127.0.0.1`.
|
||||
1. Run the ddns-updater.
|
||||
1. Refresh the Namesilo webpage to check the update occurred.
|
||||
37
docs/netcup.md
Normal file
37
docs/netcup.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Netcup
|
||||
|
||||
## Configuration
|
||||
|
||||
Note: This implementation does not require a domain reseller account. The warning in the dashboard can be ignored.
|
||||
|
||||
Also keep in mind, that TTL, Expire, Retry and Refresh values of the given Domain are not updated. They can be manually set in the dashboard. For DDNS purposes low numbers should be used.
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "netcup",
|
||||
"domain": "domain.com",
|
||||
"api_key": "xxxxx",
|
||||
"password": "yyyyy",
|
||||
"customer_number": "111111",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`) or the wildcard `*.example.com`.
|
||||
- `"api_key"` is your api key (generated in the [customercontrolpanel](https://www.customercontrolpanel.de))
|
||||
- `"password"` is your api password (generated in the [customercontrolpanel](https://www.customercontrolpanel.de)). Netcup only allows one ApiPassword. This is not the account password. This password is used for all api keys.
|
||||
- `"customer_number"` is your customer number (viewable in the [customercontrolpanel](https://www.customercontrolpanel.de) next to your name). As seen in the example above, provide the number as string value.
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -10,10 +10,9 @@
|
||||
{
|
||||
"provider": "njalla",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"key": "key",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,14 +20,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"key"` is the key for your record
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
|
||||
11
docs/noip.md
11
docs/noip.md
@@ -10,10 +10,10 @@
|
||||
{
|
||||
"provider": "noip",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,14 +21,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
31
docs/nowdns.md
Normal file
31
docs/nowdns.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Now-DNS
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "nowdns",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` your full domain name (FQDN)
|
||||
- `"username"` your email address
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
@@ -8,13 +8,12 @@
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "dyn",
|
||||
"provider": "opendns",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -22,14 +21,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
12
docs/ovh.md
12
docs/ovh.md
@@ -10,11 +10,10 @@
|
||||
{
|
||||
"provider": "ovh",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -22,8 +21,7 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
|
||||
#### Using DynHost
|
||||
|
||||
@@ -35,14 +33,14 @@
|
||||
- `"api_endpoint"` default value is `"ovh-eu"`
|
||||
- `"app_key"` which you can create at [eu.api.ovh.com/createApp](https://eu.api.ovh.com/createApp/)
|
||||
- `"app_secret"`
|
||||
- `"consumer_key"`
|
||||
- `"consumer_key"` which you can get at [www.ovh.com/auth/api/createToken](https://www.ovh.com/auth/api/createToken)
|
||||
|
||||
The ZoneDNS implementation allows you to update any record name including *.yourdomain.tld
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"mode"` select between two modes, OVH's dynamic hosting service (`"dynamic"`) or OVH's API (`"api"`). Default is `"dynamic"`
|
||||
|
||||
## Domain setup
|
||||
|
||||
52
docs/porkbun.md
Normal file
52
docs/porkbun.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Porkbun
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "porkbun",
|
||||
"domain": "domain.com",
|
||||
"api_key": "pk1_5299b57125c8f3cdf347d2fe0e713311ee3a1e11f11a14942b26472593e35368",
|
||||
"secret_api_key": "sk1_7d119e3f656b00ae042980302e1425a04163c476efec1833q3cb0w54fc6f5022",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory Parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"api_key"`
|
||||
- `"secret_api_key"`
|
||||
- `"ttl"` optional integer value corresponding to a number of seconds
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
- Create an API key at [porkbun.com/account/api](https://porkbun.com/account/api)
|
||||
- From the [Domain Management page](https://porkbun.com/account/domainsSpeedy), toggle on **API ACCESS** for your domain.
|
||||
|
||||
💁 [Official setup documentation](https://kb.porkbun.com/article/190-getting-started-with-the-porkbun-dns-api)
|
||||
|
||||
## Record creation
|
||||
|
||||
In case you don't have an A or AAAA record for your host and domain combination, it will be created by DDNS-Updater.
|
||||
|
||||
Porkbun creates default DNS entries for new domains, which can conflict with creating a root or wildcard A/AAAA record. Therefore, ddns-updater automatically removes any conflicting default record before creating records, as described in the table below:
|
||||
|
||||
| Record type | Owner | Record value | Situation requiring a removal |
|
||||
| --- | --- | --- | --- |
|
||||
| `ALIAS` | `@` | pixie.porkbun.com | Creating A or AAAA record for the root domain **or** wildcard domain |
|
||||
| `CNAME` | `*` | pixie.porkbun.com | Creating A or AAAA record for the wildcard domain |
|
||||
|
||||
More details is in [this comment by @everydaycombat](https://github.com/qdm12/ddns-updater/issues/546#issuecomment-1773960193).
|
||||
56
docs/route53.md
Normal file
56
docs/route53.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# AWS
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "route53",
|
||||
"domain": "domain.com",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": "",
|
||||
"access_key": "ffffffffffffffffffff",
|
||||
"secret_key": "ffffffffffffffffffffffffffffffffffffffff",
|
||||
"zone_id": "A30888735ZF12K83Z6F00",
|
||||
"ttl": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"access_key"` is the `AWS_ACCESS_KEY`
|
||||
- `"secret_key"` is the `AWS_SECRET_ACCESS_KEY`
|
||||
- `"zone_id"` is identification of your hosted zone
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifiersuffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw public IPv6 address obtained is used in the record updating.
|
||||
- `"ttl"` amount of time, in seconds, that you want DNS recursive resolvers to cache information about this record. Defaults to `300`.
|
||||
|
||||
## Domain setup
|
||||
|
||||
Amazon has [an extensive documentation on registering or tranfering your domain to route53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html).
|
||||
|
||||
### User permissions
|
||||
|
||||
Create a policy to grant access to change record sets, you can use a wildcard `*` in case you want to grant access to all your hosted zones.
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "route53:ChangeResourceRecordSets",
|
||||
"Resource": "arn:aws:route53:::hostedzone/A30888735ZF12K83Z6F00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
36
docs/scaleway.md
Normal file
36
docs/scaleway.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Example.com
|
||||
|
||||
## Configuration
|
||||
|
||||
If something is unclear in the documentation below, please refer to the [scaleway API documentation](https://www.scaleway.com/en/developers/api/domains-and-dns/#path-records-update-records-within-a-dns-zone).
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "scaleway",
|
||||
"domain": "domain.com",
|
||||
"secret_key": "<SECRET_KEY>",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"secret_key"` is your secret key
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"ttl"` is the TTL of the DNS record to update, if you want to specify it.
|
||||
|
||||
## Domain setup
|
||||
|
||||
If you need more information about how to configure your domain, you can check the [scaleway official documentation](https://www.scaleway.com/en/docs/network/domains-and-dns/).
|
||||
@@ -10,10 +10,10 @@
|
||||
{
|
||||
"provider": "selfhost.de",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4"
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,13 +21,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"username"` is your DynDNS username
|
||||
- `"password"` is your DynDNS password
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
37
docs/servercow.md
Normal file
37
docs/servercow.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Servercow
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "servercow",
|
||||
"domain": "domain.com",
|
||||
"username": "servercow_username",
|
||||
"password": "servercow_password",
|
||||
"ttl": 600,
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"` is the username for your DNS API User
|
||||
- `"password"` is the password for your DNS API User
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ttl"` can be set to an integer value for record TTL in seconds (if not set the default is 120)
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
See [their article](https://wiki.servercow.de/en/domains/dns_api/api-syntax/)
|
||||
@@ -10,12 +10,11 @@
|
||||
{
|
||||
"provider": "spdyn",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"user": "user",
|
||||
"password": "password",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,8 +22,7 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
|
||||
#### Using user and password
|
||||
|
||||
@@ -37,5 +35,5 @@
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (**not IPv6**)automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
{
|
||||
"provider": "strato",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,14 +20,13 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"password"` is your dyndns password
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
{
|
||||
"provider": "variomedia",
|
||||
"domain": "domain.com",
|
||||
"host": "@",
|
||||
"email": "email@domain.com",
|
||||
"password": "password",
|
||||
"ip_version": "ipv4",
|
||||
"provider_ip": true
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -22,15 +21,14 @@
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"`
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
|
||||
- `"email"`
|
||||
- `"password"` is your DNS settings password, not your account password ⚠️
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
|
||||
39
docs/vercel.md
Normal file
39
docs/vercel.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Vercel
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "vercel",
|
||||
"domain": "domain.com",
|
||||
"token": "yourtoken",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": "",
|
||||
"team_id": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"token"` is your Vercel API token. You can create one at [Vercel Account Settings > Tokens](https://vercel.com/account/tokens).
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"team_id"` is your Vercel team ID. Required if the domain belongs to a team rather than your personal account. You can find this in your team settings.
|
||||
- `"ttl"` is the TTL in seconds for the DNS record. It defaults to being not set, using the existing TTL configured for the record in Vercel or the default TTL determined by Vercel.
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
1. Ensure your domain is added to Vercel. You can do this via the [Vercel Dashboard](https://vercel.com/dashboard) under your project's Domain settings, or via the API.
|
||||
1. Your domain must be using Vercel's nameservers or have DNS management delegated to Vercel for this provider to work.
|
||||
1. Create an API token with appropriate permissions at [Vercel Account Settings > Tokens](https://vercel.com/account/tokens).
|
||||
1. If the domain belongs to a team, make sure to include the `team_id` parameter in your configuration.
|
||||
30
docs/vultr.md
Normal file
30
docs/vultr.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Vultr
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "vultr",
|
||||
"domain": "potato.example.com",
|
||||
"apikey": "AAAAAAAAAAAAAAA",
|
||||
"ttl": 300,
|
||||
"ip_version": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be a root domain (i.e. `example.com`) or a subdomain (i.e. `potato.example.com`) or a wildcard (i.e. `*.example.com`). In case of a wildcard, it only works if there is no existing wildcard records of any record type.
|
||||
- `"apikey"` is your API key which can be obtained from [my.vultr.com/settings/](https://my.vultr.com/settings/#settingsapi).
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
- `"ttl"` is the record TTL which defaults to 900 seconds.
|
||||
41
docs/zoneedit.md
Normal file
41
docs/zoneedit.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Zoneedit
|
||||
|
||||
## Configuration
|
||||
|
||||
⚠️ zoneedit.com for some reason requires at least a 10 minutes period between update request sent.
|
||||
|
||||
DDNS-Updater only sends update requests when it detects your domain name IP address mismatches your current public IP address,
|
||||
so it should be fine in most cases since this happens rarely (in hours/days). But in case it happens and you want to avoid this,
|
||||
set the environment variable as `PERIOD=11m` to check your public IP address and update every 11 minutes only.
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "zoneedit",
|
||||
"domain": "domain.com",
|
||||
"username": "username",
|
||||
"token": "token",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` is the domain to update. It can be `example.com` (root domain), `sub.example.com` (subdomain of `example.com`) or `*.example.com` for the wildcard.
|
||||
- `"username"`
|
||||
- `"token"`
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
|
||||
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw temporary IPv6 address of the machine is used in the record updating. You might want to set this to use your permanent IPv6 address instead of your temporary IPv6 address.
|
||||
|
||||
## Domain setup
|
||||
|
||||
[support.zoneedit.com/en/knowledgebase/article/dynamic-dns](https://support.zoneedit.com/en/knowledgebase/article/dynamic-dns)
|
||||
43
go.mod
43
go.mod
@@ -1,14 +1,39 @@
|
||||
module github.com/qdm12/ddns-updater
|
||||
|
||||
go 1.16
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/containrrr/shoutrrr v0.4.4
|
||||
github.com/go-chi/chi v1.5.4
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/miekg/dns v1.1.42
|
||||
github.com/qdm12/golibs v0.0.0-20210514224620-c025cb0da211
|
||||
github.com/qdm12/goshutdown v0.1.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/breml/rootcerts v0.3.5
|
||||
github.com/containrrr/shoutrrr v0.8.0
|
||||
github.com/go-chi/chi/v5 v5.2.5
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/qdm12/goservices v0.1.0
|
||||
github.com/qdm12/gosettings v0.4.4
|
||||
github.com/qdm12/gosplash v0.2.0
|
||||
github.com/qdm12/gotree v0.3.0
|
||||
github.com/qdm12/log v0.1.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.uber.org/mock v0.6.0
|
||||
golang.org/x/mod v0.35.0
|
||||
golang.org/x/net v0.53.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.77 // indirect
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 // indirect
|
||||
)
|
||||
|
||||
452
go.sum
452
go.sum
@@ -1,383 +1,79 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
||||
github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
||||
github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
||||
github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
||||
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
|
||||
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containrrr/shoutrrr v0.4.4 h1:vHZ4E/76pKVY+Jyn/qhBz3X540Bn8NI5ppPHK4PyILY=
|
||||
github.com/containrrr/shoutrrr v0.4.4/go.mod h1:zqL2BvfC1W4FujrT4b3/ZCLxvD+uoeEpBL7rg9Dqpbg=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/breml/rootcerts v0.3.5 h1:oi7YiZ25HH52+mrKyjrMkcAFfnRDUf6HO8aUDr7RlJI=
|
||||
github.com/breml/rootcerts v0.3.5/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw=
|
||||
github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec=
|
||||
github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.11.0 h1:l4iX0RqNnx/pU7rY2DB/I+znuYY0K3x6Ywac6EIr0PA=
|
||||
github.com/fatih/color v1.11.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||
github.com/go-interpreter/wagon v0.5.1-0.20190713202023-55a163980b6c/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
|
||||
github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
|
||||
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
|
||||
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4=
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY=
|
||||
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.6 h1:11TGpSHY7Esh/i/qnq02Jo5oVrI1Gue8Slbq0ujPZFQ=
|
||||
github.com/nxadm/tail v1.4.6/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/qdm12/golibs v0.0.0-20210514224620-c025cb0da211 h1:tpjavgiEPlyZtsXO1xMzN1JpeDwhCMnn4c9dFVtl0i0=
|
||||
github.com/qdm12/golibs v0.0.0-20210514224620-c025cb0da211/go.mod h1:Is1wBOULKFH6NFPVBR+ksPWkm8njbKyQkOinLLfFAuE=
|
||||
github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw=
|
||||
github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
github.com/qdm12/goservices v0.1.0 h1:9sODefm/yuIGS7ynCkEnNlMTAYn9GzPhtcK4F69JWvc=
|
||||
github.com/qdm12/goservices v0.1.0/go.mod h1:/JOFsAnHFiSjyoXxa5FlfX903h20K5u/3rLzCjYVMck=
|
||||
github.com/qdm12/gosettings v0.4.4 h1:SM6tOZDf6k8qbjWU8KWyBF4mWIixfsKCfh9DGRLHlj4=
|
||||
github.com/qdm12/gosettings v0.4.4/go.mod h1:CPrt2YC4UsURTrslmhxocVhMCW03lIrqdH2hzIf5prg=
|
||||
github.com/qdm12/gosplash v0.2.0 h1:DOxCEizbW6ZG+FgpH2oK1atT6bM8MHL9GZ2ywSS4zZY=
|
||||
github.com/qdm12/gosplash v0.2.0/go.mod h1:k+1PzhO0th9cpX4q2Nneu4xTsndXqrM/x7NTIYmJ4jo=
|
||||
github.com/qdm12/gotree v0.3.0 h1:Q9f4C571EFK7ZEsPkEL2oGZX7I+ZhVxhh1ZSydW+5yI=
|
||||
github.com/qdm12/gotree v0.3.0/go.mod h1:iz06uXmRR4Aq9v6tX7mosXStO/yGHxRA1hbyD0UVeYw=
|
||||
github.com/qdm12/log v0.1.0 h1:jYBd/xscHYpblzZAd2kjZp2YmuYHjAAfbTViJWxoPTw=
|
||||
github.com/qdm12/log v0.1.0/go.mod h1:Vchi5M8uBvHfPNIblN4mjXn/oSbiWguQIbsgF1zdQPI=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gosrc.io/xmpp v0.5.1 h1:Rgrm5s2rt+npGggJH3HakQxQXR8ZZz3+QRzakRQqaq4=
|
||||
gosrc.io/xmpp v0.5.1/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
|
||||
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.77 h1:iQtQTjFUOcTT19fI8sTCzYXsjeVs56et3D8AbKS2Uks=
|
||||
kernel.org/pub/linux/libs/security/libcap/cap v1.2.77/go.mod h1:oV+IO8kGh0B7TxErbydDe2+BRmi9g/W0CkpVV+QBTJU=
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 h1:Z06sMOzc0GNCwp6efaVrIrz4ywGJ1v+DP0pjVkOfDuA=
|
||||
kernel.org/pub/linux/libs/security/libcap/psx v1.2.77/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
|
||||
|
||||
5
internal/backup/interfaces.go
Normal file
5
internal/backup/interfaces.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package backup
|
||||
|
||||
type Logger interface {
|
||||
Info(message string)
|
||||
}
|
||||
99
internal/backup/service.go
Normal file
99
internal/backup/service.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
// Injected fields
|
||||
backupPeriod time.Duration
|
||||
dataDir string
|
||||
outputDir string
|
||||
logger Logger
|
||||
|
||||
// Internal fields
|
||||
stopCh chan<- struct{}
|
||||
done <-chan struct{}
|
||||
}
|
||||
|
||||
func New(backupPeriod time.Duration,
|
||||
dataDir, outputDir string, logger Logger,
|
||||
) *Service {
|
||||
return &Service{
|
||||
logger: logger,
|
||||
backupPeriod: backupPeriod,
|
||||
dataDir: dataDir,
|
||||
outputDir: outputDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) String() string {
|
||||
return "backup"
|
||||
}
|
||||
|
||||
func makeZipFileName() string {
|
||||
return "ddns-updater-backup-" + strconv.Itoa(int(time.Now().UnixNano())) + ".zip"
|
||||
}
|
||||
|
||||
func (s *Service) Start(ctx context.Context) (runError <-chan error, startErr error) {
|
||||
ready := make(chan struct{})
|
||||
runErrorCh := make(chan error)
|
||||
stopCh := make(chan struct{})
|
||||
s.stopCh = stopCh
|
||||
done := make(chan struct{})
|
||||
s.done = done
|
||||
go run(ready, runErrorCh, stopCh, done,
|
||||
s.outputDir, s.dataDir, s.backupPeriod, s.logger)
|
||||
select {
|
||||
case <-ready:
|
||||
case <-ctx.Done():
|
||||
return nil, s.Stop()
|
||||
}
|
||||
return runErrorCh, nil
|
||||
}
|
||||
|
||||
func run(ready chan<- struct{}, runError chan<- error, stopCh <-chan struct{},
|
||||
done chan<- struct{}, outputDir, dataDir string, backupPeriod time.Duration,
|
||||
logger Logger,
|
||||
) {
|
||||
defer close(done)
|
||||
|
||||
if backupPeriod == 0 {
|
||||
close(ready)
|
||||
logger.Info("disabled")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("each " + backupPeriod.String() +
|
||||
"; writing zip files to directory " + outputDir)
|
||||
timer := time.NewTimer(backupPeriod)
|
||||
close(ready)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-stopCh:
|
||||
_ = timer.Stop()
|
||||
return
|
||||
}
|
||||
err := zipFiles(
|
||||
filepath.Join(outputDir, makeZipFileName()),
|
||||
filepath.Join(dataDir, "config.json"),
|
||||
filepath.Join(dataDir, "updates.json"),
|
||||
)
|
||||
if err != nil {
|
||||
runError <- err
|
||||
return
|
||||
}
|
||||
timer.Reset(backupPeriod)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Stop() (err error) {
|
||||
close(s.stopCh)
|
||||
<-s.done
|
||||
return nil
|
||||
}
|
||||
@@ -6,26 +6,8 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type Ziper interface {
|
||||
ZipFiles(outputFilepath string, inputFilepaths ...string) error
|
||||
}
|
||||
|
||||
type ziper struct {
|
||||
createFile func(name string) (*os.File, error)
|
||||
openFile func(name string) (*os.File, error)
|
||||
ioCopy func(dst io.Writer, src io.Reader) (written int64, err error)
|
||||
}
|
||||
|
||||
func NewZiper() Ziper {
|
||||
return &ziper{
|
||||
createFile: os.Create,
|
||||
openFile: os.Open,
|
||||
ioCopy: io.Copy,
|
||||
}
|
||||
}
|
||||
|
||||
func (z *ziper) ZipFiles(outputFilepath string, inputFilepaths ...string) error {
|
||||
f, err := z.createFile(outputFilepath)
|
||||
func zipFiles(outputFilepath string, inputFilepaths ...string) error {
|
||||
f, err := os.Create(outputFilepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -33,15 +15,16 @@ func (z *ziper) ZipFiles(outputFilepath string, inputFilepaths ...string) error
|
||||
w := zip.NewWriter(f)
|
||||
defer w.Close()
|
||||
for _, filepath := range inputFilepaths {
|
||||
if err := z.addFile(w, filepath); err != nil {
|
||||
err = addFile(w, filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *ziper) addFile(w *zip.Writer, filepath string) error {
|
||||
f, err := z.openFile(filepath)
|
||||
func addFile(w *zip.Writer, filepath string) error {
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -62,6 +45,6 @@ func (z *ziper) addFile(w *zip.Writer, filepath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = z.ioCopy(ioWriter, f)
|
||||
_, err = io.Copy(ioWriter, f)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,19 +3,45 @@ package config
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/gosettings"
|
||||
"github.com/qdm12/gosettings/reader"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
type Backup struct {
|
||||
Period time.Duration
|
||||
Directory string
|
||||
Period *time.Duration
|
||||
Directory *string
|
||||
}
|
||||
|
||||
func (b *Backup) get(env params.Env) (err error) {
|
||||
b.Period, err = env.Duration("BACKUP_PERIOD", params.Default("0"))
|
||||
func (b *Backup) setDefaults() {
|
||||
b.Period = gosettings.DefaultPointer(b.Period, 0)
|
||||
b.Directory = gosettings.DefaultPointer(b.Directory, "./data")
|
||||
}
|
||||
|
||||
func (b Backup) Validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b Backup) String() string {
|
||||
return b.toLinesNode().String()
|
||||
}
|
||||
|
||||
func (b Backup) toLinesNode() *gotree.Node {
|
||||
if *b.Period == 0 {
|
||||
return gotree.New("Backup: disabled")
|
||||
}
|
||||
node := gotree.New("Backup")
|
||||
node.Appendf("Period: %s", b.Period)
|
||||
node.Appendf("Directory: %s", *b.Directory)
|
||||
return node
|
||||
}
|
||||
|
||||
func (b *Backup) read(reader *reader.Reader) (err error) {
|
||||
b.Period, err = reader.DurationPtr("BACKUP_PERIOD")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Directory, err = env.Path("BACKUP_DIRECTORY", params.Default("./data"))
|
||||
return err
|
||||
|
||||
b.Directory = reader.Get("BACKUP_DIRECTORY")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,14 +3,39 @@ package config
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/gosettings"
|
||||
"github.com/qdm12/gosettings/reader"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func (c *Client) get(env params.Env) (err error) {
|
||||
c.Timeout, err = env.Duration("HTTP_TIMEOUT", params.Default("10s"))
|
||||
return err
|
||||
func (c *Client) setDefaults() {
|
||||
const defaultTimeout = 20 * time.Second
|
||||
c.Timeout = gosettings.DefaultComparable(c.Timeout, defaultTimeout)
|
||||
}
|
||||
|
||||
func (c Client) Validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Client) String() string {
|
||||
return c.toLinesNode().String()
|
||||
}
|
||||
|
||||
func (c Client) toLinesNode() *gotree.Node {
|
||||
node := gotree.New("HTTP client")
|
||||
node.Appendf("Timeout: %s", c.Timeout)
|
||||
return node
|
||||
}
|
||||
|
||||
func (c *Client) read(reader *reader.Reader) (err error) {
|
||||
c.Timeout, err = reader.Duration("HTTP_TIMEOUT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Client Client
|
||||
Update Update
|
||||
PubIP PubIP
|
||||
IPv6 IPv6
|
||||
Server Server
|
||||
Health Health
|
||||
Paths Paths
|
||||
Backup Backup
|
||||
Logger Logger
|
||||
Shoutrrr Shoutrrr
|
||||
}
|
||||
|
||||
func (c *Config) Get(env params.Env) (warnings []string, err error) {
|
||||
if err := c.Client.get(env); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
warning, err := c.Update.get(env)
|
||||
warnings = appendIfNotEmpty(warnings, warning)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
newWarnings, err := c.PubIP.get(env)
|
||||
warnings = append(warnings, newWarnings...)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if err := c.IPv6.get(env); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
warning, err = c.Server.get(env)
|
||||
warnings = appendIfNotEmpty(warnings, warning)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
warning, err = c.Health.Get(env)
|
||||
warnings = appendIfNotEmpty(warnings, warning)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if err := c.Paths.get(env); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if err := c.Backup.get(env); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
if err := c.Logger.get(env); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
newWarnings, err = c.Shoutrrr.get(env)
|
||||
warnings = append(warnings, newWarnings...)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
@@ -1,31 +1,67 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/gosettings"
|
||||
"github.com/qdm12/gosettings/reader"
|
||||
"github.com/qdm12/gosettings/validate"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
type Health struct {
|
||||
ServerAddress string
|
||||
Port uint16 // obtained from ServerAddress
|
||||
// ServerAddress is the listening address:port of the
|
||||
// health server, which defaults to the empty string,
|
||||
// meaning the server will not run.
|
||||
ServerAddress *string
|
||||
HealthchecksioBaseURL string
|
||||
HealthchecksioUUID *string
|
||||
}
|
||||
|
||||
func (h *Health) Get(env params.Env) (warning string, err error) {
|
||||
h.ServerAddress, warning, err = env.ListeningAddress(
|
||||
"HEALTH_SERVER_ADDRESS", params.Default("127.0.0.1:9999"))
|
||||
if err != nil {
|
||||
return warning, err
|
||||
}
|
||||
_, portStr, err := net.SplitHostPort(h.ServerAddress)
|
||||
if err != nil {
|
||||
return warning, err
|
||||
}
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return warning, err
|
||||
}
|
||||
h.Port = uint16(port)
|
||||
return warning, nil
|
||||
func (h *Health) SetDefaults() {
|
||||
h.ServerAddress = gosettings.DefaultPointer(h.ServerAddress, "")
|
||||
h.HealthchecksioBaseURL = gosettings.DefaultComparable(h.HealthchecksioBaseURL, "https://hc-ping.com")
|
||||
h.HealthchecksioUUID = gosettings.DefaultPointer(h.HealthchecksioUUID, "")
|
||||
}
|
||||
|
||||
func (h Health) Validate() (err error) {
|
||||
if *h.ServerAddress != "" {
|
||||
err = validate.ListeningAddress(*h.ServerAddress, os.Getuid())
|
||||
if err != nil {
|
||||
return fmt.Errorf("server listening address: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = url.Parse(h.HealthchecksioBaseURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("healthchecks.io base URL: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h Health) String() string {
|
||||
return h.toLinesNode().String()
|
||||
}
|
||||
|
||||
func (h Health) toLinesNode() *gotree.Node {
|
||||
node := gotree.New("Health")
|
||||
if *h.ServerAddress == "" {
|
||||
node.Appendf("Server is disabled")
|
||||
} else {
|
||||
node.Appendf("Server listening address: %s", *h.ServerAddress)
|
||||
}
|
||||
if *h.HealthchecksioUUID != "" {
|
||||
node.Appendf("Healthchecks.io base URL: %s", h.HealthchecksioBaseURL)
|
||||
node.Appendf("Healthchecks.io UUID: %s", *h.HealthchecksioUUID)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func (h *Health) Read(reader *reader.Reader) {
|
||||
h.ServerAddress = reader.Get("HEALTH_SERVER_ADDRESS")
|
||||
h.HealthchecksioBaseURL = reader.String("HEALTH_HEALTHCHECKSIO_BASE_URL")
|
||||
h.HealthchecksioUUID = reader.Get("HEALTH_HEALTHCHECKSIO_UUID")
|
||||
}
|
||||
|
||||
3
internal/config/helpers.go
Normal file
3
internal/config/helpers.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package config
|
||||
|
||||
const all = "all"
|
||||
@@ -1,56 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
|
||||
type IPv6 struct {
|
||||
Mask net.IPMask
|
||||
}
|
||||
|
||||
func (i *IPv6) get(env params.Env) (err error) {
|
||||
maskStr, err := env.Get("IPV6_PREFIX", params.Default("/128"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Mask, err = ipv6DecimalPrefixToMask(maskStr)
|
||||
return err
|
||||
}
|
||||
|
||||
var ErrParsePrefix = errors.New("cannot parse IP prefix")
|
||||
|
||||
func ipv6DecimalPrefixToMask(prefixDecimal string) (ipMask net.IPMask, err error) {
|
||||
if prefixDecimal == "" {
|
||||
return nil, fmt.Errorf("%w: empty prefix", ErrParsePrefix)
|
||||
}
|
||||
|
||||
prefixDecimal = strings.TrimPrefix(prefixDecimal, "/")
|
||||
|
||||
const bits = 8 * net.IPv6len
|
||||
|
||||
ones, consumed, ok := decimalToInteger(prefixDecimal)
|
||||
if !ok || consumed != len(prefixDecimal) || ones < 0 || ones > bits {
|
||||
return nil, fmt.Errorf("%w: %s", ErrParsePrefix, prefixDecimal)
|
||||
}
|
||||
|
||||
return net.CIDRMask(ones, bits), nil
|
||||
}
|
||||
|
||||
func decimalToInteger(s string) (ones int, i int, ok bool) {
|
||||
const big = 0xFFFFFF // Bigger than we need, not too big to worry about overflow
|
||||
const ten = 10
|
||||
|
||||
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
ones = ones*ten + int(s[i]-'0')
|
||||
if ones >= big {
|
||||
return big, i, false
|
||||
}
|
||||
}
|
||||
|
||||
return ones, i, true
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_ipv6DecimalPrefixToMask(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
prefixDecimal string
|
||||
ipMask net.IPMask
|
||||
err error
|
||||
}{
|
||||
"empty": {
|
||||
err: fmt.Errorf("cannot parse IP prefix: empty prefix"),
|
||||
},
|
||||
"malformed": {
|
||||
prefixDecimal: "malformed",
|
||||
err: fmt.Errorf("cannot parse IP prefix: malformed"),
|
||||
},
|
||||
"with leading slash": {
|
||||
prefixDecimal: "/78",
|
||||
ipMask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
"without leading slash": {
|
||||
prefixDecimal: "78",
|
||||
ipMask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
"full IPv6 mask": {
|
||||
prefixDecimal: "/128",
|
||||
ipMask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||
},
|
||||
"zero IPv6 mask": {
|
||||
prefixDecimal: "/0",
|
||||
ipMask: net.IPMask{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ipMask, err := ipv6DecimalPrefixToMask(testCase.prefixDecimal)
|
||||
|
||||
if testCase.err != nil {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, testCase.ipMask, ipMask)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,65 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/params"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gosettings"
|
||||
"github.com/qdm12/gosettings/reader"
|
||||
"github.com/qdm12/gosettings/validate"
|
||||
"github.com/qdm12/gotree"
|
||||
"github.com/qdm12/log"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
Caller logging.Caller
|
||||
Level logging.Level
|
||||
Level string
|
||||
Caller string
|
||||
}
|
||||
|
||||
func (l *Logger) get(env params.Env) (err error) {
|
||||
l.Caller, err = env.LogCaller("LOG_CALLER", params.Default("hidden"))
|
||||
func (l *Logger) setDefaults() {
|
||||
l.Level = gosettings.DefaultComparable(l.Level, log.LevelInfo.String())
|
||||
l.Caller = gosettings.DefaultComparable(l.Caller, "hidden")
|
||||
}
|
||||
|
||||
func (l Logger) Validate() (err error) {
|
||||
_, err = log.ParseLevel(l.Level)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("log level: %w", err)
|
||||
}
|
||||
|
||||
l.Level, err = env.LogLevel("LOG_LEVEL", params.Default("info"))
|
||||
return err
|
||||
err = validate.IsOneOf(l.Caller, "hidden", "short")
|
||||
if err != nil {
|
||||
return fmt.Errorf("log caller: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l Logger) ToOptions() (options []log.Option) {
|
||||
level, _ := log.ParseLevel(l.Level)
|
||||
options = append(options, log.SetLevel(level))
|
||||
if l.Caller == "short" {
|
||||
options = append(options, log.SetCallerFile(true), log.SetCallerLine(true))
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func (l Logger) String() string {
|
||||
return l.toLinesNode().String()
|
||||
}
|
||||
|
||||
func (l Logger) toLinesNode() *gotree.Node {
|
||||
node := gotree.New("Logger")
|
||||
node.Appendf("Level: %s", l.Level)
|
||||
node.Appendf("Caller: %s", l.Caller)
|
||||
return node
|
||||
}
|
||||
|
||||
func (l *Logger) read(reader *reader.Reader) {
|
||||
l.Level = reader.String("LOG_LEVEL")
|
||||
// Retro compatibility
|
||||
if strings.ToLower(l.Level) == "warning" {
|
||||
l.Level = "warn"
|
||||
}
|
||||
l.Caller = reader.String("LOG_CALLER")
|
||||
}
|
||||
|
||||
@@ -1,22 +1,73 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/golibs/params"
|
||||
"github.com/qdm12/gosettings"
|
||||
"github.com/qdm12/gosettings/reader"
|
||||
"github.com/qdm12/gotree"
|
||||
)
|
||||
|
||||
type Paths struct {
|
||||
DataDir string
|
||||
JSON string // obtained from DataDir
|
||||
DataDir *string
|
||||
Config *string
|
||||
// Umask is the custom umask to use for the system, if different than zero.
|
||||
// If it is set to zero, the system umask is unchanged.
|
||||
// It cannot be nil in the internal state.
|
||||
Umask *fs.FileMode
|
||||
}
|
||||
|
||||
func (p *Paths) get(env params.Env) (err error) {
|
||||
p.DataDir, err = env.Path("DATADIR", params.Default("./data"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (p *Paths) setDefaults() {
|
||||
p.DataDir = gosettings.DefaultPointer(p.DataDir, "./data")
|
||||
defaultConfig := filepath.Join(*p.DataDir, "config.json")
|
||||
p.Config = gosettings.DefaultPointer(p.Config, defaultConfig)
|
||||
p.Umask = gosettings.DefaultPointer(p.Umask, fs.FileMode(0))
|
||||
}
|
||||
|
||||
p.JSON = filepath.Join(p.DataDir, "config.json")
|
||||
func (p Paths) Validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Paths) String() string {
|
||||
return p.toLinesNode().String()
|
||||
}
|
||||
|
||||
func (p Paths) toLinesNode() *gotree.Node {
|
||||
node := gotree.New("Paths")
|
||||
node.Appendf("Data directory: %s", *p.DataDir)
|
||||
node.Appendf("Config file: %s", *p.Config)
|
||||
umaskString := "system default"
|
||||
if *p.Umask != 0 {
|
||||
umaskString = p.Umask.String()
|
||||
}
|
||||
node.Appendf("Umask: %s", umaskString)
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *Paths) read(reader *reader.Reader) (err error) {
|
||||
p.DataDir = reader.Get("DATADIR")
|
||||
p.Config = reader.Get("CONFIG_FILEPATH")
|
||||
|
||||
umaskString := reader.String("UMASK")
|
||||
if umaskString != "" {
|
||||
umask, err := parseUmask(umaskString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse umask: %w", err)
|
||||
}
|
||||
p.Umask = &umask
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseUmask(s string) (umask fs.FileMode, err error) {
|
||||
const base, bitSize = 8, 32
|
||||
umaskUint64, err := strconv.ParseUint(s, base, bitSize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return fs.FileMode(umaskUint64), nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user