131 lines
3.1 KiB
Go
131 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"net/http"
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
|
|
const API_DOMAIN string = "https://api.porkbun.com"
|
|
const ENDPOINT_GET string = "/api/json/v3/dns/retrieve/"
|
|
const ENDPOINT_SET string = "/api/json/v3/dns/edit/"
|
|
|
|
// error helper
|
|
func checkE(e error) {
|
|
if e != nil {
|
|
log.Fatal(e)
|
|
panic(e)
|
|
}
|
|
}
|
|
|
|
// http request helper
|
|
func createBody(bodyMap map[string]string) io.Reader {
|
|
bodyData, err := json.Marshal(bodyMap)
|
|
checkE(err)
|
|
body := string(bodyData)
|
|
return strings.NewReader(body)
|
|
}
|
|
|
|
// http response helper
|
|
func readBody(res *http.Response) []byte {
|
|
body, err := io.ReadAll(res.Body)
|
|
res.Body.Close()
|
|
if res.StatusCode > 299 {
|
|
log.Fatalf("Response failed with status code: %d and\nbody: %s\n", res.StatusCode, body)
|
|
}
|
|
checkE(err)
|
|
return body
|
|
}
|
|
|
|
func syncDns(domain string, apiSecret string, apiKey string, serverIp string) bool {
|
|
// construct endpoint url
|
|
url := API_DOMAIN + ENDPOINT_GET + domain
|
|
|
|
// use api keys to construct request body for porkbun
|
|
body := createBody(map[string]string{
|
|
"secretapikey" : apiSecret,
|
|
"apikey" : apiKey,
|
|
})
|
|
|
|
// submit request for domain's DNS records
|
|
res, err := http.Post(url, "application/json", body)
|
|
checkE(err)
|
|
|
|
// parse records out from response body
|
|
var bodyJson map[string]interface{}
|
|
err = json.Unmarshal(readBody(res), &bodyJson)
|
|
checkE(err)
|
|
dnsRecords := bodyJson["records"].([]interface{})
|
|
|
|
// identify outdated A records
|
|
aRecordsToUpdate := make(map[string]string)
|
|
for i := range len(dnsRecords) {
|
|
record := dnsRecords[i].(map[string]interface{})
|
|
if record["type"].(string) != "A" { continue }
|
|
if record["content"].(string) == serverIp { continue }
|
|
aRecordsToUpdate[record["name"].(string)] = record["id"].(string)
|
|
}
|
|
|
|
// we are potentially done now
|
|
if len(aRecordsToUpdate) == 0 {
|
|
fmt.Println(domain + " is up to date")
|
|
return false
|
|
}
|
|
|
|
// update each outdated A record
|
|
for recordName, recordId := range aRecordsToUpdate {
|
|
url = API_DOMAIN + ENDPOINT_SET + domain + "/" + recordId
|
|
subdomain := strings.Split(recordName, ".")[0]
|
|
body = createBody(map[string]string{
|
|
"secretapikey" : apiSecret,
|
|
"apikey" : apiKey,
|
|
"content" : serverIp,
|
|
"name" : subdomain,
|
|
"type": "A",
|
|
"ttl": "600",
|
|
})
|
|
|
|
// make request to update A record
|
|
res, err = http.Post(url, "application/json", body)
|
|
checkE(err)
|
|
fmt.Println("UPDATED " + recordName)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func main() {
|
|
// open + deserialize env JSON
|
|
envJson, err := os.ReadFile(".env")
|
|
checkE(err)
|
|
var env map[string]interface{}
|
|
err = json.Unmarshal(envJson, &env)
|
|
checkE(err)
|
|
|
|
// parse/organize api keys + domains to update
|
|
apiSecret := env["secretapikey"].(string)
|
|
apiKey := env["apikey"].(string)
|
|
domains := env["domains"].([]interface{})
|
|
|
|
// determine server's current ip
|
|
res, err := http.Get("https://api.ipify.org")
|
|
checkE(err)
|
|
ip := string(readBody(res))
|
|
|
|
fmt.Println("SERVER IP: " + ip)
|
|
|
|
// loop through all domains + update DNS records
|
|
var domain string
|
|
for i := range len(domains) {
|
|
domain = domains[i].(string)
|
|
fmt.Println("checking " + domain)
|
|
syncDns(domain, apiSecret, apiKey, ip)
|
|
}
|
|
}
|
|
|