mirror of
https://github.com/dalbodeule/sh0rt.kr-pdns.git
synced 2025-06-08 18:58:20 +00:00
fix pdns api clients (retry 5x)
This commit is contained in:
parent
4a72cd0453
commit
a41d8ae23c
@ -42,6 +42,9 @@ dependencies {
|
|||||||
implementation("com.google.code.gson:gson:2.11.0")
|
implementation("com.google.code.gson:gson:2.11.0")
|
||||||
implementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
|
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")
|
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
|
||||||
|
|
||||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package space.mori.dnsapi
|
package space.mori.dnsapi
|
||||||
|
|
||||||
import com.google.gson.Gson
|
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.beans.factory.annotation.Value
|
||||||
import org.springframework.http.*
|
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.web.client.RestTemplate
|
|
||||||
import space.mori.dnsapi.dto.RecordRequestDTO
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class PowerDNSApiClient {
|
class PowerDNSAPIClient() {
|
||||||
@Value("\${pdns.api.url}")
|
@Value("\${pdns.api.url}")
|
||||||
private lateinit var apiUrl: String
|
private lateinit var apiUrl: String
|
||||||
|
|
||||||
@ -18,68 +19,73 @@ class PowerDNSApiClient {
|
|||||||
@Value("\${pdns.ns}")
|
@Value("\${pdns.ns}")
|
||||||
private lateinit var nameserver: String
|
private lateinit var nameserver: String
|
||||||
|
|
||||||
private val restTemplate = RestTemplate()
|
|
||||||
private val gson = Gson()
|
private val gson = Gson()
|
||||||
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
private fun createHeaders(): HttpHeaders {
|
fun createZone(zoneName: String): Boolean {
|
||||||
val headers = HttpHeaders()
|
val body = gson.toJson(mapOf(
|
||||||
headers.set("X-API-Key", apiKey)
|
"name" to zoneName,
|
||||||
headers.contentType = MediaType.APPLICATION_JSON
|
"nameservers" to nameserver.split(","))
|
||||||
return headers
|
).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> {
|
fun deleteZone(zoneName: String): Boolean {
|
||||||
val url = "$apiUrl/api/v1/servers/localhost/zones"
|
val request = Request.Builder()
|
||||||
val headers = createHeaders()
|
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName")
|
||||||
val domainRequest = DomainRequest("$name.", "Native", arrayOf(), nameserver.split(",").toTypedArray())
|
.addHeader("Authorization", "Bearer $apiKey")
|
||||||
val body = gson.toJson(domainRequest)
|
.delete()
|
||||||
val entity = HttpEntity(body, headers)
|
.build()
|
||||||
return restTemplate.exchange(url, HttpMethod.POST, entity, String::class.java)
|
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
return response.isSuccessful
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRecord(domainName: String, recordRequest: RecordRequestDTO): ResponseEntity<String> {
|
fun createRecord(zoneName: String, recordName: String, recordType: String, recordContent: String): Boolean {
|
||||||
val url = "$apiUrl/api/v1/servers/localhost/zones/$domainName."
|
val body = gson.toJson(mapOf(
|
||||||
val headers = createHeaders()
|
"name" to recordName,
|
||||||
val record = RecordRequest(
|
"type" to recordType,
|
||||||
name = "${recordRequest.name}.$domainName.",
|
"content" to recordContent
|
||||||
type = recordRequest.type,
|
)).toRequestBody("application/json; charset=utf-8".toMediaType())
|
||||||
ttl = recordRequest.ttl,
|
val request = Request.Builder()
|
||||||
changetype = "REPLACE",
|
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName/records")
|
||||||
records = arrayOf(RecordContent(recordRequest.content, false))
|
.addHeader("Authorization", "Bearer $apiKey")
|
||||||
)
|
.post(body)
|
||||||
val body = gson.toJson(RecordRequestWrapper(arrayOf(record)))
|
.build()
|
||||||
val entity = HttpEntity(body, headers)
|
|
||||||
return restTemplate.exchange(url, HttpMethod.PATCH, entity, String::class.java)
|
val response = client.newCall(request).execute()
|
||||||
|
return response.isSuccessful
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteDomain(name: String): ResponseEntity<String> {
|
fun updateRecord(zoneName: String, recordName: String, recordType: String, recordContent: String): Boolean {
|
||||||
val url = "$apiUrl/api/v1/servers/localhost/zones/$name."
|
val body = gson.toJson(mapOf(
|
||||||
val headers = createHeaders()
|
"content" to recordContent
|
||||||
val entity = HttpEntity<String>(headers)
|
)).toRequestBody("application/json; charset=utf-8".toMediaType())
|
||||||
return restTemplate.exchange(url, HttpMethod.DELETE, entity, String::class.java)
|
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(
|
fun deleteRecord(zoneName: String, recordName: String, recordType: String): Boolean {
|
||||||
val name: String,
|
val request = Request.Builder()
|
||||||
val kind: String,
|
.url("$apiUrl/api/v1/servers/localhost/zones/$zoneName/records/$recordName/$recordType")
|
||||||
val masters: Array<String>,
|
.addHeader("Authorization", "Bearer $apiKey")
|
||||||
val nameservers: Array<String>
|
.delete()
|
||||||
)
|
.build()
|
||||||
|
|
||||||
private data class RecordRequestWrapper(
|
val response = client.newCall(request).execute()
|
||||||
val rrsets: Array<RecordRequest>
|
return response.isSuccessful
|
||||||
)
|
}
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package space.mori.dnsapi.service
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Service
|
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.Domain
|
||||||
import space.mori.dnsapi.db.DomainRepository
|
import space.mori.dnsapi.db.DomainRepository
|
||||||
import space.mori.dnsapi.dto.DomainRequestDTO
|
import space.mori.dnsapi.dto.DomainRequestDTO
|
||||||
@ -14,7 +14,7 @@ class DomainService(
|
|||||||
@Autowired
|
@Autowired
|
||||||
private val domainRepository: DomainRepository,
|
private val domainRepository: DomainRepository,
|
||||||
@Autowired
|
@Autowired
|
||||||
private val powerDNSApiClient: PowerDNSApiClient
|
private val powerDNSApiClient: PowerDNSAPIClient
|
||||||
) {
|
) {
|
||||||
fun getAllDomains(): List<Domain> {
|
fun getAllDomains(): List<Domain> {
|
||||||
val user = getCurrentUser()
|
val user = getCurrentUser()
|
||||||
@ -38,7 +38,7 @@ class DomainService(
|
|||||||
fun createDomain(domain: DomainRequestDTO): Domain {
|
fun createDomain(domain: DomainRequestDTO): Domain {
|
||||||
val user = getCurrentUser()
|
val user = getCurrentUser()
|
||||||
|
|
||||||
powerDNSApiClient.createDomain(domain.name)
|
powerDNSApiClient.createZone(domain.name)
|
||||||
val saved_domain = domainRepository.save(Domain(name=domain.name, user=user))
|
val saved_domain = domainRepository.save(Domain(name=domain.name, user=user))
|
||||||
|
|
||||||
return saved_domain
|
return saved_domain
|
||||||
@ -49,7 +49,7 @@ class DomainService(
|
|||||||
throw RuntimeException("Domain with CFID $domain_id not found")
|
throw RuntimeException("Domain with CFID $domain_id not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
powerDNSApiClient.deleteDomain(domain.name)
|
powerDNSApiClient.deleteZone(domain.name)
|
||||||
val count = domainRepository.deleteByCfid(domain_id)
|
val count = domainRepository.deleteByCfid(domain_id)
|
||||||
|
|
||||||
if(count > 0) throw RuntimeException("Domain with CFID $domain_id not found")
|
if(count > 0) throw RuntimeException("Domain with CFID $domain_id not found")
|
||||||
|
@ -3,12 +3,10 @@ package space.mori.dnsapi.service
|
|||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
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.DomainRepository
|
||||||
import space.mori.dnsapi.db.Record as DomainRecord
|
import space.mori.dnsapi.db.Record as DomainRecord
|
||||||
import space.mori.dnsapi.db.RecordRepository
|
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.RecordRequestDTO
|
||||||
import space.mori.dnsapi.dto.RecordResponseDTO
|
import space.mori.dnsapi.dto.RecordResponseDTO
|
||||||
import space.mori.dnsapi.filter.getCurrentUser
|
import space.mori.dnsapi.filter.getCurrentUser
|
||||||
@ -19,13 +17,11 @@ import java.util.*
|
|||||||
@Service
|
@Service
|
||||||
class RecordService(
|
class RecordService(
|
||||||
@Autowired
|
@Autowired
|
||||||
private val powerDNSApiClient: PowerDNSApiClient,
|
private val powerDNSApiClient: PowerDNSAPIClient,
|
||||||
@Autowired
|
@Autowired
|
||||||
private val domainRepository: DomainRepository,
|
private val domainRepository: DomainRepository,
|
||||||
@Autowired
|
@Autowired
|
||||||
private val recordRepository: RecordRepository,
|
private val recordRepository: RecordRepository,
|
||||||
@Autowired
|
|
||||||
private val userRepository: UserRepository,
|
|
||||||
) {
|
) {
|
||||||
fun createRecord(domain_id: String, recordRequest: RecordRequestDTO): RecordResponseDTO {
|
fun createRecord(domain_id: String, recordRequest: RecordRequestDTO): RecordResponseDTO {
|
||||||
val domain = domainRepository.findByCfid(domain_id).orElseThrow {
|
val domain = domainRepository.findByCfid(domain_id).orElseThrow {
|
||||||
@ -36,9 +32,9 @@ class RecordService(
|
|||||||
if(domain.user.id != user.id)
|
if(domain.user.id != user.id)
|
||||||
throw RuntimeException("Unauthorized to create record in API: $domain_id")
|
throw RuntimeException("Unauthorized to create record in API: $domain_id")
|
||||||
|
|
||||||
val response = powerDNSApiClient.createRecord(domain.name, recordRequest)
|
val response = powerDNSApiClient.createRecord(domain.name, recordRequest.name, recordRequest.type, recordRequest.content)
|
||||||
if (!response.statusCode.is2xxSuccessful) {
|
if (!response) {
|
||||||
throw RuntimeException("Failed to create record in PowerDNS: ${response.body}")
|
throw RuntimeException("Failed to create record in PowerDNS")
|
||||||
}
|
}
|
||||||
val record = DomainRecord(
|
val record = DomainRecord(
|
||||||
domain = domain,
|
domain = domain,
|
||||||
@ -146,9 +142,9 @@ class RecordService(
|
|||||||
record.comment = updatedRecord.comment
|
record.comment = updatedRecord.comment
|
||||||
record.modifiedOn = Date()
|
record.modifiedOn = Date()
|
||||||
|
|
||||||
val response = powerDNSApiClient.createRecord(domain!!.name, updatedRecord)
|
val response = powerDNSApiClient.updateRecord(domain!!.name, updatedRecord.name, updatedRecord.type, updatedRecord.content)
|
||||||
if (!response.statusCode.is2xxSuccessful) {
|
if (!response) {
|
||||||
throw RuntimeException("Failed to update record in PowerDNS: ${response.body}")
|
throw RuntimeException("Failed to update record in PowerDNS")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 저장
|
// 저장
|
||||||
@ -179,9 +175,13 @@ class RecordService(
|
|||||||
if(domain.user.id != user.id)
|
if(domain.user.id != user.id)
|
||||||
throw RuntimeException("Unauthorized to create record in API: $domain_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")
|
powerDNSApiClient.deleteRecord(domain.name, record.name, record.type)
|
||||||
else return record_id
|
recordRepository.deleteByDomainIdAndCfid(domain.id!!, record_id)
|
||||||
|
|
||||||
|
return record_id
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user