diff --git a/main.go b/main.go index 1df93ba..2f1837e 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,8 @@ import ( ionos "realdnydns/pkg/dnsProvider/ionos" "realdnydns/pkg/externalIpProvider" plainExternalIpProvider "realdnydns/pkg/externalIpProvider/plain" + "realdnydns/pkg/notificationProvider" + notificationProviderConsole "realdnydns/pkg/notificationProvider/console" "time" "github.com/go-co-op/gocron" @@ -50,7 +52,14 @@ func main() { panic(fmt.Errorf("unknown DNS provider: %s", configClient.DNSProvider.Type)) } - changeDetector := changeDetector.New(externalIpProvider, dnsProvider, configClient.Domains) + var notificationProvider notificationProvider.NotificationProvider + switch configClient.NotificationProvider.Type { + default: + // Use default console notification provider + notificationProvider = notificationProviderConsole.New() + } + + changeDetector := changeDetector.New(externalIpProvider, dnsProvider, notificationProvider, configClient.Domains) s := gocron.NewScheduler(time.UTC) s.SingletonMode() diff --git a/pkg/changeDetector/changeDetector.go b/pkg/changeDetector/changeDetector.go index 3cc9fab..10d0c8e 100644 --- a/pkg/changeDetector/changeDetector.go +++ b/pkg/changeDetector/changeDetector.go @@ -1,26 +1,31 @@ package changeDetector import ( + "fmt" "realdnydns/pkg/config" "realdnydns/pkg/dnsProvider" "realdnydns/pkg/externalIpProvider" + "realdnydns/pkg/notificationProvider" ) type ChangeDetector struct { - externalIpProvider externalIpProvider.ExternalIpProvider - dnsProvider dnsProvider.DNSProvider - domains []config.DomainConfig + externalIpProvider externalIpProvider.ExternalIpProvider + dnsProvider dnsProvider.DNSProvider + notificationProvider notificationProvider.NotificationProvider + domains []config.DomainConfig } func New( externalIpProvider externalIpProvider.ExternalIpProvider, dnsProvider dnsProvider.DNSProvider, + notificationProvider notificationProvider.NotificationProvider, domains []config.DomainConfig, ) ChangeDetector { return ChangeDetector{ - externalIpProvider: externalIpProvider, - dnsProvider: dnsProvider, - domains: domains, + externalIpProvider: externalIpProvider, + dnsProvider: dnsProvider, + notificationProvider: notificationProvider, + domains: domains, } } @@ -40,6 +45,14 @@ func (c *ChangeDetector) DetectAndApplyChanges() (int, error) { } if currentRecord.IP != externalIp.String() { + err = c.notificationProvider.SendNotification( + fmt.Sprintf("Update %s.%s", subdomain, domain.TLD), + fmt.Sprintf("The IP of %s has changed from %s to %s", domain.TLD, currentRecord.IP, externalIp.String()), + ) + if err != nil { + return numberUpdated, err + } + _, err = c.dnsProvider.UpdateRecord(domain.TLD, subdomain, externalIp, currentRecord.TTL, currentRecord.Prio, currentRecord.Disabled) numberUpdated++ if err != nil { diff --git a/pkg/changeDetector/changeDetector_test.go b/pkg/changeDetector/changeDetector_test.go index ffe0123..87e6988 100644 --- a/pkg/changeDetector/changeDetector_test.go +++ b/pkg/changeDetector/changeDetector_test.go @@ -43,6 +43,14 @@ func (m *MockDNSProviderImpl) UpdateRecord(tld string, subdomain string, ip net. }, nil } +type MockedNotificationProvider struct{} + +type MockedNotificationProviderImpl struct{} + +func (m *MockedNotificationProviderImpl) SendNotification(title string, message string) error { + return nil +} + func TestDetectAndApplyChanges(t *testing.T) { t.Run("with changes", testDetectAndApplyChangesWithChanges()) t.Run("without changes", testDetectAndApplyChangesWithoutChanges()) @@ -55,15 +63,16 @@ func testDetectAndApplyChangesWithChanges() func(t *testing.T) { }, &MockDNSProviderImpl{ GetRecordIpResponse: "127.0.0.2", UpdateRecordIpResponse: "127.0.0.1", - }, []config.DomainConfig{ - { - TLD: "example.com", - Subdomains: []string{ - "test", - "@", + }, &MockedNotificationProviderImpl{}, + []config.DomainConfig{ + { + TLD: "example.com", + Subdomains: []string{ + "test", + "@", + }, }, - }, - }) + }) numberUpdated, err := changeDetector.DetectAndApplyChanges() if err != nil { @@ -83,15 +92,16 @@ func testDetectAndApplyChangesWithoutChanges() func(t *testing.T) { }, &MockDNSProviderImpl{ GetRecordIpResponse: "127.0.0.1", UpdateRecordIpResponse: "127.0.0.1", - }, []config.DomainConfig{ - { - TLD: "example.com", - Subdomains: []string{ - "test", - "@", + }, &MockedNotificationProviderImpl{}, + []config.DomainConfig{ + { + TLD: "example.com", + Subdomains: []string{ + "test", + "@", + }, }, - }, - }) + }) numberUpdated, err := changeDetector.DetectAndApplyChanges() if err != nil { diff --git a/pkg/config/config.go b/pkg/config/config.go index c0f30a5..28b9488 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -12,10 +12,11 @@ type DomainConfig struct { } type Config struct { - ExternalIPProvider ExternalIpProviderConfig `yaml:"ip_provider"` - DNSProvider DNSProviderConfig `yaml:"dns_provider"` - Domains []DomainConfig `yaml:"domains"` - CheckInterval string `yaml:"check_interval"` + ExternalIPProvider ExternalIpProviderConfig `yaml:"ip_provider"` + DNSProvider DNSProviderConfig `yaml:"dns_provider"` + NotificationProvider NotificationProviderConfig `yaml:"notification_provider,omitempty"` + Domains []DomainConfig `yaml:"domains"` + CheckInterval string `yaml:"check_interval"` } type ExternalIpProviderConfig struct { @@ -28,6 +29,11 @@ type DNSProviderConfig struct { ProviderConfig yaml.Node `yaml:"config"` } +type NotificationProviderConfig struct { + Type string `yaml:"type"` + ProviderConfig yaml.Node `yaml:"config"` +} + func (c *Config) Load(filePath string) error { err := yaml.Unmarshal([]byte(filePath), c) if err != nil { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index dfd3e71..4e60fbd 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -49,6 +49,10 @@ dns_provider: config: api_key: exampleAPIKey base_url: https://example.com +notification_provider: + type: gotify + config: + url: https://example.com domains: - tld: example.com subdomains: @@ -56,7 +60,7 @@ domains: - www check_interval: 0 0 0/6 * * * *`) - want := c.DNSProvider.Type == "ionos" && c.ExternalIPProvider.Type == "plain" + want := c.DNSProvider.Type == "ionos" && c.ExternalIPProvider.Type == "plain" && c.NotificationProvider.Type == "gotify" if !want || err != nil { t.Fatalf("DnsProviderName couldn't be properly loaded or unmarshaled, Load() = %v, want %v", err, want) diff --git a/pkg/notificationProvider/console/console.go b/pkg/notificationProvider/console/console.go new file mode 100644 index 0000000..bfac9c3 --- /dev/null +++ b/pkg/notificationProvider/console/console.go @@ -0,0 +1,16 @@ +package notificationProviderConsole + +import ( + "fmt" +) + +type NotificationProviderImplConsole struct{} + +func New() *NotificationProviderImplConsole { + return &NotificationProviderImplConsole{} +} + +func (p *NotificationProviderImplConsole) SendNotification(title string, message string) error { + fmt.Printf("%s: %s\n", title, message) + return nil +} diff --git a/pkg/notificationProvider/notificationProvider.go b/pkg/notificationProvider/notificationProvider.go new file mode 100644 index 0000000..3956566 --- /dev/null +++ b/pkg/notificationProvider/notificationProvider.go @@ -0,0 +1,5 @@ +package notificationProvider + +type NotificationProvider interface { + SendNotification(title string, message string) error +}