feat(app): add codebase
This commit is contained in:
parent
833241b870
commit
2c9df62b2a
89
adapter/directadmin/client.go
Normal file
89
adapter/directadmin/client.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package directadmin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type daClient struct {
|
||||||
|
encoder *schema.Encoder
|
||||||
|
decoder *schema.Decoder
|
||||||
|
|
||||||
|
addr string
|
||||||
|
username string
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates new DirectAdmin client.
|
||||||
|
func New(addr, username, token string) DirectAdminClient {
|
||||||
|
encoder := schema.NewEncoder()
|
||||||
|
encoder.RegisterEncoder(net.IP{}, func(v reflect.Value) string {
|
||||||
|
ip := net.IP(v.Bytes())
|
||||||
|
return ip.String()
|
||||||
|
})
|
||||||
|
|
||||||
|
decoder := schema.NewDecoder()
|
||||||
|
decoder.IgnoreUnknownKeys(true)
|
||||||
|
|
||||||
|
return &daClient{
|
||||||
|
encoder: encoder,
|
||||||
|
decoder: decoder,
|
||||||
|
addr: addr,
|
||||||
|
username: username,
|
||||||
|
token: token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSControl implements DirectAdminClient.
|
||||||
|
func (c *daClient) DNSControl(params *DNSControlParams) (*DNSControlResponse, error) {
|
||||||
|
var (
|
||||||
|
formReq = url.Values{}
|
||||||
|
respObj = &DNSControlResponse{}
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := c.encoder.Encode(params, formReq); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(
|
||||||
|
http.MethodPost,
|
||||||
|
c.addr+"/CMD_API_DNS_CONTROL",
|
||||||
|
strings.NewReader(formReq.Encode()),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.SetBasicAuth(c.username, c.token)
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
respData, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
formResp, err := resolveParams(string(respData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.decoder.Decode(respObj, formResp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return respObj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ DirectAdminClient = &daClient{}
|
||||||
|
)
|
92
adapter/directadmin/dns_control.go
Normal file
92
adapter/directadmin/dns_control.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package directadmin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSContolActionType represents DNS control action type.
|
||||||
|
type DNSControlActionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DNSControlActionAdd represents "add" action for DNS control.
|
||||||
|
DNSControlActionAdd DNSControlActionType = "add"
|
||||||
|
// DNSControlActionEdit represents "edit" action for DNS control.
|
||||||
|
DNSControlActionEdit DNSControlActionType = "edit"
|
||||||
|
// DNSControlActionDelete represents "delete" action for DNS control.
|
||||||
|
DNSControlActionDelete DNSControlActionType = "delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSControlRecordType represents DNS control record type.
|
||||||
|
type DNSControlRecordType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DNSControlRecordTypeA represents DNS A record type for DNS control.
|
||||||
|
DNSControlRecordTypeA DNSControlRecordType = "A"
|
||||||
|
// DNSControlRecordTypeNS represents DNS NS record type for DNS control.
|
||||||
|
DNSControlRecordTypeNS DNSControlRecordType = "NS"
|
||||||
|
// DNSControlRecordTypeMX represents DNS MX record type for DNS control.
|
||||||
|
DNSControlRecordTypeMX DNSControlRecordType = "MX"
|
||||||
|
// DNSControlRecordTypeCNAME represents DNS CNAME record type for DNS control.
|
||||||
|
DNSControlRecordTypeCNAME DNSControlRecordType = "CNAME"
|
||||||
|
// DNSControlRecordTypePTR represents DNS PTR record type for DNS control.
|
||||||
|
DNSControlRecordTypePTR DNSControlRecordType = "PTR"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSControlParams represents DNS control request params.
|
||||||
|
type DNSControlParams struct {
|
||||||
|
// Domain represents domain name from DirectAdmin panel.
|
||||||
|
Domain string `schema:"domain"`
|
||||||
|
// Action represents DNS control action type.
|
||||||
|
Action DNSControlActionType `schema:"action"`
|
||||||
|
// Type represents DNS record type.
|
||||||
|
Type DNSControlRecordType `schema:"type"`
|
||||||
|
// Name represents DNS record name.
|
||||||
|
Name string `schema:"name"`
|
||||||
|
// Value represents DNS record value.
|
||||||
|
Value string `schema:"value"`
|
||||||
|
// TTL represents DNS record time-to-live value.
|
||||||
|
TTL *uint16 `schema:"ttl,omitempty"`
|
||||||
|
// ARECS0 represents aresc0 DNS record query (required for DNS record editing and deleting).
|
||||||
|
ARECS0 string `schema:"arecs0,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (p *DNSControlParams) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.
|
||||||
|
Str("domain", p.Domain).
|
||||||
|
Str("action", string(p.Action)).
|
||||||
|
Str("type", string(p.Type)).
|
||||||
|
Str("name", p.Name).
|
||||||
|
Str("value", p.Value)
|
||||||
|
|
||||||
|
if p.TTL != nil {
|
||||||
|
e.Uint16("ttl", *p.TTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.ARECS0 != "" {
|
||||||
|
e.Str("arecs0", p.ARECS0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSControlResponse represents DNS control request response.
|
||||||
|
type DNSControlResponse struct {
|
||||||
|
// Error determines if error occurred during request processing.
|
||||||
|
Error bool `schema:"error"`
|
||||||
|
// Text represents request execution summary.
|
||||||
|
Text string `schema:"text"`
|
||||||
|
// Details represents additional information to request execution status.
|
||||||
|
Details string `schema:"details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (r *DNSControlResponse) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.
|
||||||
|
Bool("error", r.Error).
|
||||||
|
Str("text", r.Text).
|
||||||
|
Str("details", r.Details)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ zerolog.LogObjectMarshaler = &DNSControlParams{}
|
||||||
|
_ zerolog.LogObjectMarshaler = &DNSControlResponse{}
|
||||||
|
)
|
7
adapter/directadmin/interface.go
Normal file
7
adapter/directadmin/interface.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package directadmin
|
||||||
|
|
||||||
|
// DirectAdminClient represents DirectAdmin API client.
|
||||||
|
type DirectAdminClient interface {
|
||||||
|
// DNSControl represents DNS control request.
|
||||||
|
DNSControl(params *DNSControlParams) (*DNSControlResponse, error)
|
||||||
|
}
|
26
adapter/directadmin/utils.go
Normal file
26
adapter/directadmin/utils.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package directadmin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resolveParams(src string) (map[string][]string, error) {
|
||||||
|
dst := map[string][]string{}
|
||||||
|
|
||||||
|
for _, pair := range strings.Split(src, "&") {
|
||||||
|
key, val, ok := strings.Cut(pair, "=")
|
||||||
|
if ok {
|
||||||
|
if _, ok := dst[key]; !ok {
|
||||||
|
dst[key] = []string{}
|
||||||
|
}
|
||||||
|
unesc, err := url.QueryUnescape(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dst[key] = append(dst[key], unesc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst, nil
|
||||||
|
}
|
9
adapter/dnsresolver/interface.go
Normal file
9
adapter/dnsresolver/interface.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package dnsresolver
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
// DomainNameResolver represents domain name resolver.
|
||||||
|
type DomainNameResolver interface {
|
||||||
|
// ResolveA resolves domain's A records agains given nameserver.
|
||||||
|
ResolveA(domain, nameserver string) ([]net.IP, error)
|
||||||
|
}
|
45
adapter/dnsresolver/resolver.go
Normal file
45
adapter/dnsresolver/resolver.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package dnsresolver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dnsResolver struct {
|
||||||
|
resolver dns.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates new DomainNameResolver.
|
||||||
|
func New() DomainNameResolver {
|
||||||
|
return &dnsResolver{
|
||||||
|
resolver: dns.Client{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveA implements DomainNameResolver.
|
||||||
|
func (r *dnsResolver) ResolveA(domain, nameserver string) ([]net.IP, error) {
|
||||||
|
msg := &dns.Msg{}
|
||||||
|
msg.SetQuestion(domain+".", dns.TypeA)
|
||||||
|
|
||||||
|
answer, _, err := r.resolver.Exchange(msg, nameserver+":53")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(answer.Answer) == 0 {
|
||||||
|
return nil, errors.New("empty answer")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]net.IP, len(answer.Answer))
|
||||||
|
for idx, rr := range answer.Answer {
|
||||||
|
record := rr.(*dns.A)
|
||||||
|
result[idx] = record.A
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ DomainNameResolver = &dnsResolver{}
|
||||||
|
)
|
122
app/service.go
Normal file
122
app/service.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/adapter/directadmin"
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/adapter/dnsresolver"
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/config"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
da directadmin.DirectAdminClient
|
||||||
|
resolv dnsresolver.DomainNameResolver
|
||||||
|
|
||||||
|
srcDomain string
|
||||||
|
srcNameserver string
|
||||||
|
|
||||||
|
dstDomain string
|
||||||
|
dstNameserver string
|
||||||
|
|
||||||
|
entryDomain string
|
||||||
|
entryName string
|
||||||
|
entryTTL uint16
|
||||||
|
|
||||||
|
resyncTime time.Duration
|
||||||
|
|
||||||
|
log zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(
|
||||||
|
directadminClient directadmin.DirectAdminClient,
|
||||||
|
dnsResolver dnsresolver.DomainNameResolver,
|
||||||
|
domainConfig config.DomainConfig,
|
||||||
|
resyncTime time.Duration,
|
||||||
|
log zerolog.Logger,
|
||||||
|
) *Service {
|
||||||
|
return &Service{
|
||||||
|
da: directadminClient,
|
||||||
|
resolv: dnsResolver,
|
||||||
|
srcDomain: domainConfig.Source.Name,
|
||||||
|
srcNameserver: domainConfig.Source.Nameserver,
|
||||||
|
dstDomain: domainConfig.Destination.Name,
|
||||||
|
dstNameserver: domainConfig.Destination.Nameserver,
|
||||||
|
entryName: domainConfig.Entry.Name,
|
||||||
|
entryDomain: domainConfig.Entry.Domain,
|
||||||
|
entryTTL: domainConfig.Entry.TTL,
|
||||||
|
resyncTime: resyncTime,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) update() {
|
||||||
|
srcIPs, err := s.resolv.ResolveA(s.srcDomain, s.srcNameserver)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Warn().Err(err).Msg("unable to get source IP address")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcIP := srcIPs[0]
|
||||||
|
s.log.Info().IPAddr("ip", srcIP).Msg("got source IP address")
|
||||||
|
|
||||||
|
dstIPs, err := s.resolv.ResolveA(s.dstDomain, s.dstNameserver)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Warn().Err(err).Msg("unable to get destination IP address")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dstIP := dstIPs[0]
|
||||||
|
s.log.Info().IPAddr("ip", dstIP).Msg("got destination IP address")
|
||||||
|
|
||||||
|
if srcIP.Equal(dstIP) {
|
||||||
|
s.log.Info().Msg("addresses match, no update needed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.log.Info().Msg("addresses mismatch, updating")
|
||||||
|
|
||||||
|
params := &directadmin.DNSControlParams{
|
||||||
|
Domain: s.entryDomain,
|
||||||
|
Action: directadmin.DNSControlActionEdit,
|
||||||
|
Type: directadmin.DNSControlRecordTypeA,
|
||||||
|
Name: s.entryName,
|
||||||
|
Value: srcIP.String(),
|
||||||
|
TTL: &s.entryTTL,
|
||||||
|
ARECS0: fmt.Sprintf(
|
||||||
|
"name=%s&value=%s",
|
||||||
|
s.entryName,
|
||||||
|
dstIP.String(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
log := s.log.With().Object("params", params).Logger()
|
||||||
|
|
||||||
|
resp, err := s.da.DNSControl(params)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("unable to update domain params")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log.With().Object("response", resp).Logger()
|
||||||
|
if resp.Error {
|
||||||
|
log.Error().Msg("unable to update domain params")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info().Msg("successfully updated domain params")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Run(ctx context.Context) error {
|
||||||
|
ticker := time.NewTicker(s.resyncTime)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
s.update()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
case <-ticker.C:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
config/config.go
Normal file
25
config/config.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents application's configuration.
|
||||||
|
type Config struct {
|
||||||
|
DirectAdmin DirectAdminConfig `envPrefix:"DA_"`
|
||||||
|
Domain DomainConfig `envPrefix:"DOMAIN_"`
|
||||||
|
ResyncTime time.Duration `env:"RESYNC_TIME" envDefault:"15m"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (c *Config) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.
|
||||||
|
Object("directAdmin", &c.DirectAdmin).
|
||||||
|
Object("domain", &c.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ zerolog.LogObjectMarshaler = &Config{}
|
||||||
|
)
|
27
config/directadmin.go
Normal file
27
config/directadmin.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/rs/zerolog"
|
||||||
|
|
||||||
|
// DirectAdminConfig represents DirectAdmin configuration.
|
||||||
|
type DirectAdminConfig struct {
|
||||||
|
URL string `env:"URL" envDefault:"https://s149.cyber-folks.pl:2223"`
|
||||||
|
User string `env:"USER,notEmpty"`
|
||||||
|
Token string `env:"TOKEN,notEmpty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (c *DirectAdminConfig) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
token := "[REDACTED]"
|
||||||
|
if len(c.Token) == 0 {
|
||||||
|
token = "[EMPTY]"
|
||||||
|
}
|
||||||
|
|
||||||
|
e.
|
||||||
|
Str("url", c.URL).
|
||||||
|
Str("user", c.User).
|
||||||
|
Str("token", token)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ zerolog.LogObjectMarshaler = &DirectAdminConfig{}
|
||||||
|
)
|
52
config/domain.go
Normal file
52
config/domain.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/rs/zerolog"
|
||||||
|
|
||||||
|
// DomainDetails represents domain details.
|
||||||
|
type DomainDetails struct {
|
||||||
|
Name string `env:"NAME,notEmpty"`
|
||||||
|
Nameserver string `env:"NAMESERVER,notEmpty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (d DomainDetails) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.
|
||||||
|
Str("name", d.Name).
|
||||||
|
Str("nameserver", d.Nameserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EntryDetails represents DNS entry details.
|
||||||
|
type EntryDetails struct {
|
||||||
|
Domain string `env:"DN,notEmpty"`
|
||||||
|
Name string `env:"NAME,notEmpty"`
|
||||||
|
TTL uint16 `env:"TTL" envDefault:"60"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (d *EntryDetails) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.
|
||||||
|
Str("domain", d.Domain).
|
||||||
|
Str("name", d.Name).
|
||||||
|
Uint16("ttl", d.TTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainConfig represents domain configuration.
|
||||||
|
type DomainConfig struct {
|
||||||
|
Source DomainDetails `envPrefix:"SRC_"`
|
||||||
|
Destination DomainDetails `envPrefix:"DST_"`
|
||||||
|
Entry EntryDetails `envPrefix:"ENTRY_"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalZerologObject implements zerolog.LogObjectMarshaler.
|
||||||
|
func (c *DomainConfig) MarshalZerologObject(e *zerolog.Event) {
|
||||||
|
e.
|
||||||
|
Object("source", &c.Source).
|
||||||
|
Object("destination", &c.Destination).
|
||||||
|
Object("entry", &c.Entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ zerolog.LogObjectMarshaler = &DomainDetails{}
|
||||||
|
_ zerolog.LogObjectMarshaler = &EntryDetails{}
|
||||||
|
_ zerolog.LogObjectMarshaler = &DomainConfig{}
|
||||||
|
)
|
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module git.ext.icikowski.pl/icikowski/ip-ddns
|
||||||
|
|
||||||
|
go 1.21.4
|
||||||
|
|
||||||
|
require github.com/miekg/dns v1.1.56
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/caarlos0/env/v10 v10.0.0
|
||||||
|
github.com/gorilla/schema v1.2.1
|
||||||
|
github.com/rs/zerolog v1.31.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
|
golang.org/x/net v0.15.0 // indirect
|
||||||
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
|
)
|
31
go.sum
Normal file
31
go.sum
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
|
||||||
|
github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM=
|
||||||
|
github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||||
|
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||||
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||||
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
75
main.go
Normal file
75
main.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/adapter/directadmin"
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/adapter/dnsresolver"
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/app"
|
||||||
|
"git.ext.icikowski.pl/icikowski/ip-ddns/config"
|
||||||
|
"github.com/caarlos0/env/v10"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
conf *config.Config = &config.Config{}
|
||||||
|
mainLog zerolog.Logger = zerolog.
|
||||||
|
New(zerolog.ConsoleWriter{
|
||||||
|
Out: os.Stdout,
|
||||||
|
NoColor: false,
|
||||||
|
TimeFormat: "2006-01-02 15:04:05",
|
||||||
|
}).
|
||||||
|
Level(zerolog.DebugLevel).
|
||||||
|
With().
|
||||||
|
Timestamp().
|
||||||
|
Logger()
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log := mainLog.With().Str("unit", "init").Logger()
|
||||||
|
|
||||||
|
if err := env.ParseWithOptions(conf, env.Options{
|
||||||
|
Prefix: "SYNCER_",
|
||||||
|
}); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("unable to parse config")
|
||||||
|
}
|
||||||
|
log.Info().Object("conf", conf).Msg("loaded configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := mainLog.With().Str("unit", "cmd").Logger()
|
||||||
|
|
||||||
|
ctx, cancel := signal.NotifyContext(
|
||||||
|
context.Background(),
|
||||||
|
os.Interrupt,
|
||||||
|
syscall.SIGKILL,
|
||||||
|
)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resolv := dnsresolver.New()
|
||||||
|
daClient := directadmin.New(
|
||||||
|
conf.DirectAdmin.URL,
|
||||||
|
conf.DirectAdmin.User,
|
||||||
|
conf.DirectAdmin.Token,
|
||||||
|
)
|
||||||
|
|
||||||
|
svc := app.NewService(
|
||||||
|
daClient,
|
||||||
|
resolv,
|
||||||
|
conf.Domain,
|
||||||
|
conf.ResyncTime,
|
||||||
|
mainLog.With().Str("unit", "service").Logger(),
|
||||||
|
)
|
||||||
|
done := make(chan error)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Info().Msg("starting service")
|
||||||
|
done <- svc.Run(ctx)
|
||||||
|
log.Info().Msg("service stopped")
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-done
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user