inital commit

This commit is contained in:
Tim Lappe 2025-03-23 11:55:06 +01:00
commit 88d02078b3
12 changed files with 356 additions and 0 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
DNS_ZONE=strolap.com
HETZNER_API_TOKEN=JnSFwq4YNXxifbRZaBHv6vsgAaz5VFmR

BIN
DynDNS Executable file

Binary file not shown.

31
cron_log.log Normal file

File diff suppressed because one or more lines are too long

10
env/env_file.go vendored Normal file
View File

@ -0,0 +1,10 @@
package env
import "github.com/gofor-little/env"
func LoadEnv() {
err := env.Load(".env")
if err != nil {
panic(err)
}
}

16
env/read_envs.go vendored Normal file
View File

@ -0,0 +1,16 @@
package env
import (
"os"
"strings"
)
func ReadZones() []string {
zoneEnv := os.Getenv("DNS_ZONE")
if zoneEnv == "" {
panic("DNS_ZONE not set")
}
zones := strings.Split(zoneEnv, ",")
return zones
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module DynDNS
go 1.21
require github.com/gofor-little/env v1.0.17 // indirect

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/gofor-little/env v1.0.17 h1:TAB4jwL11nH59mg+4+6uycWY8Wc7MVe7Ip2uSmT0v0Q=
github.com/gofor-little/env v1.0.17/go.mod h1:2BE2i6c9e/C6EaGnfhpqzfNERUqkzJ+s/ApnRyl+588=

121
hetzner/api.go Normal file
View File

@ -0,0 +1,121 @@
package hetzner
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
)
func SendGetZones(token string, names ...string) []string {
client := &http.Client{}
var ids []string
for _, name := range names {
fmt.Println("Getting zone: ", name)
req, err := http.NewRequest("GET", "https://dns.hetzner.com/api/v1/zones?name="+name, nil)
req.Header.Add("Auth-API-Token", token)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
respBody, _ := io.ReadAll(resp.Body)
fmt.Println("response Status : ", resp.Status)
var responseObject struct {
Zones []Zone
}
err = json.Unmarshal(respBody, &responseObject)
if err != nil {
panic(err)
}
ids = append(ids, responseObject.Zones[0].Id)
}
return ids
}
func SendGetRecords(token string, zoneId string) []Record {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://dns.hetzner.com/api/v1/records?zone_id="+zoneId, nil)
req.Header.Add("Auth-API-Token", token)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Failure : ", err)
}
respBody, _ := io.ReadAll(resp.Body)
fmt.Println("Response Status : ", resp.Status)
var responseObject struct {
Records []Record
}
err = json.Unmarshal(respBody, &responseObject)
if err != nil {
panic(err)
}
fmt.Println("Records: ", responseObject.Records)
return responseObject.Records
}
func SendUpdateRecords(token string, records []Record) {
client := &http.Client{}
var recordsReq struct {
Records []Record `json:"records"`
}
recordsReq.Records = records
jsonReq, err := json.Marshal(recordsReq)
if err != nil {
panic(err)
}
fmt.Println("Request: ", string(jsonReq))
req, err := http.NewRequest("PUT", "https://dns.hetzner.com/api/v1/records/bulk", bytes.NewBuffer(jsonReq))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Auth-API-Token", token)
if err != nil {
panic(err)
}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
respBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response Status: ", resp.Status)
fmt.Println("response Body: ", string(respBody))
var responseObject struct {
Records []Record `json:"records"`
FailedRecords []Record `json:"failed_records"`
}
err = json.Unmarshal(respBody, &responseObject)
if err != nil {
panic(err)
}
if len(responseObject.FailedRecords) > 0 {
fmt.Println("Failed records: ", responseObject.FailedRecords)
panic("Failed to update records: " + string(respBody))
}
}

14
hetzner/model.go Normal file
View File

@ -0,0 +1,14 @@
package hetzner
type Zone = struct {
Id string `json:"id"`
}
type Record = struct {
Id string `json:"id"`
ZoneId string `json:"zone_id"`
Type string `json:"type"`
Name string `json:"name"`
Value string `json:"value"`
TTL int `json:"ttl"`
}

57
ip/ip.go Normal file
View File

@ -0,0 +1,57 @@
package ip
import (
"fmt"
"io/ioutil"
"net/http"
)
func readIp() (string, error) {
ip, err := _readIpFromIpify()
if err == nil {
return ip, nil
}
ip, err = _readIpFromIdentMe()
if err == nil {
return ip, nil
}
return "", fmt.Errorf("error getting IP from all services: %v", err)
}
func _readIpFromIpify() (string, error) {
fmt.Printf("Getting IP address from ipify ...\n")
resp, err := http.Get("https://api.ipify.org?format=text")
if err != nil {
return "", fmt.Errorf("error getting IP from ipify: %v", err)
}
defer resp.Body.Close()
ip, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading IP from ipify: %v", err)
}
return string(ip), nil
}
func _readIpFromIdentMe() (string, error) {
fmt.Printf("Getting IP address from identme ...\n")
resp, err := http.Get("https://4.ident.me")
if err != nil {
return "", fmt.Errorf("error getting IP from identme: %v", err)
}
defer resp.Body.Close()
ip, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading IP from identme: %v", err)
}
return string(ip), nil
}

57
publicIp/ip.go Normal file
View File

@ -0,0 +1,57 @@
package publicIp
import (
"fmt"
"io"
"net/http"
)
func ReadIp() (string, error) {
ip, err := _readIpFromIpify()
if err == nil {
return ip, nil
}
ip, err = _readIpFromIdentMe()
if err == nil {
return ip, nil
}
return "", fmt.Errorf("error getting IP from all services: %v", err)
}
func _readIpFromIpify() (string, error) {
fmt.Printf("Getting IP address from ipify ...\n")
resp, err := http.Get("https://api.ipify.org?format=text")
if err != nil {
return "", fmt.Errorf("error getting IP from ipify: %v", err)
}
defer resp.Body.Close()
ip, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading IP from ipify: %v", err)
}
return string(ip), nil
}
func _readIpFromIdentMe() (string, error) {
fmt.Printf("Getting IP address from identme ...\n")
resp, err := http.Get("https://4.ident.me")
if err != nil {
return "", fmt.Errorf("error getting IP from identme: %v", err)
}
defer resp.Body.Close()
ip, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading IP from identme: %v", err)
}
return string(ip), nil
}

41
service.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"DynDNS/env"
"DynDNS/hetzner"
"DynDNS/publicIp"
"fmt"
"os"
)
func main() {
env.LoadEnv()
ip, err := publicIp.ReadIp()
if err != nil {
fmt.Printf("Error getting IP: %v\n", err)
return
}
fmt.Printf("My IP is: %s\n", ip)
zoneNames := env.ReadZones()
fmt.Printf("Zones: %v\n", zoneNames)
token := os.Getenv("HETZNER_API_TOKEN")
zoneIds := hetzner.SendGetZones(token, zoneNames...)
fmt.Printf("Zone IDs: %v\n", zoneIds)
for _, zoneId := range zoneIds {
records := hetzner.SendGetRecords(token, zoneId)
for i := range records {
record := &records[i]
if record.Type == "A" {
fmt.Printf("Updating record: %s\n", record.Name)
record.Value = ip
}
}
hetzner.SendUpdateRecords(token, records)
}
}