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