Skip to content

Commit 9701368

Browse files
committed
refactor: query porkbun record before create
1 parent 11cde81 commit 9701368

File tree

9 files changed

+122
-62
lines changed

9 files changed

+122
-62
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/davidramiro/frigabun
33
go 1.20
44

55
require (
6+
github.com/go-faker/faker/v4 v4.0.0
67
github.com/rs/zerolog v1.29.0
78
gopkg.in/yaml.v3 v3.0.1
89
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1
44
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
66
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/go-faker/faker/v4 v4.0.0 h1:tfgFaeizVlYGOS1tVo/vcWcKhkNgG1NWm8ibRG0f+aQ=
8+
github.com/go-faker/faker/v4 v4.0.0/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0/FUTTYbhg=
79
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
810
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
911
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=

internal/api/api.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,25 @@ func HandleUpdateRequest(c echo.Context) error {
5656

5757
for i := range subdomains {
5858
if request.Registrar == "gandi" {
59-
dnsInfo := &gandi.GandiDnsInfo{
59+
dns := &gandi.GandiDns{
6060
IP: request.IP,
6161
Domain: request.Domain,
6262
Subdomain: subdomains[i],
6363
ApiKey: request.ApiKey,
6464
}
65-
err := dnsInfo.AddRecord()
65+
err := dns.SaveRecord()
6666
if err != nil {
6767
return c.String(err.Code, err.Message)
6868
}
6969
} else if request.Registrar == "porkbun" {
70-
dnsInfo := &porkbun.PorkbunDnsInfo{
70+
dns := &porkbun.PorkbunDns{
7171
IP: request.IP,
7272
Domain: request.Domain,
7373
Subdomain: subdomains[i],
7474
ApiKey: request.ApiKey,
7575
SecretApiKey: request.ApiSecretKey,
7676
}
77-
err := dnsInfo.AddRecord()
77+
err := dns.AddRecord()
7878
if err != nil {
7979
return c.String(err.Code, err.Message)
8080
}

internal/api/api_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"testing"
1212

1313
"github.com/davidramiro/frigabun/internal/config"
14+
"github.com/go-faker/faker/v4"
1415
"github.com/labstack/echo/v4"
1516
"github.com/stretchr/testify/assert"
1617
)
@@ -62,7 +63,7 @@ func TestInvalidDomain(t *testing.T) {
6263

6364
func TestGandiUpdateWithValidRequest(t *testing.T) {
6465
q := make(url.Values)
65-
q.Set("ip", config.AppConfig.Test.Gandi.IP)
66+
q.Set("ip", faker.IPv4())
6667
q.Set("domain", config.AppConfig.Test.Gandi.Domain)
6768
q.Set("subdomain", config.AppConfig.Test.Gandi.Subdomain+"2")
6869
q.Set("apiKey", config.AppConfig.Test.Gandi.ApiKey)
@@ -83,7 +84,7 @@ func TestGandiUpdateWithValidRequest(t *testing.T) {
8384

8485
func TestPorkbunUpdateWithValidRequest(t *testing.T) {
8586
q := make(url.Values)
86-
q.Set("ip", config.AppConfig.Test.Porkbun.IP)
87+
q.Set("ip", faker.IPv4())
8788
q.Set("domain", config.AppConfig.Test.Porkbun.Domain)
8889
q.Set("subdomain", config.AppConfig.Test.Porkbun.Subdomain+"2")
8990
q.Set("apikey", config.AppConfig.Test.Porkbun.ApiKey)

main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import (
1414
func main() {
1515
logger.InitLog()
1616

17-
config.InitConfig()
17+
err := config.InitConfig()
18+
if err != nil {
19+
logger.Log.Fatal().Err(err).Msg("error initializing config")
20+
}
1821

1922
initEcho()
2023
}

pkg/gandi/gandi.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/davidramiro/frigabun/internal/logger"
1212
)
1313

14-
type GandiDnsInfo struct {
14+
type GandiDns struct {
1515
IP string
1616
Domain string
1717
Subdomain string
@@ -30,7 +30,7 @@ type GandiUpdateError struct {
3030
Message string
3131
}
3232

33-
func (g *GandiDnsInfo) AddRecord() *GandiUpdateError {
33+
func (g *GandiDns) SaveRecord() *GandiUpdateError {
3434

3535
gandiRequest := &GandiApiRequest{
3636
Subdomain: g.Subdomain,

pkg/gandi/gandi_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,41 @@ func init() {
2424
}
2525

2626
func TestUpdateEndpointWithValidRequest(t *testing.T) {
27-
testDnsInfo := &GandiDnsInfo{
27+
testDnsInfo := &GandiDns{
2828
IP: config.AppConfig.Test.Gandi.IP,
2929
Domain: config.AppConfig.Test.Gandi.Domain,
3030
Subdomain: config.AppConfig.Test.Gandi.Subdomain,
3131
ApiKey: config.AppConfig.Test.Gandi.ApiKey,
3232
}
3333

34-
err := testDnsInfo.AddRecord()
34+
err := testDnsInfo.SaveRecord()
3535

3636
assert.Nil(t, err)
3737
}
3838

3939
func TestUpdateEndpointWithInvalidIp(t *testing.T) {
40-
testDnsInfo := &GandiDnsInfo{
40+
testDnsInfo := &GandiDns{
4141
IP: "::1",
4242
Domain: config.AppConfig.Test.Gandi.Domain,
4343
Subdomain: config.AppConfig.Test.Gandi.Subdomain,
4444
ApiKey: config.AppConfig.Test.Gandi.ApiKey,
4545
}
4646

47-
err := testDnsInfo.AddRecord()
47+
err := testDnsInfo.SaveRecord()
4848

4949
assert.NotNil(t, err)
5050
assert.Equal(t, http.StatusBadRequest, err.Code)
5151
assert.Contains(t, err.Message, "IPv4")
5252
}
5353

5454
func TestUpdateEndpointWithMissingParam(t *testing.T) {
55-
testDnsInfo := &GandiDnsInfo{
55+
testDnsInfo := &GandiDns{
5656
Domain: config.AppConfig.Test.Gandi.Domain,
5757
Subdomain: config.AppConfig.Test.Gandi.Subdomain,
5858
ApiKey: config.AppConfig.Test.Gandi.ApiKey,
5959
}
6060

61-
err := testDnsInfo.AddRecord()
61+
err := testDnsInfo.SaveRecord()
6262

6363
assert.NotNil(t, err)
6464
assert.Equal(t, http.StatusBadRequest, err.Code)
@@ -67,13 +67,13 @@ func TestUpdateEndpointWithMissingParam(t *testing.T) {
6767
}
6868

6969
func TestUpdateEndpointWithMissingAuth(t *testing.T) {
70-
testDnsInfo := &GandiDnsInfo{
70+
testDnsInfo := &GandiDns{
7171
IP: config.AppConfig.Test.Gandi.IP,
7272
Domain: config.AppConfig.Test.Gandi.Domain,
7373
Subdomain: config.AppConfig.Test.Gandi.Subdomain,
7474
}
7575

76-
err := testDnsInfo.AddRecord()
76+
err := testDnsInfo.SaveRecord()
7777

7878
assert.NotNil(t, err)
7979
assert.Equal(t, http.StatusForbidden, err.Code)
@@ -82,14 +82,14 @@ func TestUpdateEndpointWithMissingAuth(t *testing.T) {
8282
}
8383

8484
func TestUpdateEndpointWithInvalidDomain(t *testing.T) {
85-
testDnsInfo := &GandiDnsInfo{
85+
testDnsInfo := &GandiDns{
8686
Domain: "example.com",
8787
IP: config.AppConfig.Test.Gandi.IP,
8888
Subdomain: config.AppConfig.Test.Gandi.Subdomain,
8989
ApiKey: config.AppConfig.Test.Gandi.ApiKey,
9090
}
9191

92-
err := testDnsInfo.AddRecord()
92+
err := testDnsInfo.SaveRecord()
9393

9494
assert.NotNil(t, err)
9595
assert.Equal(t, http.StatusNotFound, err.Code)

pkg/porkbun/porkbun.go

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import (
66
"fmt"
77
"io"
88
"net/http"
9-
"time"
109

1110
"github.com/davidramiro/frigabun/internal/config"
1211
"github.com/davidramiro/frigabun/internal/logger"
1312
)
1413

15-
type PorkbunDnsInfo struct {
14+
type PorkbunDns struct {
1615
IP string
1716
Domain string
1817
Subdomain string
@@ -29,12 +28,19 @@ type PorkbunApiRequest struct {
2928
SecretApiKey string `json:"secretapikey"`
3029
}
3130

31+
type PorkbunQueryResponse struct {
32+
Status string `json:"status"`
33+
Records []struct {
34+
Name string `json:"name"`
35+
} `json:"records"`
36+
}
37+
3238
type PorkbunUpdateError struct {
3339
Code int
3440
Message string
3541
}
3642

37-
func (p *PorkbunDnsInfo) AddRecord() *PorkbunUpdateError {
43+
func (p *PorkbunDns) AddRecord() *PorkbunUpdateError {
3844

3945
porkbunRequest := &PorkbunApiRequest{
4046
Subdomain: p.Subdomain,
@@ -45,45 +51,83 @@ func (p *PorkbunDnsInfo) AddRecord() *PorkbunUpdateError {
4551
SecretApiKey: p.SecretApiKey,
4652
}
4753

48-
deleteErr := deleteOldRecord(p, porkbunRequest)
49-
if deleteErr != nil {
50-
logger.Log.Warn().Msg("deleting old porkbun request failed")
54+
queryErr := porkbunRequest.queryRecord(p)
55+
56+
if queryErr != nil && queryErr.Code == 409 {
57+
58+
logger.Log.Info().Msg("record exists, updating")
59+
updateErr := porkbunRequest.updateRecord(p)
60+
61+
if updateErr != nil {
62+
logger.Log.Error().Str("err", updateErr.Message).Msg("porkbun rejected updated record")
63+
return &PorkbunUpdateError{Code: 400, Message: updateErr.Message}
64+
}
65+
66+
} else {
67+
createErr := porkbunRequest.createRecord(p)
68+
if createErr != nil {
69+
logger.Log.Error().Str("err", createErr.Message).Msg("porkbun rejected new record")
70+
return &PorkbunUpdateError{Code: 400, Message: createErr.Message}
71+
}
5172
}
5273

53-
time.Sleep(2 * time.Second)
74+
return nil
75+
}
76+
77+
func (p *PorkbunApiRequest) queryRecord(dns *PorkbunDns) *PorkbunUpdateError {
78+
endpoint := fmt.Sprintf("%s/dns/retrieveByNameType/%s/A/%s", config.AppConfig.Porkbun.BaseUrl, dns.Domain, dns.Subdomain)
5479

55-
postErr := porkbunRequest.postNewRecord(p)
56-
if postErr != nil {
57-
logger.Log.Error().Str("err", postErr.Message).Msg("porkbun rejected new record")
58-
return &PorkbunUpdateError{Code: 400, Message: postErr.Message}
80+
logger.Log.Info().Str("subdomain", p.Subdomain).Str("endpoint", endpoint).Str("IP", p.IP).Msg("checking if record exists")
81+
82+
var r PorkbunQueryResponse
83+
84+
resp, updErr := executeRequest(endpoint, p)
85+
if updErr != nil {
86+
return updErr
5987
}
6088

61-
time.Sleep(2 * time.Second)
89+
b, _ := io.ReadAll(resp.Body)
90+
err := json.Unmarshal(b, &r)
91+
92+
if resp.StatusCode != http.StatusOK || r.Status != "SUCCESS" || err != nil {
93+
94+
logger.Log.Error().Msg("could not query record:" + string(b))
95+
return &PorkbunUpdateError{400, "could not query record: " + string(b)}
96+
}
97+
98+
if r.Status == "SUCCESS" && len(r.Records) > 0 {
99+
for _, e := range r.Records {
100+
if e.Name == dns.Subdomain+"."+dns.Domain {
101+
return &PorkbunUpdateError{409, "record already exists"}
102+
}
103+
}
104+
}
62105

63106
return nil
64107
}
65108

66-
func deleteOldRecord(dnsInfo *PorkbunDnsInfo, porkbunRequest *PorkbunApiRequest) *PorkbunUpdateError {
67-
endpoint := fmt.Sprintf("%s/dns/deleteByNameType/%s/A/%s", config.AppConfig.Porkbun.BaseUrl, dnsInfo.Domain, dnsInfo.Subdomain)
109+
func (p *PorkbunApiRequest) createRecord(dns *PorkbunDns) *PorkbunUpdateError {
110+
endpoint := fmt.Sprintf("%s/dns/create/%s", config.AppConfig.Porkbun.BaseUrl, dns.Domain)
68111

69-
logger.Log.Info().Str("subdomain", porkbunRequest.Subdomain).Str("endpoint", endpoint).Str("IP", porkbunRequest.IP).Msg("deleting old record")
112+
logger.Log.Info().Str("subdomain", p.Subdomain).Str("endpoint", endpoint).Str("IP", p.IP).Msg("creating new record")
70113

71-
resp, err := executeRequest(endpoint, porkbunRequest)
114+
resp, err := executeRequest(endpoint, p)
72115
if err != nil {
73116
return err
74117
}
75118

76-
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusBadRequest {
77-
return &PorkbunUpdateError{400, "could not delete old record"}
119+
if resp.StatusCode != http.StatusOK {
120+
b, _ := io.ReadAll(resp.Body)
121+
return &PorkbunUpdateError{400, "could not create record: " + string(b)}
78122
}
79123

80124
return nil
81125
}
82126

83-
func (p *PorkbunApiRequest) postNewRecord(dnsInfo *PorkbunDnsInfo) *PorkbunUpdateError {
84-
endpoint := fmt.Sprintf("%s/dns/create/%s", config.AppConfig.Porkbun.BaseUrl, dnsInfo.Domain)
127+
func (p *PorkbunApiRequest) updateRecord(dns *PorkbunDns) *PorkbunUpdateError {
128+
endpoint := fmt.Sprintf("%s/dns/editByNameType/%s/A/%s", config.AppConfig.Porkbun.BaseUrl, dns.Domain, dns.Subdomain)
85129

86-
logger.Log.Info().Str("subdomain", p.Subdomain).Str("endpoint", endpoint).Str("IP", p.IP).Msg("creating new record")
130+
logger.Log.Info().Str("subdomain", p.Subdomain).Str("endpoint", endpoint).Str("IP", p.IP).Msg("updating record")
87131

88132
resp, err := executeRequest(endpoint, p)
89133
if err != nil {
@@ -92,8 +136,7 @@ func (p *PorkbunApiRequest) postNewRecord(dnsInfo *PorkbunDnsInfo) *PorkbunUpdat
92136

93137
if resp.StatusCode != http.StatusOK {
94138
b, _ := io.ReadAll(resp.Body)
95-
logger.Log.Error().Msg("porkbun rejected request")
96-
return &PorkbunUpdateError{400, "could not create record: " + string(b)}
139+
return &PorkbunUpdateError{400, "could not update record: " + string(b)}
97140
}
98141

99142
return nil

0 commit comments

Comments
 (0)