Compare commits
No commits in common. "main" and "1.1.1-rc.1" have entirely different histories.
main
...
1.1.1-rc.1
12 changed files with 254 additions and 348 deletions
|
@ -1,4 +1,4 @@
|
||||||
name: 'Build and release binary file and packages'
|
name: 'Build and Release Binary File'
|
||||||
author: 'Neshura'
|
author: 'Neshura'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
@ -51,6 +51,13 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Checking Out Repository Code
|
name: Checking Out Repository Code
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
-
|
||||||
|
name: Installing cargo-deb dependencies
|
||||||
|
run: apt install -y liblzma-dev
|
||||||
|
-
|
||||||
|
name: Installing cargo-deb
|
||||||
|
run: |
|
||||||
|
cargo install cargo-deb
|
||||||
-
|
-
|
||||||
name: Prepare build environment
|
name: Prepare build environment
|
||||||
run: mkdir dist
|
run: mkdir dist
|
||||||
|
@ -63,11 +70,8 @@ jobs:
|
||||||
name: Bundle .deb package
|
name: Bundle .deb package
|
||||||
run: |
|
run: |
|
||||||
cargo deb
|
cargo deb
|
||||||
DEBIAN_REF=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " " | tr - \~)
|
DEBIAN_REF=$(echo ${{ github.ref_name }} | tr - \~)
|
||||||
echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env
|
mv target/debian/${{ github.event.repository.name }}_$DEBIAN_REF-1_amd64.deb dist/${{ github.event.repository.name }}_$DEBIAN_REF-1_amd64.deb
|
||||||
DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ")
|
|
||||||
echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env
|
|
||||||
mv target/debian/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb dist/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb
|
|
||||||
-
|
-
|
||||||
name: Uploading Build Artifact
|
name: Uploading Build Artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
@ -76,7 +80,7 @@ jobs:
|
||||||
path: dist
|
path: dist
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
upload-generic-package:
|
upload-release:
|
||||||
needs: build
|
needs: build
|
||||||
if: success()
|
if: success()
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
|
@ -89,55 +93,23 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
||||||
--upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
|
--upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
|
||||||
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ github.event.repository.name }}-linux-amd64'
|
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/chellaris-rust-api-linux-amd64'
|
||||||
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
||||||
--upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
|
--upload-file release_blobs/${{ github.event.repository.name }}-linux-amd64 \
|
||||||
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/${{ github.event.repository.name }}-linux-amd64
|
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/generic/${{ github.event.repository.name }}/${{ github.ref_name }}/chellaris-rust-api-linux-amd64
|
||||||
|
|
||||||
upload-debian-package:
|
|
||||||
needs: build
|
|
||||||
if: success()
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
-
|
-
|
||||||
name: Downloading All Build Artifacts
|
name: Upload Debian Package
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
-
|
|
||||||
name: Upload Debian Package to staging
|
|
||||||
run: |
|
run: |
|
||||||
source release_blobs/build.env
|
DEBIAN_REF=$(echo ${{ github.ref_name }} | tr - \~)
|
||||||
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
||||||
--upload-file release_blobs/${{ github.event.repository.name }}_'"$DEBIAN_REF""$DEBIAN_REV"'_amd64.deb \
|
--upload-file release_blobs/${{ github.event.repository.name }}_$DEBIAN_REF-1_amd64.deb \
|
||||||
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/staging/upload'
|
|
||||||
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
|
||||||
--upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \
|
|
||||||
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/staging/upload
|
|
||||||
-
|
|
||||||
name: Upload Debian Package to main
|
|
||||||
if: (! contains(github.ref_name, '-rc'))
|
|
||||||
run: |
|
|
||||||
source release_blobs/build.env
|
|
||||||
echo 'curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
|
||||||
--upload-file release_blobs/${{ github.event.repository.name }}_'"$DEBIAN_REF""$DEBIAN_REV"'_amd64.deb \
|
|
||||||
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload'
|
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload'
|
||||||
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
curl -v --user ${{ secrets.FORGEJO_USERNAME }}:${{ secrets.FORGEJO_TOKEN }} \
|
||||||
--upload-file release_blobs/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb \
|
--upload-file release_blobs/${{ github.event.repository.name }}_$DEBIAN_REF-1_amd64.deb \
|
||||||
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload
|
https://forgejo.neshweb.net/api/packages/${{ secrets.FORGEJO_USERNAME }}/debian/pool/bookworm/main/upload
|
||||||
|
|
||||||
create-release:
|
|
||||||
needs: build
|
|
||||||
if: success()
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Downloading All Build Artifacts
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
-
|
|
||||||
name: Filter out env files
|
|
||||||
run: rm release_blobs/build.env
|
|
||||||
-
|
-
|
||||||
name: Release New Version
|
name: Release New Version
|
||||||
uses: actions/forgejo-release@v2
|
uses: actions/forgejo-release@v1
|
||||||
with:
|
with:
|
||||||
direction: upload
|
direction: upload
|
||||||
url: https://forgejo.neshweb.net
|
url: https://forgejo.neshweb.net
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
name: 'Build binary file and bundle packages'
|
|
||||||
author: 'Neshura'
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: docker
|
|
||||||
container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Add Clippy
|
|
||||||
run: rustup component add clippy
|
|
||||||
-
|
|
||||||
name: Checking Out Repository Code
|
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
|
||||||
-
|
|
||||||
name: Set Up Cargo Cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/bin/
|
|
||||||
~/.cargo/registry/index/
|
|
||||||
~/.cargo/registry/cache/
|
|
||||||
~/.cargo/git/db/
|
|
||||||
target/
|
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
-
|
|
||||||
name: Run Clippy
|
|
||||||
run: cargo clippy
|
|
||||||
|
|
||||||
build:
|
|
||||||
needs: test
|
|
||||||
if: success()
|
|
||||||
runs-on: docker
|
|
||||||
container: forgejo.neshweb.net/ci-docker-images/rust-node:latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checking Out Repository Code
|
|
||||||
uses: https://code.forgejo.org/actions/checkout@v3
|
|
||||||
-
|
|
||||||
name: Prepare build environment
|
|
||||||
run: mkdir dist
|
|
||||||
-
|
|
||||||
name: Compiling To Linux Target
|
|
||||||
run: |
|
|
||||||
cargo build -r
|
|
||||||
mv target/release/${{ github.event.repository.name }} dist/${{ github.event.repository.name }}-linux-amd64
|
|
||||||
-
|
|
||||||
name: Bundle .deb package
|
|
||||||
run: |
|
|
||||||
cargo deb
|
|
||||||
DEBIAN_REF=$(cat Cargo.toml | grep -E "(^|\|)version =" | cut -f2- -d= | tr -d \" | tr -d " " | tr - \~)
|
|
||||||
echo "DEBIAN_REF=$DEBIAN_REF" >> dist/build.env
|
|
||||||
DEBIAN_REV=-$(cat Cargo.toml | grep -E "(^|\|)revision =" | cut -f2- -d= | tr -d \" | tr -d " ")
|
|
||||||
echo "DEBIAN_REV=$DEBIAN_REV" >> dist/build.env
|
|
||||||
mv target/debian/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb dist/${{ github.event.repository.name }}_"$DEBIAN_REF""$DEBIAN_REV"_amd64.deb
|
|
||||||
-
|
|
||||||
name: Uploading Build Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: release_blobs
|
|
||||||
path: dist
|
|
||||||
if-no-files-found: error
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,5 +4,6 @@ venv/
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
/.env
|
||||||
/interfaces.toml
|
/interfaces.toml
|
||||||
/zones.d
|
/zones.d
|
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -114,10 +114,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cloudflare-dns-updater"
|
name = "cloudflare-dns-updater"
|
||||||
version = "1.1.9"
|
version = "1.1.1-rc.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"confy",
|
"confy",
|
||||||
|
"dotenv",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"log",
|
"log",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -177,6 +178,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenv"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.33"
|
||||||
|
@ -620,7 +627,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -821,7 +828,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -868,15 +875,26 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum_macros"
|
name = "strum_macros"
|
||||||
version = "0.25.3"
|
version = "0.24.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
|
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -951,7 +969,7 @@ checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1129,7 +1147,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1163,7 +1181,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -1,25 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
authors = ["Neshura"]
|
authors = ["Neshura"]
|
||||||
name = "cloudflare-dns-updater"
|
name = "cloudflare-dns-updater"
|
||||||
version = "1.1.9"
|
version = "1.1.1-rc.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Application for automatically updating Cloudflare DNS records"
|
description = "Application for automatically updating Cloudflare DNS records"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
[package.metadata.deb]
|
|
||||||
extended-description = "Application for automatically updating Cloudflare DNS records"
|
|
||||||
maintainer-scripts = "debian/"
|
|
||||||
revision = "1"
|
|
||||||
depends = ["libc6", "libssl3", "systemd"]
|
|
||||||
assets = [
|
|
||||||
[
|
|
||||||
"target/release/cloudflare-dns-updater",
|
|
||||||
"/usr/local/bin/cloudflare-dns-updater",
|
|
||||||
"755",
|
|
||||||
]
|
|
||||||
]
|
|
||||||
systemd-units = { enable = false }
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -28,9 +14,10 @@ reqwest = { version = "^0.11.14", features = ["blocking", "json"] }
|
||||||
serde = "^1.0.152"
|
serde = "^1.0.152"
|
||||||
serde_derive = "^1.0.152"
|
serde_derive = "^1.0.152"
|
||||||
serde_json = "^1.0.93"
|
serde_json = "^1.0.93"
|
||||||
strum_macros = "^0.25.3"
|
strum_macros = "^0.24.3"
|
||||||
log = "^0.4.20"
|
log = "^0.4.20"
|
||||||
systemd-journal-logger = "^2.1.1"
|
systemd-journal-logger = "^2.1.1"
|
||||||
confy = "^0.5.1"
|
confy = "^0.5.1"
|
||||||
|
dotenv = "^0.15.0"
|
||||||
ipnet = "^2.9.0"
|
ipnet = "^2.9.0"
|
||||||
url = "2.5.0"
|
url = "2.5.0"
|
||||||
|
|
31
README.md
31
README.md
|
@ -7,18 +7,23 @@
|
||||||
The application necessarily requires a valid Cloudflare API Token.
|
The application necessarily requires a valid Cloudflare API Token.
|
||||||
Further the application must be located in the same network as the configured zones.
|
Further the application must be located in the same network as the configured zones.
|
||||||
|
|
||||||
The actual configuration happens in three or more files located in `~/.config/cloudflare-dns-updater/`:
|
| Environment Variable | Required | Usage |
|
||||||
`config.toml` contains general configuration parameters for the application
|
|:--------------------:|:--------:|:----------------------------------:|
|
||||||
|
| CF_API_TOKEN | x | Cloudflare API Token |
|
||||||
|
| STATUS_POST_URL | | Post Endpoint for a Uptime Monitor |
|
||||||
|
*Note: Variables can be stored in a .env file*
|
||||||
|
|
||||||
|
The actual configuration happens in two or more files:
|
||||||
`interfaces.toml` contains all IPv6 interfaces available/used by the zone config files.
|
`interfaces.toml` contains all IPv6 interfaces available/used by the zone config files.
|
||||||
`.toml` files in `zones.d` contain settings for individual zones.
|
`.toml` files in `zone.d` contain settings for individual zones.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
*config.toml*
|
*.env*
|
||||||
```toml
|
```text
|
||||||
cf_api_token = "0123456789abcdef0123456789abcdef01234" # Cloudflare API Token
|
CF_API_TOKEN=0123456789abcdef0123456789abcdef01234
|
||||||
check_interval_seconds = 30 # Defaults to 60 if missing
|
CHECK_INTERVAL_SECONDS=30 // Defaults to 60 if missing
|
||||||
uptime_url = "https://example.org/uptime/id12" # Post Endpoint for a Uptime Monitor
|
UPTIME_URL=https://example.org/uptime/id12 // Entirely optional
|
||||||
```
|
```
|
||||||
|
|
||||||
*interfaces.toml*
|
*interfaces.toml*
|
||||||
|
@ -29,7 +34,7 @@ host_address = "::edcb:a098:7654:3210"
|
||||||
example-interface = "::0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host
|
example-interface = "::0123:4567:890a:bcde" # static part of the IP, the rest will be dynamically generated using the host
|
||||||
```
|
```
|
||||||
|
|
||||||
*zones.d/example.org.toml*
|
*zone.d/example.org.toml*
|
||||||
```toml
|
```toml
|
||||||
email = "owner@example.org" # Email of User owning the Zone
|
email = "owner@example.org" # Email of User owning the Zone
|
||||||
zone = "example.org" # Zone Name
|
zone = "example.org" # Zone Name
|
||||||
|
@ -42,10 +47,4 @@ interface = "example-interface" # Only required on type values 6 and 10
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debian Repository
|
## Debian Repository
|
||||||
|
TODO!
|
||||||
Currently supported:
|
|
||||||
- Debian 12 'Bookworm'
|
|
||||||
|
|
||||||
Includes systemd system and user unit files
|
|
||||||
|
|
||||||
For more details see [the package registry](https://forgejo.neshweb.net/Neshura/-/packages/debian/cloudflare-dns-updater)
|
|
74
cloudflare.json
Normal file
74
cloudflare.json
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"AAAA": [
|
||||||
|
"books",
|
||||||
|
"calibre",
|
||||||
|
"docs.gitlab",
|
||||||
|
"element",
|
||||||
|
"files",
|
||||||
|
"gitlab",
|
||||||
|
"*.gitpages",
|
||||||
|
"gitpages",
|
||||||
|
"hentai",
|
||||||
|
"ipv6",
|
||||||
|
"jellyfin",
|
||||||
|
"komga",
|
||||||
|
"manga",
|
||||||
|
"mastodon",
|
||||||
|
"matrix",
|
||||||
|
"minecraft",
|
||||||
|
"monitoring",
|
||||||
|
"mstreaming",
|
||||||
|
"music",
|
||||||
|
"navidrome",
|
||||||
|
"neshura-server.net",
|
||||||
|
"nextcloud",
|
||||||
|
"nginx",
|
||||||
|
"picard",
|
||||||
|
"porn",
|
||||||
|
"portainer",
|
||||||
|
"readyornot",
|
||||||
|
"registry.gitlab",
|
||||||
|
"temp1",
|
||||||
|
"temp2",
|
||||||
|
"tube",
|
||||||
|
"video",
|
||||||
|
"www",
|
||||||
|
"zomboid"
|
||||||
|
],
|
||||||
|
"A": [
|
||||||
|
"books",
|
||||||
|
"calibre",
|
||||||
|
"docs.gitlab",
|
||||||
|
"element",
|
||||||
|
"files",
|
||||||
|
"gitlab",
|
||||||
|
"*.gitpages",
|
||||||
|
"gitpages",
|
||||||
|
"hentai",
|
||||||
|
"ipv4",
|
||||||
|
"jellyfin",
|
||||||
|
"komga",
|
||||||
|
"manga",
|
||||||
|
"mastodon",
|
||||||
|
"matrix",
|
||||||
|
"minecraft",
|
||||||
|
"monitoring",
|
||||||
|
"mstreaming",
|
||||||
|
"music",
|
||||||
|
"navidrome",
|
||||||
|
"neshura-server.net",
|
||||||
|
"nextcloud",
|
||||||
|
"nginx",
|
||||||
|
"picard",
|
||||||
|
"porn",
|
||||||
|
"portainer",
|
||||||
|
"readyornot",
|
||||||
|
"registry.gitlab",
|
||||||
|
"temp1",
|
||||||
|
"temp2",
|
||||||
|
"tube",
|
||||||
|
"video",
|
||||||
|
"www",
|
||||||
|
"zomboid"
|
||||||
|
]
|
||||||
|
}
|
38
config.json
Normal file
38
config.json
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"ipv6_interface": ":da5e:d3ff:feeb:4346",
|
||||||
|
"zones": [
|
||||||
|
{
|
||||||
|
"email": "neshura@proton.me",
|
||||||
|
"name": "neshura.net",
|
||||||
|
"id": "0183f167a051f1e432c0d931478638b5",
|
||||||
|
"dns_entries": [
|
||||||
|
{
|
||||||
|
"name": "*.neshura.net",
|
||||||
|
"type4": false,
|
||||||
|
"type6": true,
|
||||||
|
"interface": ":da5e:d3ff:feeb:4346"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "neshura.net",
|
||||||
|
"type4": false,
|
||||||
|
"type6": true,
|
||||||
|
"interface": ":da5e:d3ff:feeb:4346"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "neshura@proton.me",
|
||||||
|
"name": "neshura-server.net",
|
||||||
|
"id": "146d4cd6a1777376b423aaedc6824818",
|
||||||
|
"dns_entries": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "neshura@proton.me",
|
||||||
|
"name": "neshweb.net",
|
||||||
|
"id": "75b0d52229357478b734ae0f6d075c15",
|
||||||
|
"dns_entries": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
debian/cloudflare-dns-updater@.service
vendored
14
debian/cloudflare-dns-updater@.service
vendored
|
@ -1,14 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description="Application for automatically updating Cloudflare DNS records"
|
|
||||||
After=syslog.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=%i
|
|
||||||
ExecStart=/usr/local/bin/cloudflare-dns-updater
|
|
||||||
Restart=always
|
|
||||||
RestartSec=3
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,4 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::env::VarError;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
|
@ -9,7 +11,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||||
use strum_macros::{Display, IntoStaticStr};
|
use strum_macros::{Display, IntoStaticStr};
|
||||||
use systemd_journal_logger::connected_to_journal;
|
use systemd_journal_logger::connected_to_journal;
|
||||||
use url::ParseError;
|
use url::ParseError;
|
||||||
use crate::config::{AppConfig, ZoneConfig, ZoneEntry};
|
use crate::config::{ZoneConfig, ZoneEntry};
|
||||||
|
|
||||||
const API_BASE: &str = "https://api.cloudflare.com/client/v4";
|
const API_BASE: &str = "https://api.cloudflare.com/client/v4";
|
||||||
|
|
||||||
|
@ -32,14 +34,14 @@ pub(crate) struct CloudflareZone {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CloudflareZone {
|
impl CloudflareZone {
|
||||||
pub(crate) fn new(zone: &ZoneConfig, config: &AppConfig) -> Self {
|
pub(crate) fn new(zone: &ZoneConfig) -> Result<Self, VarError> {
|
||||||
let key = config.cloudflare_api_token.clone();
|
let key = env::var("CF_API_TOKEN")?;
|
||||||
Self {
|
Ok(Self {
|
||||||
name: zone.name.clone(),
|
name: zone.name.clone(),
|
||||||
email: zone.email.clone(),
|
email: zone.email.clone(),
|
||||||
key,
|
key,
|
||||||
id: zone.id.clone(),
|
id: zone.id.clone(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_auth_headers(&self) -> HeaderMap {
|
fn generate_auth_headers(&self) -> HeaderMap {
|
||||||
|
@ -64,7 +66,7 @@ impl CloudflareZone {
|
||||||
let entries = match response.json::<CloudflareApiResults>() {
|
let entries = match response.json::<CloudflareApiResults>() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to parse API response: {e}");
|
let err_msg = format!("Unable to parse API response. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -75,7 +77,7 @@ impl CloudflareZone {
|
||||||
|
|
||||||
Ok(entries.result)
|
Ok(entries.result)
|
||||||
} else {
|
} else {
|
||||||
let err_msg = format!("Unable to fetch Cloudflare Zone Entries for {}: {}",self.name ,response.status());
|
let err_msg = format!("Unable to fetch Cloudflare Zone Entries for {}. Error: {}",self.name ,response.status());
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -84,7 +86,7 @@ impl CloudflareZone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to access Cloudflare API: {e}");
|
let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -107,7 +109,7 @@ impl CloudflareZone {
|
||||||
self.validate_response(response)
|
self.validate_response(response)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to access Cloudflare API: {e}");
|
let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -133,7 +135,7 @@ impl CloudflareZone {
|
||||||
self.validate_response(response)
|
self.validate_response(response)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to access Cloudflare API: {e}");
|
let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -174,7 +176,7 @@ impl CloudflareZone {
|
||||||
self.validate_response(response)
|
self.validate_response(response)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to access Cloudflare API: {e}");
|
let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -200,7 +202,7 @@ impl CloudflareZone {
|
||||||
self.validate_response(response)
|
self.validate_response(response)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to access Cloudflare API: {e}");
|
let err_msg = format!("Unable to access Cloudflare API. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -270,7 +272,7 @@ impl CloudflareZone {
|
||||||
match Url::parse(input) {
|
match Url::parse(input) {
|
||||||
Ok(url) => Ok(url),
|
Ok(url) => Ok(url),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to parse URL: {}", e);
|
let err_msg = format!("Unable to parse URL. Error: {}", e);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -293,7 +295,7 @@ impl CloudflareZone {
|
||||||
let data = match response.json::<CloudflareApiResult>() {
|
let data = match response.json::<CloudflareApiResult>() {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to parse API response: {e}");
|
let err_msg = format!("Unable to parse API response. Error: {e}");
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -305,7 +307,7 @@ impl CloudflareZone {
|
||||||
match data.success {
|
match data.success {
|
||||||
true => Ok(()),
|
true => Ok(()),
|
||||||
false => {
|
false => {
|
||||||
let err_msg = format!("Unexpected error while updating DNS record: {:?}", data);
|
let err_msg = format!("Unexpected error while updating DNS record. Info: {:?}", data);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -314,7 +316,7 @@ impl CloudflareZone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_msg = format!("Unable to post/put Cloudflare DNS entry: {}", response.status());
|
let err_msg = format!("Unable to post/put Cloudflare DNS entry. Error: {}", response.status());
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl InterfaceConfig {
|
||||||
let interface_address = match self.interfaces.get(interface_name) {
|
let interface_address = match self.interfaces.get(interface_name) {
|
||||||
Some(address) => *address,
|
Some(address) => *address,
|
||||||
None => {
|
None => {
|
||||||
let err_msg = format!("Malformed or missing IP in interfaces.toml for interface {}", interface_name);
|
let err_msg = "Malformed IP in interfaces.toml";
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -140,46 +140,3 @@ impl Default for ZoneConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub(crate) struct AppConfig {
|
|
||||||
pub(crate) cloudflare_api_token: String,
|
|
||||||
pub(crate) check_interval_seconds: Option<u16>,
|
|
||||||
pub(crate) uptime_url: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppConfig {
|
|
||||||
pub(crate) fn load() -> Result<Self, Box<dyn Error>> {
|
|
||||||
let cfg: Self = match confy::load(env!("CARGO_PKG_NAME"),"config") {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(e) => {
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => error!("[ERROR] {e}"),
|
|
||||||
false => eprintln!("[ERROR] {e}")
|
|
||||||
}
|
|
||||||
return Err(Box::new(e));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if cfg.cloudflare_api_token.is_empty() {
|
|
||||||
let err_msg = "Cloudflare api token not specified. The app cannot work without this";
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => error!("[ERROR] {err_msg}"),
|
|
||||||
false => eprintln!("[ERROR] {err_msg}")
|
|
||||||
}
|
|
||||||
panic!("{err_msg}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(cfg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AppConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
cloudflare_api_token: "".to_owned(),
|
|
||||||
check_interval_seconds: None,
|
|
||||||
uptime_url: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
149
src/main.rs
149
src/main.rs
|
@ -1,15 +1,16 @@
|
||||||
/*use cloudflare_old::{Instance, CloudflareDnsType};*/
|
/*use cloudflare_old::{Instance, CloudflareDnsType};*/
|
||||||
use reqwest::blocking::get;
|
use reqwest::blocking::get;
|
||||||
use std::{thread::{sleep}};
|
use std::{env, thread::{sleep}};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use chrono::{Utc, Duration};
|
use chrono::{Utc, Duration};
|
||||||
|
use dotenv::dotenv;
|
||||||
use log::{info, warn, error, LevelFilter};
|
use log::{info, warn, error, LevelFilter};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use systemd_journal_logger::{connected_to_journal, JournalLog};
|
use systemd_journal_logger::{connected_to_journal, JournalLog};
|
||||||
use crate::cloudflare::{CloudflareZone, DnsRecordType};
|
use crate::cloudflare::{CloudflareZone, DnsRecordType};
|
||||||
use crate::config::{AppConfig, InterfaceConfig, ZoneConfig, ZoneEntry};
|
use crate::config::{InterfaceConfig, ZoneConfig, ZoneEntry};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod cloudflare;
|
mod cloudflare;
|
||||||
|
@ -24,8 +25,8 @@ struct Addresses {
|
||||||
impl Addresses {
|
impl Addresses {
|
||||||
fn new() -> Result<Self, Box<dyn Error>> {
|
fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
let mut ret = Self {
|
let mut ret = Self {
|
||||||
ipv4_uri: "http://ip4only.me/api/".to_owned(),
|
ipv4_uri: "https://am.i.mullvad.net/ip".to_owned(),
|
||||||
ipv6_uri: "http://ip6only.me/api/".to_owned(),
|
ipv6_uri: "https://ipv6.am.i.mullvad.net/ip".to_owned(),
|
||||||
ipv4: Ipv4Addr::new(0, 0, 0, 0),
|
ipv4: Ipv4Addr::new(0, 0, 0, 0),
|
||||||
ipv6: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)
|
ipv6: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
};
|
};
|
||||||
|
@ -61,14 +62,6 @@ impl Addresses {
|
||||||
match self.get_v4() {
|
match self.get_v4() {
|
||||||
Ok(ip) => {
|
Ok(ip) => {
|
||||||
if ip != self.ipv4 {
|
if ip != self.ipv4 {
|
||||||
if ip == Ipv4Addr::new(0,0,0,0) {
|
|
||||||
let warn_msg = "'0.0.0.0' detected as new IPv4, skipping changes".to_owned();
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => warn!("[WARN] {warn_msg}"),
|
|
||||||
false => println!("[WARN] {warn_msg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let info_msg = format!("IPv4 changed from '{}' to '{}'", self.ipv4, ip);
|
let info_msg = format!("IPv4 changed from '{}' to '{}'", self.ipv4, ip);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => info!("[INFO] {info_msg}"),
|
true => info!("[INFO] {info_msg}"),
|
||||||
|
@ -77,12 +70,11 @@ impl Addresses {
|
||||||
self.ipv4 = ip;
|
self.ipv4 = ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let error_msg = format!("Unable to fetch IPv4 from '{}': {}", self.ipv4_uri, e);
|
let warn_msg = format!("Unable to fetch IPv4 from '{}'. Error: {}", self.ipv4_uri, e);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {error_msg}"),
|
true => warn!("[WARN] {warn_msg}"),
|
||||||
false => eprintln!("[ERROR] {error_msg}"),
|
false => println!("[WARN] {warn_msg}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,14 +82,6 @@ impl Addresses {
|
||||||
match self.get_v6() {
|
match self.get_v6() {
|
||||||
Ok(ip) => {
|
Ok(ip) => {
|
||||||
if ip != self.ipv6 {
|
if ip != self.ipv6 {
|
||||||
if ip == Ipv6Addr::new(0,0,0,0,0,0,0,0) {
|
|
||||||
let warn_msg = "'::' detected as new IPv6, skipping changes".to_owned();
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => warn!("[WARN] {warn_msg}"),
|
|
||||||
false => println!("[WARN] {warn_msg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let info_msg = format!("IPv6 changed from '{}' to '{}'", self.ipv6, ip);
|
let info_msg = format!("IPv6 changed from '{}' to '{}'", self.ipv6, ip);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => info!("[INFO] {info_msg}"),
|
true => info!("[INFO] {info_msg}"),
|
||||||
|
@ -106,12 +90,11 @@ impl Addresses {
|
||||||
self.ipv6 = ip;
|
self.ipv6 = ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let error_msg = format!("Unable to fetch IPv6 from '{}': {}", self.ipv6_uri, e);
|
let warn_msg = format!("Unable to fetch IPv6 from '{}'. Error: {}", self.ipv6_uri, e);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {error_msg}"),
|
true => warn!("[WARN] {warn_msg}"),
|
||||||
false => eprintln!("[ERROR] {error_msg}"),
|
false => println!("[WARN] {warn_msg}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +105,7 @@ impl Addresses {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
match res.status() {
|
match res.status() {
|
||||||
StatusCode::OK => {
|
StatusCode::OK => {
|
||||||
let ip_string = res.text().expect("Returned data should always contain text").trim_end().split(',').collect::<Vec<&str>>()[1].to_owned();
|
let ip_string = res.text().expect("Returned data should always contain text").trim_end().to_owned();
|
||||||
Ok(Ipv4Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable"))
|
Ok(Ipv4Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable"))
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -145,7 +128,7 @@ impl Addresses {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
match res.status() {
|
match res.status() {
|
||||||
StatusCode::OK => {
|
StatusCode::OK => {
|
||||||
let ip_string: String = res.text().expect("Returned data should always contain text").trim_end().split(',').collect::<Vec<&str>>()[1].to_owned();
|
let ip_string = res.text().expect("Returned data should always contain text").trim_end().to_owned();
|
||||||
Ok(Ipv6Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable"))
|
Ok(Ipv6Addr::from_str(ip_string.as_str()).expect("Returned IP should always be parseable"))
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -250,10 +233,10 @@ fn compare_zones(old_zone: &ZoneConfig, new_zone: &ZoneConfig) -> Vec<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
dotenv().ok();
|
||||||
JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error");
|
JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error");
|
||||||
log::set_max_level(LevelFilter::Info);
|
log::set_max_level(LevelFilter::Info);
|
||||||
|
|
||||||
let mut config = AppConfig::load().unwrap();
|
|
||||||
let mut ifaces = InterfaceConfig::load().unwrap();
|
let mut ifaces = InterfaceConfig::load().unwrap();
|
||||||
let mut zone_cfgs = ZoneConfig::load().unwrap();
|
let mut zone_cfgs = ZoneConfig::load().unwrap();
|
||||||
|
|
||||||
|
@ -265,22 +248,32 @@ fn main() {
|
||||||
Err(e) => panic!("{}", e)
|
Err(e) => panic!("{}", e)
|
||||||
};
|
};
|
||||||
|
|
||||||
let reload_interval = config.check_interval_seconds.unwrap_or_else(|| {
|
let reload_interval = match env::var("CHECK_INTERVAL_SECONDS") {
|
||||||
let warn_msg = "Reload interval option not set, defaulting to 60";
|
Ok(interval_string) => i64::from_str(&interval_string).unwrap_or_else(|e| {
|
||||||
|
let warn_msg = format!("Expected integer number, got '{interval_string}'. Defaulting to 60");
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => warn!("[WARN] {warn_msg}"),
|
||||||
|
false => println!("[WARN] {warn_msg}"),
|
||||||
|
};
|
||||||
|
60
|
||||||
|
}),
|
||||||
|
Err(_) => {
|
||||||
|
let warn_msg = "Reload interval env not set, defaulting to 60";
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => warn!("[WARN] {warn_msg}"),
|
true => warn!("[WARN] {warn_msg}"),
|
||||||
false => println!("[WARN] {warn_msg}"),
|
false => println!("[WARN] {warn_msg}"),
|
||||||
}
|
}
|
||||||
60
|
60
|
||||||
}) as i64;
|
},
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
now = Utc::now();
|
now = Utc::now();
|
||||||
if now >= start + Duration::seconds(reload_interval) {
|
if now >= start + Duration::seconds(reload_interval) {
|
||||||
start = now;
|
start = now;
|
||||||
|
|
||||||
if let Some(uptime_url) = &config.uptime_url {
|
if let Ok(uptime_url) = env::var("UPTIME_URL") {
|
||||||
let _ = get(uptime_url);
|
get(uptime_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
match InterfaceConfig::load() {
|
match InterfaceConfig::load() {
|
||||||
|
@ -360,9 +353,10 @@ fn main() {
|
||||||
|
|
||||||
ifaces = new_cfg
|
ifaces = new_cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to load ínterfaces.toml with error: {e}");
|
let err_msg = format!("Unable to load ínterfaces.toml with error: {}", e);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -502,72 +496,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_msg = format!("Unable to load from zones.d with error: {e}");
|
let err_msg = format!("Unable to load from zones.d with error: {}", e);
|
||||||
match connected_to_journal() {
|
|
||||||
true => error!("[ERROR] {err_msg}"),
|
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match AppConfig::load() {
|
|
||||||
Ok(new_cfg) => {
|
|
||||||
if config != new_cfg {
|
|
||||||
if config.cloudflare_api_token != new_cfg.cloudflare_api_token {
|
|
||||||
let info_msg = "API token in config.toml changed";
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => info!("[INFO] {info_msg}"),
|
|
||||||
false => println!("[INFO] {info_msg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.check_interval_seconds != new_cfg.check_interval_seconds {
|
|
||||||
let info_msg = match config.check_interval_seconds {
|
|
||||||
Some(old_interval) => {
|
|
||||||
match new_cfg.check_interval_seconds {
|
|
||||||
Some(new_interval) => format!("Check interval in config.toml changed from {old_interval}s to {new_interval}s"),
|
|
||||||
None => format!("Check interval in config.toml changed from {old_interval}s to 60s"),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
match new_cfg.check_interval_seconds {
|
|
||||||
Some(new_interval) => format!("Check interval in config.toml changed from 60s to {new_interval}s"),
|
|
||||||
None => "This is a unicorn error, congratulations.".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => info!("[INFO] {info_msg}"),
|
|
||||||
false => println!("[INFO] {info_msg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.uptime_url != new_cfg.uptime_url {
|
|
||||||
let info_msg = match &config.uptime_url {
|
|
||||||
Some(old_url) => {
|
|
||||||
match &new_cfg.uptime_url {
|
|
||||||
Some(new_url) => format!("Uptime URL in config.toml changed from '{old_url}' to '{new_url}'"),
|
|
||||||
None => "Uptime URL in config.toml was removed".to_owned(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
match &new_cfg.uptime_url {
|
|
||||||
Some(new_url) => format!("Uptime URL '{new_url}' was added to config.toml"),
|
|
||||||
None => "This is a unicorn error, congratulations.".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match connected_to_journal() {
|
|
||||||
true => info!("[INFO] {info_msg}"),
|
|
||||||
false => println!("[INFO] {info_msg}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config = new_cfg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let err_msg = format!("Unable to load config.toml with error: {e}");
|
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => error!("[ERROR] {err_msg}"),
|
true => error!("[ERROR] {err_msg}"),
|
||||||
false => eprintln!("[ERROR] {err_msg}"),
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
@ -577,7 +506,17 @@ fn main() {
|
||||||
|
|
||||||
ips.update();
|
ips.update();
|
||||||
for zone in &zone_cfgs {
|
for zone in &zone_cfgs {
|
||||||
let cf_zone = CloudflareZone::new(zone, &config);
|
let cf_zone = match CloudflareZone::new(zone) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(e) => {
|
||||||
|
let err_msg = format!("Cloudflare Token likely not set. Error: {}", e);
|
||||||
|
match connected_to_journal() {
|
||||||
|
true => error!("[ERROR] {err_msg}"),
|
||||||
|
false => eprintln!("[ERROR] {err_msg}"),
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let cf_entries = match cf_zone.get_entries() {
|
let cf_entries = match cf_zone.get_entries() {
|
||||||
Ok(entries) => entries,
|
Ok(entries) => entries,
|
||||||
|
@ -656,7 +595,7 @@ fn main() {
|
||||||
if cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4).is_ok() {
|
if cf_zone.update(entry, r#type, &cf_entry.id, ipv6, ipv4).is_ok() {
|
||||||
let info_msg = format!("Updated {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name);
|
let info_msg = format!("Updated {} DNS Record for entry '{}' in zone '{}'", r#type, entry.name, zone.name);
|
||||||
match connected_to_journal() {
|
match connected_to_journal() {
|
||||||
true => info!("[INFO] {info_msg}"),
|
true => warn!("[INFO] {info_msg}"),
|
||||||
false => println!("[INFO] {info_msg}"),
|
false => println!("[INFO] {info_msg}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue