This commit is contained in:
2025-12-14 23:03:10 +01:00
parent cc37d6bcb0
commit 4a8244466e
10 changed files with 74 additions and 107 deletions

View File

@@ -1,13 +1,11 @@
package com.infendro.ddns package com.infendro.ddns
import io.ktor.client.HttpClient import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.json import io.ktor.serialization.kotlinx.json.*
object Http { val http = HttpClient {
val client = HttpClient { install(ContentNegotiation) {
install(ContentNegotiation) { json()
json()
}
} }
} }

View File

@@ -13,7 +13,7 @@ import com.infendro.ddns.Argument.SUBDOMAIN
import com.infendro.ddns.argument.ipService import com.infendro.ddns.argument.ipService
import com.infendro.ddns.config.Config import com.infendro.ddns.config.Config
import com.infendro.ddns.dns.DnsService import com.infendro.ddns.dns.DnsService
import com.infendro.ddns.dns.DnsServices.PORKBUN import com.infendro.ddns.dns.DnsService.Companion.PORKBUN
import com.infendro.ddns.dns.model.DnsRecord import com.infendro.ddns.dns.model.DnsRecord
import com.infendro.ddns.dns.porkbun.Porkbun import com.infendro.ddns.dns.porkbun.Porkbun
import com.infendro.ddns.ip.IpService import com.infendro.ddns.ip.IpService

View File

@@ -3,13 +3,16 @@ package com.infendro.ddns.argument
import com.infendro.cli.argument.Argument import com.infendro.cli.argument.Argument
import com.infendro.cli.command.Command import com.infendro.cli.command.Command
import com.infendro.ddns.ip.IpService import com.infendro.ddns.ip.IpService
import com.infendro.ddns.ip.IpServices import com.infendro.ddns.ip.ipify.Ipify
object IpServiceParser : Argument.Parser<IpService> { object IpServiceParser : Argument.Parser<IpService> {
override fun parse( override fun parse(
text: String?, text: String?,
): IpService { ): IpService {
return IpServices.get(text!!) return when (text) {
IpService.IPIFY -> Ipify
else -> throw Exception()
}
} }
} }
@@ -17,7 +20,7 @@ class IpServiceOrDefaultArgument(
override val name: String, override val name: String,
) : Argument.SingleOrElse<IpService>() { ) : Argument.SingleOrElse<IpService>() {
override val other: IpService override val other: IpService
get() = IpServices.DEFAULT get() = IpService.DEFAULT
override val parser: Parser<IpService> override val parser: Parser<IpService>
get() = IpServiceParser get() = IpServiceParser

View File

@@ -2,13 +2,13 @@ package com.infendro.ddns.dns
import com.infendro.ddns.dns.model.DnsRecord import com.infendro.ddns.dns.model.DnsRecord
abstract class DnsService { interface DnsService {
abstract fun getRecords( fun getRecords(
domain: String, domain: String,
subdomains: List<String>, subdomains: List<String>,
): List<DnsRecord> ): List<DnsRecord>
abstract fun updateRecord( fun updateRecord(
record: DnsRecord, record: DnsRecord,
value: String, value: String,
) )
@@ -22,4 +22,8 @@ abstract class DnsService {
updateRecord(record, value) updateRecord(record, value)
} }
} }
companion object {
const val PORKBUN = "porkbun"
}
} }

View File

@@ -1,5 +0,0 @@
package com.infendro.ddns.dns
object DnsServices {
const val PORKBUN = "porkbun"
}

View File

@@ -1,5 +0,0 @@
package com.infendro.ddns.dns
object Regex {
val DOMAIN = Regex("""(?:(?<subdomain>(?:\w+\.)*\w+)\.)?(?<domain>\w+\.\w+)""")
}

View File

@@ -1,36 +1,52 @@
package com.infendro.ddns.dns.porkbun package com.infendro.ddns.dns.porkbun
import com.infendro.ddns.Http.client
import com.infendro.ddns.dns.Regex.DOMAIN
import com.infendro.ddns.dns.DnsService import com.infendro.ddns.dns.DnsService
import com.infendro.ddns.dns.model.DnsRecord import com.infendro.ddns.dns.model.DnsRecord
import com.infendro.ddns.dns.porkbun.dto.request.GetRecordsRequest import com.infendro.ddns.dns.porkbun.dto.request.GetRecordsRequest
import com.infendro.ddns.dns.porkbun.dto.request.UpdateRecordRequest import com.infendro.ddns.dns.porkbun.dto.request.UpdateRecordRequest
import com.infendro.ddns.dns.porkbun.dto.response.GetRecordsResponse import com.infendro.ddns.dns.porkbun.dto.response.GetRecordsResponse
import io.ktor.client.call.body import com.infendro.ddns.http
import io.ktor.client.request.post import io.ktor.client.call.*
import io.ktor.client.request.setBody import io.ktor.client.request.*
import io.ktor.http.ContentType import io.ktor.http.*
import io.ktor.http.contentType
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
class Porkbun( class Porkbun(
val key: String, val key: String,
val secretkey: String, val secretkey: String,
) : DnsService() { ) : DnsService {
override fun getRecords( override fun getRecords(
domain: String, domain: String,
subdomains: List<String>, subdomains: List<String>,
): List<DnsRecord> = runBlocking { ): List<DnsRecord> = runBlocking {
val response = client.post("https://api.porkbun.com/api/json/v3/dns/retrieve/$domain") { val response = http.post("https://api.porkbun.com/api/json/v3/dns/retrieve/$domain") {
val request = getRecordsRequest() val request = GetRecordsRequest(
apikey = key,
secretapikey = secretkey,
)
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(request) setBody(request)
} }
val body = response.body<GetRecordsResponse>() val body = response.body<GetRecordsResponse>()
body.records body.records
.map { model(it) } .map {
val labels = it.name.split('.')
val subdomain = labels.dropLast(2).joinToString(".")
val domain = labels.takeLast(2).joinToString(".")
DnsRecord(
id = it.id,
type = DnsRecord.Type.valueOf(it.type),
domain = DnsRecord.Domain(
subdomain = subdomain,
domain = domain,
),
value = it.content,
ttl = it.ttl.toInt(),
priority = it.prio?.toInt(),
)
}
.filter { it.type in DnsRecord.Type.address && (it.domain.subdomain ?: "") in subdomains } .filter { it.type in DnsRecord.Type.address && (it.domain.subdomain ?: "") in subdomains }
} }
@@ -38,51 +54,17 @@ class Porkbun(
record: DnsRecord, record: DnsRecord,
value: String, value: String,
): Unit = runBlocking { ): Unit = runBlocking {
client.post("https://api.porkbun.com/api/json/v3/dns/edit/${record.domain.domain}/${record.id}") { http.post("https://api.porkbun.com/api/json/v3/dns/edit/${record.domain.domain}/${record.id}") {
val request = updateRecordRequest(record, value) val request = UpdateRecordRequest(
apikey = key,
secretapikey = secretkey,
type = record.type.toString(),
name = record.domain.subdomain ?: "",
content = value,
ttl = record.ttl.toString(),
)
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(request) setBody(request)
} }
} }
private fun model(
record: GetRecordsResponse.Record,
): DnsRecord {
val match = DOMAIN.matchEntire(record.name)!!
val domain = match.groups["domain"]!!.value
val subdomain = match.groups["subdomain"]?.value
return DnsRecord(
id = record.id,
type = DnsRecord.Type.valueOf(record.type),
domain = DnsRecord.Domain(
subdomain = subdomain,
domain = domain,
),
value = record.content,
ttl = record.ttl.toInt(),
priority = record.prio?.toInt(),
)
}
private fun getRecordsRequest(): GetRecordsRequest {
return GetRecordsRequest(
apikey = key,
secretapikey = secretkey,
)
}
private fun updateRecordRequest(
record: DnsRecord,
ip: String,
): UpdateRecordRequest {
return UpdateRecordRequest(
apikey = key,
secretapikey = secretkey,
type = record.type.toString(),
name = record.domain.subdomain ?: "",
content = ip,
ttl = record.ttl.toString(),
)
}
} }

View File

@@ -1,9 +1,16 @@
package com.infendro.ddns.ip package com.infendro.ddns.ip
abstract class IpService { import com.infendro.ddns.ip.ipify.Ipify
abstract val ipv4: String
abstract val ipv6: String
val ip: List<String> interface IpService {
get() = listOf(ipv4, ipv6) val ipv4: String
val ipv6: String
val ip: Pair<String, String>
get() = Pair(ipv4, ipv6)
companion object {
const val IPIFY = "ipify"
val DEFAULT = Ipify
}
} }

View File

@@ -1,17 +0,0 @@
package com.infendro.ddns.ip
import com.infendro.ddns.ip.ipify.Ipify
object IpServices {
const val IPIFY = "ipify"
val DEFAULT = Ipify
fun get(
name: String,
): IpService {
return when (name) {
IPIFY -> Ipify
else -> throw Exception()
}
}
}

View File

@@ -1,23 +1,23 @@
package com.infendro.ddns.ip.ipify package com.infendro.ddns.ip.ipify
import com.infendro.ddns.Http import com.infendro.ddns.http
import com.infendro.ddns.ip.IpService import com.infendro.ddns.ip.IpService
import com.infendro.ddns.ip.ipify.dto.response.GetIpResponse import com.infendro.ddns.ip.ipify.dto.response.GetIpResponse
import io.ktor.client.call.body import io.ktor.client.call.*
import io.ktor.client.request.get import io.ktor.client.request.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
object Ipify : IpService() { object Ipify : IpService {
override val ipv4: String override val ipv4: String
get() = runBlocking { get() = runBlocking {
val response = Http.client.get("https://api.ipify.org?format=json") val response = http.get("https://api.ipify.org?format=json")
val body = response.body<GetIpResponse>() val body = response.body<GetIpResponse>()
body.ip body.ip
} }
override val ipv6: String override val ipv6: String
get() = runBlocking { get() = runBlocking {
val response = Http.client.get("https://api64.ipify.org?format=json") val response = http.get("https://api6.ipify.org?format=json")
val body = response.body<GetIpResponse>() val body = response.body<GetIpResponse>()
body.ip body.ip
} }