fix pdns api clients (retry 5x)

This commit is contained in:
dalbodeule 2024-06-07 00:38:42 +09:00
parent 4a72cd0453
commit a41d8ae23c
No known key found for this signature in database
GPG Key ID: EFA860D069C9FA65
4 changed files with 85 additions and 76 deletions

View File

@ -42,6 +42,9 @@ dependencies {
implementation("com.google.code.gson:gson:2.11.0")
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
implementation("com.squareup.okhttp3:okhttp:4.12.0")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
testImplementation("org.springframework.boot:spring-boot-starter-test")

View File

@ -1,14 +1,15 @@
package space.mori.dnsapi
import com.google.gson.Gson
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.*
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
import space.mori.dnsapi.dto.RecordRequestDTO
@Service
class PowerDNSApiClient {
class PowerDNSAPIClient() {
@Value("\${pdns.api.url}")
private lateinit var apiUrl: String
@ -18,68 +19,73 @@ class PowerDNSApiClient {
@Value("\${pdns.ns}")
private lateinit var nameserver: String
private val restTemplate = RestTemplate()
private val gson = Gson()
private val client = OkHttpClient()
private fun createHeaders(): HttpHeaders {
val headers = HttpHeaders()
headers.set("X-API-Key", apiKey)
headers.contentType = MediaType.APPLICATION_JSON
return headers
fun createZone(zoneName: String): Boolean {
val body = gson.toJson(mapOf(
"name" to zoneName,
"nameservers" to nameserver.split(","))
).toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("$apiUrl/api/v1/servers/localhost/zones")
.addHeader("Authorization", "Bearer $apiKey")
.post(body)
.build()
val response = client.newCall(request).execute()
return response.isSuccessful
}
fun createDomain(name: String): ResponseEntity<String> {
val url = "$apiUrl/api/v1/servers/localhost/zones"
val headers = createHeaders()
val domainRequest = DomainRequest("$name.", "Native", arrayOf(), nameserver.split(",").toTypedArray())
val body = gson.toJson(domainRequest)
val entity = HttpEntity(body, headers)
return restTemplate.exchange(url, HttpMethod.POST, entity, String::class.java)
fun deleteZone(zoneName: String): Boolean {
val request = Request.Builder()
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName")
.addHeader("Authorization", "Bearer $apiKey")
.delete()
.build()
val response = client.newCall(request).execute()
return response.isSuccessful
}
fun createRecord(domainName: String, recordRequest: RecordRequestDTO): ResponseEntity<String> {
val url = "$apiUrl/api/v1/servers/localhost/zones/$domainName."
val headers = createHeaders()
val record = RecordRequest(
name = "${recordRequest.name}.$domainName.",
type = recordRequest.type,
ttl = recordRequest.ttl,
changetype = "REPLACE",
records = arrayOf(RecordContent(recordRequest.content, false))
)
val body = gson.toJson(RecordRequestWrapper(arrayOf(record)))
val entity = HttpEntity(body, headers)
return restTemplate.exchange(url, HttpMethod.PATCH, entity, String::class.java)
fun createRecord(zoneName: String, recordName: String, recordType: String, recordContent: String): Boolean {
val body = gson.toJson(mapOf(
"name" to recordName,
"type" to recordType,
"content" to recordContent
)).toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName/records")
.addHeader("Authorization", "Bearer $apiKey")
.post(body)
.build()
val response = client.newCall(request).execute()
return response.isSuccessful
}
fun deleteDomain(name: String): ResponseEntity<String> {
val url = "$apiUrl/api/v1/servers/localhost/zones/$name."
val headers = createHeaders()
val entity = HttpEntity<String>(headers)
return restTemplate.exchange(url, HttpMethod.DELETE, entity, String::class.java)
fun updateRecord(zoneName: String, recordName: String, recordType: String, recordContent: String): Boolean {
val body = gson.toJson(mapOf(
"content" to recordContent
)).toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName/records/$recordName/$recordType")
.addHeader("Authorization", "Bearer $apiKey")
.put(body)
.build()
val response = client.newCall(request).execute()
return response.isSuccessful
}
private data class DomainRequest(
val name: String,
val kind: String,
val masters: Array<String>,
val nameservers: Array<String>
)
fun deleteRecord(zoneName: String, recordName: String, recordType: String): Boolean {
val request = Request.Builder()
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName/records/$recordName/$recordType")
.addHeader("Authorization", "Bearer $apiKey")
.delete()
.build()
private data class RecordRequestWrapper(
val rrsets: Array<RecordRequest>
)
private data class RecordRequest(
val name: String,
val type: String,
val ttl: Int,
val changetype: String,
val records: Array<RecordContent>
)
private data class RecordContent(
val content: String,
val disabled: Boolean
)
val response = client.newCall(request).execute()
return response.isSuccessful
}
}

View File

@ -2,7 +2,7 @@ package space.mori.dnsapi.service
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import space.mori.dnsapi.PowerDNSApiClient
import space.mori.dnsapi.PowerDNSAPIClient
import space.mori.dnsapi.db.Domain
import space.mori.dnsapi.db.DomainRepository
import space.mori.dnsapi.dto.DomainRequestDTO
@ -14,7 +14,7 @@ class DomainService(
@Autowired
private val domainRepository: DomainRepository,
@Autowired
private val powerDNSApiClient: PowerDNSApiClient
private val powerDNSApiClient: PowerDNSAPIClient
) {
fun getAllDomains(): List<Domain> {
val user = getCurrentUser()
@ -38,7 +38,7 @@ class DomainService(
fun createDomain(domain: DomainRequestDTO): Domain {
val user = getCurrentUser()
powerDNSApiClient.createDomain(domain.name)
powerDNSApiClient.createZone(domain.name)
val saved_domain = domainRepository.save(Domain(name=domain.name, user=user))
return saved_domain
@ -49,7 +49,7 @@ class DomainService(
throw RuntimeException("Domain with CFID $domain_id not found")
}
powerDNSApiClient.deleteDomain(domain.name)
powerDNSApiClient.deleteZone(domain.name)
val count = domainRepository.deleteByCfid(domain_id)
if(count > 0) throw RuntimeException("Domain with CFID $domain_id not found")

View File

@ -3,12 +3,10 @@ package space.mori.dnsapi.service
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.mori.dnsapi.PowerDNSApiClient
import space.mori.dnsapi.PowerDNSAPIClient
import space.mori.dnsapi.db.DomainRepository
import space.mori.dnsapi.db.Record as DomainRecord
import space.mori.dnsapi.db.RecordRepository
import space.mori.dnsapi.db.UserRepository
import space.mori.dnsapi.dto.DomainRequestDTO
import space.mori.dnsapi.dto.RecordRequestDTO
import space.mori.dnsapi.dto.RecordResponseDTO
import space.mori.dnsapi.filter.getCurrentUser
@ -19,13 +17,11 @@ import java.util.*
@Service
class RecordService(
@Autowired
private val powerDNSApiClient: PowerDNSApiClient,
private val powerDNSApiClient: PowerDNSAPIClient,
@Autowired
private val domainRepository: DomainRepository,
@Autowired
private val recordRepository: RecordRepository,
@Autowired
private val userRepository: UserRepository,
) {
fun createRecord(domain_id: String, recordRequest: RecordRequestDTO): RecordResponseDTO {
val domain = domainRepository.findByCfid(domain_id).orElseThrow {
@ -36,9 +32,9 @@ class RecordService(
if(domain.user.id != user.id)
throw RuntimeException("Unauthorized to create record in API: $domain_id")
val response = powerDNSApiClient.createRecord(domain.name, recordRequest)
if (!response.statusCode.is2xxSuccessful) {
throw RuntimeException("Failed to create record in PowerDNS: ${response.body}")
val response = powerDNSApiClient.createRecord(domain.name, recordRequest.name, recordRequest.type, recordRequest.content)
if (!response) {
throw RuntimeException("Failed to create record in PowerDNS")
}
val record = DomainRecord(
domain = domain,
@ -146,9 +142,9 @@ class RecordService(
record.comment = updatedRecord.comment
record.modifiedOn = Date()
val response = powerDNSApiClient.createRecord(domain!!.name, updatedRecord)
if (!response.statusCode.is2xxSuccessful) {
throw RuntimeException("Failed to update record in PowerDNS: ${response.body}")
val response = powerDNSApiClient.updateRecord(domain!!.name, updatedRecord.name, updatedRecord.type, updatedRecord.content)
if (!response) {
throw RuntimeException("Failed to update record in PowerDNS")
}
// 저장
@ -179,9 +175,13 @@ class RecordService(
if(domain.user.id != user.id)
throw RuntimeException("Unauthorized to create record in API: $domain_id")
val deletedCount = recordRepository.deleteByDomainIdAndCfid(domain.id!!, record_id)
val record = recordRepository.findByDomainIdAndCfid(domain.id!!, record_id).orElseThrow {
RuntimeException("Failed to find record in API: $record_id")
}
if(deletedCount == 0) throw RuntimeException("Failed to find record in API: $record_id")
else return record_id
powerDNSApiClient.deleteRecord(domain.name, record.name, record.type)
recordRepository.deleteByDomainIdAndCfid(domain.id!!, record_id)
return record_id
}
}