diff --git a/cloudflare-worker/index.js b/cloudflare-worker/index.js index a732696..fd02413 100644 --- a/cloudflare-worker/index.js +++ b/cloudflare-worker/index.js @@ -10,10 +10,15 @@ export default { fetch(request, env, ctx) { - return new Response(JSON.stringify( + return new Response( + JSON.stringify( + { + ip: request.headers.get('cf-connecting-ip'), + }, + ), { - ip: request.headers.get('cf-connecting-ip'), - }, - )) + headers: {'content-type': 'application/json'}, + } + ) } } diff --git a/src/main/kotlin/io/heapy/ddns/Client.kt b/src/main/kotlin/io/heapy/ddns/Client.kt index 9bbf34e..eebcfd3 100644 --- a/src/main/kotlin/io/heapy/ddns/Client.kt +++ b/src/main/kotlin/io/heapy/ddns/Client.kt @@ -3,16 +3,15 @@ package io.heapy.ddns import io.heapy.ddns.dns_clients.CloudflareDnsClient import io.heapy.ddns.dns_clients.DigitalOceanDnsClient import io.heapy.ddns.dns_clients.DnsClient +import io.heapy.ddns.ip_provider.IpProvider +import io.heapy.ddns.ip_provider.ServerIpProvider import io.heapy.ddns.notifiers.Notifier import io.heapy.ddns.notifiers.TelegramNotifier import io.ktor.client.HttpClient -import io.ktor.client.call.body import io.ktor.client.engine.cio.CIO import io.ktor.serialization.kotlinx.json.json import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.request.post import kotlinx.coroutines.delay -import kotlinx.serialization.Serializable import org.slf4j.LoggerFactory import java.time.ZonedDateTime import kotlin.concurrent.thread @@ -75,6 +74,7 @@ open class ClientFactory( ?: error("DIGITALOCEAN_DOMAIN_NAME is not set"), subdomain = config["DIGITALOCEAN_SUBDOMAIN"] ?: error("DIGITALOCEAN_SUBDOMAIN is not set"), + ttl = configuration.checkPeriod.inWholeSeconds, ) } @@ -93,6 +93,7 @@ open class ClientFactory( ?: error("CLOUDFLARE_TOKEN is not set"), domainName = config["CLOUDFLARE_DOMAIN_NAME"] ?: error("CLOUDFLARE_DOMAIN_NAME is not set"), + ttl = configuration.checkPeriod.inWholeSeconds, ) } @@ -178,9 +179,11 @@ class SimpleUpdater( IP = newIP log.info("IP changed to $IP") dnsClients.forEach { - it.createOrUpdateRecord(IP) + val oldIp = it.createOrUpdateRecord(IP) + if (oldIp != IP) { + notifier?.notify(IP) + } } - notifier?.notify(IP) } } @@ -188,23 +191,3 @@ class SimpleUpdater( private val log = LoggerFactory.getLogger(SimpleUpdater::class.java) } } - -interface IpProvider { - suspend fun getIp(): String -} - -class ServerIpProvider( - private val httpClient: HttpClient, - private val serverUrl: String, -) : IpProvider { - @Serializable - data class Response( - val ip: String, - ) - - override suspend fun getIp(): String { - return httpClient.post(serverUrl) - .body() - .ip - } -} diff --git a/src/main/kotlin/io/heapy/ddns/dns_clients/CloudflareDnsClient.kt b/src/main/kotlin/io/heapy/ddns/dns_clients/CloudflareDnsClient.kt index dd6ddac..18d20f1 100644 --- a/src/main/kotlin/io/heapy/ddns/dns_clients/CloudflareDnsClient.kt +++ b/src/main/kotlin/io/heapy/ddns/dns_clients/CloudflareDnsClient.kt @@ -13,21 +13,29 @@ class CloudflareDnsClient( private val httpClient: HttpClient, private val configuration: Configuration, ) : DnsClient { - override suspend fun createOrUpdateRecord(ip: String) { + override suspend fun createOrUpdateRecord( + ip: String, + ): String? { val record = getRecord( name = configuration.domainName, zoneId = configuration.zoneId, token = configuration.token, ) - if (record != null) { - log.info("Updating record with id=${record.id}") - updateRecord( - id = record.id, - ip = ip, - name = configuration.domainName, - zoneId = configuration.zoneId, - token = configuration.token, - ) + return if (record != null) { + if (record.content == ip) { + log.info("Record already exists and with up to date ip: $ip") + } else { + log.info("Updating record with id=${record.id}") + updateRecord( + id = record.id, + ip = ip, + name = configuration.domainName, + zoneId = configuration.zoneId, + token = configuration.token, + ttl = configuration.ttl, + ) + } + record.content } else { log.info("Creating record with ip=$ip") createRecord( @@ -35,7 +43,9 @@ class CloudflareDnsClient( name = configuration.domainName, zoneId = configuration.zoneId, token = configuration.token, + ttl = configuration.ttl, ) + null } } @@ -44,7 +54,7 @@ class CloudflareDnsClient( name: String, zoneId: String, token: String, - ttl: Long = 300, + ttl: Long, proxied: Boolean = false, recordType: String = "A", ) { @@ -74,7 +84,7 @@ class CloudflareDnsClient( name: String, zoneId: String, token: String, - ttl: Long = 300, + ttl: Long, proxied: Boolean = false, recordType: String = "A", ) { @@ -122,6 +132,7 @@ class CloudflareDnsClient( val zoneId: String, val token: String, val domainName: String, + val ttl: Long, ) @Serializable diff --git a/src/main/kotlin/io/heapy/ddns/dns_clients/DigitalOceanDnsClient.kt b/src/main/kotlin/io/heapy/ddns/dns_clients/DigitalOceanDnsClient.kt index 43d1ac3..0d54111 100644 --- a/src/main/kotlin/io/heapy/ddns/dns_clients/DigitalOceanDnsClient.kt +++ b/src/main/kotlin/io/heapy/ddns/dns_clients/DigitalOceanDnsClient.kt @@ -12,17 +12,23 @@ class DigitalOceanDnsClient( ) : DnsClient { override suspend fun createOrUpdateRecord( ip: String, - ) { + ): String? { val records = getDomainRecords() val record = records.domain_records .find { it.name == configuration.subdomain } - if (record == null) { + return if (record == null) { log.info("Creating new record") createDomainRecord(ip) + null } else { - log.info("Record already exists, updating $record") - updateDomainRecord(record.id, ip) + if (record.data == ip) { + log.info("Record already exists and with up to date ip: $ip") + } else { + log.info("Record already exists, updating $record") + updateDomainRecord(record.id, ip) + } + record.data } } @@ -108,6 +114,7 @@ class DigitalOceanDnsClient( val token: String, val domainName: String, val subdomain: String, + val ttl: Long, ) private val doUrl: String diff --git a/src/main/kotlin/io/heapy/ddns/dns_clients/DnsClient.kt b/src/main/kotlin/io/heapy/ddns/dns_clients/DnsClient.kt index 683ff47..ebc9b32 100644 --- a/src/main/kotlin/io/heapy/ddns/dns_clients/DnsClient.kt +++ b/src/main/kotlin/io/heapy/ddns/dns_clients/DnsClient.kt @@ -1,5 +1,5 @@ package io.heapy.ddns.dns_clients interface DnsClient { - suspend fun createOrUpdateRecord(ip: String) + suspend fun createOrUpdateRecord(ip: String): String? } diff --git a/src/main/kotlin/io/heapy/ddns/ip_provider/IpProvider.kt b/src/main/kotlin/io/heapy/ddns/ip_provider/IpProvider.kt new file mode 100644 index 0000000..76e170b --- /dev/null +++ b/src/main/kotlin/io/heapy/ddns/ip_provider/IpProvider.kt @@ -0,0 +1,5 @@ +package io.heapy.ddns.ip_provider + +interface IpProvider { + suspend fun getIp(): String +} diff --git a/src/main/kotlin/io/heapy/ddns/ip_provider/ServerIpProvider.kt b/src/main/kotlin/io/heapy/ddns/ip_provider/ServerIpProvider.kt new file mode 100644 index 0000000..811b6de --- /dev/null +++ b/src/main/kotlin/io/heapy/ddns/ip_provider/ServerIpProvider.kt @@ -0,0 +1,22 @@ +package io.heapy.ddns.ip_provider + +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.request.* +import kotlinx.serialization.Serializable + +class ServerIpProvider( + private val httpClient: HttpClient, + private val serverUrl: String, +) : IpProvider { + @Serializable + data class Response( + val ip: String, + ) + + override suspend fun getIp(): String { + return httpClient.post(serverUrl) + .body() + .ip + } +}