Initial commit

This commit is contained in:
2021-09-12 20:09:08 -04:00
commit 10cf806b73
5 changed files with 168 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
package digitalocean
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
)
type client struct {
baseUrl string
domain string
token string
httpClient *http.Client
}
func NewClient(baseUrl, token, domain string) *client {
return &client{
baseUrl: baseUrl,
domain: domain,
token: token,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
func (c *client) GetARecordID() (string, error) {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/domains/%s/records", c.baseUrl, c.domain), nil)
if err != nil {
return "", err
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token))
var records RecordsResponse
err = c.doRequest(req, &records)
if err != nil {
return "", err
}
for _, record := range records.DomainRecords {
if record.Name == "vpn" && record.Type == "A" {
return strconv.Itoa(int(record.Id)), nil
}
}
return "", ErrNotFound
}
func (c *client) SetARecord(id, ip string) error {
body := fmt.Sprintf(`{"data":%q}`, strings.ReplaceAll(ip, "\n", ""))
req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%s/v2/domains/%s/records/%s", c.baseUrl, c.domain, id), bytes.NewBufferString(body))
if err != nil {
return err
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token))
req.Header.Add("Content-Type", "application/json")
var resp map[string]interface{}
err = c.doRequest(req, &resp)
return err
}
func (c *client) doRequest(req *http.Request, output interface{}) error {
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusInternalServerError {
return fmt.Errorf("%w: statuscode %d", ErrServerError, resp.StatusCode)
}
if resp.StatusCode >= http.StatusBadRequest {
return fmt.Errorf("%w: statuscode %d", ErrClientError, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(body, &output)
return err
}
+9
View File
@@ -0,0 +1,9 @@
package digitalocean
import "errors"
var (
ErrNotFound = errors.New("not found")
ErrClientError = errors.New("client error")
ErrServerError = errors.New("server error")
)
+12
View File
@@ -0,0 +1,12 @@
package digitalocean
type DomainRecords struct {
Data string `json:"data"`
Id int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
}
type RecordsResponse struct {
DomainRecords []DomainRecords `json:"domain_records"`
}
+3
View File
@@ -0,0 +1,3 @@
module github.com/davidlick/digitalocean-ddns
go 1.16
+54
View File
@@ -0,0 +1,54 @@
package main
import (
"io/ioutil"
"log"
"net/http"
"os"
"github.com/davidlick/digitalocean-ddns/digitalocean"
)
var (
client = &http.Client{}
)
func main() {
token := os.Getenv("DIGITALOCEAN_TOKEN")
doBaseUrl := os.Getenv("DIGITALOCEAN_URL")
doDomain := os.Getenv("DIGITALOCEAN_DOMAIN")
client := digitalocean.NewClient(doBaseUrl, token, doDomain)
ip, err := getPublicIP()
if err != nil {
log.Fatalf("failed to get public ip: %v", err)
}
id, err := client.GetARecordID()
if err != nil {
log.Fatalf("failed to get A record ID: %v", err)
}
err = client.SetARecord(id, ip)
if err != nil {
log.Fatalf("failed to set A record ip: %v", err)
}
log.Printf("successfully set DNS A record id %s to %s", id, ip)
}
func getPublicIP() (string, error) {
resp, err := http.Get("https://icanhazip.com")
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}