521 Commits
v2 ... v2.6.0

Author SHA1 Message Date
Quentin McGaw
c338c28ce3 fix(ci): set write contents permissions for goreleaser 2024-02-03 18:50:31 +00:00
CyberAustin
aa12ccc0b9 feat(provider): goip.de support (#623) 2024-02-03 19:28:46 +01:00
Quentin McGaw
cfdc1f3dbe docs(readme): use relative links in readme and make it compatible with docker hub
- use `enable-url-completion` in docker hub description step
2024-02-03 09:16:36 +00:00
Quentin McGaw
85b64cbeeb feat(publicip/http): add ipleak support for all IP versions 2024-02-03 09:16:33 +00:00
Quentin McGaw
1c76c84bc3 docs(readme): add missing ipify option for HTTP IPv4 or IPv6 public IP fetching 2024-02-03 08:18:17 +00:00
Quentin McGaw
2afec6c0ac feat(publicip/http): add Spdyn provider 2024-02-03 08:13:37 +00:00
Quentin McGaw
7b573266e9 chore(config): remove pointer from validate function receivers 2024-02-03 07:59:40 +00:00
Quentin McGaw
3107e00048 chore(config): fix providers slice capacity to right size 2024-02-03 07:59:08 +00:00
Quentin McGaw
1c80aaa3ec chore(ci): rename created -> date versioning variable
- to match goreleaser default ldflags `main.date`
- rename Dockerfile ldflag from created to date
- remove goreleaser custom ldflags and use defaults ones
2024-02-03 07:55:33 +00:00
Quentin McGaw
3935b3640b feat(logs): split logs for cooldown and ban
- cooldown happens sometimes, especially after the first program run. It is logged at the debug level.
- ban should not happen often and is logged at the info level
- adds more information to both log calls
2024-02-02 18:25:25 +00:00
Quentin McGaw
0247d5ca0a chore(update): better log message when succeeding fetching the public ip after a failure 2024-02-02 15:55:09 +00:00
Quentin McGaw
c494662976 fix(publicip): remove noip due to only http availability 2024-02-02 15:51:07 +00:00
Quentin McGaw
6f71982a7e feat(update): getip return explicit IPv6 not supported error
- only if all tries result in an error pointing to IPv6 being not supported
2024-02-01 16:46:57 +00:00
Quentin McGaw
a018b75609 chore(update): remove unneeded ip validity check 2024-02-01 16:36:17 +00:00
Quentin McGaw
e3ed94ff43 feat(update): more accurate time of events
- update call may take some time
- event time should be AFTER update is finished
- new ban time should be set to time AFTER update is finished
2024-02-01 16:36:17 +00:00
Quentin McGaw
2aaf02537a chore(update): requery current time on each update need check
- Checking with DNS resolution each record can take some time
- More "exact", although impact should be none
2024-02-01 16:36:17 +00:00
Quentin McGaw
f7db1ac7cf chore(update): requery current time after checking which records need an update
- Checking with DNS resolution each record can take some time
- More "exact", although impact should be none
2024-02-01 16:36:17 +00:00
Quentin McGaw
ebd852fcc1 fix(update): set initial fail status if public ip fetching failed 2024-02-01 16:36:17 +00:00
Quentin McGaw
443daad0fa chore(getip): change log messages
- log each error as they happen at the debug level
- return all errors joined if all tries fail and log at the error level
2024-02-01 16:36:13 +00:00
Quentin McGaw
b6e8f699a6 hotfix(ipv6): restore compatibility with old IPV6_PREFIX 2024-02-01 09:23:28 +00:00
Quentin McGaw
8d282cd6d9 hotfix(params): only log warning if ipv6 suffix is set for ipv4 2024-02-01 09:07:16 +00:00
Quentin McGaw
ed0a0d07ff chore(ci): move config files to .github/workflows/configs 2024-02-01 07:13:20 +00:00
Quentin McGaw
875cd027d7 fix(publicip/dns): use address of Cloudflare for ipv4 or ipv6 2024-01-31 14:31:35 +00:00
Quentin McGaw
dc2b7aaa0b chore(publicip/dns): set dial timeout to fetcher timeout 2024-01-31 14:18:07 +00:00
Quentin McGaw
816406dc41 feat(publicip/http): add api64.ipify.org 2024-01-31 14:18:02 +00:00
Quentin McGaw
621c3a13be docs(typo): indentifier -> identifier 2024-01-29 17:11:37 +00:00
Quentin McGaw
bad0d3aeda fix(ipv6): add JSON IPv6 suffix parameter (#611)
- Remove `IPV6_PREFIX` environment variable (unneeded) and remove associated code
- Update all documentation for each provider supporting IPv6
- Build IPv6 as prefix:suffix when getting it from a public IP source for each record IPv6 suffix parameter
- Automatically disable provider_ip if public ip is IPv6 and IPv6 suffix is set (they are not compatible with each other)
2024-01-29 17:31:07 +01:00
Quentin McGaw
25b8e02acc fix(spdyn): validate host value even if the token is set 2024-01-29 16:26:34 +00:00
Quentin McGaw
9b1c98ce49 chore(servercow): validation of host within main switch block 2024-01-29 16:26:03 +00:00
Quentin McGaw
9e6b7e5b99 feat(config): add :53 to resolver address if no port is given 2024-01-29 14:08:40 +00:00
Quentin McGaw
00a9653e95 hotfix(ci): increase markdown link check timeout to 20s 2024-01-29 10:02:53 +00:00
Quentin McGaw
b771b5c9b5 chore(namecheap): make code only IPv4 compatible
- Namecheap does not support IPv6 dynamic updates still
2024-01-29 09:43:01 +00:00
Quentin McGaw
67b83d402a docs(ip_version): add missing parameter for 4 providers
- allinkl, name.com, netcup, nowdns, porkbun
2024-01-29 09:42:57 +00:00
Quentin McGaw
fc500f7443 docs(ip_version): update description of all providers 2024-01-29 09:37:33 +00:00
Quentin McGaw
2658f609da chore(servercow): use injected domain instead of re-parsing it 2024-01-29 09:06:07 +00:00
Quentin McGaw
f60f7212c3 hotfix(dondominio): use DDNS only endpoint dondns.dondominio.com/json 2024-01-29 08:12:30 +00:00
Quentin McGaw
eee8485543 hotfix(dondominio): update API endpoint to simple-api.dondominio.net 2024-01-29 07:42:42 +00:00
Quentin McGaw
5c166a6ab6 hotfix(markdown): fix links and add link check config 2024-01-28 12:10:49 +00:00
Quentin McGaw
417a26282e feat(dondominio): allow any host values including wildcard 2024-01-28 09:42:39 +00:00
Quentin McGaw
8839db93dc feat(dondominio): create record if it does not exist 2024-01-28 09:41:03 +00:00
Quentin McGaw
7eee3fcccf fix(dondominio): use endpoint api.dondominio.com 2024-01-28 09:40:28 +00:00
Quentin McGaw
8888694b9a hotfix(ci): fix markdown issues 2024-01-28 08:31:41 +00:00
Quentin McGaw
b3cae52145 feat(build): workflow to have binaries on releases
- Fixes #294
2024-01-28 08:22:21 +00:00
Quentin McGaw
005c0c8f04 feat(ci): add markdown workflow
- Fixes #592
- Remove docker hub description workflow
- Build workflow ignores md files for spellcheck
2024-01-28 08:22:21 +00:00
Quentin McGaw
bc54ba5aca feat(update): skip update if public IP not found 2024-01-28 07:28:03 +00:00
Quentin McGaw
481321b5d3 chore(update): simplify shouldUpdateRecord functions
- `shouldUpdateRecordNoLookup`
- `shouldUpdateRecordWithLookup`
2024-01-28 07:28:03 +00:00
Slavik
115ce8faac docs(readme): fix shoutrrr bad link (#609) 2024-01-27 22:29:16 +01:00
Quentin McGaw
e665684fa4 fix(google): removed since no longer functional
- Logs error if provider google is used
- Documentation updated
- Fixes #605
2024-01-27 21:24:27 +00:00
Quentin McGaw
74168ad4ab hotfix(database): fix historical events reading
- Regression introduced in 7ed63a036e (PR #514)
2024-01-27 21:17:19 +00:00
Quentin McGaw
7f0e858fa0 fix(update): do not write empty IP on fetch fail 2024-01-24 20:02:07 +00:00
Quentin McGaw
41de082ffc fix(log): ipv4 no lookup update log 2024-01-24 19:55:53 +00:00
Quentin McGaw
6521103359 fix(infomaniak): handle new response prefixes
- Fixes #604
2024-01-23 15:39:24 +00:00
Quentin McGaw
af9d45702d chore(deps): tidy go modules dependencies 2024-01-23 15:38:43 +00:00
Quentin McGaw
f8bb927de6 fix(dnsomatic): remove username validation regex
- Fixes #398
2024-01-20 08:53:56 +00:00
Quentin McGaw
0c561d4378 feat(custom): add custom provider
- Sends HTTP GET request to url given with ip information
- Configurable ipv4 and ipv6 query parameter keys
- Configurable response success detection with a regex
- Treat non status OK 200 responses as failures
2024-01-19 19:54:43 +00:00
Quentin McGaw
12c46e7635 hotfix(settings): allow /proc/sys/net/ipv4/ip_unprivileged_port_start to be absent
- Assume default unprivileged start port is 1024
2024-01-19 17:01:14 +00:00
Quentin McGaw
c51a41e1a4 fix(settings): web ui listening port validation
- Check start of unprivileged ports on Linux
- Check if running program has bind capability on Linux
- Fixes #335
2024-01-19 14:17:30 +00:00
Quentin McGaw
1ff838e0ae fix(dnsomatic): treat 'all' host as proxied to reduce updates
Every period, compare the public IP address with the last IP address stored in updates.json. Only update when a difference is found
2024-01-19 11:51:06 +00:00
Quentin McGaw
010634db28 chore(github): additional labels
- Good idea
- Motivated!
- Foolproof settings
- Wildcard
2024-01-19 11:42:56 +00:00
Quentin McGaw
ce792ac753 chore(github): change order of category labels 2024-01-19 11:42:56 +00:00
Quentin McGaw
59d8791b8d fix(dyndns): allow wildcard hosts 2024-01-19 11:42:54 +00:00
Quentin McGaw
c8ba7edcb7 fix(dyn): always give myip parameter, provider_ip setting disabled 2024-01-19 11:42:50 +00:00
Quentin McGaw
8096944623 fix(http): bump timeout from 10s to 20s 2024-01-19 11:42:48 +00:00
Quentin McGaw
869b010853 fix(porkbun): delete ALIAS record before creating an A or AAAA record 2024-01-18 17:15:41 +00:00
Quentin McGaw
46054fb631 chore(porkbun): split API specific code to api.go 2024-01-18 17:03:59 +00:00
Quentin McGaw
28b2e121da feat(porkbun): json decode error messages 2024-01-18 16:58:49 +00:00
Quentin McGaw
37f473160c fix(desec): allow wildcard hosts 2024-01-18 14:18:59 +00:00
Quentin McGaw
b1e71b7a77 fix(desec): default host to "@" if empty 2024-01-18 14:18:59 +00:00
Quentin McGaw
d02fe17d55 chore(github): remove labels from_name fields 2024-01-18 14:18:59 +00:00
dependabot[bot]
f9f5d590c3 chore(deps): bump github/codeql-action from 2 to 3 (#583) 2024-01-18 15:07:30 +01:00
dependabot[bot]
80d0755bfb chore(deps): bump docker/build-push-action from 4.0.0 to 5.1.0 (#582) 2024-01-18 15:07:16 +01:00
dependabot[bot]
3d619566c1 chore(deps): bump docker/metadata-action from 4 to 5 (#585) 2024-01-18 15:06:56 +01:00
dependabot[bot]
68b0dbf5f1 chore(deps): bump docker/setup-buildx-action from 2 to 3 (#587) 2024-01-18 15:06:30 +01:00
Quentin McGaw
1a9cf0e0a6 chore(github): update labels 2024-01-18 14:05:17 +00:00
Quentin McGaw
706dcc7ba2 feat(shoutrrr): bump from v0.7.0 to v0.8.0 2024-01-18 11:21:32 +00:00
Quentin McGaw
ec1ee7803b docs(inwx,nowdns): remove trail comma in JSON example 2024-01-18 11:11:16 +00:00
Quentin McGaw
8dc39453e1 hotfix(config): change Dockerfile default LISTENING_PORT -> LISTENING_ADDRESS 2024-01-18 11:08:02 +00:00
Quentin McGaw
3e638326ed feat(config): LISTENING_ADDRESS configuration key (#590) 2024-01-18 08:18:15 +01:00
Quentin McGaw
015d3fca55 docs: fix documentation for SHOUTRRR_DEFAULT_TITLE 2024-01-16 10:18:28 +00:00
Quentin McGaw
0a41ff5b3b feat(config): read config from flags then environment variables 2024-01-16 10:15:22 +00:00
Quentin McGaw
ab6908b8cc chore(config): upgrade to qdm12/gosettings v0.4.0-rc6 2024-01-16 10:13:28 +00:00
Quentin McGaw
e100656d6f chore(deps): remove dependency on golang.org/x/net 2024-01-15 14:40:21 +00:00
Quentin McGaw
d36384744b chore(deps): bump golang.org/x/mod from v0.12.0 to v0.14.0 2024-01-15 14:39:12 +00:00
Quentin McGaw
1eaa51d871 chore(deps): bump github.com/miekg/dns from v1.1.54 to v1.1.57 2024-01-15 14:38:35 +00:00
Quentin McGaw
343ab1cf96 chore(github): add dependabot configuration 2024-01-15 14:38:24 +00:00
Quentin McGaw
bb807330bb chore(deps): bump breml/rootcerts from v0.2.11 to v0.2.14 2024-01-15 14:38:22 +00:00
Quentin McGaw
f132a82c0a chore(deps): upgrade chi from v4 to v5.0.11 2024-01-15 14:35:58 +00:00
Quentin McGaw
f16945e128 chore(build): bump Go from 1.20 to 1.21 2024-01-15 14:25:19 +00:00
Quentin McGaw
e97ced5608 chore(Docker): review environment variables set in Dockerfile
- Remove outdated `SHOUTRRR_DEFAULT_TITLE`
- Add `IPV6_PREFIX=/128`
- Add `HEALTH_SERVER_ADDRESS=127.0.0.1:9999`
- Add `HEALTH_HEALTHCHECKSIO_UUID=`
2024-01-15 14:24:28 +00:00
Quentin McGaw
f98f0874a0 chore(lint): bump linter from v1.53.2 to v1.55.2 2024-01-15 14:21:14 +00:00
Quentin McGaw
e50b38b331 chore(internal/update): simplify log calls 2024-01-15 14:21:09 +00:00
Quentin McGaw
9d5c48dbf3 chore(build): bump Alpine from 3.18 to 3.19 2024-01-15 14:02:56 +00:00
Quentin McGaw
91741a5aad feat(health): HEALTH_HEALTHCHECKSIO_UUID for healthchecks.io 2024-01-15 13:56:56 +00:00
Quentin McGaw (desktop)
ff5767aa38 feat(provider): add Ionos provider 2024-01-15 13:11:02 +00:00
Felix Wirth
7ed63a036e fix(internal/persistence/json): get events by IP version (#514) 2024-01-15 10:46:06 +01:00
Quentin McGaw
cdae4fcced chore(persistence/json): simplify StoreNewIP method 2024-01-15 09:40:58 +00:00
Quentin McGaw
44c77ef848 chore(persistence/json): improve data check error messages 2024-01-15 09:40:21 +00:00
Quentin McGaw
45881d137b chore(main): rename s -> provider in providers for loop 2024-01-15 09:38:57 +00:00
Quentin McGaw
1c982f7d8d chore(internal/data): remove unused GetEvents method 2024-01-15 09:13:16 +00:00
Quentin McGaw
658fe5be2a chore: fix data .gitignore path 2024-01-15 09:12:59 +00:00
3deep5me
679a1d1b70 docs: add Kubernetes examples & documentation (#402)
Fix #396
2024-01-14 22:28:31 +01:00
varner-owl
ee495bbfea fix(gandi): personal access token support (#568)
- `"token"` replaced with `"personal_access_token"` (retro-compatible)
2024-01-14 20:44:45 +01:00
dependabot[bot]
ba2e77798a chore(deps): bump google.golang.org/grpc from 1.53.0 to 1.56.3 (#552) 2023-11-17 15:05:19 +01:00
Lieblinger
301e4d6cc4 feat(provider): support Hetzner (#503) 2023-11-17 15:04:05 +01:00
Quentin McGaw
496781ac34 fix(dnsomatic): allow email aliases for username 2023-11-17 14:02:04 +00:00
dependabot[bot]
e7558b7e2b chore(deps): bump golang.org/x/net from 0.10.0 to 0.17.0 (#540) 2023-11-17 14:52:56 +01:00
Quentin McGaw
daa5edb56f hotfix(dynv6): fix lint error for nested if 2023-11-17 13:51:11 +00:00
Profiles
6669b39d45 fix(dynv6): set url ip field as 'auto' when provider_ip is true (#549) 2023-11-17 14:04:57 +01:00
michelwi
e635b19af6 fix(duckdns): support for ipv6 (#542)
* fix `ip6` -> `ipv6` url parameter key name
* fix ipv6 response handling
2023-10-14 11:52:53 +02:00
guangwu
c7c5468e5f docs: fix typos (#532) 2023-09-28 16:42:06 +02:00
Artur Pragacz
13ccb4ffd9 fix(ui): fix current ip ipinfo.io href link (#529) 2023-09-13 21:43:53 +02:00
Felix Wirth
7fac178b7d fix(ipv6): mask IPv6 address fetched (#515) 2023-08-08 09:07:39 +02:00
Felix Wirth
5c0b2012b7 fix(ui): include ip version for all providers (#512) 2023-08-08 09:06:08 +02:00
dependabot[bot]
a20cc6e1c2 chore(deps): bump google.golang.org/grpc from 1.50.1 to 1.53.0 (#500) 2023-08-04 16:27:44 +02:00
CyberAustin
5c3e407272 feat(providers): support now-dns (#504) 2023-08-04 16:26:56 +02:00
Felix Wirth
b92274347a fix(log): IPv4 and IPv6 only debug logging (#513) 2023-08-04 15:56:18 +02:00
biochron
7b3b6609fd feat(provider): support deSEC (#496) 2023-06-30 09:09:44 +02:00
Quentin McGaw
6f75aa1457 feat(ci): tagged Docker images without v prefix 2023-06-19 06:35:23 +00:00
Quentin McGaw
27456b628d fix(inwx): fix success codes detection 2023-06-17 14:08:18 +00:00
Quentin McGaw
0c98229588 hotfix(publicip/dns): fix error for unsupported any answer 2023-06-17 13:57:41 +00:00
Quentin McGaw
320d91d8e3 change(publicip/dns): use DNS over TLS only
- Fix critical issue #492
- Remove `google` dns provider since it does not support DNS over TLS
2023-06-17 13:57:41 +00:00
nils måsén
828373da7f docs(readme): pin shoutrrr link to go.mod shoutrrr version (#491) 2023-06-17 10:37:24 +02:00
Quentin McGaw
336bf057ab chore(update): rename run.go to service.go 2023-06-16 09:18:38 +00:00
Quentin McGaw
5a353c1b66 chore(models): remove unneeded HTML string alias type 2023-06-16 09:14:43 +00:00
Quentin McGaw
a5e49eb866 fix(server/ui): favicon href link fixed 2023-06-16 06:34:43 +00:00
Quentin McGaw
e4bb82d316 feat(shoutrrr): add SHOUTRRR_DEFAULT_TITLE
- Disable `SHOUTRRR_PARAMS` and log a warning if used
- Only add `&title=` parameter to shoutrrr address if it's not set
2023-06-16 06:31:33 +00:00
Quentin McGaw
3fdda01509 hotfix(sources/env): UPDATE_PERIOD back to PERIOD 2023-06-16 05:57:07 +00:00
Quentin McGaw
330fae1469 chore(ovh): remove unneeded sentinel errors 2023-06-15 07:13:19 +00:00
Quentin McGaw
1033711ab4 chore(provider): review update errors and wrappings
- `ErrBadHTTPStatus` -> `ErrHTTPStatusNotValid`
- `ErrNumberOfResultsReceived` -> `ErrResultsCountReceived`
- `ErrNoResultReceived` -> `ErrReceivedNoResult`
- `ErrAbuse` -> `ErrBannedAbuse`
- `ErrInvalidSystemParam` -> `ErrSystemParamNotValid`
- `ErrNoIPInResponse` -> `ErrReceivedNoIP`
- `ErrUnsuccessfulResponse` -> `ErrUnsuccessful`
- Remove `ErrRequestEncode` and `ErrRequestMarshal` and wrap errors with string instead
- Remove `ErrUnmarshalResponse` and wrap errors with string instead
- Add `ErrResponseTooShort`
- Remove `ErrNotFound` and replace with more precise not found errors
- Add `ErrRecordResourceSetNotFound` error
- Sort errors list alphabetically
2023-06-15 07:13:14 +00:00
Quentin McGaw
f2b56afda7 chore(providers): review error wrappings for client.Do
- Do not wrap with sentinel error ErrUnsuccesfulResponse
- Wrap with text `doing http request`
2023-06-15 06:42:38 +00:00
Quentin McGaw
c26b3fc0d3 chore(provider/errors): review error names and text
- Move problem part at end of error variable name for better autocompletion
- Move problem part at end of error message to match error variable name
- Change `Empty` -> `NotSet`
- Change `Malformed` -> `NotValid`
- Sort all errors alphabetically
2023-06-15 06:31:29 +00:00
Quentin McGaw
99f83f3f12 chore(providers): review error wrappings for validation errors 2023-06-15 06:23:39 +00:00
Quentin McGaw
9f6e9750c3 chore(provider): remove intermediary errors
- Wrap errors with strings instead of sentinel errors
2023-06-15 06:15:25 +00:00
Quentin McGaw
05473044a6 fix(providers): review all http headers
- Add missing header(s) for some providers
- Remove provider receiver on `setHeaders` function when not needed
- Inline `setHeaders` function body when used only once for provider
- Remove unneeded headers such as content-type for GET requests
2023-06-15 06:14:13 +00:00
Quentin McGaw
e5188906bf fix(duckdns): send ip address when provider_ip is false 2023-06-15 06:04:46 +00:00
Quentin McGaw
6e65c4f3a5 chore(providers): use errors.BadRequest for badrequest responses only
- Replace HTTP request creation error wrappings with string
- Add missing HTTP request creation error wrappings
2023-06-15 05:48:07 +00:00
Joseph Diekhoff
06b6288e58 feat(providers): support name.com (#474) 2023-06-15 07:29:36 +02:00
Quentin McGaw
4ac2bd7933 docs(zoneedit): warn about minimum update period 2023-06-14 07:07:30 +00:00
Quentin McGaw
31374c3634 chore(deps): run go mod tidy 2023-06-14 07:01:55 +00:00
Quentin McGaw
c98f8d6052 feat(providers): support zoneedit.com (#482) 2023-06-14 09:01:16 +02:00
Quentin McGaw
07cdce1fa2 hotfix(sources/env): leave HTTPIPProviders as nil if not value is found
- Bug introduced in feaac82c11 due to retro-compatibility
2023-06-14 06:32:07 +00:00
Quentin McGaw
4a6020558f chore(all): rename settings package to provider
- internal/provider: rename `Settings` interface to `Provider`
- internal/params: rename *Settings* to *Providers*
- internal/records: rename `Settings` to `Provider`
2023-06-14 06:25:56 +00:00
Azorimor
09eb9e706d feat(provider): support netcup.de (#361) 2023-06-14 08:16:40 +02:00
Quentin McGaw
5455207852 docs(readme): undocument SHOUTRRR_PARAMS 2023-06-13 14:59:46 +00:00
Quentin McGaw
feaac82c11 docs(publicip): remove outdated ddnss http option 2023-06-13 14:41:40 +00:00
Quentin McGaw
baacd052ce fix(publicip/http): remove opendns option 2023-06-13 14:41:37 +00:00
Quentin McGaw
c12b7e50d7 feat(pkg/publicip/dns): add opendns option 2023-06-13 14:41:35 +00:00
Quentin McGaw
954dffd3a7 fix(ipv6): replace bad regex with custom IPv6 extract function
- Fix HTTP IPv6 fetching invalid extraction result
- Affects IPv6 comparison for allinkl, dnsomatic, google, he and noip
2023-06-13 07:16:55 +00:00
Quentin McGaw
204be2072e fix(ci): write creation date correctly 2023-06-13 05:43:43 +00:00
Quentin McGaw
bfa6883f37 hotfix(settings): fix settings string test 2023-06-12 21:19:38 +00:00
Quentin McGaw
6513e5869a fix(sources/env): take into account absent values for PUBLICIP_FETCHERS 2023-06-12 20:26:46 +00:00
Quentin McGaw
81b336fe07 hotfix(main): fix bad HTTP -> DNS public ip enable flag 2023-06-12 20:26:16 +00:00
Quentin McGaw
83488e2020 hotfix(settings): do not log backup settings if it's disabled 2023-06-12 20:21:08 +00:00
Quentin McGaw
4922b1db0b hotfix(resolver): do not validate address as a listening address 2023-06-12 20:16:51 +00:00
Quentin McGaw
0f127bd58d hotfix(settings): IPV6_MASK -> IPV6_PREFIX 2023-06-12 15:54:46 +00:00
Quentin McGaw
9b5e0e1187 docs(cloudflare): wildcard hosts are possible now 2023-06-12 15:50:19 +00:00
Quentin McGaw
9d7244fade fix(noip): do not check for received IP when using provider ip 2023-06-12 15:16:27 +00:00
Quentin McGaw
936bf4386e docs(readme): add missing easydns entry 2023-06-12 14:47:12 +00:00
Quentin McGaw
5676673e9e chore(lint): bump golangci-lint v1.52.2 -> v1.53.2 2023-06-12 14:34:43 +00:00
charlydaily
6a9ba7ecb4 feat: add support for easydns (#480) 2023-06-12 16:34:00 +02:00
Quentin McGaw
2949112ad1 hotfix(settings): fix settings string test 2023-06-12 14:18:46 +00:00
Quentin McGaw
6d9568c7e5 chore(shoutrrr): create internal/shoutrrr package 2023-06-12 14:13:34 +00:00
Dennis Gaida
0241e32e92 docs(readme): document SHOUTRRR_PARAMS environment variable (#411) 2023-06-12 14:54:16 +02:00
Quentin McGaw
a9cb4ec63b feat(settings): log out tree of settings 2023-06-12 12:22:05 +00:00
Quentin McGaw
3e4d359140 chore(settings): default root url to / (cannot be empty) 2023-06-12 12:19:30 +00:00
Quentin McGaw
ac67611b06 hotfix(settings/shoutrrr): fix defaulting 2023-06-12 11:17:16 +00:00
Quentin McGaw
aebe5988f1 feat(shoutrrr): add debug logs 2023-06-12 11:10:44 +00:00
Quentin McGaw
b1eea74db1 chore(github): update issue labels 2023-06-12 11:10:26 +00:00
Quentin McGaw
6d018d920d chore(.github): more explicit title requirement 2023-06-12 10:00:02 +00:00
Quentin McGaw
88f98aebe4 chore(github): remove automated qdm12 assignee 2023-06-12 09:59:09 +00:00
Quentin McGaw
9a4a268926 chore(all): migrate from net.IP* to net/netip 2023-06-12 09:48:43 +00:00
Quentin McGaw
3e6fb5ead4 hotfix(sources/env): fix server address read from HEALTH_SERVER_ADDRESS 2023-06-12 09:46:39 +00:00
Quentin McGaw
330d65dc8f hotfix(settings/ipv6): set mask default only when unset 2023-06-12 09:16:59 +00:00
Quentin McGaw
baabc80b07 chore(providers): replace ip nil check with useProviderIP check
- Affects duckdns, infomaniak and namecheap
2023-06-12 08:50:23 +00:00
Quentin McGaw
d3d6725021 fix(opendns): do not compare ip received when using "provider_ip": true 2023-06-12 08:49:09 +00:00
Quentin McGaw
e4fd4592fd fix(njalla): do not compare ip received when using "provider_ip": true 2023-06-12 08:48:58 +00:00
Quentin McGaw
dfcd97f6c1 fix(update): getNewIPs error handling
- If get ip for v4 or v6 fails, do not use resulting nil ip
- If get ip for v6 fails, do not mask resulting nil ip
2023-06-12 08:31:25 +00:00
Quentin McGaw
619f626ae8 fix(opendns): new IP comparison check 2023-06-12 08:12:33 +00:00
Quentin McGaw
4b51c8b35b chore(config): rework entire global settings reading
- Use qdm12/gosettings
- No functional changes (at least intended)
- Remove qdm12/golibs dependency

chore(params): remove dependency on golibs
2023-06-12 06:17:24 +00:00
Quentin McGaw
05b2535fee chore(pkg/publicip/info): remove dependency on qdm12/golibs 2023-06-08 18:15:41 +00:00
Quentin McGaw
ec072d2862 chore(settings): no dependency on qdm12/golibs/verification 2023-06-08 18:11:48 +00:00
Quentin McGaw
57a928df92 chore(main): replace golibs/connectivity with health.CheckHTTP 2023-06-08 12:57:08 +00:00
Quentin McGaw
62808c6d1c chore(deps): run go mod tidy 2023-06-08 12:32:56 +00:00
Stavros Kois
61ead94870 docs: add missing provider_ip field in examples (#466)
- he.net
- selfhost.de
- servercow
2023-06-07 20:24:24 +02:00
Stavros Kois
10b6b79c70 docs(inwx): fix bad provider name opendns -> inwx (#468)
Closes #410
2023-06-07 20:09:43 +02:00
Stavros Kois
625b687b77 docs(noip): add missing username field in example (#469) 2023-06-07 20:08:49 +02:00
Stavros Kois
4028e8ade2 docs(opendns): fix bad provider name dyn to opendns in example (#470) 2023-06-07 20:08:24 +02:00
Stavros Kois
9ee74a3e75 docs(infomaniak): add missing username field in example (#467) 2023-06-07 20:07:35 +02:00
Stavros Kois
35c8928bd4 docs(dnsomatic): fix settings fields (#463)
- replace invalid `token` with `username` + `password`
- add `provider_ip` field
2023-06-07 20:06:35 +02:00
Stavros Kois
d31318d1fe docs(gcp): fix typo for provider name (#465) 2023-06-07 20:03:15 +02:00
Quentin McGaw
e41a39008e chore(persistence/json): drop dependency on qdm12/golibs 2023-06-07 17:58:25 +00:00
Quentin McGaw
97ca03544a chore(deps): bump github.com/stretchr/testify from v1.8.1 to v1.8.4 2023-06-07 09:19:49 +00:00
Quentin McGaw
8631b4e8d5 chore(deps): bump github.com/miekg/dns from v1.1.42 to v1.1.54 2023-06-07 09:19:10 +00:00
Quentin McGaw
a6e5053800 chore(deps): bump chi from v1.5.4 to v4.1.2 2023-06-07 09:18:40 +00:00
Quentin McGaw
5f107ffc2c chore(deps): bump golang.org/x/net from v0.1.0 to v0.10.0 2023-06-07 09:18:02 +00:00
Quentin McGaw
c39df2796f chore(ci): add mocks check step 2023-06-07 09:17:38 +00:00
Quentin McGaw
790ff8b78b chore(ci): add build-skip workflow 2023-06-07 09:13:47 +00:00
Quentin McGaw
39faaa771f chore(ci): rework entire workflow 2023-06-07 09:13:04 +00:00
Quentin McGaw
a6f72d97bc chore(dev): update devcontainer definitions 2023-06-07 07:51:11 +00:00
Quentin McGaw
5fff4f4a56 chore(deps): bump containrrr/shoutrrr to v0.7.0 2023-06-07 07:45:53 +00:00
Quentin McGaw
41d848f9a7 chore(deps): bump breml/rootcerts to v0.2.11 2023-06-07 07:43:40 +00:00
Quentin McGaw
e42feb933f chore(lint): add linters musttag and gocheckcompilerdirectives 2023-06-07 07:42:04 +00:00
Quentin McGaw
e7ebfc4b9a chore(lint): remove unused exclude rules 2023-06-07 07:28:24 +00:00
Quentin McGaw
23043b73d9 chore(build): bump Alpine from 3.17 to 3.18 2023-06-07 07:25:57 +00:00
Quentin McGaw
4fb7526ce7 chore(Dockerfile): remove empty lines between ENV 2023-06-07 07:25:34 +00:00
Eduard Marbach
43d405cfd8 feat(cloudflare): create record if it does not exist (#477) 2023-06-05 08:47:30 +02:00
Stavros Kois
b0493e9c1c fix(inwx): initialize url values to avoid panic (#473) 2023-05-09 04:19:29 -07:00
Stavros Kois
28509cfb36 fix(cloudflare): key -> userServiceKey variable name (#462) 2023-05-03 13:01:03 +02:00
Quentin McGaw
522cc7c9c6 chore(aliyun): remove Alibaba SDK dependency (#253)
- Remove `region` parameter
- Support AAAA records
- Files split around
2023-04-17 04:26:09 -07:00
Quentin McGaw
519b9c4380 hotfix: fix nil error in shouldUpdateRecordWithLookup 2023-04-13 17:42:25 +00:00
Quentin McGaw
464a557adb docs(readme): document RESOLVER_ADDRESS variable 2023-04-13 17:11:51 +00:00
Quentin McGaw
5ae4668296 docs(readme): auto-format 2023-04-13 17:11:47 +00:00
Quentin McGaw
934c67d710 fix(ci): restrict publish image trigger events 2023-04-13 17:00:19 +00:00
Quentin McGaw
9e6d456fc9 chore(all): wrap all sentinel errors 2023-04-13 16:58:38 +00:00
Quentin McGaw
15c6ad4b01 chore(all): use string comparisons instead of length for string variables 2023-04-13 16:58:33 +00:00
Quentin McGaw
3339c838fa chore(all): remove short if error checks 2023-04-13 15:38:05 +00:00
Quentin McGaw
18751973c1 chore(build): bump Go from 1.19 to 1.20 2023-04-13 09:44:46 +00:00
Quentin McGaw
49b779813a chore(lint): bump from v1.50.1 to v1.52.2 2023-04-13 09:44:27 +00:00
Quentin McGaw
f779a2159e chore(gcp): use common http client 2023-04-13 09:33:02 +00:00
Quentin McGaw
43f47fdee0 chore(build): bump Alpine from 3.16 to 3.17 2023-04-13 09:26:14 +00:00
Quentin McGaw
0e3703b8f2 chore(settings): validate using local regexes 2023-04-13 09:25:35 +00:00
Quentin McGaw
4a2a6d83d5 fix(cloudflare): use correct service key regex 2023-04-13 09:21:16 +00:00
Quentin McGaw
1aebdef825 fix(dnsomatic): remove password regex check
- Fixes #448
2023-04-13 08:45:54 +00:00
Erwan Daubert
8819e5f030 fix(ovh): fix signature in api mode (#431) 2023-04-07 02:30:36 -07:00
Frederic R
228fc507a7 Revert "#319 Fix OVH Invalid Signature (#414)" (#426)
This reverts commit eaa7200734.
2023-02-02 00:37:38 +00:00
Erwan Daubert
eaa7200734 #319 Fix OVH Invalid Signature (#414)
Co-authored-by: Erwan Daubert <erwan.daubert@kaz.bzh>
2023-02-02 00:33:30 +00:00
Quentin McGaw
bfbd68f9d5 chore(dev): update ssh bind mount for newer dev container 2023-01-09 08:05:27 +00:00
Quentin McGaw
83a9a2d999 fix(dynu): subdomain update, fixes #386 2023-01-09 08:05:27 +00:00
orbatschow
8a18aec420 fix(gcp): disable custom http client (#405) 2023-01-08 10:02:53 -08:00
Quentin McGaw
3fd82ab89f chore(dyn): change password -> client_key 2022-12-14 20:18:56 +00:00
Quentin McGaw
2165b69137 chore(log): use github.com/qdm12/log 2022-12-07 10:10:18 +00:00
Quentin McGaw
3ad972b718 chore(health): remove unneeded logger argument in newHandler 2022-12-07 10:08:43 +00:00
Quentin McGaw
e197478638 chore(health): remove unneeded logger argument 2022-12-07 10:08:43 +00:00
Quentin McGaw
3b0fae84e5 feat(dns): specify resolver address and timeout
- `RESOLVER_ADDRESS`
- `RESOLVER_TIMEOUT`
2022-12-07 10:08:39 +00:00
Quentin McGaw
46b1d649f0 docs(readme): add missing AllInkl provider 2022-12-07 08:05:00 +00:00
Philipp Susen
bd4b87032f feat(provider): support for inwx (#379) 2022-12-07 02:57:55 -05:00
Quentin McGaw
59bffe99ba fix(dnsomatic): allow email addresses as user field 2022-11-30 19:35:08 +00:00
Quentin McGaw
d8de9d25ad feat(pkg/publicip): blacklist providers banning us 2022-11-03 09:45:01 +00:00
Quentin McGaw
ba8cd00326 feat(pkg): pkg/publicip/info package (#189) 2022-11-03 04:32:11 -04:00
Quentin McGaw
9671407502 chore(lint): bump to v1.50.1 and add linters
- `dupword` linter
- `paralleltest` linter
2022-11-03 07:44:34 +00:00
Quentin McGaw
3a3c892385 chore(dyn): more information on bad request error 2022-09-20 13:30:03 +00:00
Quentin McGaw
cd37ab1646 fix(ddnss.de): add dual_stack parameter
- Discussed on #270
- Original fix proposed by @quantum-byte
2022-09-17 21:08:17 +00:00
orbatschow
7c815924ff feat(provider): add support for GCP (#337)
Co-authored-by: Quentin McGaw <quentin.mcgaw@gmail.com>
2022-09-17 13:51:25 -07:00
Tao Tien
675c4ae64e fix(docs): remove unneeded steps in cloudflare.md (#363) 2022-09-17 07:27:06 -07:00
vulubalulu
71b9b92288 fix(docs): gandi.md JSON example syntax (#362) 2022-09-16 14:42:05 -07:00
Quentin McGaw
23ec8d7907 feat(dnspod): log entire JSON on error 2022-09-06 12:43:49 +00:00
Quentin McGaw
89711cd76d fix(ovh): support nochg responses 2022-09-06 12:36:00 +00:00
Quentin McGaw
f09ca2dd0a fix(linode): name field when creating record 2022-09-04 03:00:53 +00:00
Quentin McGaw
251112697a fix(build): revert xcputranslate to v0.6.0 2022-09-02 21:24:53 +00:00
Quentin McGaw
7fcd0f902b chore(lint): add goerr113 linter
- Change database id to be `uint` instead of `int`
- Define and use sentinel errors
- Return `string` instead of `error` when appropriate (linode)
2022-08-30 01:23:45 +00:00
Quentin McGaw
88474c8a3a fix(namecheap): allow empty IP XML field 2022-08-28 22:30:09 +00:00
Quentin McGaw
5674102019 chore(build): bump Go from 1.18 to 1.19 2022-08-28 22:20:28 +00:00
Quentin McGaw
41a64bbd55 chore(build): bump Go from 1.17 to 1.18 2022-08-28 22:20:04 +00:00
Quentin McGaw
f793454476 chore(lint): add new linters and fix issues
- Added: asasalint, bidichk, containedctx, cyclop, decorder, durationcheck, errchkjson, errname, errorlint, execinquery, forcetypeassert, gomoddirectives, grouper, interfacebloat, maintidx, makezero, nilnil, nosprintfhostport, promlinter, reassign, tenv, usestdlibvars
2022-08-28 22:19:21 +00:00
Quentin McGaw
bcbf0938c1 chore(lint): add revive linter and fix issues
- Export returned struct types
- Do not export interfaces for other packages to use
2022-08-28 22:18:19 +00:00
Quentin McGaw
1561babd76 chore(lint): add ireturn linter
- Return concrete structs
- Accept interfaces
- Define narrow interfaces locally where needed
2022-08-28 19:50:02 +00:00
Quentin McGaw
45c684232d chore(lint): ignore dupl linter for test files 2022-08-28 19:21:43 +00:00
Quentin McGaw
d725c7c856 chore(lint): enable all default linters 2022-08-28 15:13:52 +00:00
Quentin McGaw
bc081ef3c1 chore(lint): remove deprecated linters
- deadcode
- structcheck
- varcheck
2022-08-28 15:11:06 +00:00
Quentin McGaw
a688255270 chore(lint): bump from v1.46.2 to v1.49.0 2022-08-28 15:09:50 +00:00
Trenton Holmes
0655f1a080 chore(ci): bump GitHub actions versions (#353)
- actions/checkout from v2 to v3
- docker/setup-qemu-action from v1 to v2
- docker/setup-buildx-action from v1 to v2
- docker/login-action from v1 to v2
- docker/build-push-action from v2 to v3
- peter-evans/dockerhub-description from v2.1.0 to v3.1.0
- crazy-max/ghaction-github-labeler from v1 to v4
2022-08-28 07:59:41 -07:00
Trenton Holmes
b3a7669d2a chore(build): update Alpine, Golangci-lint and xcputranslate (#354)
- Alpine from 3.15 to 3.16
- Xcputranslate from v0.6.0 to v0.7.0
- Golangci-lint from v1.44.2 to v1.46.2
2022-08-28 07:57:21 -07:00
Quentin McGaw
f455c2a880 fix(dd24): non-empty response support, fix #358 2022-08-28 14:32:09 +00:00
Michael Miklis
584597d5cb feat(provider): add support for all-inkl.com (#309) 2022-03-15 10:35:16 -04:00
Quentin McGaw
1f9eb467ca docs(duckdns): fix provider_ip not for ipv6 2022-03-09 21:45:41 +00:00
Quentin McGaw
b1a69e028e chore(lint): upgrade golangci-lint to v1.44.2 2022-02-26 13:19:06 +00:00
Quentin McGaw
39c3732202 chore(devcontainer): update files 2022-02-26 13:19:03 +00:00
Quentin McGaw
b2634a15fa chore(providers): rework IP string search code 2022-02-26 13:18:56 +00:00
Marvin
5b426afc57 feat(provider): add support for dynu.com (#285) 2022-02-05 13:12:07 -04:00
Quentin McGaw (desktop)
f5b76f214e fix(dnspod): IPv6 record ID finder 2022-01-21 13:34:03 +00:00
Quentin McGaw (desktop)
e4f0c0d561 fix(freedns): unmarshal no ip change messages 2022-01-21 12:44:28 +00:00
Quentin McGaw
0f28807240 chore(docker): upgrade Alpine to 3.15 2021-12-15 09:58:20 +00:00
Quentin McGaw
d1fbbbe9d4 chore(lint): upgrade golangci-lint to v1.43.0 2021-12-15 09:57:34 +00:00
Quentin McGaw
ae7f10448d fix(linode): error decoding 2021-12-15 09:54:00 +00:00
Quentin McGaw
01334ad91f fix(spdyn): nochg response 2021-12-15 09:45:41 +00:00
Quentin McGaw
fa15abb781 fix(spdyn): good message processing 2021-12-04 21:52:12 +00:00
Quentin McGaw
87ffce17b1 fix(namecheap): XML decoding error 2021-11-21 12:36:31 +00:00
Quentin McGaw
7382c5bb92 deps(rootcerts): upgrade to v0.2.0 2021-11-21 12:36:31 +00:00
adamus1red
5b657dfa92 feat(ci): add GHCR registry image (#259)
Co-authored-by: adamus1red <adamus1red@noreply.example.com>
2021-11-17 21:21:06 +01:00
Quentin McGaw (desktop)
ecda99a217 Feat: allow to specify host for Dreamhost 2021-10-24 15:45:01 +00:00
Quentin McGaw (desktop)
7fccf2c979 Feat: additional logging about config read 2021-10-21 03:19:47 +00:00
Quentin McGaw (desktop)
14cd7b0a47 Maint: add error context for OVH 2021-10-15 14:39:10 +00:00
Quentin McGaw (desktop)
d0f03c8ae1 Maint: remove tidy CI check (due to Go 1.17) 2021-10-15 14:20:04 +00:00
王文慧
7acc9b931a Feat: support Aliyun (#252)
Co-authored-by: Jack Wang <jack.wang@hp.com>

Fixes #234 and #198 
Closes #209
2021-10-14 07:02:08 -07:00
lymanepp
ea215dec6b Fix: LuaDNS: match configured host instead of first record (#249) 2021-10-06 08:57:54 -04:00
Quentin McGaw (desktop)
87f06eeb28 Doc: cloudflare host parameter should be @ 2021-10-02 18:04:00 +00:00
Quentin McGaw (desktop)
963da5aab7 Fix: Porkbun not updating records (fix #247) 2021-09-28 14:21:30 +00:00
Quentin McGaw (desktop)
9e73f99ee1 Maint: upgrade qdm12/goshutdown to v0.3.0 2021-09-27 13:43:27 +00:00
Quentin McGaw (desktop)
01f4044404 Fix: SHOUTRRR_ADDRESSES case sensitivity 2021-09-12 01:12:43 +00:00
Quentin McGaw (desktop)
904c59d6ac Maint: upgrade Go to 1.17 2021-09-11 20:36:46 +00:00
Quentin McGaw (desktop)
242c47bbed Maint: upgrade golangci-lint to v1.42.1 2021-09-11 20:35:12 +00:00
Quentin McGaw (desktop)
00a1d25847 Feat: upgrade shoutrrr library 2021-09-11 20:34:37 +00:00
Quentin McGaw (desktop)
8b327f81c7 Feat: add destination to notification errors 2021-09-10 22:07:18 +00:00
Quentin McGaw
b29d6bb9d2 Docs: fix contributing document links 2021-09-10 11:14:39 -04:00
Jordan Hotmann
cdbebc2323 Doc: freedns: domain setup documentation (#238) 2021-08-30 14:44:56 -04:00
Quentin McGaw (desktop)
335c82b4be Maint: upgrade qdm12/golibs
- Fix logging settings inheritance
- Fix debug logging for HTTP client
2021-08-25 00:24:57 +00:00
Quentin McGaw (desktop)
28e2d00198 Fix: shoutrrr validation error wrapping, fix #233 2021-08-24 18:52:34 +00:00
Quentin McGaw (desktop)
c584f9b3d5 Fix: dd24 API call and fix #236 2021-08-24 15:55:02 +00:00
Quentin McGaw (desktop)
51dd14cb66 Fix: gosplash title, fix #232 2021-08-16 12:37:10 +00:00
Quentin McGaw (desktop)
41e61ddaa6 Fix: better error messages for JSON unmarshal 2021-08-10 22:27:17 +00:00
Quentin McGaw (desktop)
ee69feaaea Maint: rename params.go to reader.go 2021-08-10 22:20:50 +00:00
Quentin McGaw (desktop)
9252bca505 Maint: http client logger middleware 2021-08-10 20:47:36 +00:00
Quentin McGaw (desktop)
8f8d187a32 Maint: upgrade qdm12/golibs
- Wrap errors in config package with environment variable name
- Update logger calls to use a single string
2021-08-10 19:34:47 +00:00
Quentin McGaw (desktop)
d837acd3a2 Maint: backup package refactor 2021-08-10 19:11:07 +00:00
Quentin McGaw (desktop)
ad57321ea3 Maint: remove GetAllDomainsHosts method 2021-08-10 17:33:56 +00:00
Quentin McGaw (desktop)
e20a9b8634 Maint: interface composition for Database 2021-08-10 17:31:04 +00:00
Quentin McGaw (desktop)
5847e9fe8f Maint: remove unneeded splash constants 2021-08-10 17:28:35 +00:00
Quentin McGaw (desktop)
e608a016db Hotfix: use breml/rootcerts for TLS certs 2021-08-10 12:24:29 +00:00
Quentin McGaw (desktop)
050b34a9bd Maint: use qdm12/gosplash 2021-08-10 11:44:52 +00:00
Quentin McGaw (desktop)
96cf980b36 Doc: clarify how to use another user ID 2021-08-10 11:41:47 +00:00
Quentin McGaw (desktop)
3fc3b115f8 Doc: add Build the image section 2021-08-10 11:41:28 +00:00
Quentin McGaw (desktop)
6a3789f78c Maint: remove unneeded /tmp/data in Dockerfile 2021-08-10 11:35:34 +00:00
Quentin McGaw (desktop)
cd6d46e146 Maint: upgrade to Alpine 3.14 2021-08-10 11:34:51 +00:00
Quentin McGaw (desktop)
6fdbaa1b97 Maint: UID and GID build arguments 2021-08-10 11:32:14 +00:00
Quentin McGaw (desktop)
4684dfc9a3 Maint: remove unneeded alpine tzdata 2021-08-10 11:31:06 +00:00
Quentin McGaw (desktop)
9b094fff7a Doc: remove lint warnings from the readme 2021-08-10 11:23:58 +00:00
Quentin McGaw (desktop)
a4af81dddb Hotfix: lint errors 2021-08-09 14:54:04 +00:00
Bastian Wagner
e735ef335d Feat: add servercow provider (#224) 2021-08-09 07:51:46 -07:00
Frederic R
3ece0c967e Feat: Porkbun provider support (#217) 2021-08-09 06:13:21 -07:00
DiamondPrecisionComputing
523bbee5b8 Doc: fix google provider JSON example (#223) 2021-07-30 11:26:21 -07:00
Quentin McGaw (desktop)
86f559c0b3 Doc: rework metadata badges in readme 2021-07-20 15:22:41 +00:00
Quentin McGaw (desktop)
4be365574d Maint: remove microbadger (EOL) 2021-07-20 15:13:26 +00:00
Quentin McGaw (desktop)
10a952f7b9 Doc: remove sanitize query param for readme svg 2021-07-20 15:12:57 +00:00
Frederic R
3606f7a461 Fix line endings to lf (#220) 2021-07-20 14:39:13 +01:00
Frederic R
ae24ab8d56 CI: allow slash in branch name for docker image tags (#219) 2021-07-19 15:35:57 -07:00
Quentin McGaw (desktop)
bc1a5b206f Fix: DNS public IP fetching timeout 2021-07-02 18:30:42 +00:00
Quentin McGaw (desktop)
2dceab7452 Feat: retry getting IP address 3 times 2021-07-02 03:15:28 +00:00
Quentin McGaw (desktop)
1e74dc6179 Fix: write JSON file from CONFIG variable 2021-06-30 01:36:08 +00:00
Quentin McGaw (desktop)
fe00994522 Feat: PUBLICIP_DNS_TIMEOUT variable 2021-06-29 20:37:56 +00:00
Quentin McGaw (desktop)
d22dc41903 Maint: pubip: default timeout to 3s for DNS 2021-06-29 20:32:00 +00:00
Quentin McGaw (desktop)
76b1f5f0df Maint: local scoped buildInfo 2021-06-29 20:28:02 +00:00
Quentin McGaw (desktop)
c5de1358dd Doc: add missing architectures to readme 2021-06-29 20:26:06 +00:00
Quentin McGaw (desktop)
4133dbfdc7 Feat: Support Shoutrrr addresses 2021-06-29 20:05:06 +00:00
Quentin McGaw (desktop)
c4f50992c2 Maint: use qdm12/goshutdown 2021-06-29 19:15:55 +00:00
Quentin McGaw (desktop)
f8b67e5261 Maint: context dependent DNS resolutions 2021-06-29 19:13:24 +00:00
Quentin McGaw (desktop)
1b154df8aa Maint: move setupGotify inline in _main function 2021-06-29 18:49:32 +00:00
Quentin McGaw (desktop)
f9edf75540 Maint: more robust main logic
- Encapsulating main() handling OS signals
- _main returns an error instead of an exit code
2021-06-29 18:49:20 +00:00
Quentin McGaw (desktop)
ae748fb80d Maint: use health server port for client query 2021-06-29 18:39:23 +00:00
Quentin McGaw (desktop)
edb6c491bf Maint: use signal.NotifyContext 2021-06-29 18:35:22 +00:00
Quentin McGaw (desktop)
2f196b4886 Maint: pass default parent logger as arg to _main 2021-06-29 18:29:58 +00:00
Quentin McGaw (desktop)
2d7d016650 Maint: pass os.Args as argument to _main 2021-06-29 18:26:28 +00:00
Quentin McGaw (desktop)
1da19b3d6d Maint: pass env as argument to _main 2021-06-29 18:25:44 +00:00
Quentin McGaw (desktop)
0c520eddda Maint: set test stage entrypoint in Dockerfile 2021-06-29 16:25:36 +00:00
Quentin McGaw (desktop)
050825d399 Maint: upgrade xcputranslate to v0.6.0 2021-06-29 16:24:33 +00:00
Quentin McGaw (desktop)
53212df518 Maint: upgrade golangci-lint to v1.41.1 2021-06-29 16:24:05 +00:00
Quentin McGaw (desktop)
6fe4743bc0 Maint: optimize Dockerfile for caching + x-builds
- Pull xcputranslate for build platform only (faster x-builds)
- Install golangci-lint from qmcgaw/binpot (faster)
- Install g++ in base stage (for caching)
- Copy xcputranslate in base stage (for caching)
- Install golangci-lint in base stage (for caching)
- Push ARG TARGETPLATFORM down in build stage (faster x-builds)
- Push versioning ARGs and LABEL down in final stage (for caching)
- Move data directory COPY up in final stage (for caching)
2021-06-29 16:23:21 +00:00
Quentin McGaw (desktop)
4cdc71b45c Maint: server listens on all interfaces 2021-06-29 16:16:07 +00:00
Quentin McGaw (desktop)
36a6275bd2 Maint: config package for environment variables 2021-06-29 16:12:28 +00:00
Quentin McGaw
ee66170a87 Support for domaindiscount24.com (#207) 2021-06-14 08:35:29 -07:00
Quentin McGaw (desktop)
bb62a9d9e1 Fix: wildcard hosts in URL query parameters
- Keep multi-dots wildcard host structure in display strings
- Use another function BuildURLQueryHostname for API calls
- Send the wildcard character in API calls
- Fix issue #214
- Fix behavior for wildcard hosts for:
  - cloudflare
  - ddnss.de
  - digitalocean
  - dnsomatic
  - dreamhost
  - dyn
  - dynv6
  - google
  - informaniak
  - njalla
  - noip
  - opendns
  - ovh
  - selfhost.de
  - spdyn
  - strato
  - variomedia
2021-06-14 00:55:42 +00:00
Quentin McGaw (desktop)
e04c1f83df Maintenance: use time/tzdata instead of Alpine's 2021-06-08 01:43:06 +00:00
Quentin McGaw (desktop)
e19cabc894 Fix: DATADIR defaults to /updater/data 2021-06-08 01:39:29 +00:00
Quentin McGaw (desktop)
682821efd0 Fix: healthcheck query to 127.0.0.1:port 2021-06-07 17:21:36 +00:00
Quentin McGaw (desktop)
276c1c02fd Maintenance: remove github.com/ovh/go-ovh dependency 2021-06-07 16:41:07 +00:00
Quentin McGaw (desktop)
9b8ec3298b Maintenance: simplify file paths logic 2021-06-07 13:51:45 +00:00
Quentin McGaw (desktop)
cc670b3939 Feature: request url and body debug logs 2021-06-07 12:58:17 +00:00
Quentin McGaw (desktop)
0c3d258620 Feature: HEALTH_SERVER_ADDRESS 2021-06-07 12:05:30 +00:00
Quentin McGaw (desktop)
d39ecd6fd3 Fix: IPv6 prefix support to avoid unneeded updates 2021-06-03 14:19:21 +00:00
Quentin McGaw (desktop)
43dd02dbd5 Documentation: add debug log level option 2021-05-27 16:25:39 +00:00
Quentin McGaw
381af0cd90 Feature: allow to run without settings 2021-05-23 22:16:50 +00:00
Quentin McGaw
2bede070de Fix: ROOT_URL behavior when served not at the root 2021-05-23 01:51:17 +00:00
Quentin McGaw
09b810732f Maintenance: use embed for static UI, fix #134 2021-05-23 01:44:33 +00:00
Quentin McGaw
398566850d Fix custom URL for HTTP ip method (fix #203) 2021-05-22 19:42:07 +00:00
Quentin McGaw
201df818d3 Maintenance: rename all provider file to provider.go 2021-05-20 15:29:38 +00:00
Quentin McGaw
b1a3740059 Maintenance: use io instead of ioutil if possible 2021-05-20 15:27:54 +00:00
Quentin McGaw
ae978e007b Maintenance: common receiver struct for all providers 2021-05-20 15:26:05 +00:00
Quentin McGaw
3688208030 Maintenance: split each provider in own package 2021-05-20 15:02:21 +00:00
Quentin McGaw
da4341fbba Maintenance: move settings creation
- From params package to settings package
- Remove constructor type
- Remove regex.Matcher arg if not needed for some providers
2021-05-20 14:47:40 +00:00
Quentin McGaw
1705afeada Maintenance: settings shared code in sub packages 2021-05-20 14:35:52 +00:00
Quentin McGaw
844904aa7b Maintenance: upgrade linting setup
- Update Golangci-lint to v1.40.1
- Add more linters
- Remove rules from .golangci.yml in favor of inline nolint comments
- Fix linting errors
2021-05-19 01:00:42 +00:00
Quentin McGaw
803232e670 Maintenance: upgrade all Go dependencies 2021-05-19 00:41:41 +00:00
Quentin McGaw
2f7ee832b8 Maintenance: go mod tidy 2021-05-19 00:34:56 +00:00
Quentin McGaw
943d1486b3 Maintenance: upgrade devcontainer settings 2021-05-19 00:34:13 +00:00
Quentin McGaw
d727feefc4 Feature: Variomedia support (#208)
- Taken from #174 with added fixes to support IPv6
- Closes #174
2021-05-18 20:28:36 -04:00
Quentin McGaw
aee3022a11 Fix: Dreamhost: create record before removing outdated one (#206) 2021-05-18 19:54:02 -04:00
Quentin McGaw
864a696680 Maintenance: upgrade golibs 2021-04-17 01:20:42 +00:00
Quentin McGaw
34623e1b81 Fix Spdyn authentication 2021-04-10 02:18:43 +00:00
Matthew Hill
c0e57c6e1d Fix: read case sensitive CONFIG variable (#192)
Converting the string to lowercase breaks case sensitive api keys/secrets
Signed-off-by: Matthew Hill <matthewchill7@gmail.com>
2021-04-04 11:14:38 -04:00
Quentin McGaw
1ff629dc17 Feature: provider IP for SPDyn 2021-03-23 23:09:40 +00:00
Quentin McGaw
23f4897365 Fix: ipversion display, fixes #190 2021-03-23 22:54:36 +00:00
Quentin McGaw
e3472476e2 SPDyn support, fixes #182 (#179) 2021-03-23 11:18:02 -04:00
Quentin McGaw
869cf52c7d Njalla support fixes #180 (#181) 2021-03-22 22:15:15 -04:00
Quentin McGaw
8b2e83a69e Feature: HTTP and DNS Public IP fetching options, fixes #136 (#187) 2021-03-22 17:49:58 -04:00
Quentin McGaw
106bcae966 Maintenance: simplify top level publicip package API (#186) 2021-03-21 21:49:47 -04:00
Quentin McGaw
0a89666d1d Feature: public IP package to work over HTTPs and DNS (#158) 2021-03-21 17:59:17 -04:00
Quentin McGaw
3ad9168576 Maintenance: underscore for unused matcher args 2021-03-15 03:26:08 +00:00
Quentin McGaw
1eaa495e66 Maintenance: remove unused Insert database method 2021-03-15 03:10:19 +00:00
Frederic R
5e0cc687ea Fixes #175 documentation (#176) 2021-03-11 08:03:25 -05:00
Quentin McGaw
289536b145 Documentation: architecture section 2021-03-07 01:22:48 +00:00
Quentin McGaw
d5e4936679 Feature: 5-tries DNS resolution per hostname 2021-03-04 14:34:42 +00:00
Quentin McGaw
6e18e921b7 Maintenance: upgrade golangci-lint to 1.37.0 2021-03-02 02:28:10 +00:00
Quentin McGaw
40c92eebf5 Maintenance: Docker build stage uses Alpine 3.13 2021-03-02 02:27:50 +00:00
Quentin McGaw
8adc0556ba Maintenance: upgrade to Go 1.15 2021-03-02 02:27:30 +00:00
Quentin McGaw
e7824014ee Feature: debug logs 2021-03-02 02:21:09 +00:00
Quentin McGaw
6e0d48f7c1 Maintenance: remove no_dns_lookup and replace DNSLookup() method with Proxied() (#160) 2021-03-02 02:07:52 +00:00
Quentin McGaw
a10fb64ffd Maintenance: golibs logger update (#170) 2021-03-01 20:51:22 -05:00
Quentin McGaw
a649f8a4a8 FreeDNS support (#173) 2021-02-20 20:30:16 -05:00
Quentin McGaw
72018451b3 Fix: DuckDNS documentation, fixes #171 2021-02-16 00:28:58 +00:00
Quentin McGaw
2e5b3c7924 Fix: remove Godaddy secret check 2021-02-13 20:26:59 +00:00
Quentin McGaw
c985595969 Fix: godaddy: key regex, fixing #169 2021-02-12 14:37:06 +00:00
Frederic R
caa4840a61 Feature: ddnsgopher favicon (#159) 2021-02-11 08:38:09 -05:00
Quentin McGaw
a86ddd42d1 HTTP server refactor (#164)
* Rework current server
* Update call is blocking
* Run first update without blocking
2021-02-08 21:22:05 -05:00
Frederic R
ce1a447e0a Readme: fix broken links on docker hub (#167) 2021-02-07 19:27:31 -05:00
Frederic R
22c8b587c9 fix typo noticed in issue #165 2021-02-07 18:41:53 +00:00
Quentin McGaw
78c86b0e24 Fix: DNSOMatic fixing #161 2021-01-30 19:58:32 +00:00
Quentin McGaw
fa771cd4b2 CI: use Alpine 3.13 to build Go program 2021-01-29 00:48:04 +00:00
Frederic R
4e94823f69 OVH subdomains (*) support with zone DNS (#154), fixes #153 2021-01-28 19:41:37 -05:00
Frederic R
96521addd5 introduce ddnsgopher logo (#157) 2021-01-28 10:33:53 +00:00
Frederic R
85ddad6da1 Update contributing.md with how to build the program with Go (#151) 2021-01-26 21:57:00 -05:00
Frederic R
d54c334e1c Gandi support (#149)
Authored-by: Frederic R. fredericrous
Co-authored-by: Quentin McGaw <quentin.mcgaw@gmail.com>
2021-01-26 21:54:18 -05:00
Quentin McGaw
49392003e0 CI: rework Github workflows 2021-01-26 02:09:43 +00:00
Quentin McGaw
5a5e0c7375 Fix: Listening port defaults to 8000, fixes #152 2021-01-26 02:04:18 +00:00
Quentin McGaw
180092f47e Linode support (#144) 2021-01-20 18:31:19 -05:00
Quentin McGaw
43e168581c FIX: OVH allow domain parameter, refers to #111 2021-01-20 19:13:45 +00:00
Quentin McGaw
76a40e47c7 CI: Fix BUILD_DATE variable setting 2021-01-19 22:58:01 -05:00
Quentin McGaw
50b1997dbb Change: do not log unhealthy 2021-01-19 06:46:03 +00:00
Quentin McGaw
d75398d71b Maintenance: add response body to error 2021-01-18 00:48:55 +00:00
Quentin McGaw
7191e93932 Maintenance: streamline HTTP headers setting 2021-01-17 21:16:40 +00:00
Quentin McGaw
ec1f7acbde Maintenance: use native Go HTTP client (#145) 2021-01-17 15:48:00 -05:00
Quentin McGaw
a8a8d5793b Maintenance: DNSPod: encode values in body only 2021-01-17 17:37:41 +00:00
Quentin McGaw
dde28ebd1f Maintenance: cloudflare setHeaders receiver 2021-01-17 17:15:01 +00:00
Quentin McGaw
cd5f04acaf CI: Push PR image in PR workflow 2021-01-17 17:06:27 +00:00
Quentin McGaw
5d1809aca6 CI: Fix Docker image tag name in PR workflow 2021-01-17 17:06:04 +00:00
Quentin McGaw
46ae9af6a4 CI: Remove old build workflow 2021-01-17 17:04:56 +00:00
Quentin McGaw
b9a357fc1c Fix: default BUILDPLATFORM for older Docker builds 2021-01-17 17:04:42 +00:00
Quentin McGaw
1e7c909baf Maintenance: additional fixes and changes to errrors 2021-01-17 04:53:59 +00:00
Quentin McGaw
e83857b3db Maintenance: rename params getters 2021-01-16 20:33:13 +00:00
Quentin McGaw
5ab1607f97 Feature: UPDATE_COOLDOWN_PERIOD refers to #140 2021-01-16 20:30:21 +00:00
Quentin McGaw
4bb09e86dd Maintenance: use filepath to join file paths 2021-01-16 20:22:21 +00:00
Quentin McGaw
9460fa969b Maintenance: opendns and ovh receiver letter fixed 2021-01-16 20:15:03 +00:00
Quentin McGaw
157e041b28 Maintenance: use BuildDomainName for url params 2021-01-16 20:12:59 +00:00
Quentin McGaw
b40391bb4e Maintenance: rework errors in settings package 2021-01-16 20:12:31 +00:00
Quentin McGaw
60cf3130e3 Fix: precise 1 hour duration in abuse log 2021-01-16 15:14:53 +00:00
Quentin McGaw
5557c32135 Fix: remove geoblocked ddnss.de ip echo services 2021-01-16 15:12:18 +00:00
Quentin McGaw
46e8647d35 Fix: do not log abuse error twice 2021-01-16 15:10:49 +00:00
Quentin McGaw
eb1f925576 strato: remove user agent string 2021-01-16 15:06:44 +00:00
Quentin McGaw
18b058f188 Maintenance: fix linting errors 2021-01-14 02:01:22 +00:00
Quentin McGaw
be89e798d2 OpenDNS support, fix #121 (#126) 2021-01-13 20:50:53 -05:00
Quentin McGaw
20704eea8c Dynv6 support, fix #89 (#133) 2021-01-13 20:49:01 -05:00
Quentin McGaw
6416c6fed3 OVH support, fix #111 (#127) 2021-01-13 20:48:04 -05:00
Quentin McGaw
a317809ff8 Fix: Strato bad authentication error 2021-01-14 01:43:34 +00:00
Quentin McGaw
7ae9f72038 CI: rework build workflows 2021-01-14 06:16:06 +00:00
Quentin McGaw
5b1bc29ad4 Fix: stop updating when record is in abuse state 2021-01-14 06:07:19 +00:00
Quentin McGaw
e937ee741c Strato: Treat abuse response case 2021-01-13 14:12:49 +00:00
Quentin McGaw
688aebbc4f Fix: Strato: nochg as success, refers to #140 2021-01-12 01:16:41 +00:00
Quentin McGaw
6c2c2cf7cb Maintenance: Update golibs and rework params 2021-01-10 23:45:28 +00:00
Quentin McGaw
1c3e4cdef7 Strato: unknown response print bug fix 2021-01-10 23:17:08 +00:00
Quentin McGaw
d8a7fef6bd LuaDNS support (#137) fix #135 2020-12-30 14:50:41 -05:00
Quentin McGaw
87f59bf498 Remove uuid dependency 2020-12-17 06:05:45 +00:00
Quentin McGaw
818f7471dd Using http.NewRequestWithContext 2020-12-17 06:01:15 +00:00
Quentin McGaw
b35658f32c Remove nolint comments in favor of golangci.yml 2020-12-17 05:58:44 +00:00
Quentin McGaw
48fb1a4b95 Fix noip6 and noip8245_6 links in readme 2020-12-17 05:50:56 +00:00
Quentin McGaw
2e069ccf1d Strato support (#132) 2020-12-16 18:48:19 -05:00
Quentin McGaw
0d38e9385f Remove local vscode directory 2020-12-15 07:48:47 +00:00
Quentin McGaw
b4310ad822 Build information written to Go binary at build 2020-12-15 07:48:27 +00:00
Quentin McGaw
878cf4cc45 Upgrade golangci-lint to 1.33.0 2020-12-15 07:47:44 +00:00
Quentin McGaw
89faafe377 Add .dockerignore to paths for build workflows 2020-12-15 07:47:31 +00:00
Quentin McGaw
bf3f78f9f9 Refactor HTTP servers and upgrade dependencies 2020-12-13 19:09:05 +00:00
Quentin McGaw
6bf82d7be1 Wiki in repository (#128)
* Split provider specific instructions in docs/
* Update links to be relative
* Update Google doc, co-authored by @gauravspatel
2020-12-12 19:38:21 -05:00
Quentin McGaw
77f1681c4c Simplify documentation to ease DNS providers addition 2020-12-13 02:36:43 +00:00
Quentin McGaw
82e3d60db5 Digital ocean support, fix #98 (#110) 2020-12-12 16:20:26 -05:00
Quentin McGaw
c65c8d63bd Selfhost.de support, fix #120 (#122) 2020-12-10 09:19:23 -05:00
Quentin McGaw
0f1ddfb9b0 Upgrade golangci-lint to v1.32.2 2020-12-10 14:11:16 +00:00
Quentin McGaw
1d466cdc83 Workflow trigger paths adjustments 2020-12-10 19:04:18 +00:00
Quentin McGaw
0a6ef7ffbf Small simplification in Runner code 2020-12-10 09:51:34 +00:00
Quentin McGaw
e7ae5ac4cc Fix #104 2020-12-07 04:27:34 +00:00
Quentin McGaw
701ae125bf Upgrade dependencies, refers to #119 2020-11-25 14:27:04 +00:00
Quentin McGaw
b775798b65 Better error log, refers to 119 2020-11-22 02:28:54 +00:00
Quentin McGaw
166b0c7095 Fix #115 2020-11-11 22:28:20 +00:00
Quentin McGaw
3240bb7d26 Fix #113 2020-10-31 14:40:52 +00:00
Quentin McGaw
3047c83ee9 Add linters and fix lint issues 2020-10-25 16:47:27 +00:00
Quentin McGaw
3b29a33849 Update golibs, fixing #112
- All network requests are run with a context for faster shutdowns
- No node id variable for logging
- Stability improvements
2020-10-25 16:31:26 +00:00
Quentin McGaw
860bc02e2e VSCode development container changes
- SSH bind mount as read write
- More Golang linters
2020-10-13 01:10:22 +00:00
Quentin McGaw
cd2d3c46cc DNS-O-Matic support (#97), fixes #88 2020-10-13 01:02:53 +00:00
Quentin McGaw
e630dd9889 Fix up readme instructions, fixes #106 2020-10-04 14:33:05 +00:00
Quentin McGaw
b2d96787b8 Add empty data directory in Dockerfile, fixes #107 2020-10-04 14:32:45 +00:00
Quentin McGaw
5b52255601 He.net, fixes #95 (#96) 2020-09-29 22:43:27 +00:00
Quentin McGaw
04c55028a1 No DNS lookup update detection, fix #104 (#105) 2020-09-27 13:09:02 -04:00
Quentin McGaw
e07e8da31c Rework regexp and fix #101 2020-09-20 21:37:00 +00:00
Quentin McGaw
af2f3a3257 Minor Dockerfile changes 2020-09-18 23:05:55 +00:00
Quentin McGaw
00efca4af4 Remove regex domain check, fix #92 2020-09-05 15:04:52 +00:00
Quentin McGaw
3272612db2 DynDNS Support (#56), fixes #55 2020-08-19 21:50:24 -04:00
Quentin McGaw
5b7968c468 Update dependencies 2020-08-02 15:03:21 +00:00
Quentin McGaw
7ec39c1256 Remove rewrap VScode extension 2020-07-25 15:27:13 -04:00
Quentin McGaw
96857f3bae TZ variable, fix #90 2020-07-25 15:26:23 -04:00
Quentin McGaw
57c7d1be2d Don dominio (#85)
- Fix #85
2020-07-19 18:33:02 -04:00
Shammi Shailaj
53b6f533a8 FIX for issue where cloudflare tokens with a '-' (hyphen/dash) were being deemed invalid (#86)
Thanks!
2020-06-26 15:36:10 -04:00
Quentin McGaw
a82ed93169 Merge branch 'master' of github.com:qdm12/ddns-updater 2020-06-24 14:23:06 +00:00
Quentin McGaw
d07fcc664b Update go extension name for dev container 2020-06-24 14:22:47 +00:00
Quentin McGaw
d013ceb869 Using Alpine 3.12 for building 2020-06-24 14:22:35 +00:00
nu50218
d3506e9792 Fix bug in (*params.reader).GetIPv6Method (#83) 2020-06-17 22:37:19 -04:00
nu50218
c0249672bf Fix incorrect link in README (#82) 2020-06-17 13:40:53 -04:00
Quentin McGaw
cfeb95872a Fix unset status for existing up to date records 2020-06-03 12:26:47 +00:00
Quentin McGaw
091cf5f855 Fix #81 2020-06-03 12:06:16 +00:00
Quentin McGaw
7001add533 Better log messages when IP address changes 2020-06-03 12:02:25 +00:00
Quentin McGaw
9ccdbbd2d3 May fix #80 2020-06-01 21:15:57 +00:00
Quentin McGaw
f8a3ab63c6 Fix #79 2020-06-01 21:01:47 +00:00
Quentin McGaw
14033223d9 Fix healthcheck for ipv4+ipv6 records 2020-05-31 17:58:23 +00:00
Quentin McGaw
18161a6064 Better ip comparison mechanism
- Resolves ip addresses for each fqdn to compare with the IP addresses obtained, instead of comparing with db values
- Using db instead of records, for thread safe operation
- UNSET status at creation of new record
2020-05-31 17:58:09 +00:00
Quentin McGaw
216b8ab1ae String method for settings contains ip version 2020-05-31 15:59:17 +00:00
Quentin McGaw
4af23a756b No logger for Updater 2020-05-31 12:58:05 +00:00
Quentin McGaw
96c84a5a4f Hot fix #76 2020-05-31 12:12:19 +00:00
Quentin McGaw
4564d16c06 Fix version/vcs/build date env variables 2020-05-31 00:50:33 +00:00
Quentin McGaw
6e4a56b3cf Add noip echo service and improve documentation (#74) 2020-05-30 20:38:55 -04:00
Quentin McGaw
919ab65985 IPv6 for all providers but Namecheap (#72)
* DNSPod ipv6 support
* Dreamhost ipv6 support
* GoDaddy ipv6 support
* DuckDNS ipv6 support
* NoIP ipv6 support
* Namecheap does not support ipv6
2020-05-30 20:36:59 -04:00
Quentin McGaw
e023aae909 Cloudflare: get identifier automatically and supports ipv6 (#71) 2020-05-30 20:12:31 -04:00
Quentin McGaw
066bcdd3bf Google domain names support (#70) 2020-05-30 19:46:24 -04:00
Quentin McGaw
0a6c6b9bc7 Fix buildx branch workflow name 2020-05-30 23:44:18 +00:00
Quentin McGaw
8cdff8e4d3 Google ip method, fixes #69 2020-05-30 20:52:22 +00:00
Quentin McGaw
bffc30264f Paths ignore adjustments in docker build workflows 2020-05-30 20:47:44 +00:00
Quentin McGaw
4f141c20a0 Branch building workflow 2020-05-30 20:45:31 +00:00
Quentin McGaw
582ce626c8 Sort code constants DNS providers alphabetically 2020-05-30 20:18:35 +00:00
Quentin McGaw
13b29aeba4 DNS provider names sorted alphabetically 2020-05-30 20:05:56 +00:00
Quentin McGaw
a5afca15d1 Fix #62 CONFIG env variable 2020-05-30 18:41:31 +00:00
Quentin McGaw
25ee692242 Fixes #68: using hosts for duckdns 2020-05-30 18:14:18 +00:00
Quentin McGaw
922146efd3 Removed some Github workflows
- Greetings doesn't work on forked PRs
- Misspell is done with golangci-lint
- Security doesn't run on Scratch based docker images
2020-05-30 17:42:34 +00:00
Quentin McGaw
db9959cf59 Readme improved and sections moved to Wiki 2020-05-30 17:40:28 +00:00
Quentin McGaw
50303aef7b Issue templates 2020-05-30 17:32:00 +00:00
Quentin McGaw
137e372102 Fix #58 2020-05-30 17:10:11 +00:00
Quentin McGaw
f300c59411 Remove debug log lines 2020-05-30 17:03:23 +00:00
Quentin McGaw
c23998bd09 Refactoring (#63)
- Only calls DNS API(s) once the public IP address changes
- Only one ip method per ip version (ipv4, ipv6, ipv4/v6)
- Gets the ip address once every period for all records
- More object oriented coding instead of functional
- Support to update ipv4 and ipv6 records separately, for supported DNS providers
2020-05-29 20:38:01 -04:00
324 changed files with 20897 additions and 3708 deletions

View File

@@ -0,0 +1,5 @@
.dockerignore
devcontainer.json
docker-compose.yml
Dockerfile
README.md

1
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1 @@
FROM qmcgaw/godevcontainer

69
.devcontainer/README.md Normal file
View File

@@ -0,0 +1,69 @@
# Development container
Development container that can be used with VSCode.
It works on Linux, Windows 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
- [Docker](https://www.docker.com/products/docker-desktop) installed and running
- [Docker Compose](https://docs.docker.com/compose/install/) installed
## Setup
1. Create the following files on your host if you don't have them:
```sh
touch ~/.gitconfig ~/.zsh_history
```
Note that the development container will create the empty directories `~/.docker`, `~/.ssh` and `~/.kube` if you don't have them.
1. **For Docker on OSX or Windows without WSL**: ensure your home directory `~` is 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.
## Customization
### Customize the image
You can make changes to the [Dockerfile](Dockerfile) and then rebuild the image. For example, your Dockerfile could be:
```Dockerfile
FROM qmcgaw/godevcontainer
RUN apk add curl
```
To rebuild the image, either:
- 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 `/root/.welcome.sh` to replace the [current welcome script](https://github.com/qdm12/basedevcontainer/blob/master/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). You can also now do it directly with VSCode without restarting the container.
### 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:
```yml
database:
image: postgres
restart: always
environment:
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.

View File

@@ -1,117 +1,75 @@
{
"name": "ddns-dev",
"dockerComposeFile": [
"docker-compose.yml"
],
"service": "vscode",
"runServices": [
"vscode"
],
"shutdownAction": "stopCompose",
"postCreateCommand": "go mod download",
"workspaceFolder": "/workspace",
"appPort": 8000,
"extensions": [
"ms-vscode.go",
"IBM.output-colorizer",
"eamodio.gitlens",
"mhutchie.git-graph",
"davidanson.vscode-markdownlint",
"shardulm94.trailing-spaces",
"alefragnani.Bookmarks",
"Gruntfuggly.todo-tree",
"mohsen1.prettify-json",
"quicktype.quicktype",
"spikespaz.vscode-smoothtype",
"stkb.rewrap",
"vscode-icons-team.vscode-icons"
],
"settings": {
// General settings
"files.eol": "\n",
// Docker
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
// Golang general settings
"go.useLanguageServer": true,
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
"--enable",
"rowserrcheck",
"--enable",
"bodyclose",
"--enable",
"dogsled",
"--enable",
"dupl",
"--enable",
"gochecknoglobals",
"--enable",
"gochecknoinits",
"--enable",
"gocognit",
"--enable",
"goconst",
"--enable",
"gocritic",
"--enable",
"gocyclo",
"--enable",
"goimports",
"--enable",
"golint",
"--enable",
"gosec",
"--enable",
"interfacer",
"--enable",
"maligned",
"--enable",
"misspell",
"--enable",
"nakedret",
"--enable",
"prealloc",
"--enable",
"scopelint",
"--enable",
"unconvert",
"--enable",
"unparam",
"--enable",
"whitespace"
],
// Golang on save
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Golang testing
"go.toolsEnvVars": {
"GOFLAGS": "-tags=integration"
},
"gopls.env": {
"GOFLAGS": "-tags=integration"
},
"go.testEnvVars": {},
"go.testFlags": [
"-v"
],
"go.testTimeout": "600s"
}
{
"name": "ddns-dev",
"dockerComposeFile": [
"docker-compose.yml"
],
"service": "vscode",
"runServices": [
"vscode"
],
"shutdownAction": "stopCompose",
"postCreateCommand": "~/.windows.sh && go mod download && go mod tidy",
"workspaceFolder": "/workspace",
// "overrideCommand": "",
"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": true
}
},
"gopls": {
"usePlaceholders": false,
"staticcheck": true,
"vulncheck": "Imports"
},
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
}
}
}
}
}

View File

@@ -2,14 +2,26 @@ version: "3.7"
services:
vscode:
image: qmcgaw/godevcontainer
build: .
volumes:
- ../:/workspace
- ~/.ssh:/home/vscode/.ssh:ro
- ~/.ssh:/root/.ssh:ro
# Docker socket to access Docker server
- /var/run/docker.sock:/var/run/docker.sock
# 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.
- ~/.ssh:/mnt/ssh
# Shell history persistence
- ~/.zsh_history:/root/.zsh_history
# Git config
- ~/.gitconfig:/root/.gitconfig
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"
entrypoint: [ "zsh", "-c", "while sleep 1000; do :; done" ]

View File

@@ -2,9 +2,11 @@
.git
.github
.vscode
docs
readme
!readme/*.go
.gitignore
config.json
docker-compose.yml
LICENSE
README.md
ui/favicon.svg

42
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@@ -0,0 +1,42 @@
---
name: Bug
about: Report a bug
title: 'Bug: FILL THIS TEXT OR ISSUE WILL BE CLOSED'
labels:
---
<!--
YOU CAN CHAT THERE EVENTUALLY:
https://github.com/qdm12/ddns-updater/discussions
-->
**TLDR**: *Describe your issue in a one liner here*
1. Is this urgent: Yes/No
2. DNS provider(s) you use: Answer here
3. Program version:
<!-- See the line at the top of your logs -->
`Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`
4. What are you using to run the container: docker-compose
5. Extra information (optional)
Logs:
```log
```
Configuration file (**remove your credentials!**):
```json
```
Host OS:

View File

@@ -0,0 +1,19 @@
---
name: Feature request
about: Suggest a feature to add to this project
title: 'Feature request: FILL THIS TEXT OR ISSUE WILL BE CLOSED'
labels:
---
1. What's the feature?
2. Extra information?
<!--
YOU CAN CHAT THERE EVENTUALLY:
https://github.com/qdm12/ddns-updater/discussions
-->

42
.github/ISSUE_TEMPLATE/help.md vendored Normal file
View File

@@ -0,0 +1,42 @@
---
name: Help
about: Ask for help
title: 'Help: FILL THIS TEXT OR ISSUE WILL BE CLOSED'
labels:
---
<!--
HAVE A CHAT FIRST!
https://github.com/qdm12/ddns-updater/discussions
-->
**TLDR**: *Describe your issue in a one liner here*
1. Is this urgent: Yes/No
2. DNS provider(s) you use: Answer here
3. Program version:
<!-- See the line at the top of your logs -->
`Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)`
4. What are you using to run the container: docker-compose
5. Extra information (optional)
Logs:
```log
```
Configuration file (**remove your credentials!**):
```json
```
Host OS:

15
.github/dependabot.yml vendored Normal file
View 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
View File

@@ -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
View 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

View File

@@ -1,12 +1,163 @@
name: Docker build
name: CI
on:
release:
types:
- published
push:
branches:
- master
paths:
- .github/workflows/build.yml
- cmd/**
- internal/**
- pkg/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
pull_request:
branches: [master]
paths:
- .github/workflows/build.yml
- cmd/**
- internal/**
- pkg/**
- .dockerignore
- .golangci.yml
- Dockerfile
- go.mod
- go.sum
jobs:
build:
verify:
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
env:
DOCKER_BUILDKIT: "1"
steps:
- uses: actions/checkout@v3
- uses: reviewdog/action-misspell@v1
with:
locale: "US"
level: error
exclude: |
*.md
- name: Linting
run: docker build --target lint .
- name: Mocks check
run: docker build --target mocks .
- name: Build test image
run: docker build --target test -t test-container .
- name: Run tests in test container
run: |
touch coverage.txt
docker run --rm \
-v "$(pwd)/coverage.txt:/tmp/gobuild/coverage.txt" \
test-container
- name: Build final image
run: docker build -t final-image .
codeql:
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v3
- uses: github/codeql-action/init@v3
with:
languages: go
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
publish:
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:
- name: Checkout
uses: actions/checkout@v2
- name: Build image
run: docker build .
- uses: actions/checkout@v3
with:
fetch-depth: 0 # for gorelease last step
# extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
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/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v2
with:
username: qmcgaw
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- uses: docker/login-action@v2
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@v5.1.0
with:
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: |
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_name == 'release'
uses: actions/setup-go@v2
with:
go-version: 1.21
- if: github.event_name == 'release'
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --clean --config .github/workflows/configs/.goreleaser.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,40 +0,0 @@
name: Buildx latest
on:
push:
branches: [master]
paths-ignore:
- .github/workflows/buildx-release.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/greetings.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/workflows/security.yml
- .dockerignore
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Buildx setup
uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg VERSION=latest \
-t qmcgaw/ddns-updater:latest \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/ddns-updater/t2fcZxog8ce_kJYJ61JjkYwHF5s= || exit 0

View File

@@ -1,40 +0,0 @@
name: Buildx release
on:
release:
types: [published]
paths-ignore:
- .github/workflows/buildx-latest.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/greetings.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/workflows/security.yml
- .dockerignore
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: Dockerhub login
run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u qmcgaw --password-stdin 2>&1
- name: Run Buildx
run: |
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/386,linux/arm64,linux/arm/v7 \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg VERSION=${GITHUB_REF##*/} \
-t qmcgaw/ddns-updater:${GITHUB_REF##*/} \
--push \
.
- run: curl -X POST https://hooks.microbadger.com/images/qmcgaw/ddns-updater/t2fcZxog8ce_kJYJ61JjkYwHF5s= || exit 0

View File

@@ -0,0 +1,30 @@
before:
hooks:
- go mod download
builds:
- main: ./cmd/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

View File

@@ -0,0 +1,35 @@
{
"ignorePatterns": [
{
"pattern": "^http://localhost"
},
{
"pattern": "^https://api6.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://ipv6.ipleak.net/json"
}
],
"timeout": "20s",
"retryOn429": false,
"fallbackRetryDelay": "30s",
"aliveStatusCodes": [
200,
206
]
}

View File

@@ -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

View File

@@ -1,11 +0,0 @@
name: Greetings
on: [pull_request, issues]
jobs:
greeting:
runs-on: ubuntu-latest
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: 'Thanks for creating your first issue :+1: Feel free to use [Slack](https://join.slack.com/t/qdm12/shared_invite/enQtODMwMDQyMTAxMjY1LTU1YjE1MTVhNTBmNTViNzJiZmQwZWRmMDhhZjEyNjVhZGM4YmIxOTMxOTYzN2U0N2U2YjQ2MDk3YmYxN2NiNTc) if you just need some quick help or want to chat'
pr-message: 'Thank you so much for contributing, that means a lot to me :wink:'

View File

@@ -1,18 +1,18 @@
name: labels
on:
push:
branches: ["master"]
branches: [master]
paths:
- '.github/labels.yml'
- '.github/workflows/labels.yml'
- .github/labels.yml
- .github/workflows/labels.yml
jobs:
labeler:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Labeler
if: success()
uses: crazy-max/ghaction-github-labeler@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: crazy-max/ghaction-github-labeler@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

21
.github/workflows/markdown-skip.yml vendored Normal file
View 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
View 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@v4
- uses: DavidAnson/markdownlint-cli2-action@v14
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@v3
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

View File

@@ -1,16 +0,0 @@
name: Misspells
on:
pull_request:
branches: [master]
push:
branches: [master]
jobs:
misspell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: reviewdog/action-misspell@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
locale: "US"
level: error

View File

@@ -1,59 +0,0 @@
name: Security scan of Docker image
on:
push:
branches: [master]
paths-ignore:
- .github/workflows/buildx-release.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/greetings.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/workflows/security.yml
- .dockerignore
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
pull_request:
branches: [master]
paths-ignore:
- .github/workflows/buildx-release.yml
- .github/workflows/dockerhub-description.yml
- .github/workflows/greetings.yml
- .github/workflows/labels.yml
- .github/workflows/misspell.yml
- .github/workflows/security.yml
- .dockerignore
- .gitignore
- docker-compose.yml
- LICENSE
- README.md
- title.svg
schedule:
- cron: '0 9 * * *'
jobs:
security-analysis:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Check for scratch
id: scratchCheck
run: echo ::set-output name=scratch::$(cat Dockerfile | grep 'FROM scratch')
- name: Build image
if: steps.scratchCheck.outputs.scratch == ''
run: docker build -t image .
- name: Phonito
if: steps.scratchCheck.outputs.scratch == ''
uses: phonito/phonito-scanner-action@master
with:
image: image
fail-level: LOW
phonito-token: ${{ secrets.PHONITO_TOKEN }}
- name: Trivy
if: steps.scratchCheck.outputs.scratch == ''
uses: homoluctus/gitrivy@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
image: image

4
.gitignore vendored
View File

@@ -1,3 +1 @@
*.exe
updater
.vscode
/data

View File

@@ -4,46 +4,84 @@ linters-settings:
misspell:
locale: US
issues:
exclude-rules:
- path: _test\.go
linters:
- containedctx
- dupl
- goerr113
linters:
disable-all: true
enable:
# - cyclop
- asasalint
- asciicheck
- bidichk
- bodyclose
- deadcode
- containedctx
- decorder
- dogsled
- dupl
- errcheck
- dupword
- durationcheck
- errchkjson
- errname
- errorlint
- execinquery
- exhaustive
- exportloopref
- forcetypeassert
- gci
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- goerr113
- goheader
- goimports
- golint
- gomnd
- gomoddirectives
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- maligned
- grouper
- importas
- interfacebloat
- ireturn
- lll
- maintidx
- makezero
- misspell
- musttag
- nakedret
- nestif
- nilerr
- nilnil
- noctx
- nolintlint
- nosprintfhostport
- paralleltest
- prealloc
- predeclared
- promlinter
- reassign
- revive
- rowserrcheck
- scopelint
- staticcheck
- structcheck
- typecheck
- sqlclosecheck
- tenv
- thelper
- tparallel
- unconvert
- unparam
- unused
- varcheck
- usestdlibvars
- wastedassign
- whitespace
run:
skip-dirs:
- .devcontainer
- .github
service:
golangci-lint-version: 1.26.x # use the fixed version to not introduce new linters unexpectedly

8
.markdownlint.json Normal file
View File

@@ -0,0 +1,8 @@
{
"MD013": false,
"MD033": {
"allowed_elements": [
"img"
]
}
}

88
.vscode/settings.json vendored
View File

@@ -1,88 +0,0 @@
{
// General settings
"files.eol": "\n",
// Docker
"remote.extensionKind": {
"ms-azuretools.vscode-docker": "workspace"
},
// Golang general settings
"go.useLanguageServer": true,
"go.autocompleteUnimportedPackages": true,
"go.gotoSymbol.includeImports": true,
"go.gotoSymbol.includeGoroot": true,
"gopls": {
"completeUnimported": true,
"deepCompletion": true,
"usePlaceholders": false
},
"go.lintTool": "golangci-lint",
"go.lintFlags": [
"--fast",
"--enable",
"rowserrcheck",
"--enable",
"bodyclose",
"--enable",
"dogsled",
"--enable",
"dupl",
"--enable",
"gochecknoglobals",
"--enable",
"gochecknoinits",
"--enable",
"gocognit",
"--enable",
"goconst",
"--enable",
"gocritic",
"--enable",
"gocyclo",
"--enable",
"goimports",
"--enable",
"golint",
"--enable",
"gosec",
"--enable",
"interfacer",
"--enable",
"maligned",
"--enable",
"misspell",
"--enable",
"nakedret",
"--enable",
"prealloc",
"--enable",
"scopelint",
"--enable",
"unconvert",
"--enable",
"unparam",
"--enable",
"whitespace"
],
// Golang on save
"go.buildOnSave": "workspace",
"go.lintOnSave": "workspace",
"go.vetOnSave": "workspace",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Golang testing
"go.toolsEnvVars": {
"GOFLAGS": "-tags="
},
"gopls.env": {
"GOFLAGS": "-tags="
},
"go.testEnvVars": {},
"go.testFlags": [
"-v"
],
"go.testTimeout": "600s"
}

View File

@@ -1,54 +1,114 @@
ARG ALPINE_VERSION=3.11
ARG GO_VERSION=1.14
ARG BUILDPLATFORM=linux/amd64
ARG ALPINE_VERSION=3.19
ARG GO_VERSION=1.21
ARG XCPUTRANSLATE_VERSION=v0.6.0
ARG GOLANGCI_LINT_VERSION=v1.55.2
ARG MOCKGEN_VERSION=v1.6.0
FROM alpine:${ALPINE_VERSION} AS alpine
RUN apk --update add ca-certificates tzdata
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} qmcgaw/binpot:mockgen-${MOCKGEN_VERSION} AS mockgen
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
ARG GOLANGCI_LINT_VERSION=v1.26.0
RUN apk --update add git
ENV CGO_ENABLED=0
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s ${GOLANGCI_LINT_VERSION}
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
WORKDIR /tmp/gobuild
COPY .golangci.yml .
ENV CGO_ENABLED=0
# 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 2>&1
RUN go mod download
COPY pkg/ ./pkg/
COPY cmd/ ./cmd/
COPY internal/ ./internal/
COPY cmd/updater/main.go .
RUN go test ./...
RUN go build -ldflags="-s -w" -o app
FROM --platform=$BUILDPLATFORM base AS test
# Note on the go race detector:
# - 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 mocks
RUN git init && \
git config user.email ci@localhost && \
git config user.name ci && \
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
ARG VERSION=unknown
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.date=$CREATED' \
-X 'main.commit=$COMMIT' \
" -o app cmd/updater/main.go
FROM scratch
ARG BUILD_DATE
ARG VCS_REF
ARG VERSION
EXPOSE 8000
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=2 CMD ["/updater/app", "healthcheck"]
ARG UID=1000
ARG GID=1000
USER ${UID}:${GID}
ENTRYPOINT ["/updater/app"]
ENV \
# Core
CONFIG= \
PERIOD=5m \
UPDATE_COOLDOWN_PERIOD=5m \
PUBLICIP_FETCHERS=all \
PUBLICIP_HTTP_PROVIDERS=all \
PUBLICIPV4_HTTP_PROVIDERS=all \
PUBLICIPV6_HTTP_PROVIDERS=all \
PUBLICIP_DNS_PROVIDERS=all \
PUBLICIP_DNS_TIMEOUT=3s \
HTTP_TIMEOUT=10s \
DATADIR=/updater/data \
RESOLVER_ADDRESS= \
RESOLVER_TIMEOUT=5s \
# Web UI
LISTENING_ADDRESS=:8000 \
ROOT_URL=/ \
# Backup
BACKUP_PERIOD=0 \
BACKUP_DIRECTORY=/updater/data \
# Other
LOG_LEVEL=info \
LOG_CALLER=hidden \
SHOUTRRR_ADDRESSES= \
SHOUTRRR_DEFAULT_TITLE="DDNS Updater" \
TZ= \
HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \
HEALTH_HEALTHCHECKSIO_UUID=
ARG VERSION=unknown
ARG CREATED="an unknown date"
ARG COMMIT=unknown
LABEL \
org.opencontainers.image.authors="quentin.mcgaw@gmail.com" \
org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.version=$VERSION \
org.opencontainers.image.revision=$VCS_REF \
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. Works with Namecheap, Cloudflare, GoDaddy, DuckDns, Dreamhost, DNSPod and NoIP"
COPY --from=alpine --chown=1000 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=alpine --chown=1000 /usr/share/zoneinfo /usr/share/zoneinfo
EXPOSE 8000
HEALTHCHECK --interval=60s --timeout=5s --start-period=10s --retries=2 CMD ["/updater/app", "healthcheck"]
USER 1000
ENTRYPOINT ["/updater/app"]
ENV DELAY=10m \
ROOT_URL=/ \
LISTENING_PORT=8000 \
LOG_ENCODING=console \
LOG_LEVEL=info \
NODE_ID=0 \
HTTP_TIMEOUT=10s \
GOTIFY_URL= \
GOTIFY_TOKEN= \
BACKUP_PERIOD=0 \
BACKUP_DIRECTORY=/updater/data
COPY --from=builder --chown=1000 /tmp/gobuild/app /updater/app
COPY --chown=1000 ui/* /updater/ui/
org.opencontainers.image.description="Universal DNS updater with WebUI"
COPY --from=build --chown=${UID}:${GID} /tmp/gobuild/app /updater/app

758
README.md
View File

@@ -1,390 +1,368 @@
# Lightweight universal DDNS Updater with Docker and web UI
*Light container updating DNS A records periodically for GoDaddy, Namecheap, Cloudflare, Dreamhost, NoIP, DNSPod, Infomaniak, ddnss.de and DuckDNS*
[![DDNS Updater by Quentin McGaw](https://github.com/qdm12/ddns-updater/raw/master/readme/title.png)](https://hub.docker.com/r/qmcgaw/ddns-updater)
[![Build status](https://github.com/qdm12/ddns-updater/workflows/Buildx%20latest/badge.svg)](https://github.com/qdm12/ddns-updater/actions?query=workflow%3A%22Buildx+latest%22)
[![Docker Pulls](https://img.shields.io/docker/pulls/qmcgaw/ddns-updater.svg)](https://hub.docker.com/r/qmcgaw/ddns-updater)
[![Docker Stars](https://img.shields.io/docker/stars/qmcgaw/ddns-updater.svg)](https://hub.docker.com/r/qmcgaw/ddns-updater)
[![Image size](https://images.microbadger.com/badges/image/qmcgaw/ddns-updater.svg)](https://microbadger.com/images/qmcgaw/ddns-updater)
[![Image version](https://images.microbadger.com/badges/version/qmcgaw/ddns-updater.svg)](https://microbadger.com/images/qmcgaw/ddns-updater)
[![Join Slack channel](https://img.shields.io/badge/slack-@qdm12-yellow.svg?logo=slack)](https://join.slack.com/t/qdm12/shared_invite/enQtODMwMDQyMTAxMjY1LTU1YjE1MTVhNTBmNTViNzJiZmQwZWRmMDhhZjEyNjVhZGM4YmIxOTMxOTYzN2U0N2U2YjQ2MDk3YmYxN2NiNTc)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues)
## Features
- Updates periodically A records for different DNS providers: Namecheap, GoDaddy, Cloudflare, NoIP, Dreamhost, DuckDNS, DNSPod and Infomaniak (ask for more)
- Web User interface
![Web UI](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/webui.png)
- 12.3MB 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
- Sends notifications to your Android phone, see the [**Gotify**](#Gotify) section (it's free, open source and self hosted 🆒)
- Compatible with `amd64`, `386`, `arm64`, `arm32v7` (Raspberry Pis) CPU architectures.
## Setup
1. To setup your domains initially, see the [Domain set up](#domain-set-up) section.
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. Modify the *data/config.json* file similarly to:
```json
{
"settings": [
{
"provider": "namecheap",
"domain": "example.com",
"host": "@",
"ip_method": "provider",
"delay": 86400,
"password": "e5322165c1d74692bfa6d807100c0310"
},
{
"provider": "duckdns",
"domain": "example.duckdns.org",
"ip_method": "provider",
"token": "00000000-0000-0000-0000-000000000000"
},
{
"provider": "godaddy",
"domain": "example.org",
"host": "subdomain",
"ip_method": "duckduckgo",
"key": "aaaaaaaaaaaaaaaa",
"secret": "aaaaaaaaaaaaaaaa"
}
]
}
```
See more information in the [configuration section](#configuration)
1. Use the following command:
```bash
docker run -d -p 8000:8000/tcp -v "$(pwd)"/data:/updater/data qmcgaw/ddns-updater
```
You can also use [docker-compose.yml](https://github.com/qdm12/ddns-updater/blob/master/docker-compose.yml) with:
```sh
docker-compose up -d
```
1. 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*:
```json
{
"settings": [
{
"provider": "",
"domain": "",
"ip_method": "",
},
{
"provider": "",
"domain": "",
"ip_method": "",
}
]
}
```
The following parameters are to be added in *config.json*
For all record update configuration, you need the following:
- `"provider"` is the DNS provider and can be `"godaddy"`, `"namecheap"`, `"duckdns"`, `"dreamhost"`, `"cloudflare"`, `"noip"`, `"dnspod"` or `"ddnss"`
- `"domain"`
- `"ip_method"` is the method to obtain your public IP address and can be:
- `"provider"` means the public IP is automatically determined by the DNS provider (**only for DuckDNs, Namecheap, Infomaniak and NoIP**), most reliable.
- `"opendns"` using [https://diagnostic.opendns.com/myip](https://diagnostic.opendns.com/myip) (reliable)
- `"ifconfig"` using [https://ifconfig.io/ip](https://ifconfig.io/ip) (may be rate limited)
- `"ipinfo"` using [https://ipinfo.io/ip](https://ipinfo.io/ip) (may be rate limited)
- `"ipify"` using [https://api.ipify.org](https://api.ipify.org) (may be rate limited)
- `"ipify6"` using [https://api6.ipify.org](https://api.ipify.org) for IPv6 only (may be rate limited)
- `"ddnss"` using [https://ddnss.de/meineip.php](https://ddnss.de/meineip.php)
- `"ddnss4"` using [https://ip4.ddnss.de/meineip.php](https://ip4.ddnss.de/meineip.php) for IPv4 only
- `"ddnss6"` using [https://ip6.ddnss.de/meineip.php](https://ip6.ddnss.de/meineip.php) for IPv6 only
- `"cycle"` to cycle between each external methods, in order to avoid being rate limited
- You can also specify an HTTPS URL to obtain your public IP address (i.e. `"ip_method": "https://ipinfo.io/ip"`)
You can optionnally add the parameters:
- `"delay"` is the delay in seconds between each update. It defaults to the `DELAY` environment variable value.
- `"no_dns_lookup"` can be `true` or `false` and allows, if `true`, to prevent the periodic Docker healthcheck from running a DNS lookup on your domain.
For each DNS provider exist some specific parameters you need to add, as described below:
Namecheap:
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
- `"password"`
Cloudflare:
- `"zone_identifier"` is the Zone ID of your site
- `"identifier"` is the DNS record identifier as returned by the Cloudflare "List DNS Records" API (see below)
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
- `"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.
- *Optionally*, `"proxied"` can be `true` or `false` to use the proxy services of Cloudflare
GoDaddy:
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
- `"key"`
- `"secret"`
DuckDNS:
- `"token"`
Dreamhost:
- `"key"`
NoIP:
- `"host"` is your host and can be a subdomain or `"@"`
- `"username"`
- `"password"`
DNSPOD:
- `"host"` is your host and can be a subdomain or `"@"`
- `"token"`
Infomaniak:
- `"user"`
- `"password"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records)
DDNSS.de:
- `"user"`
- `"password"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records)
### Environment variables
| Environment variable | Default | Description |
| --- | --- | --- |
| `DELAY` | `10m` | Default delay between updates, following [this format](https://golang.org/pkg/time/#ParseDuration) |
| `ROOT_URL` | `/` | URL path to append to all paths to the webUI (i.e. `/ddns` for accessing `https://example.com/ddns` through a proxy) |
| `LISTENING_PORT` | `8000` | Internal TCP listening port for the web UI |
| `LOG_ENCODING` | `console` | Format of logging, `json` or `console` |
| `LOG_LEVEL` | `info` | Level of logging, `info`, `warning` or `error` |
| `NODE_ID` | `0` | Node ID (for distributed systems), can be any integer |
| `HTTP_TIMEOUT` | `10s` | Timeout for all HTTP requests |
| `GOTIFY_URL` | | (optional) HTTP(s) URL to your Gotify server |
| `GOTIFY_TOKEN` | | (optional) Token to access your Gotify server |
| `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`.
### Host firewall
If you have a host firewall in place, this container needs the following ports:
- TCP 443 outbound for outbound HTTPS
- TCP 80 outbound if you use a local unsecured HTTP connection to your Gotify server
- UDP 53 outbound for outbound DNS resolution
- TCP 8000 inbound (or other) for the WebUI
## Domain set up
### Namecheap
[![Namecheap Website](https://github.com/qdm12/ddns-updater/raw/master/readme/namecheap.png)](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)
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*
![https://ap.www.namecheap.com/Domains/DomainControlPanel/mealracle.com/advancedns](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/namecheap1.png)
1. Select the following settings and create the *A + Dynamic DNS Record*:
![https://ap.www.namecheap.com/Domains/DomainControlPanel/mealracle.com/advancedns](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/namecheap2.png)
1. Scroll down and turn on the switch for *DYNAMIC DNS*
![https://ap.www.namecheap.com/Domains/DomainControlPanel/mealracle.com/advancedns](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/namecheap3.png)
1. The Dynamic DNS Password will appear, which is `0e4512a9c45a4fe88313bcc2234bf547` in this example.
![https://ap.www.namecheap.com/Domains/DomainControlPanel/mealracle.com/advancedns](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/namecheap4.png)
***
### GoDaddy
[![GoDaddy Website](https://github.com/qdm12/ddns-updater/raw/master/readme/godaddy.png)](https://godaddy.com)
1. Login to [https://developer.godaddy.com/keys](https://developer.godaddy.com/keys/) with your account credentials.
[![GoDaddy Developer Login](https://github.com/qdm12/ddns-updater/raw/master/readme/godaddy1.gif)](https://developer.godaddy.com/keys)
1. Generate a Test key and secret.
[![GoDaddy Developer Test Key](https://github.com/qdm12/ddns-updater/raw/master/readme/godaddy2.gif)](https://developer.godaddy.com/keys)
1. Generate a **Production** key and secret.
[![GoDaddy Developer Production Key](https://github.com/qdm12/ddns-updater/raw/master/readme/godaddy3.gif)](https://developer.godaddy.com/keys)
Obtain the **key** and **secret** of that production key.
In this example, the key is `dLP4WKz5PdkS_GuUDNigHcLQFpw4CWNwAQ5` and the secret is `GuUFdVFj8nJ1M79RtdwmkZ`.
***
### DuckDNS
[![DuckDNS Website](https://github.com/qdm12/ddns-updater/raw/master/readme/duckdns.png)](https://duckdns.org)
*See [duckdns website](https://duckdns.org)*
### Cloudflare
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*
Special thanks to @Starttoaster for helping out with the [documentation](https://gist.github.com/Starttoaster/07d568c2a99ad7631dd776688c988326) and testing.
## Gotify
[![Gotify](https://github.com/qdm12/ddns-updater/blob/master/readme/gotify.png?raw=true)](https://gotify.net)
[**Gotify**](https://gotify.net) is a simple server for sending and receiving messages, and it is **free**, **private** and **open source**
- It has an [Android app](https://play.google.com/store/apps/details?id=com.github.gotify) to receive notifications
- The app does not drain your battery 👍
- The notification server is self hosted, see [how to set it up with Docker](https://gotify.net/docs/install)
- The notifications only go through your own server (ideally through HTTPS though)
To set it up with DDNS updater:
1. Go to the Web GUI of Gotify
1. Login with the admin credentials
1. Create an app and copy the generated token to the environment variable `GOTIFYTOKEN` (for this container)
1. Set the `GOTIFYURL` variable to the URL of your Gotify server address (i.e. `http://127.0.0.1:8080` or `https://bla.com/gotify`)
## 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 check manually at:
- GoDaddy: [https://dcc.godaddy.com/manage/yourdomain.com/dns](https://dcc.godaddy.com/manage/yourdomain.com/dns) (replace yourdomain.com)
[![GoDaddy DNS management](https://github.com/qdm12/ddns-updater/raw/master/readme/godaddydnsmanagement.png)](https://dcc.godaddy.com/manage/)
You might want to try to change the IP address to `127.0.0.1` to see if the update actually occurs.
## Development
1. Setup your environment
<details><summary>Using VSCode and Docker (easier)</summary><p>
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: So you can discard it and update it easily!
</p></details>
<details><summary>Locally</summary><p>
1. Install [Go](https://golang.org/dl/), [Docker](https://www.docker.com/products/docker-desktop) and [Git](https://git-scm.com/downloads)
1. Install Go dependencies with
```sh
go mod download
```
1. Install [golangci-lint](https://github.com/golangci/golangci-lint#install)
1. 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](https://github.com/qdm12/ddns-updater/master/.vscode/settings.json).
</p></details>
1. Commands available:
```sh
# Build the binary
go build cmd/app/main.go
# Test the code
go test ./...
# Lint the code
golangci-lint run
# Build the Docker image
docker build -t qmcgaw/ddns-updater .
```
1. See [Contributing](https://github.com/qdm12/ddns-updater/master/.github/CONTRIBUTING.md) for more information on how to contribute to this repository.
## Used in external projects
- [Starttoaster/docker-traefik](https://github.com/Starttoaster/docker-traefik#home-networks-extra-credit-dynamic-dns)
## TODOs
- [ ] Update dependencies
- [ ] Mockgen instead of mockery
- [ ] Other types or records
- [ ] icon.ico for webpage
- [ ] Record events log
- [ ] Hot reload of config.json
- [ ] Unit tests
- [ ] ReactJS frontend
- [ ] Live update of website
- [ ] Change settings
# 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">
[![Build status](https://github.com/qdm12/ddns-updater/actions/workflows/build.yml/badge.svg)](https://github.com/qdm12/ddns-updater/actions/workflows/build.yml)
[![dockeri.co](https://dockeri.co/image/qmcgaw/ddns-updater)](https://hub.docker.com/r/qmcgaw/ddns-updater)
![Last release](https://img.shields.io/github/release/qdm12/ddns-updater?label=Last%20release)
![Last Docker tag](https://img.shields.io/docker/v/qmcgaw/ddns-updater?sort=semver&label=Last%20Docker%20tag)
[![Last release size](https://img.shields.io/docker/image-size/qmcgaw/ddns-updater?sort=semver&label=Last%20released%20image)](https://hub.docker.com/r/qmcgaw/ddns-updater/tags?page=1&ordering=last_updated)
![GitHub last release date](https://img.shields.io/github/release-date/qdm12/ddns-updater?label=Last%20release%20date)
![Commits since release](https://img.shields.io/github/commits-since/qdm12/ddns-updater/latest?sort=semver)
[![Latest size](https://img.shields.io/docker/image-size/qmcgaw/ddns-updater/latest?label=Latest%20image)](https://hub.docker.com/r/qmcgaw/ddns-updater/tags)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/commits/main)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/graphs/contributors)
[![GitHub closed PRs](https://img.shields.io/github/issues-pr-closed/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/pulls?q=is%3Apr+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues)
[![GitHub closed issues](https://img.shields.io/github/issues-closed/qdm12/ddns-updater.svg)](https://github.com/qdm12/ddns-updater/issues?q=is%3Aissue+is%3Aclosed)
[![Lines of code](https://img.shields.io/tokei/lines/github/qdm12/ddns-updater)](https://github.com/qdm12/ddns-updater)
![Code size](https://img.shields.io/github/languages/code-size/qdm12/ddns-updater)
![GitHub repo size](https://img.shields.io/github/repo-size/qdm12/ddns-updater)
![Go version](https://img.shields.io/github/go-mod/go-version/qdm12/ddns-updater)
[![MIT](https://img.shields.io/github/license/qdm12/ddns-updater)](LICENSE)
![Visitors count](https://visitor-badge.laobi.icu/badge?page_id=ddns-updater.readme)
## Features
- Updates periodically A records for different DNS providers:
- Aliyun
- AllInkl
- Cloudflare
- DD24
- DDNSS.de
- deSEC
- DigitalOcean
- DonDominio
- DNSOMatic
- DNSPod
- Dreamhost
- DuckDNS
- DynDNS
- Dynu
- EasyDNS
- FreeDNS
- Gandi
- GCP
- GoDaddy
- GoIP.de
- He.net
- Hetzner
- Infomaniak
- INWX
- Ionos
- Linode
- LuaDNS
- Name.com
- Namecheap
- Netcup
- NoIP
- Now-DNS
- Njalla
- OpenDNS
- OVH
- Porkbun
- Selfhost.de
- Servercow.de
- Spdyn
- Strato.de
- Variomedia.de
- Zoneedit
- **Want more?** [Create an issue for it](https://github.com/qdm12/ddns-updater/issues/new/choose)!
- Web User interface
![Web UI](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/webui.png)
- 11MB Docker image based on a Go static binary in a Scratch Docker image
- 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/v0.8/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
```
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. 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. (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
#### Docker-Compose
You can also use [docker-compose.yml](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).
#### Kubernetes
Check out the [k8s directory](k8s) for an installation guide and examples.
### GHCR
Images are also added to the Github Container Registry. To use the GHCR container replace `qmcgaw/ddns-updater` to `ghcr.io/qdm12/ddns-updater`, further details are available [here](https://github.com/qdm12/ddns-updater/pkgs/container/ddns-updater)
## 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)
- [Cloudflare](docs/cloudflare.md)
- [Custom](docs/custom.md)
- [DDNSS.de](docs/ddnss.de.md)
- [deSEC](docs/desec.md)
- [DigitalOcean](docs/digitalocean.md)
- [DD24](docs/dd24.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)
- [Infomaniak](docs/infomaniak.md)
- [INWX](docs/inwx.md)
- [Ionos](docs/ionos.md)
- [Linode](docs/linode.md)
- [LuaDNS](docs/luadns.md)
- [Name.com](docs/name.com.md)
- [Namecheap](docs/namecheap.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)
- [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)
- [Zoneedit](docs/zoneedit.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
🆕 There are now flags equivalent for each variable below, for example `--ipv6-prefix`.
| 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 |
| `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_UUID` | | UUID for [healthchecks.io](https://healthchecks.io) to send a heartbeat on every update check |
| `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`. |
| `RESOLVER_ADDRESS` | Your network DNS | A plaintext DNS address to use, such as `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` |
#### 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)
- `google` using [https://domains.google.com/checkip](https://domains.google.com/checkip)
- `spdyn` using [https://checkip.spdyn.de](https://checkip.spdyn.de/)
- `ipleak` using [https://ipleak.net/json](https://ipleak.net/json)
- 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:
- `ipleak` using [https://ipv4.ipleak.net/json](https://ipv4.ipleak.net/json)
- `ipify` using [https://api.ipify.org](https://api.ipify.org)
- 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:
- `ipleak` using [https://ipv6.ipleak.net/json](https://ipv6.ipleak.net/json)
- `ipify` using [https://api6.ipify.org](https://api6.ipify.org)
- 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:
- `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 qmcgaw/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](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](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 🥇👍

View File

@@ -2,266 +2,318 @@ package main
import (
"context"
"net"
"os/signal"
"syscall"
"fmt"
"net/http"
"os"
"os/signal"
"path/filepath"
"strconv"
"syscall"
"time"
_ "time/tzdata"
"github.com/qdm12/golibs/admin"
libhealthcheck "github.com/qdm12/golibs/healthcheck"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/network"
"github.com/qdm12/golibs/network/connectivity"
libparams "github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/server"
_ "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/handlers"
"github.com/qdm12/ddns-updater/internal/healthcheck"
"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/params"
"github.com/qdm12/ddns-updater/internal/persistence"
"github.com/qdm12/ddns-updater/internal/splash"
"github.com/qdm12/ddns-updater/internal/trigger"
jsonparams "github.com/qdm12/ddns-updater/internal/params"
persistence "github.com/qdm12/ddns-updater/internal/persistence/json"
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/update"
"github.com/qdm12/ddns-updater/pkg/publicip"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/goshutdown"
"github.com/qdm12/gosplash"
"github.com/qdm12/log"
)
//nolint:gochecknoglobals
var (
version = "unknown"
commit = "unknown"
date = "an unknown date"
)
func main() {
os.Exit(_main(context.Background(), time.Now))
// returns 1 on error
// returns 2 on os signal
buildInfo := models.BuildInformation{
Version: version,
Commit: commit,
Date: 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, timeNow func() time.Time) int {
if libhealthcheck.Mode(os.Args) {
func _main(ctx context.Context, reader *reader.Reader, args []string, logger log.LoggerInterface,
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
if err := libhealthcheck.Query(); err != nil {
fmt.Println(err)
return 1
var healthSettings config.Health
healthSettings.Read(reader)
healthSettings.SetDefaults()
err = healthSettings.Validate()
if err != nil {
return fmt.Errorf("health settings: %w", err)
}
return 0
}
logger, err := setupLogger()
if err != nil {
fmt.Println(err)
return 1
}
paramsReader := params.NewReader(logger)
fmt.Println(splash.Splash(
paramsReader.GetVersion(),
paramsReader.GetVcsRef(),
paramsReader.GetBuildDate()))
notify, err := setupGotify(paramsReader, logger)
if err != nil {
logger.Error(err)
return 1
client := health.NewClient()
return client.Query(ctx, *healthSettings.ServerAddress)
}
dir, dataDir, listeningPort, rootURL, defaultPeriod, backupPeriod, backupDirectory, err := getParams(paramsReader)
announcementExp, err := time.Parse(time.RFC3339, "2023-07-15T00:00:00Z")
if err != nil {
logger.Error(err)
notify(4, err)
return 1
return err
}
splashSettings := gosplash.Settings{
User: "qdm12",
Repository: "ddns-updater",
Emails: []string{"quentin.mcgaw@gmail.com"},
Version: buildInfo.Version,
Commit: buildInfo.Commit,
BuildDate: buildInfo.Date,
Announcement: "Public IP dns provider GOOGLE, see https://github.com/qdm12/ddns-updater/issues/492",
AnnounceExp: announcementExp,
// Sponsor information
PaypalUser: "qmcgaw",
GithubSponsor: "qdm12",
}
for _, line := range gosplash.MakeLines(splashSettings) {
fmt.Println(line)
}
persistentDB, err := persistence.NewJSON(dataDir)
var config config.Config
err = config.Read(reader, logger)
if err != nil {
logger.Error(err)
notify(4, err)
return 1
return fmt.Errorf("reading settings: %w", err)
}
settings, warnings, err := paramsReader.GetSettings(dataDir + "/config.json")
config.SetDefaults()
err = config.Validate()
if err != nil {
return fmt.Errorf("settings validation: %w", err)
}
logger.Patch(config.Logger.ToOptions()...)
logger.Info(config.String())
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)
jsonFilepath := filepath.Join(*config.Paths.DataDir, "config.json")
providers, warnings, err := jsonReader.JSONProviders(jsonFilepath)
for _, w := range warnings {
logger.Warn(w)
notify(2, w)
shoutrrrClient.Notify(w)
}
if err != nil {
logger.Error(err)
notify(4, err)
return 1
shoutrrrClient.Notify(err.Error())
return err
}
if len(settings) > 1 {
logger.Info("Found %d settings to update records", len(settings))
} else if len(settings) == 1 {
logger.Info("Found single setting to update records")
L := len(providers)
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 " + fmt.Sprint(len(providers)) + " settings to update records")
}
for _, err := range connectivity.NewConnectivity(5 * time.Second).Checks("google.com") {
logger.Warn(err)
client := &http.Client{Timeout: config.Client.Timeout}
err = health.CheckHTTP(ctx, client)
if err != nil {
logger.Warn(err.Error())
}
records := make([]models.Record, len(settings))
idToPeriod := make(map[int]time.Duration)
i := 0
for id, setting := range settings {
logger.Info("Reading history from database: domain %s host %s", setting.Domain, setting.Host)
events, err := persistentDB.GetEvents(setting.Domain, setting.Host)
records := make([]recordslib.Record, len(providers))
for i, provider := range providers {
logger.Info("Reading history from database: domain " +
provider.Domain() + " host " + provider.Host() +
" " + provider.IPVersion().String())
events, err := persistentDB.GetEvents(provider.Domain(),
provider.Host(), provider.IPVersion())
if err != nil {
logger.Error(err)
notify(4, err)
return 1
shoutrrrClient.Notify(err.Error())
return err
}
records[i] = models.NewRecord(setting, events)
idToPeriod[id] = defaultPeriod
if setting.Delay > 0 {
idToPeriod[id] = setting.Delay
}
i++
records[i] = recordslib.New(provider, events)
}
HTTPTimeout, err := paramsReader.GetHTTPTimeout()
if err != nil {
logger.Error(err)
notify(4, err)
return 1
}
client := network.NewClient(HTTPTimeout)
defer client.Close()
defer client.CloseIdleConnections()
db := data.NewDatabase(records, persistentDB)
defer func() {
if err := db.Close(); err != nil {
logger.Error(err)
}
}()
updater := update.NewUpdater(db, logger, client, notify)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
checkError := func(err error) {
err := db.Close()
if err != nil {
logger.Error(err)
logger.Error(err.Error())
}
}
forceUpdate := trigger.StartUpdates(ctx, updater, idToPeriod, checkError)
forceUpdate()
productionHandlerFunc := handlers.NewHandler(rootURL, dir, db, logger, forceUpdate, checkError).GetHandlerFunc()
healthcheckHandlerFunc := libhealthcheck.GetHandler(func() error {
return healthcheck.IsHealthy(db, net.LookupIP, logger)
})
logger.Info("Web UI listening at address 0.0.0.0:%s with root URL %s", listeningPort, rootURL)
notify(1, fmt.Sprintf("Launched with %d records to watch", len(records)))
serverErrors := make(chan []error)
go func() {
serverErrors <- server.RunServers(ctx,
server.Settings{Name: "production", Addr: "0.0.0.0:" + listeningPort, Handler: productionHandlerFunc},
server.Settings{Name: "healthcheck", Addr: "127.0.0.1:9999", Handler: healthcheckHandlerFunc},
)
}()
go backupRunLoop(ctx, backupPeriod, dir, backupDirectory, logger, timeNow)
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals,
syscall.SIGINT,
syscall.SIGTERM,
os.Interrupt,
)
select {
case errors := <-serverErrors:
for _, err := range errors {
logger.Error(err)
}
return 1
case signal := <-osSignals:
message := fmt.Sprintf("Stopping program: caught OS signal %q", signal)
logger.Warn(message)
notify(2, message)
return 2
case <-ctx.Done():
message := fmt.Sprintf("Stopping program: %s", ctx.Err())
logger.Warn(message)
return 1
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.HealthchecksioUUID)
updater := update.NewUpdater(db, client, shoutrrrClient, logger, timeNow)
runner := update.NewRunner(db, updater, ipGetter, config.Update.Period,
config.Update.Cooldown, logger, resolver, timeNow, hioClient)
runnerHandler, runnerCtx, runnerDone := goshutdown.NewGoRoutineHandler("runner")
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, resolver)
healthLogger := logger.New(log.SetComponent("healthcheck server"))
healthServer := health.NewServer(*config.Health.ServerAddress,
healthLogger, isHealthy)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler("health server")
go healthServer.Run(healthServerCtx, healthServerDone)
serverLogger := logger.New(log.SetComponent("http server"))
server := server.New(ctx, config.Server.ListeningAddress, config.Server.RootURL,
db, serverLogger, runner)
serverHandler, serverCtx, serverDone := goshutdown.NewGoRoutineHandler("server")
go server.Run(serverCtx, serverDone)
shoutrrrClient.Notify("Launched with " + strconv.Itoa(len(records)) + " records to watch")
backupHandler, backupCtx, backupDone := goshutdown.NewGoRoutineHandler("backup")
backupLogger := logger.New(log.SetComponent("backup"))
go backupRunLoop(backupCtx, backupDone, *config.Backup.Period, *config.Paths.DataDir,
*config.Backup.Directory, backupLogger, timeNow)
shutdownGroup := goshutdown.NewGroupHandler("")
shutdownGroup.Add(runnerHandler, healthServerHandler, serverHandler, backupHandler)
<-ctx.Done()
err = shutdownGroup.Shutdown(context.Background())
if err != nil {
shoutrrrClient.Notify(err.Error())
return err
}
return nil
}
func setupLogger() (logging.Logger, error) {
paramsReader := params.NewReader(nil)
encoding, level, nodeID, err := paramsReader.GetLoggerConfig()
if err != nil {
return nil, err
}
return logging.NewLogger(encoding, level, nodeID)
type InfoErroer interface {
Info(s string)
Error(s string)
}
func setupGotify(paramsReader params.Reader, logger logging.Logger) (notify func(priority int, messageArgs ...interface{}), err error) {
gotifyURL, err := paramsReader.GetGotifyURL()
if err != nil {
return nil, err
} else if gotifyURL == nil {
return func(priority int, messageArgs ...interface{}) {}, nil
}
gotifyToken, err := paramsReader.GetGotifyToken()
if err != nil {
return nil, err
}
gotify := admin.NewGotify(*gotifyURL, gotifyToken, &http.Client{Timeout: time.Second})
return func(priority int, messageArgs ...interface{}) {
if err := gotify.Notify("DDNS Updater", priority, messageArgs...); err != nil {
logger.Error(err)
}
}, nil
}
func getParams(paramsReader params.Reader) (
dir, dataDir,
listeningPort, rootURL string,
defaultPeriod time.Duration,
backupPeriod time.Duration, backupDirectory string,
err error) {
dir, err = paramsReader.GetExeDir()
if err != nil {
return "", "", "", "", 0, 0, "", err
}
dataDir, err = paramsReader.GetDataDir(dir)
if err != nil {
return "", "", "", "", 0, 0, "", err
}
listeningPort, _, err = paramsReader.GetListeningPort()
if err != nil {
return "", "", "", "", 0, 0, "", err
}
rootURL, err = paramsReader.GetRootURL()
if err != nil {
return "", "", "", "", 0, 0, "", err
}
defaultPeriod, err = paramsReader.GetDelay(libparams.Default("10m"))
if err != nil {
return "", "", "", "", 0, 0, "", err
}
backupPeriod, err = paramsReader.GetBackupPeriod()
if err != nil {
return "", "", "", "", 0, 0, "", err
}
backupDirectory, err = paramsReader.GetBackupDirectory()
if err != nil {
return "", "", "", "", 0, 0, "", err
}
return dir, dataDir, listeningPort, rootURL, defaultPeriod, backupPeriod, backupDirectory, nil
}
func backupRunLoop(ctx context.Context, backupPeriod time.Duration, exeDir, outputDir string,
logger logging.Logger, timeNow func() time.Time) {
logger = logger.WithPrefix("backup: ")
func backupRunLoop(ctx context.Context, done chan<- struct{}, backupPeriod time.Duration,
dataDir, outputDir string, logger InfoErroer, 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)
logger.Info("each " + backupPeriod.String() +
"; writing zip files to directory " + outputDir)
ziper := backup.NewZiper()
timer := time.NewTimer(backupPeriod)
for {
filepath := fmt.Sprintf("%s/ddns-updater-backup-%d.zip", outputDir, timeNow().UnixNano())
if err := ziper.ZipFiles(
filepath,
fmt.Sprintf("%s/data/updates.json", exeDir),
fmt.Sprintf("%s/data/config.json", exeDir)); err != nil {
logger.Error(err)
fileName := "ddns-updater-backup-" + strconv.Itoa(int(timeNow().UnixNano())) + ".zip"
zipFilepath := filepath.Join(outputDir, fileName)
err := ziper.ZipFiles(
zipFilepath,
filepath.Join(dataDir, "updates.json"),
filepath.Join(dataDir, "config.json"),
)
if err != nil {
logger.Error(err.Error())
}
select {
case <-timer.C:

View File

@@ -4,28 +4,23 @@
"provider": "namecheap",
"domain": "example.com",
"host": "@",
"ip_method": "provider",
"delay": 86400,
"password": "e5322165c1d74692bfa6d807100c0310"
},
{
"provider": "duckdns",
"domain": "example.duckdns.org",
"ip_method": "provider",
"token": "00000000-0000-0000-0000-000000000000"
},
{
"provider": "godaddy",
"domain": "example.org",
"host": "subdomain",
"ip_method": "google",
"key": "aaaaaaaaaaaaaaaa",
"secret": "aaaaaaaaaaaaaaaa"
},
{
"provider": "dreamhost",
"domain": "example.info",
"ip_method": "opendns",
"key": "aaaaaaaaaaaaaaaa"
}
]

View File

@@ -9,15 +9,27 @@ services:
volumes:
- ./data:/updater/data
environment:
- DELAY=300s
- ROOT_URL=/
- LISTENING_PORT=8000
- LOG_ENCODING=console
- LOG_LEVEL=info
- NODE_ID=0
- CONFIG=
- PERIOD=5m
- UPDATE_COOLDOWN_PERIOD=5m
- PUBLICIP_FETCHERS=all
- PUBLICIP_HTTP_PROVIDERS=all
- PUBLICIPV4_HTTP_PROVIDERS=all
- PUBLICIPV6_HTTP_PROVIDERS=all
- PUBLICIP_DNS_PROVIDERS=all
- PUBLICIP_DNS_TIMEOUT=3s
- HTTP_TIMEOUT=10s
- GOTIFY_URL=
- GOTIFY_TOKEN=
- BACKUP_PERIOD=0
# Web UI
- LISTENING_ADDRESS=:8000
- ROOT_URL=/
# Backup
- BACKUP_PERIOD=0 # 0 to disable
- BACKUP_DIRECTORY=/updater/data
# Other
- LOG_LEVEL=info
- LOG_CALLER=hidden
- SHOUTRRR_ADDRESSES=
restart: always

35
docs/aliyun.md Normal file
View File

@@ -0,0 +1,35 @@
# Aliyun
## Configuration
### Example
```json
{
"settings": [
{
"provider": "aliyun",
"domain": "domain.com",
"host": "@",
"access_key_id": "your access_key_id",
"access_secret": "your access_secret",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
## Domain setup

35
docs/allinkl.md Normal file
View File

@@ -0,0 +1,35 @@
# All-Inkl
## Configuration
### Example
```json
{
"settings": [
{
"provider": "allinkl",
"domain": "domain.com",
"host": "host",
"username": "dynXXXXXXX",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host (subdomain)
- `"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 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.
## Domain setup

42
docs/cloudflare.md Normal file
View File

@@ -0,0 +1,42 @@
# Cloudflare
## Configuration
### Example
```json
{
"settings": [
{
"provider": "cloudflare",
"zone_identifier": "some id",
"domain": "domain.com",
"host": "@",
"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"`
- `"host"` is your host and can be `"@"`, a subdomain or 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 ([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) 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.
Special thanks to @Starttoaster for helping out with the [documentation](https://gist.github.com/Starttoaster/07d568c2a99ad7631dd776688c988326) and testing.

52
docs/contributing.md Normal file
View File

@@ -0,0 +1,52 @@
# 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).
## 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.

42
docs/custom.md Normal file
View File

@@ -0,0 +1,42 @@
# 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",
"host": "@",
"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 name to update
- `"host"` is the host to update, which can be `"@"` (root), `"*"` or a subdomain
- `"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`.
- `"ipv6key"` is the URL query parameter name for the IPv6 address, for example `ipv6` will be added to the URL with `&ipv6=::aaff`. Even if you don't use IPv6, this must be set to something.
- `"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 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.

31
docs/dd24.md Normal file
View File

@@ -0,0 +1,31 @@
# Domain Discount 24
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dd24",
"domain": "domain.com",
"host": "@",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"password"` is your 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 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.

40
docs/ddnss.de.md Normal file
View File

@@ -0,0 +1,40 @@
# DDNSS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "ddnss",
"provider_ip": true,
"domain": "domain.com",
"host": "@",
"username": "user",
"password": "password",
"dual_stack": false,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"username"`
- `"password"`
### Optional parameters
- `"dual_stack"` can be set to `true` **if you have turn on dual stack for your record** to update both IPv4 and IPv6 addresses. Note it is ignored if `"provider_ip": true`. 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 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.
## Domain setup

37
docs/desec.md Normal file
View File

@@ -0,0 +1,37 @@
# deSEC
## Configuration
### Example
```json
{
"settings": [
{
"provider": "desec",
"domain": "dedyn.io",
"host": "host",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": false
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` can be `@` for the root domain or a subdomain or a wildcard subdomain (`*`), defaults to `@`
- `"token"` is your token that you can create [here](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 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.
- `"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.
## Domain setup
[desec.io/domains](https://desec.io/domains)

33
docs/digitalocean.md Normal file
View File

@@ -0,0 +1,33 @@
# Digital Ocean
## Configuration
### Example
```json
{
"settings": [
{
"provider": "digitalocean",
"domain": "domain.com",
"host": "@",
"token": "yourtoken",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### 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)
### 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.
## Domain setup

37
docs/dnsomatic.md Normal file
View File

@@ -0,0 +1,37 @@
# DNS-O-Matic
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dnsomatic",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"provider_ip": true,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"username"`
- `"password"`
- `"provider_ip"`
### 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.
## Domain setup

33
docs/dnspod.md Normal file
View File

@@ -0,0 +1,33 @@
# DNSPod
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dnspod",
"domain": "domain.com",
"host": "@",
"token": "yourtoken",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
## Domain setup

39
docs/dondominio.md Normal file
View File

@@ -0,0 +1,39 @@
# Don Dominio
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dondominio",
"domain": "domain.com",
"host": "@",
"name": "something",
"username": "username",
"key": "key",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is the subdomain to update which can be `@`, `*` or a subdomain
- `"name"` is the name of the service/hosting
- `"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 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.
## Domain setup
See [dondominio.dev/en/dondns/docs/api/#before-start](https://dondominio.dev/en/dondns/docs/api/#before-start)

33
docs/dreamhost.md Normal file
View File

@@ -0,0 +1,33 @@
# Dreamhost
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dreamhost",
"domain": "domain.com",
"host": "@",
"key": "key",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"key"`
### Optional parameters
- `"host"` is your host and can be a subdomain or `"@"`. It defaults to `"@"`.
- `"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.
## Domain setup

37
docs/duckdns.md Normal file
View File

@@ -0,0 +1,37 @@
# DuckDNS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "duckdns",
"host": "host",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"host"` is your host, for example `subdomain` for `subdomain.duckdns.org`
- `"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 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (**NOT** your IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup
[![DuckDNS Website](../readme/duckdns.png)](https://www.duckdns.org/)
*See the [duckdns website](https://www.duckdns.org/)*

35
docs/dyndns.md Normal file
View File

@@ -0,0 +1,35 @@
# DynDNS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dyn",
"domain": "domain.com",
"host": "@",
"username": "username",
"client_key": "client_key",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"username"`
- `"client_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 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.
## Domain setup

39
docs/dynu.md Normal file
View File

@@ -0,0 +1,39 @@
# Dynu
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dynu",
"domain": "domain.com",
"host": "@",
"group": "group",
"username": "username",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
- `"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.
- `"group"` specify the Group for which you want to set the IP (will update any domains and subdomains in the same group)
## Domain setup

35
docs/dynv6.md Normal file
View File

@@ -0,0 +1,35 @@
# DynV6
## Configuration
### Example
```json
{
"settings": [
{
"provider": "dynv6",
"domain": "domain.com",
"host": "@",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"token"` that you can obtain [here](https://dynv6.com/keys#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 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.
- `"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.
## Domain setup

37
docs/easydns.md Normal file
View File

@@ -0,0 +1,37 @@
# EasyDNS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "easydns",
"domain": "domain.com",
"host": "@",
"username": "username",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"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 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.
- `"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.
## Domain setup

36
docs/freedns.md Normal file
View File

@@ -0,0 +1,36 @@
# FreeDNS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "freedns",
"domain": "domain.com",
"host": "host",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host (subdomain)
- `"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) 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.
## 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.

39
docs/gandi.md Normal file
View File

@@ -0,0 +1,39 @@
# Gandi
This provider uses Gandi v5 API
## Configuration
### Example
```json
{
"settings": [
{
"provider": "gandi",
"domain": "domain.com",
"host": "@",
"personal_access_token": "token",
"ttl": 3600,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` which can be a subdomain, `@` or a wildcard `*`
- `"personal_access_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 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"` default is `3600`
## Domain setup
[Gandi Documentation Website](https://docs.gandi.net/en/rest_api/index.html)

39
docs/gcp.md Normal file
View File

@@ -0,0 +1,39 @@
# 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",
"host": "@",
"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 [here](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 TLD of you DNS record (without a trailing dot)
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
### 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.

63
docs/godaddy.md Normal file
View File

@@ -0,0 +1,63 @@
# GoDaddy
## Configuration
### Example
```json
{
"settings": [
{
"provider": "godaddy",
"domain": "domain.com",
"host": "@",
"key": "key",
"secret": "secret",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
- `"key"`
- `"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 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.
## Domain setup
[![GoDaddy Website](../readme/godaddy.png)](https://www.godaddy.com/en-ie)
1. Login to [https://developer.godaddy.com/keys](https://developer.godaddy.com/keys/) with your account credentials.
[![GoDaddy Developer Login](../readme/godaddy1.gif)](https://developer.godaddy.com/keys)
1. Generate a Test key and secret.
[![GoDaddy Developer Test Key](../readme/godaddy2.gif)](https://developer.godaddy.com/keys)
1. Generate a **Production** key and secret.
[![GoDaddy Developer Production Key](../readme/godaddy3.gif)](https://developer.godaddy.com/keys)
Obtain the **key** and **secret** of that production key.
In this example, the key is `dLP4WKz5PdkS_GuUDNigHcLQFpw4CWNwAQ5` and the secret is `GuUFdVFj8nJ1M79RtdwmkZ`.
## Testing
1. Go to [https://dcc.godaddy.com/manage/yourdomain.com/dns](https://dcc.godaddy.com/manage/yourdomain.com/dns) (replace yourdomain.com)
[![GoDaddy DNS management](../readme/godaddydnsmanagement.png)](https://dcc.godaddy.com/manage/)
1. Change the IP address to `127.0.0.1`
1. Run the ddns-updater
1. Refresh the Godaddy webpage to check the update occurred.

36
docs/goip.md Normal file
View File

@@ -0,0 +1,36 @@
# GoIP.de
## Configuration
### Example
```json
{
"settings": [
{
"provider": "goip",
"domain": "goip.de",
"host": "mysubdomain",
"username": "username",
"password": "password",
"provider_ip": true,
"ip_version": "",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"host"` is the full FQDN of your ddns address. sample.goip.de or something.goip.it
- `"username"` is your goip.de username listed under "Routers"
- `"password"` is your router account password
### Optional parameters
- `"domain"` is the domain name which can be `goip.de` or `goip.it`, and defaults to `goip.de` if left unset.
- `"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. This is automatically disabled for an IPv6 public address since it is not supported.
- `"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 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.

35
docs/he.net.md Normal file
View File

@@ -0,0 +1,35 @@
# He.net
## Configuration
### Example
```json
{
"settings": [
{
"provider": "he",
"domain": "domain.com",
"host": "@",
"password": "password",
"provider_ip": true,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"` (untested)
- `"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 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.
- `"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.
## Domain setup

36
docs/hetzner.md Normal file
View File

@@ -0,0 +1,36 @@
# Hetzner
## Configuration
### Example
```json
{
"settings": [
{
"provider": "hetzner",
"zone_identifier": "some id",
"domain": "domain.com",
"host": "@",
"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"`
- `"host"` is your host and can be `"@"`, a subdomain or 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 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.

37
docs/infomaniak.md Normal file
View File

@@ -0,0 +1,37 @@
# Infomaniak
## Configuration
### Example
```json
{
"settings": [
{
"provider": "infomaniak",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
- `"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.
## Domain setup

35
docs/inwx.md Normal file
View File

@@ -0,0 +1,35 @@
# INWX
## Configuration
### Example
```json
{
"settings": [
{
"provider": "inwx",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
## Domain setup

31
docs/ionos.md Normal file
View File

@@ -0,0 +1,31 @@
# Ionos
## Configuration
### Example
```json
{
"settings": [
{
"provider": "ionos",
"domain": "domain.com",
"host": "@",
"api_key": "api_key",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"api_key"` is your API key, obtained from [creating an API key](https://www.ionos.com/help/domains/configuring-your-ip-address/set-up-dynamic-dns-with-company-name/#c181598)
### 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.

36
docs/linode.md Normal file
View File

@@ -0,0 +1,36 @@
# Linode
## Configuration
### Example
```json
{
"settings": [
{
"provider": "linode",
"domain": "domain.com",
"host": "@",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"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 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.
## 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/api/guides/manage-api-tokens/).
1. The program will create the A or AAAA record for you if it doesn't exist already.

39
docs/luadns.md Normal file
View File

@@ -0,0 +1,39 @@
# LuaDNS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "luadns",
"domain": "domain.com",
"host": "@",
"email": "email",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"email"`
- `"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 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.
## Domain setup
1. Go to [api.luadns.com/settings](https://api.luadns.com/settings)
1. Enable API access
1. Obtain your API token and replace it in the parameters as the value for `token`

37
docs/name.com.md Normal file
View File

@@ -0,0 +1,37 @@
# Name.com
<img src="../readme/name.svg" alt="drawing" width="25%"/>
## Configuration
### Example
```json
{
"settings": [
{
"provider": "name.com",
"domain": "domain.com",
"host": "@",
"username": "username",
"token": "token",
"ttl": 300,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
- `"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 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.

57
docs/namecheap.md Normal file
View File

@@ -0,0 +1,57 @@
# Namecheap
## Configuration
### Example
```json
{
"settings": [
{
"provider": "namecheap",
"domain": "domain.com",
"host": "@",
"password": "password",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain, `"@"` or `"*"` generally
- `"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
[![Namecheap Website](../readme/namecheap.png)](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/](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*
![https://ap.www.namecheap.com/Domains/DomainControlPanel/example.com/advancedns](../readme/namecheap1.png)
1. Select the following settings and create the *A + Dynamic DNS Record*:
![https://ap.www.namecheap.com/Domains/DomainControlPanel/example.com/advancedns](../readme/namecheap2.png)
1. Scroll down and turn on the switch for *DYNAMIC DNS*
![https://ap.www.namecheap.com/Domains/DomainControlPanel/example.com/advancedns](../readme/namecheap3.png)
1. The Dynamic DNS Password will appear, which is `0e4512a9c45a4fe88313bcc2234bf547` in this example.
![https://ap.www.namecheap.com/Domains/DomainControlPanel/example.com/advancedns](../readme/namecheap4.png)

39
docs/netcup.md Normal file
View File

@@ -0,0 +1,39 @@
# 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",
"host": "host",
"api_key": "xxxxx",
"password": "yyyyy",
"customer_number": "111111",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"` is your domain
- `"host"` is your host (subdomain) or `"@"` for the root of the domain. It cannot be the wildcard.
- `"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 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.

37
docs/njalla.md Normal file
View File

@@ -0,0 +1,37 @@
# Njalla
## Configuration
### Example
```json
{
"settings": [
{
"provider": "njalla",
"domain": "domain.com",
"host": "@",
"key": "key",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"key"` is the key for your record
### 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.
- `"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.
## Domain setup
See [https://njal.la/docs/ddns](https://njal.la/docs/ddns/)

37
docs/noip.md Normal file
View File

@@ -0,0 +1,37 @@
# NoIP
## Configuration
### Example
```json
{
"settings": [
{
"provider": "noip",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
- `"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.
## Domain setup

31
docs/nowdns.md Normal file
View 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 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.

37
docs/opendns.md Normal file
View File

@@ -0,0 +1,37 @@
# OpenDNS
## Configuration
### Example
```json
{
"settings": [
{
"provider": "opendns",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"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 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.
- `"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.
## Domain setup

53
docs/ovh.md Normal file
View File

@@ -0,0 +1,53 @@
# OVH
## Configuration
### Example
```json
{
"settings": [
{
"provider": "ovh",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
#### Using DynHost
- `"username"`
- `"password"`
#### OR Using ZoneDNS
- `"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"`
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) 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.
- `"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.
- `"mode"` select between two modes, OVH's dynamic hosting service (`"dynamic"`) or OVH's API (`"api"`). Default is `"dynamic"`
## Domain setup
- If you use DynHost: [docs.ovh.com/ie/en/domains/hosting_dynhost](https://docs.ovh.com/ie/en/domains/hosting_dynhost/)
- If you use the ZoneDNS API: [docs.ovh.com/gb/en/customer/first-steps-with-ovh-api](https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/)

47
docs/porkbun.md Normal file
View File

@@ -0,0 +1,47 @@
# Porkbun
## Configuration
### Example
```json
{
"settings": [
{
"provider": "porkbun",
"domain": "domain.com",
"host": "@",
"api_key": "sk1_7d119e3f656b00ae042980302e1425a04163c476efec1833q3cb0w54fc6f5022",
"secret_api_key": "pk1_5299b57125c8f3cdf347d2fe0e713311ee3a1e11f11a14942b26472593e35368",
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory Parameters
- `"domain"`
- `"host"` is your host and can be a subdomain, `"*"` or `"@"`
- `"apikey"`
- `"secretapikey"`
- `"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 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.
## 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.
However, to do so, the corresponding ALIAS record, that is automatically created by Porkbun, is automatically deleted to allow this.
More details is in [this comment by @everydaycombat](https://github.com/qdm12/ddns-updater/issues/546#issuecomment-1773960193).

37
docs/selfhost.de.md Normal file
View File

@@ -0,0 +1,37 @@
# Selfhost.de
## Configuration
### Example
```json
{
"settings": [
{
"provider": "selfhost.de",
"domain": "domain.com",
"host": "@",
"username": "username",
"password": "password",
"provider_ip": true,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"username"` is your DynDNS username
- `"password"` is your DynDNS 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 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.
- `"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.
## Domain setup

41
docs/servercow.md Normal file
View File

@@ -0,0 +1,41 @@
# Servercow
## Configuration
### Example
```json
{
"settings": [
{
"provider": "servercow",
"domain": "domain.com",
"host": "",
"username": "servercow_username",
"password": "servercow_password",
"ttl": 600,
"provider_ip": true,
"ip_version": "ipv4",
"ipv6_suffix": ""
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be `""`, a subdomain or `"*"` generally
- `"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 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.
- `"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.
## Domain setup
See [their article](https://cp.servercow.de/en/plugin/support_manager/knowledgebase/view/34/dns-api-v1/7/)

43
docs/spdyn.md Normal file
View File

@@ -0,0 +1,43 @@
# Spdyn.de
## Configuration
### Example
```json
{
"settings": [
{
"provider": "spdyn",
"domain": "domain.com",
"host": "@",
"user": "user",
"password": "password",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
#### Using user and password
- `"user"` is the name of a user who can update this host
- `"password"` is the password of a user who can update this host
#### Using update tokens
- `"token"` is your update 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 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.
- `"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.

37
docs/strato.md Normal file
View File

@@ -0,0 +1,37 @@
# Strato
## Configuration
### Example
```json
{
"settings": [
{
"provider": "strato",
"domain": "domain.com",
"host": "@",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"password"` is your dyndns 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 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.
- `"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.
## Domain setup
See [their article](https://www.strato.com/faq/en_us/domain/this-is-how-easy-it-is-to-set-up-dyndns-for-your-domains/)

39
docs/variomedia.md Normal file
View File

@@ -0,0 +1,39 @@
# Variomedia
## Configuration
### Example
```json
{
"settings": [
{
"provider": "variomedia",
"domain": "domain.com",
"host": "@",
"email": "email@domain.com",
"password": "password",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"`
- `"email"`
- `"password"` is your DNS settings password, not your 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 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.
- `"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.
## Domain setup
See [dyndns.variomedia.de](https://dyndns.variomedia.de/)

45
docs/zoneedit.md Normal file
View File

@@ -0,0 +1,45 @@
# 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",
"host": "@",
"username": "username",
"token": "token",
"ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true
}
]
}
```
### Compulsory parameters
- `"domain"`
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
- `"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 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.
- `"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.
## Domain setup
[support.zoneedit.com/en/knowledgebase/article/dynamic-dns](https://support.zoneedit.com/en/knowledgebase/article/dynamic-dns)

49
go.mod
View File

@@ -1,11 +1,48 @@
module github.com/qdm12/ddns-updater
go 1.13
go 1.21
require (
github.com/golang/mock v1.4.3
github.com/google/uuid v1.1.1
github.com/kyokomi/emoji v2.2.2+incompatible
github.com/qdm12/golibs v0.0.0-20200430173218-57de728e2151
github.com/stretchr/testify v1.5.1
github.com/breml/rootcerts v0.2.14
github.com/containrrr/shoutrrr v0.8.0
github.com/go-chi/chi/v5 v5.0.11
github.com/golang/mock v1.6.0
github.com/miekg/dns v1.1.57
github.com/qdm12/gosettings v0.4.0-rc9
github.com/qdm12/goshutdown v0.3.0
github.com/qdm12/gosplash v0.1.0
github.com/qdm12/gotree v0.2.0
github.com/qdm12/log v0.1.0
github.com/stretchr/testify v1.8.4
golang.org/x/mod v0.14.0
google.golang.org/api v0.114.0
)
require (
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.15.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69 // indirect
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 // indirect
)

311
go.sum
View File

@@ -1,139 +1,210 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/breml/rootcerts v0.2.14 h1:Bu0Ullru+/GTr/S582LCzP1P57WgncIEFylXkBBXgEI=
github.com/breml/rootcerts v0.2.14/go.mod h1:S/PKh+4d1HUn4HQovEB8hPJZO6pUZYrIhmXBhsegfXw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0 h1:8JV+dzJJiK46XqGLqqLav8ZfEiJECp8jlOFhpiCdZ+0=
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 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk=
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/loads v0.17.0 h1:H22nMs3GDQk4SwAaFQ+jLNw+0xoFeCueawhZlv8MBYs=
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 h1:/ZK67ikFhQAMFFH/aPu2MaGH7QjP4wHBvHYOVIzDAw0=
github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q=
github.com/go-openapi/spec v0.17.0 h1:XNvrt8FlSVP8T1WuhbAFF6QDhJc0zsoWzX4wXARhhpE=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/strfmt v0.17.0 h1:1isAxYf//QDTnVzbLAMrUK++0k1EjeLJU/gTOR0o3Mc=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/validate v0.17.0 h1:pqoViQz3YLOGIhAmD0N4Lt6pa/3Gnj3ymKqQwq8iS6U=
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gotify/go-api-client/v2 v2.0.4 h1:0w8skCr8aLBDKaQDg31LKKHUGF7rt7zdRpR+6cqIAlE=
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o=
github.com/kyokomi/emoji v2.1.0+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/kyokomi/emoji v2.2.2+incompatible h1:gaQFbK2+uSxOR4iGZprJAbpmtqTrHhSdgOyIMD6Oidc=
github.com/kyokomi/emoji v2.2.2+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
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.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
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.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.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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
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/qdm12/golibs v0.0.0-20200430173218-57de728e2151 h1:5q8oyhJqgQyW5v427CDC34SobllqiJCLLfS3Z4EeLCI=
github.com/qdm12/golibs v0.0.0-20200430173218-57de728e2151/go.mod h1:pikkTN7g7zRuuAnERwqW1yAFq6pYmxrxpjiwGvb0Ysc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/qdm12/gosettings v0.4.0-rc9 h1:MEVPYQLZfzg3BJgp+DDuY6/9LPAWIlGvPtQ0BeCq9+4=
github.com/qdm12/gosettings v0.4.0-rc9/go.mod h1:uItKwGXibJp2pQ0am6MBKilpjfvYTGiH+zXHd10jFj8=
github.com/qdm12/goshutdown v0.3.0 h1:pqBpJkdwlZlfTEx4QHtS8u8CXx6pG0fVo6S1N0MpSEM=
github.com/qdm12/goshutdown v0.3.0/go.mod h1:EqZ46No00kCTZ5qzdd3qIzY6ayhMt24QI8Mh8LVQYmM=
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
github.com/qdm12/gotree v0.2.0 h1:+58ltxkNLUyHtATFereAcOjBVfY6ETqRex8XK90Fb/c=
github.com/qdm12/gotree v0.2.0/go.mod h1:1SdFaqKZuI46U1apbXIf25pDMNnrPuYLEqMF/qL4lY4=
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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-20190213061140-3a22650c66bd/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-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69 h1:N0m3tKYbkRMmDobh/47ngz+AWeV7PcfXMDi8xu3Vrag=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.69/go.mod h1:Tk5Ip2TuxaWGpccL7//rAsLRH6RQ/jfqTGxuN/+i/FQ=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 h1:IdrOs1ZgwGw5CI+BH6GgVVlOt+LAXoPyh7enr8lfaXs=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=

View File

@@ -6,25 +6,27 @@ import (
"os"
)
type Ziper interface {
var _ FileZiper = (*Ziper)(nil)
type FileZiper interface {
ZipFiles(outputFilepath string, inputFilepaths ...string) error
}
type ziper struct {
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{
func NewZiper() *Ziper {
return &Ziper{
createFile: os.Create,
openFile: os.Open,
ioCopy: io.Copy,
}
}
func (z *ziper) ZipFiles(outputFilepath string, inputFilepaths ...string) error {
func (z *Ziper) ZipFiles(outputFilepath string, inputFilepaths ...string) error {
f, err := z.createFile(outputFilepath)
if err != nil {
return err
@@ -33,14 +35,15 @@ 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 = z.addFile(w, filepath)
if err != nil {
return err
}
}
return nil
}
func (z *ziper) addFile(w *zip.Writer, filepath string) error {
func (z *Ziper) addFile(w *zip.Writer, filepath string) error {
f, err := z.openFile(filepath)
if err != nil {
return err

47
internal/config/backup.go Normal file
View File

@@ -0,0 +1,47 @@
package config
import (
"time"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Backup struct {
Period *time.Duration
Directory *string
}
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 = reader.Get("BACKUP_DIRECTORY")
return nil
}

41
internal/config/client.go Normal file
View File

@@ -0,0 +1,41 @@
package config
import (
"time"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Client struct {
Timeout time.Duration
}
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
}

48
internal/config/health.go Normal file
View File

@@ -0,0 +1,48 @@
package config
import (
"fmt"
"os"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gosettings/validate"
"github.com/qdm12/gotree"
)
type Health struct {
ServerAddress *string
HealthchecksioUUID *string
}
func (h *Health) SetDefaults() {
h.ServerAddress = gosettings.DefaultPointer(h.ServerAddress, "127.0.0.1:9999")
h.HealthchecksioUUID = gosettings.DefaultPointer(h.HealthchecksioUUID, "")
}
func (h Health) Validate() (err error) {
err = validate.ListeningAddress(*h.ServerAddress, os.Getuid())
if err != nil {
return fmt.Errorf("server listening address: %w", err)
}
return nil
}
func (h Health) String() string {
return h.toLinesNode().String()
}
func (h Health) toLinesNode() *gotree.Node {
node := gotree.New("Health")
node.Appendf("Server listening address: %s", *h.ServerAddress)
if *h.HealthchecksioUUID != "" {
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.HealthchecksioUUID = reader.Get("HEALTH_HEALTHCHECKSIO_UUID")
}

View File

@@ -0,0 +1,3 @@
package config
const all = "all"

65
internal/config/logger.go Normal file
View File

@@ -0,0 +1,65 @@
package config
import (
"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 {
Level string
Caller string
}
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 fmt.Errorf("log level: %w", 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")
}

33
internal/config/paths.go Normal file
View File

@@ -0,0 +1,33 @@
package config
import (
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Paths struct {
DataDir *string
}
func (p *Paths) setDefaults() {
p.DataDir = gosettings.DefaultPointer(p.DataDir, "./data")
}
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)
return node
}
func (p *Paths) read(reader *reader.Reader) {
p.DataDir = reader.Get("DATADIR")
}

330
internal/config/pubip.go Normal file
View File

@@ -0,0 +1,330 @@
package config
import (
"errors"
"fmt"
"net/url"
"strings"
"time"
"github.com/qdm12/ddns-updater/pkg/publicip/dns"
"github.com/qdm12/ddns-updater/pkg/publicip/http"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gosettings/validate"
"github.com/qdm12/gotree"
)
type PubIP struct {
HTTPEnabled *bool
HTTPIPProviders []string
HTTPIPv4Providers []string
HTTPIPv6Providers []string
DNSEnabled *bool
DNSProviders []string
DNSTimeout time.Duration
}
func (p *PubIP) setDefaults() {
p.HTTPEnabled = gosettings.DefaultPointer(p.HTTPEnabled, true)
p.HTTPIPProviders = gosettings.DefaultSlice(p.HTTPIPProviders, []string{all})
p.HTTPIPv4Providers = gosettings.DefaultSlice(p.HTTPIPv4Providers, []string{all})
p.HTTPIPv6Providers = gosettings.DefaultSlice(p.HTTPIPv6Providers, []string{all})
p.DNSEnabled = gosettings.DefaultPointer(p.DNSEnabled, true)
p.DNSProviders = gosettings.DefaultSlice(p.DNSProviders, []string{all})
const defaultDNSTimeout = 3 * time.Second
p.DNSTimeout = gosettings.DefaultComparable(p.DNSTimeout, defaultDNSTimeout)
}
func (p PubIP) Validate() (err error) {
err = p.validateHTTPIPProviders()
if err != nil {
return fmt.Errorf("HTTP IP providers: %w", err)
}
err = p.validateHTTPIPv4Providers()
if err != nil {
return fmt.Errorf("HTTP IPv4 providers: %w", err)
}
err = p.validateHTTPIPv6Providers()
if err != nil {
return fmt.Errorf("HTTP IPv6 providers: %w", err)
}
err = p.validateDNSProviders()
if err != nil {
return fmt.Errorf("DNS providers: %w", err)
}
return nil
}
func (p *PubIP) String() string {
return p.toLinesNode().String()
}
func (p *PubIP) toLinesNode() (node *gotree.Node) {
node = gotree.New("Public IP fetching")
node.Appendf("HTTP enabled: %s", gosettings.BoolToYesNo(p.HTTPEnabled))
if *p.HTTPEnabled {
childNode := node.Appendf("HTTP IP providers")
for _, provider := range p.HTTPIPProviders {
childNode.Appendf(provider)
}
childNode = node.Appendf("HTTP IPv4 providers")
for _, provider := range p.HTTPIPv4Providers {
childNode.Appendf(provider)
}
childNode = node.Appendf("HTTP IPv6 providers")
for _, provider := range p.HTTPIPv6Providers {
childNode.Appendf(provider)
}
}
node.Appendf("DNS enabled: %s", gosettings.BoolToYesNo(p.DNSEnabled))
if *p.DNSEnabled {
node.Appendf("DNS timeout: %s", p.DNSTimeout)
childNode := node.Appendf("DNS over TLS providers")
for _, provider := range p.DNSProviders {
childNode.Appendf(provider)
}
}
return node
}
// ToHTTPOptions assumes the settings have been validated.
func (p *PubIP) ToHTTPOptions() (options []http.Option) {
httpIPProviders := stringsToHTTPProviders(p.HTTPIPProviders, ipversion.IP4or6)
httpIPv4Providers := stringsToHTTPProviders(p.HTTPIPv4Providers, ipversion.IP4)
httpIPv6Providers := stringsToHTTPProviders(p.HTTPIPv6Providers, ipversion.IP6)
return []http.Option{
http.SetProvidersIP(httpIPProviders[0], httpIPProviders[1:]...),
http.SetProvidersIP4(httpIPv4Providers[0], httpIPv4Providers[1:]...),
http.SetProvidersIP6(httpIPv6Providers[0], httpIPv6Providers[1:]...),
}
}
func stringsToHTTPProviders(providers []string, ipVersion ipversion.IPVersion) (
updatedProviders []http.Provider) {
updatedProvidersSet := make(map[string]struct{}, len(providers))
for _, provider := range providers {
if provider != all {
updatedProvidersSet[provider] = struct{}{}
continue
}
allProviders := http.ListProvidersForVersion(ipVersion)
for _, provider := range allProviders {
updatedProvidersSet[string(provider)] = struct{}{}
}
}
updatedProviders = make([]http.Provider, 0, len(updatedProvidersSet))
for provider := range updatedProvidersSet {
updatedProviders = append(updatedProviders, http.Provider(provider))
}
return updatedProviders
}
// ToDNSPOptions assumes the settings have been validated.
func (p *PubIP) ToDNSPOptions() (options []dns.Option) {
uniqueProviders := make(map[string]struct{}, len(p.DNSProviders))
for _, provider := range p.DNSProviders {
if provider != all {
uniqueProviders[provider] = struct{}{}
}
allProviders := dns.ListProviders()
for _, provider := range allProviders {
uniqueProviders[string(provider)] = struct{}{}
}
}
providers := make([]dns.Provider, 0, len(uniqueProviders))
for providerString := range uniqueProviders {
providers = append(providers, dns.Provider(providerString))
}
return []dns.Option{
dns.SetTimeout(p.DNSTimeout),
dns.SetProviders(providers[0], providers[1:]...),
}
}
var (
ErrNoPublicIPDNSProvider = errors.New("no public IP DNS provider specified")
)
func (p PubIP) validateDNSProviders() (err error) {
if len(p.DNSProviders) == 0 {
return fmt.Errorf("%w", ErrNoPublicIPDNSProvider)
}
availableProviders := dns.ListProviders()
validChoices := make([]string, len(availableProviders)+1)
for i, provider := range availableProviders {
validChoices[i] = string(provider)
}
validChoices[len(validChoices)-1] = all
return validate.AreAllOneOf(p.DNSProviders, validChoices)
}
func (p PubIP) validateHTTPIPProviders() (err error) {
return validateHTTPIPProviders(p.HTTPIPProviders, ipversion.IP4or6)
}
func (p PubIP) validateHTTPIPv4Providers() (err error) {
return validateHTTPIPProviders(p.HTTPIPv4Providers, ipversion.IP4)
}
func (p PubIP) validateHTTPIPv6Providers() (err error) {
return validateHTTPIPProviders(p.HTTPIPv6Providers, ipversion.IP6)
}
var (
ErrNoPublicIPHTTPProvider = errors.New("no public IP HTTP provider specified")
)
func validateHTTPIPProviders(providerStrings []string,
version ipversion.IPVersion) (err error) {
if len(providerStrings) == 0 {
return fmt.Errorf("%w", ErrNoPublicIPHTTPProvider)
}
availableProviders := http.ListProvidersForVersion(version)
choices := make(map[string]struct{}, len(availableProviders)+1)
choices[all] = struct{}{}
for i := range availableProviders {
choices[string(availableProviders[i])] = struct{}{}
}
for _, providerString := range providerStrings {
if providerString == "noip" {
// NoIP is no longer supported because the echo service
// only works over plaintext HTTP and could be tempered with.
// Silently discard it and it will default to another HTTP IP
// echo service.
continue
}
// Custom URL check
url, err := url.Parse(providerString)
if err == nil && url != nil && url.Scheme == "https" {
continue
}
_, ok := choices[providerString]
if !ok {
return fmt.Errorf("%w: %s", validate.ErrValueNotOneOf, providerString)
}
}
return nil
}
func (p *PubIP) read(r *reader.Reader, warner Warner) (err error) {
p.HTTPEnabled, p.DNSEnabled, err = getFetchers(r)
if err != nil {
return err
}
p.HTTPIPProviders = r.CSV("PUBLICIP_HTTP_PROVIDERS",
reader.RetroKeys("IP_METHOD"))
p.HTTPIPv4Providers = r.CSV("PUBLICIPV4_HTTP_PROVIDERS",
reader.RetroKeys("IPV4_METHOD"))
p.HTTPIPv6Providers = r.CSV("PUBLICIPV6_HTTP_PROVIDERS",
reader.RetroKeys("IPV6_METHOD"))
// Retro-compatibility
for i := range p.HTTPIPProviders {
p.HTTPIPProviders[i] = handleRetroProvider(p.HTTPIPProviders[i])
}
for i := range p.HTTPIPv4Providers {
p.HTTPIPv4Providers[i] = handleRetroProvider(p.HTTPIPv4Providers[i])
}
for i := range p.HTTPIPv6Providers {
p.HTTPIPv6Providers[i] = handleRetroProvider(p.HTTPIPv6Providers[i])
}
// Retro-compatibility for now defunct opendns http provider for ipv4 or ipv6
if len(p.HTTPIPProviders) > 0 { // check to avoid transforming `nil` to `[]`
httpIPProvidersTemp := make([]string, len(p.HTTPIPProviders))
copy(httpIPProvidersTemp, p.HTTPIPProviders)
p.HTTPIPProviders = make([]string, 0, len(p.HTTPIPProviders))
for _, provider := range httpIPProvidersTemp {
if provider != "opendns" {
p.HTTPIPProviders = append(p.HTTPIPProviders, provider)
}
}
}
p.DNSProviders = r.CSV("PUBLICIP_DNS_PROVIDERS")
// Retro-compatibility
for i, provider := range p.DNSProviders {
if provider == "google" {
warner.Warnf("dns provider google will be ignored " +
"since it is no longer supported, " +
"see https://github.com/qdm12/ddns-updater/issues/492")
p.DNSProviders[i] = p.DNSProviders[len(p.DNSProviders)-1]
p.DNSProviders = p.DNSProviders[:len(p.DNSProviders)-1]
}
}
p.DNSTimeout, err = r.Duration("PUBLICIP_DNS_TIMEOUT")
if err != nil {
return err
}
return nil
}
var ErrFetcherNotValid = errors.New("fetcher is not valid")
func getFetchers(reader *reader.Reader) (http, dns *bool, err error) {
// TODO change to use reader.BoolPtr with retro-compatibility
s := reader.String("PUBLICIP_FETCHERS")
if s == "" {
return nil, nil, nil
}
http, dns = new(bool), new(bool)
fields := strings.Split(s, ",")
for i, field := range fields {
switch strings.ToLower(field) {
case "all":
*http = true
*dns = true
case "http":
*http = true
case "dns":
*dns = true
default:
return nil, nil, fmt.Errorf(
"%w: %q at position %d of %d",
ErrFetcherNotValid, field, i+1, len(fields))
}
}
return http, dns, nil
}
func handleRetroProvider(provider string) (updatedProvider string) {
switch provider {
case "ipify6":
return "ipify"
case "noip4", "noip6", "noip8245_4", "noip8245_6":
return "noip"
case "cycle":
return "all"
default:
return provider
}
}

View File

@@ -0,0 +1,80 @@
package config
import (
"errors"
"fmt"
"net"
"time"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Resolver struct {
Address *string
Timeout time.Duration
}
func (r *Resolver) setDefaults() {
r.Address = gosettings.DefaultPointer(r.Address, "")
const defaultTimeout = 5 * time.Second
r.Timeout = gosettings.DefaultComparable(r.Timeout, defaultTimeout)
}
var (
ErrAddressHostEmpty = errors.New("address host is empty")
ErrAddressPortEmpty = errors.New("address port is empty")
ErrTimeoutTooLow = errors.New("timeout is too low")
)
func (r Resolver) Validate() (err error) {
if *r.Address != "" {
host, port, err := net.SplitHostPort(*r.Address)
if err != nil {
return fmt.Errorf("splitting host and port from address: %w", err)
}
switch {
case host == "":
return fmt.Errorf("%w: in %s", ErrAddressHostEmpty, *r.Address)
case port == "":
return fmt.Errorf("%w: in %s", ErrAddressPortEmpty, *r.Address)
}
}
const minTimeout = 10 * time.Millisecond
if r.Timeout < minTimeout {
return fmt.Errorf("%w: %s is below the minimum %s",
ErrTimeoutTooLow, r.Timeout, minTimeout)
}
return nil
}
func (r Resolver) String() string {
return r.ToLinesNode().String()
}
func (r Resolver) ToLinesNode() *gotree.Node {
if *r.Address == "" {
return gotree.New("Resolver: use Go default resolver")
}
node := gotree.New("Resolver")
node.Appendf("Address: %s", *r.Address)
node.Appendf("Timeout: %s", r.Timeout)
return node
}
func (r *Resolver) read(reader *reader.Reader) (err error) {
r.Address = reader.Get("RESOLVER_ADDRESS")
if r.Address != nil { // conveniently add port 53 if not specified
_, port, err := net.SplitHostPort(*r.Address)
if err == nil && port == "" {
*r.Address += ":53"
}
}
r.Timeout, err = reader.Duration("RESOLVER_TIMEOUT")
return err
}

View File

@@ -0,0 +1,10 @@
package config
type Warner interface {
Warnf(format string, a ...interface{})
}
func handleDeprecated(warner Warner, oldKey, newKey string) {
warner.Warnf("You are using an old environment variable %s, please change it to %s",
oldKey, newKey)
}

60
internal/config/server.go Normal file
View File

@@ -0,0 +1,60 @@
package config
import (
"fmt"
"os"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gosettings/validate"
"github.com/qdm12/gotree"
)
type Server struct {
ListeningAddress string
RootURL string
}
func (s *Server) setDefaults() {
s.ListeningAddress = gosettings.DefaultComparable(s.ListeningAddress, ":8000")
s.RootURL = gosettings.DefaultComparable(s.RootURL, "/")
}
func (s Server) Validate() (err error) {
err = validate.ListeningAddress(s.ListeningAddress, os.Getuid())
if err != nil {
return fmt.Errorf("listening address: %w", err)
}
// TODO validate RootURL
return nil
}
func (s Server) String() string {
return s.toLinesNode().String()
}
func (s Server) toLinesNode() *gotree.Node {
node := gotree.New("Server")
node.Appendf("Listening address: %s", s.ListeningAddress)
node.Appendf("Root URL: %s", s.RootURL)
return node
}
func (s *Server) read(reader *reader.Reader, warner Warner) (err error) {
s.RootURL = reader.String("ROOT_URL")
// Retro-compatibility
port, err := reader.Uint16Ptr("LISTENING_PORT") // TODO change to address
if err != nil {
handleDeprecated(warner, "LISTENING_PORT", "LISTENING_ADDRESS")
return err
} else if port != nil {
s.ListeningAddress = fmt.Sprintf(":%d", *port)
}
s.ListeningAddress = reader.String("LISTENING_ADDRESS")
return err
}

125
internal/config/settings.go Normal file
View File

@@ -0,0 +1,125 @@
package config
import (
"fmt"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Config struct {
Client Client
Update Update
PubIP PubIP
Resolver Resolver
Server Server
Health Health
Paths Paths
Backup Backup
Logger Logger
Shoutrrr Shoutrrr
}
func (c *Config) SetDefaults() {
c.Client.setDefaults()
c.Update.setDefaults()
c.PubIP.setDefaults()
c.Resolver.setDefaults()
c.Server.setDefaults()
c.Health.SetDefaults()
c.Paths.setDefaults()
c.Backup.setDefaults()
c.Logger.setDefaults()
c.Shoutrrr.setDefaults()
}
func (c Config) Validate() (err error) {
type validator interface {
Validate() (err error)
}
toValidate := map[string]validator{
"client": &c.Client,
"update": &c.Update,
"public ip": &c.PubIP,
"resolver": &c.Resolver,
"server": &c.Server,
"health": &c.Health,
"paths": &c.Paths,
"backup": &c.Backup,
"logger": &c.Logger,
"shoutrrr": &c.Shoutrrr,
}
for name, v := range toValidate {
err = v.Validate()
if err != nil {
return fmt.Errorf("%s settings: %w", name, err)
}
}
return nil
}
func (c Config) String() string {
return c.toLinesNode().String()
}
func (c Config) toLinesNode() *gotree.Node {
node := gotree.New("Settings summary:")
node.AppendNode(c.Client.toLinesNode())
node.AppendNode(c.Update.toLinesNode())
node.AppendNode(c.PubIP.toLinesNode())
node.AppendNode(c.Resolver.ToLinesNode())
node.AppendNode(c.Server.toLinesNode())
node.AppendNode(c.Health.toLinesNode())
node.AppendNode(c.Paths.toLinesNode())
node.AppendNode(c.Backup.toLinesNode())
node.AppendNode(c.Logger.toLinesNode())
node.AppendNode(c.Shoutrrr.ToLinesNode())
return node
}
func (c *Config) Read(reader *reader.Reader,
warner Warner) (err error) {
err = c.Client.read(reader)
if err != nil {
return fmt.Errorf("reading client settings: %w", err)
}
err = c.Update.read(reader, warner)
if err != nil {
return fmt.Errorf("reading update settings: %w", err)
}
err = c.PubIP.read(reader, warner)
if err != nil {
return fmt.Errorf("reading public IP settings: %w", err)
}
err = c.Resolver.read(reader)
if err != nil {
return fmt.Errorf("reading resolver settings: %w", err)
}
err = c.Server.read(reader, warner)
if err != nil {
return fmt.Errorf("reading server settings: %w", err)
}
c.Health.Read(reader)
c.Paths.read(reader)
err = c.Backup.read(reader)
if err != nil {
return fmt.Errorf("reading backup settings: %w", err)
}
c.Logger.read(reader)
err = c.Shoutrrr.read(reader, warner)
if err != nil {
return fmt.Errorf("reading shoutrrr settings: %w", err)
}
return nil
}

View File

@@ -0,0 +1,48 @@
package config
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_Settings_String(t *testing.T) {
t.Parallel()
var defaultSettings Config
defaultSettings.SetDefaults()
s := defaultSettings.String()
const expected = `Settings summary:
├── HTTP client
| └── Timeout: 20s
├── Update
| ├── Period: 10m0s
| └── Cooldown: 5m0s
├── Public IP fetching
| ├── HTTP enabled: yes
| ├── HTTP IP providers
| | └── all
| ├── HTTP IPv4 providers
| | └── all
| ├── HTTP IPv6 providers
| | └── all
| ├── DNS enabled: yes
| ├── DNS timeout: 3s
| └── DNS over TLS providers
| └── all
├── Resolver: use Go default resolver
├── Server
| ├── Listening address: :8000
| └── Root URL: /
├── Health
| └── Server listening address: 127.0.0.1:9999
├── Paths
| └── Data directory: ./data
├── Backup: disabled
└── Logger
├── Level: INFO
└── Caller: hidden`
assert.Equal(t, expected, s)
}

View File

@@ -0,0 +1,87 @@
package config
import (
"fmt"
"net/url"
"path"
"github.com/containrrr/shoutrrr"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Shoutrrr struct {
Addresses []string
DefaultTitle string
}
func (s *Shoutrrr) setDefaults() {
s.Addresses = gosettings.DefaultSlice(s.Addresses, []string{})
s.DefaultTitle = gosettings.DefaultComparable(s.DefaultTitle, "DDNS Updater")
}
func (s Shoutrrr) Validate() (err error) {
_, err = shoutrrr.CreateSender(s.Addresses...)
if err != nil {
return fmt.Errorf("shoutrrr addresses: %w", err)
}
return nil
}
func (s Shoutrrr) String() string {
return s.ToLinesNode().String()
}
func (s Shoutrrr) ToLinesNode() *gotree.Node {
if len(s.Addresses) == 0 {
return nil // no address means shoutrrr is disabled
}
node := gotree.New("Shoutrrr")
node.Appendf("Default title: %s", s.DefaultTitle)
childNode := node.Appendf("Addresses")
for _, address := range s.Addresses {
childNode.Appendf(address)
}
return node
}
func (s *Shoutrrr) read(r *reader.Reader, warner Warner) (err error) {
s.Addresses = r.CSV("SHOUTRRR_ADDRESSES", reader.ForceLowercase(false))
// Retro-compatibility: GOTIFY_URL and GOTIFY_TOKEN
gotifyURLString := r.Get("GOTIFY_URL", reader.ForceLowercase(false))
if gotifyURLString != nil {
handleDeprecated(warner, "GOTIFY_URL", "SHOUTRRR_ADDRESSES")
gotifyURL, err := url.Parse(*gotifyURLString)
if err != nil {
return fmt.Errorf("gotify URL: %w", err)
}
gotifyToken := r.String("GOTIFY_TOKEN", reader.ForceLowercase(false))
handleDeprecated(warner, "GOTIFY_TOKEN", "SHOUTRRR_ADDRESSES")
gotifyShoutrrrAddress := gotifyURLTokenToShoutrrr(gotifyURL, gotifyToken)
s.Addresses = append(s.Addresses, gotifyShoutrrrAddress)
}
// Retro-compatibility
shoutrrrParamsCSV := r.Get("SHOUTRRR_PARAMS")
if shoutrrrParamsCSV != nil {
warner.Warnf("SHOUTRRR_PARAMS is disabled, you can use SHOUTRRR_DEFAULT_TITLE and SHOUTRRR_ADDRESSES")
}
s.DefaultTitle = r.String("SHOUTRRR_DEFAULT_TITLE", reader.ForceLowercase(false))
return nil
}
func gotifyURLTokenToShoutrrr(url *url.URL, token string) (address string) {
hostAndPath := path.Join(url.Host, url.Path)
address = "gotify://" + hostAndPath + "/" + token
if url.Scheme == "http" {
address += "?DisableTLS=Yes"
}
return address
}

64
internal/config/update.go Normal file
View File

@@ -0,0 +1,64 @@
package config
import (
"strconv"
"time"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type Update struct {
Period time.Duration
Cooldown time.Duration
}
func (u *Update) setDefaults() {
const defaultPeriod = 10 * time.Minute
u.Period = gosettings.DefaultComparable(u.Period, defaultPeriod)
const defaultCooldown = 5 * time.Minute
u.Cooldown = gosettings.DefaultComparable(u.Cooldown, defaultCooldown)
}
func (u Update) Validate() (err error) {
return nil
}
func (u Update) String() string {
return u.toLinesNode().String()
}
func (u Update) toLinesNode() *gotree.Node {
node := gotree.New("Update")
node.Appendf("Period: %s", u.Period)
node.Appendf("Cooldown: %s", u.Cooldown)
return node
}
func (u *Update) read(reader *reader.Reader, warner Warner) (err error) {
u.Period, err = readUpdatePeriod(reader, warner)
if err != nil {
return err
}
u.Cooldown, err = reader.Duration("UPDATE_COOLDOWN_PERIOD")
return err
}
func readUpdatePeriod(r *reader.Reader, warner Warner) (period time.Duration, err error) {
// Retro-compatibility: DELAY variable name
delayStringPtr := r.Get("DELAY")
if delayStringPtr != nil {
handleDeprecated(warner, "DELAY", "PERIOD")
// Retro-compatibility: integer only, treated as seconds
delayInt, err := strconv.Atoi(*delayStringPtr)
if err == nil {
return time.Duration(delayInt) * time.Second, nil
}
return time.ParseDuration(*delayStringPtr)
}
return r.Duration("PERIOD")
}

View File

@@ -1,36 +0,0 @@
package constants
import "github.com/qdm12/ddns-updater/internal/models"
const (
HTMLFail models.HTML = `<font color="red"><b>Failure</b></font>`
HTMLSuccess models.HTML = `<font color="green"><b>Success</b></font>`
HTMLUpdate models.HTML = `<font color="#00CC66"><b>Up to date</b></font>`
HTMLUpdating models.HTML = `<font color="orange"><b>Updating</b></font>`
)
const (
// TODO have a struct model containing URL, name for each provider
HTMLNamecheap models.HTML = "<a href=\"https://namecheap.com\">Namecheap</a>"
HTMLGodaddy models.HTML = "<a href=\"https://godaddy.com\">GoDaddy</a>"
HTMLDuckDNS models.HTML = "<a href=\"https://duckdns.org\">DuckDNS</a>"
HTMLDreamhost models.HTML = "<a href=\"https://www.dreamhost.com/\">Dreamhost</a>"
HTMLCloudflare models.HTML = "<a href=\"https://www.cloudflare.com\">Cloudflare</a>"
HTMLNoIP models.HTML = "<a href=\"https://www.noip.com/\">NoIP</a>"
HTMLDNSPod models.HTML = "<a href=\"https://www.dnspod.cn/\">DNSPod</a>"
HTMLInfomaniak models.HTML = "<a href=\"https://www.infomaniak.com/\">Infomaniak</a>"
HTMLDdnssde models.HTML = "<a href=\"https://ddnss.de/\">DDNSS.de</a>"
)
const (
HTMLGoogle models.HTML = "<a href=\"https://google.com/search?q=ip\">Google</a>"
HTMLOpenDNS models.HTML = "<a href=\"https://diagnostic.opendns.com/myip\">OpenDNS</a>"
HTMLIfconfig models.HTML = "<a href=\"https://ifconfig.io\">ifconfig.io</a>"
HTMLIpinfo models.HTML = "<a href=\"https://ipinfo.io\">ipinfo.io</a>"
HTMLIpify models.HTML = "<a href=\"https://api.ipify.org\">api.ipify.org</a>"
HTMLIpify6 models.HTML = "<a href=\"https://api6.ipify.org\">api6.ipify.org</a>"
HTMLDdnss models.HTML = "<a href=\"https://ddnss.de/meineip.php\">ddnss.de</a>"
HTMLDdnss4 models.HTML = "<a href=\"https://ip4.ddnss.de/meineip.php\">ip4.ddnss.de</a>"
HTMLDdnss6 models.HTML = "<a href=\"https://ip6.ddnss.de/meineip.php\">ip6.ddns.de</a>"
HTMLCycle models.HTML = "Cycling"
)

View File

@@ -1,8 +0,0 @@
package constants
import "github.com/qdm12/ddns-updater/internal/models"
const (
IPv4 models.IPVersion = "ipv4"
IPv6 models.IPVersion = "ipv6"
)

View File

@@ -1,53 +0,0 @@
package constants
import (
"github.com/qdm12/ddns-updater/internal/models"
)
const (
PROVIDER models.IPMethod = "provider"
OPENDNS models.IPMethod = "opendns"
IFCONFIG models.IPMethod = "ifconfig"
IPINFO models.IPMethod = "ipinfo"
IPIFY models.IPMethod = "ipify"
IPIFY6 models.IPMethod = "ipify6"
CYCLE models.IPMethod = "cycle"
DDNSS models.IPMethod = "ddnss"
DDNSS4 models.IPMethod = "ddnss4"
DDNSS6 models.IPMethod = "ddnss6"
// Retro compatibility only
GOOGLE models.IPMethod = "google"
)
func IPMethodMapping() map[models.IPMethod]string {
return map[models.IPMethod]string{
PROVIDER: string(PROVIDER),
CYCLE: string(CYCLE),
OPENDNS: "https://diagnostic.opendns.com/myip",
IFCONFIG: "https://ifconfig.io/ip",
IPINFO: "https://ipinfo.io/ip",
IPIFY: "https://api.ipify.org",
IPIFY6: "https://api6.ipify.org",
DDNSS: "https://ip4.ddnss.de/meineip.php",
DDNSS4: "https://ip4.ddnss.de/meineip.php",
DDNSS6: "https://ip6.ddnss.de/meineip.php",
}
}
func IPMethodChoices() (choices []models.IPMethod) {
for choice := range IPMethodMapping() {
choices = append(choices, choice)
}
return choices
}
func IPMethodExternalChoices() (choices []models.IPMethod) {
for _, choice := range IPMethodChoices() {
switch choice {
case PROVIDER, CYCLE:
default:
choices = append(choices, choice)
}
}
return choices
}

View File

@@ -1,20 +0,0 @@
package constants
import (
"testing"
"github.com/qdm12/ddns-updater/internal/models"
"github.com/stretchr/testify/assert"
)
func Test_IPMethodChoices(t *testing.T) {
t.Parallel()
choices := IPMethodChoices()
assert.ElementsMatch(t, []models.IPMethod{"ipinfo", "ipify", "ipify6", "provider", "cycle", "opendns", "ifconfig", "ddnss", "ddnss4", "ddnss6"}, choices)
}
func Test_IPMethodExternalChoices(t *testing.T) {
t.Parallel()
choices := IPMethodExternalChoices()
assert.ElementsMatch(t, []models.IPMethod{"ipinfo", "ipify", "ipify6", "ifconfig", "opendns", "ddnss", "ddnss4", "ddnss6"}, choices)
}

View File

@@ -1,30 +0,0 @@
package constants
import "github.com/qdm12/ddns-updater/internal/models"
// All possible provider values
const (
GODADDY models.Provider = "godaddy"
NAMECHEAP models.Provider = "namecheap"
DUCKDNS models.Provider = "duckdns"
DREAMHOST models.Provider = "dreamhost"
CLOUDFLARE models.Provider = "cloudflare"
NOIP models.Provider = "noip"
DNSPOD models.Provider = "dnspod"
INFOMANIAK models.Provider = "infomaniak"
DDNSSDE models.Provider = "ddnss"
)
func ProviderChoices() []models.Provider {
return []models.Provider{
GODADDY,
NAMECHEAP,
DUCKDNS,
DREAMHOST,
CLOUDFLARE,
NOIP,
DNSPOD,
INFOMANIAK,
DDNSSDE,
}
}

Some files were not shown because too many files have changed in this diff Show More