mirror of
https://github.com/dalbodeule/sh0rt.kr-pdns.git
synced 2025-06-08 18:58:20 +00:00
add powerdns client codes.
This commit is contained in:
parent
07aa50cd3a
commit
3dd8fc69c1
@ -31,6 +31,8 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
|
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
|
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
|
||||||
|
7
docker_build.sh
Executable file
7
docker_build.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 현재 날짜와 시간을 YYMMDD(AM/PM)HHMM 형식으로 설정
|
||||||
|
current_time=$(date +'%y%m%d%p%H%M')
|
||||||
|
|
||||||
|
# Docker 이미지 빌드 명령 실행
|
||||||
|
docker build --no-cache -t dalbodeule/dnsapi:$current_time -t dalbodeule/dnsapi:latest .
|
@ -3,6 +3,10 @@ package space.mori.dnsapi
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import io.github.cdimascio.dotenv.dotenv
|
import io.github.cdimascio.dotenv.dotenv
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
class DnsapiApplication
|
class DnsapiApplication
|
||||||
@ -13,7 +17,9 @@ fun main(args: Array<String>) {
|
|||||||
"DB_PORT" to dotenv["DB_PORT"],
|
"DB_PORT" to dotenv["DB_PORT"],
|
||||||
"DB_NAME" to dotenv["DB_NAME"],
|
"DB_NAME" to dotenv["DB_NAME"],
|
||||||
"DB_USER" to dotenv["DB_USER"],
|
"DB_USER" to dotenv["DB_USER"],
|
||||||
"DB_PASSWORD" to dotenv["DB_PASSWORD"]
|
"DB_PASSWORD" to dotenv["DB_PASSWORD"],
|
||||||
|
"PDNS_API_KEY" to dotenv["PDNS_API_KEY"],
|
||||||
|
"PDNS_API_URL" to dotenv["PDNS_API_URL"],
|
||||||
)
|
)
|
||||||
|
|
||||||
runApplication<DnsapiApplication>(*args) {
|
runApplication<DnsapiApplication>(*args) {
|
||||||
@ -23,4 +29,9 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
val dotenv = dotenv {
|
val dotenv = dotenv {
|
||||||
ignoreIfMissing = true
|
ignoreIfMissing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Date.getISOFormat(): String {
|
||||||
|
val offsetDateTime = OffsetDateTime.ofInstant(this.toInstant(), ZoneOffset.UTC)
|
||||||
|
return offsetDateTime.format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
}
|
}
|
82
src/main/kotlin/space/mori/dnsapi/PowerDNSAPIClient.kt
Normal file
82
src/main/kotlin/space/mori/dnsapi/PowerDNSAPIClient.kt
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package space.mori.dnsapi
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
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 {
|
||||||
|
@Value("\${pdns.api.url}")
|
||||||
|
private lateinit var apiUrl: String
|
||||||
|
|
||||||
|
@Value("\${pdns.api.key}")
|
||||||
|
private lateinit var apiKey: String
|
||||||
|
|
||||||
|
private val restTemplate = RestTemplate()
|
||||||
|
private val gson = Gson()
|
||||||
|
|
||||||
|
private fun createHeaders(): HttpHeaders {
|
||||||
|
val headers = HttpHeaders()
|
||||||
|
headers.set("X-API-Key", apiKey)
|
||||||
|
headers.contentType = MediaType.APPLICATION_JSON
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDomain(name: String): ResponseEntity<String> {
|
||||||
|
val url = "$apiUrl/servers/localhost/zones"
|
||||||
|
val headers = createHeaders()
|
||||||
|
val domainRequest = DomainRequest("$name.", "Master", arrayOf(), arrayOf())
|
||||||
|
val body = gson.toJson(domainRequest)
|
||||||
|
val entity = HttpEntity(body, headers)
|
||||||
|
return restTemplate.exchange(url, HttpMethod.POST, entity, String::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createRecord(domainName: String, recordRequest: RecordRequestDTO): ResponseEntity<String> {
|
||||||
|
val url = "$apiUrl/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 deleteDomain(name: String): ResponseEntity<String> {
|
||||||
|
val url = "$apiUrl/servers/localhost/zones/$name."
|
||||||
|
val headers = createHeaders()
|
||||||
|
val entity = HttpEntity<String>(headers)
|
||||||
|
return restTemplate.exchange(url, HttpMethod.DELETE, entity, String::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class DomainRequest(
|
||||||
|
val name: String,
|
||||||
|
val kind: String,
|
||||||
|
val masters: Array<String>,
|
||||||
|
val nameservers: Array<String>
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
@ -8,7 +8,6 @@ import io.swagger.v3.oas.annotations.media.Schema
|
|||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.web.ErrorResponse
|
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import space.mori.dnsapi.db.Domain
|
import space.mori.dnsapi.db.Domain
|
||||||
import space.mori.dnsapi.dto.DomainResponseDTO
|
import space.mori.dnsapi.dto.DomainResponseDTO
|
||||||
@ -68,5 +67,5 @@ class DomainController {
|
|||||||
domainService!!.deleteDomain(cfid!!)
|
domainService!!.deleteDomain(cfid!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Domain.toDTO() = DomainResponseDTO(cfid = cfid!!, domainName = domainName!!)
|
private fun Domain.toDTO() = DomainResponseDTO(id = cfid, name = name)
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,23 @@ import io.swagger.v3.oas.annotations.media.Schema
|
|||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
import io.swagger.v3.oas.annotations.responses.ApiResponses
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.data.jpa.domain.AbstractPersistable_.id
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import space.mori.dnsapi.service.RecordService
|
import space.mori.dnsapi.db.Domain
|
||||||
import space.mori.dnsapi.db.Record
|
import space.mori.dnsapi.db.Record as DomainRecord
|
||||||
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.getISOFormat
|
||||||
|
import space.mori.dnsapi.service.RecordService
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/record")
|
@RequestMapping("/zones")
|
||||||
class RecordController {
|
class RecordController(
|
||||||
@Autowired
|
@Autowired
|
||||||
private val recordService: RecordService? = null
|
private val recordService: RecordService,
|
||||||
|
) {
|
||||||
@GetMapping
|
@GetMapping("{zone_id}/dns_records")
|
||||||
@Operation(summary = "Get all records", tags=["record"])
|
@Operation(summary = "Get all records", tags=["record"])
|
||||||
@ApiResponses(value = [
|
@ApiResponses(value = [
|
||||||
ApiResponse(responseCode = "200", description = "Return All Records",
|
ApiResponse(responseCode = "200", description = "Return All Records",
|
||||||
@ -28,11 +31,11 @@ class RecordController {
|
|||||||
ApiResponse(responseCode = "400", description = "Bad request",
|
ApiResponse(responseCode = "400", description = "Bad request",
|
||||||
content = [Content(schema = Schema(implementation = Void::class))]),
|
content = [Content(schema = Schema(implementation = Void::class))]),
|
||||||
])
|
])
|
||||||
fun allRecords(@PathVariable cfid: String?): List<RecordResponseDTO?> {
|
fun allRecords(@PathVariable zone_id: String): List<RecordResponseDTO> {
|
||||||
return recordService!!.getAllRecords(cfid!!).map{ it?.toDTO() }
|
return recordService.getRecordsByDomain(zone_id)?.map{ it } ?: listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{cfid}")
|
@GetMapping("{zone_id}/dns_records/{dns_record_id}")
|
||||||
@Operation(summary = "Get Record by ID", tags=["record"])
|
@Operation(summary = "Get Record by ID", tags=["record"])
|
||||||
@ApiResponses(value = [
|
@ApiResponses(value = [
|
||||||
ApiResponse(responseCode = "200", description = "Return Record",
|
ApiResponse(responseCode = "200", description = "Return Record",
|
||||||
@ -40,11 +43,11 @@ class RecordController {
|
|||||||
ApiResponse(responseCode = "400", description = "Bad request",
|
ApiResponse(responseCode = "400", description = "Bad request",
|
||||||
content = [Content(schema = Schema(implementation = Void::class))])
|
content = [Content(schema = Schema(implementation = Void::class))])
|
||||||
])
|
])
|
||||||
fun getRecordByCfid(@PathVariable cfid: String?): Optional<RecordResponseDTO> {
|
fun getRecordByCfid(@PathVariable zone_id: String, @PathVariable dns_record_id: String): RecordResponseDTO {
|
||||||
return recordService!!.getRecordById(cfid!!).map { it.toDTO() }
|
return recordService.getRecord(zone_id, dns_record_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping("{zone_id}/dns_records")
|
||||||
@Operation(summary = "Add Record by ID", tags=["record"])
|
@Operation(summary = "Add Record by ID", tags=["record"])
|
||||||
@ApiResponses(value = [
|
@ApiResponses(value = [
|
||||||
ApiResponse(responseCode = "200", description = "Return Record",
|
ApiResponse(responseCode = "200", description = "Return Record",
|
||||||
@ -52,11 +55,11 @@ class RecordController {
|
|||||||
ApiResponse(responseCode = "400", description = "Bad request",
|
ApiResponse(responseCode = "400", description = "Bad request",
|
||||||
content = [Content(schema = Schema(implementation = Void::class))]),
|
content = [Content(schema = Schema(implementation = Void::class))]),
|
||||||
])
|
])
|
||||||
fun createRecord(@RequestBody record: RecordRequestDTO): RecordResponseDTO {
|
fun createRecord(@PathVariable zone_id: String, @RequestBody record: RecordRequestDTO): RecordResponseDTO {
|
||||||
return recordService!!.createRecord(record).toDTO()
|
return recordService.createRecord(zone_id, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{cfid}")
|
@DeleteMapping("{zone_id}/dns_records/{dns_record_id}")
|
||||||
@Operation(summary = "Remove Record by ID", tags=["record"])
|
@Operation(summary = "Remove Record by ID", tags=["record"])
|
||||||
@ApiResponses(value = [
|
@ApiResponses(value = [
|
||||||
ApiResponse(responseCode = "200", description = "Return Record",
|
ApiResponse(responseCode = "200", description = "Return Record",
|
||||||
@ -64,19 +67,33 @@ class RecordController {
|
|||||||
ApiResponse(responseCode = "400", description = "Bad request",
|
ApiResponse(responseCode = "400", description = "Bad request",
|
||||||
content = [Content(schema = Schema(implementation = Void::class))]),
|
content = [Content(schema = Schema(implementation = Void::class))]),
|
||||||
])
|
])
|
||||||
fun deleteRecord(@PathVariable cfid: String?) {
|
fun deleteRecord(@PathVariable zone_id: String, @PathVariable dns_record_id: String) {
|
||||||
recordService!!.deleteRecord(cfid!!)
|
recordService.deleteRecord(zone_id, dns_record_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Record.toDTO() = RecordResponseDTO(
|
@PatchMapping("{zone_id}/dns_records/{dns_record_id}")
|
||||||
cfid = cfid!!,
|
@Operation(summary = "Update Record by ID", tags=["record"])
|
||||||
name = name!!,
|
@ApiResponses(value = [
|
||||||
type = type!!,
|
ApiResponse(responseCode = "200", description = "Return Record",
|
||||||
content = content!!,
|
content = [Content(schema = Schema(implementation = RecordResponseDTO::class))]),
|
||||||
prio = prio!!,
|
ApiResponse(responseCode = "400", description = "Bad request",
|
||||||
ttl = ttl!!,
|
content = [Content(schema = Schema(implementation = Void::class))]),
|
||||||
changeDate = changeDate!!,
|
])
|
||||||
auth = auth,
|
fun updateRecord(@PathVariable zone_id: String, @PathVariable dns_record_id: String, @RequestBody record: RecordRequestDTO): RecordResponseDTO {
|
||||||
disabled = disabled
|
return recordService.updateRecord(zone_id, dns_record_id, record)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DomainRecord.toDTO() = RecordResponseDTO(
|
||||||
|
id = cfid,
|
||||||
|
type = type,
|
||||||
|
name = name,
|
||||||
|
content = content,
|
||||||
|
zoneId = domain.cfid,
|
||||||
|
zoneName = domain.name,
|
||||||
|
priority = prio,
|
||||||
|
ttl = ttl,
|
||||||
|
createdOn = createdOn.getISOFormat(),
|
||||||
|
modifiedOn = modifiedOn.getISOFormat(),
|
||||||
|
comment = comment
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -7,37 +7,17 @@ import java.util.*
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "domains")
|
@Table(name = "domains")
|
||||||
class Domain {
|
data class Domain(
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private val id: Long? = null
|
var id: Long? = null,
|
||||||
|
|
||||||
@Column(nullable = false, length = 255)
|
@Column(nullable = false, unique = true)
|
||||||
val domainName: String? = null
|
var name: String,
|
||||||
|
|
||||||
@Column(nullable = true, length = 128)
|
@Column(nullable = false, unique = true)
|
||||||
val master: String? = null
|
var cfid: String = UUID.randomUUID().toString().replace("-", "")
|
||||||
|
) {
|
||||||
@Column(nullable = true, name = "last_check")
|
@OneToMany(mappedBy = "domain", cascade = [CascadeType.ALL], orphanRemoval = true)
|
||||||
val lastCheck: Int? = null
|
var records: List<Record> = mutableListOf()
|
||||||
|
|
||||||
@Column(nullable = false, length = 6)
|
|
||||||
val type: String? = null
|
|
||||||
|
|
||||||
@Column(nullable = true, name = "notified_serial")
|
|
||||||
val notifiedSerial: Int? = null
|
|
||||||
|
|
||||||
@Column(nullable = false, length = 128)
|
|
||||||
val account: String? = null
|
|
||||||
|
|
||||||
@Column(unique = true, nullable = false, length = 32)
|
|
||||||
var cfid: String? = null
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "domain", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
|
||||||
private val records: Set<Record>? = null
|
|
||||||
|
|
||||||
@PrePersist
|
|
||||||
protected fun onCreate() {
|
|
||||||
this.cfid = UUID.randomUUID().toString().replace("-", "")
|
|
||||||
} // Getters and setters
|
|
||||||
}
|
}
|
@ -6,68 +6,28 @@ import java.util.*
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "records")
|
@Table(name = "records")
|
||||||
class Record {
|
data class Record(
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private val id: Long? = null
|
var id: Long? = null,
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "domain_id", nullable = false)
|
@JoinColumn(name = "domain_id", nullable = false)
|
||||||
var domain: Domain? = null
|
var domain: Domain,
|
||||||
|
|
||||||
@Column(nullable = false, length = 255)
|
var name: String,
|
||||||
var name: String? = null
|
var type: String,
|
||||||
|
var content: String,
|
||||||
|
var ttl: Int,
|
||||||
|
var prio: Int,
|
||||||
|
var disabled: Boolean,
|
||||||
|
var auth: Boolean,
|
||||||
|
|
||||||
@Column(nullable = false, length = 10)
|
var createdOn: Date,
|
||||||
var type: String? = null
|
var modifiedOn: Date,
|
||||||
|
|
||||||
@Column(nullable = false, length = 64000)
|
var comment: String,
|
||||||
var content: String? = null
|
|
||||||
|
|
||||||
@Column(nullable = true)
|
@Column(nullable = false, unique = true)
|
||||||
var ttl: Int?
|
var cfid: String = UUID.randomUUID().toString().replace("-", "")
|
||||||
|
)
|
||||||
@Column(nullable = true)
|
|
||||||
var prio: Int?
|
|
||||||
|
|
||||||
@Column(nullable = true)
|
|
||||||
var changeDate: Int?
|
|
||||||
|
|
||||||
var disabled: Boolean = false
|
|
||||||
var auth: Boolean = true
|
|
||||||
|
|
||||||
@Column(unique = true, nullable = false, length = 32)
|
|
||||||
var cfid: String? = null
|
|
||||||
|
|
||||||
@Column(nullable = true, length = 64)
|
|
||||||
var comment: String? = null
|
|
||||||
|
|
||||||
@PrePersist
|
|
||||||
private fun onCreate() {
|
|
||||||
this.cfid = UUID.randomUUID().toString().replace("-", "")
|
|
||||||
} // Getters and setters
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
name: String,
|
|
||||||
type: String,
|
|
||||||
content: String,
|
|
||||||
changeDate: Int?,
|
|
||||||
disabled: Boolean,
|
|
||||||
domain: Domain,
|
|
||||||
comment: String?,
|
|
||||||
auth: Boolean = true,
|
|
||||||
ttl: Int? = 300,
|
|
||||||
prio: Int? = 0,
|
|
||||||
) {
|
|
||||||
this.name = name
|
|
||||||
this.type = type
|
|
||||||
this.content = content
|
|
||||||
this.ttl = ttl
|
|
||||||
this.prio = prio
|
|
||||||
this.changeDate = changeDate
|
|
||||||
this.disabled = disabled
|
|
||||||
this.auth = auth
|
|
||||||
this.domain = domain
|
|
||||||
this.comment = comment
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,8 +12,11 @@ interface RecordRepository : JpaRepository<Record?, Long?> {
|
|||||||
fun findByCfid(cfid: String): Optional<Record>
|
fun findByCfid(cfid: String): Optional<Record>
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun deleteByCfid(cfid: String)
|
fun findByDomainIdAndCfid(domainId: Long, cfid: String): Optional<Record>
|
||||||
|
|
||||||
@Query("SELECT r FROM Record r WHERE r.domain.cfid = :domainCfid")
|
@Transactional
|
||||||
fun findByDomainCfid(@Param("domainCfid") cfid: String): List<Record>
|
fun deleteByCfid(cfid: String): Int
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun deleteByDomainIdAndCfid(domain_id: Long, cfid: String): Int
|
||||||
}
|
}
|
@ -5,5 +5,5 @@ import io.swagger.v3.oas.annotations.media.Schema
|
|||||||
@Schema(description = "Request DTO for Domain")
|
@Schema(description = "Request DTO for Domain")
|
||||||
data class DomainRequestDTO(
|
data class DomainRequestDTO(
|
||||||
@Schema(description = "Domain name(TLD)", example = "example.com")
|
@Schema(description = "Domain name(TLD)", example = "example.com")
|
||||||
val domainName: String
|
val name: String
|
||||||
)
|
)
|
@ -5,8 +5,8 @@ import io.swagger.v3.oas.annotations.media.Schema
|
|||||||
@Schema(description = "Response DTO for Domain")
|
@Schema(description = "Response DTO for Domain")
|
||||||
data class DomainResponseDTO(
|
data class DomainResponseDTO(
|
||||||
@Schema(description = "Domain CFID", example = "123e4567e89b12d3a456426655440000")
|
@Schema(description = "Domain CFID", example = "123e4567e89b12d3a456426655440000")
|
||||||
val cfid: String,
|
val id: String,
|
||||||
|
|
||||||
@Schema(description = "Domain name(TLD)", example = "example.com")
|
@Schema(description = "Domain name(TLD)", example = "example.com")
|
||||||
val domainName: String
|
val name: String
|
||||||
)
|
)
|
@ -4,20 +4,23 @@ import io.swagger.v3.oas.annotations.media.Schema
|
|||||||
|
|
||||||
@Schema(description = "Request DTO for Record")
|
@Schema(description = "Request DTO for Record")
|
||||||
data class RecordRequestDTO(
|
data class RecordRequestDTO(
|
||||||
@Schema(description = "Host name", example = "www")
|
|
||||||
val host: String,
|
|
||||||
|
|
||||||
@Schema(description = "Record type", example = "A")
|
@Schema(description = "Record type", example = "A")
|
||||||
val type: String,
|
val type: String,
|
||||||
|
|
||||||
|
@Schema(description = "Host name", example = "www.example.com.")
|
||||||
|
val name: String,
|
||||||
|
|
||||||
@Schema(description = "Record data", example = "192.0.2.1")
|
@Schema(description = "Record data", example = "192.0.2.1")
|
||||||
val data: String,
|
val content: String,
|
||||||
|
|
||||||
@Schema(description = "TTL (Time to Live)", example = "3600")
|
@Schema(description = "TTL (Time to Live)", example = "3600")
|
||||||
val ttl: Int,
|
val ttl: Int = 300,
|
||||||
|
|
||||||
@Schema(description = "Domain CFID", example = "123e4567e89b12d3a456426655440000")
|
@Schema(description = "Priority", example = "0")
|
||||||
val cfid: String,
|
val priority: Int? = null,
|
||||||
|
|
||||||
|
@Schema(description = "Proxied: cloudflare api compatibility", example = "false")
|
||||||
|
val proxied: Boolean = false,
|
||||||
|
|
||||||
@Schema(description = "comment", example="")
|
@Schema(description = "comment", example="")
|
||||||
val comment: String
|
val comment: String
|
||||||
|
@ -5,30 +5,45 @@ import java.util.*
|
|||||||
|
|
||||||
@Schema(description = "Response DTO for Record")
|
@Schema(description = "Response DTO for Record")
|
||||||
data class RecordResponseDTO(
|
data class RecordResponseDTO(
|
||||||
@Schema(description = "Record CFID", example = "123e4567e89b12d3a456426655440001")
|
@Schema(description = "Record ID", example = "123e4567e89b12d3a456426655440001")
|
||||||
val cfid: String,
|
val id: String,
|
||||||
|
|
||||||
@Schema(description = "Host name", example = "www.domain.tld")
|
|
||||||
val name: String,
|
|
||||||
|
|
||||||
@Schema(description = "Record type", example = "A")
|
@Schema(description = "Record type", example = "A")
|
||||||
val type: String,
|
var type: String,
|
||||||
|
|
||||||
@Schema(description = "Record data", example = "192.0.2.1")
|
@Schema(description = "Record name", example = "test.example.com")
|
||||||
val content: String,
|
var name: String,
|
||||||
|
|
||||||
@Schema(description = "TTL (Time to Live)", example = "3600")
|
@Schema(description = "Record content", example = "1.1.1.1")
|
||||||
val ttl: Int,
|
var content: String,
|
||||||
|
|
||||||
@Schema(description = "TTL per second", example = "300s")
|
@Schema(description = "Zone(TLD) ID", example = "123e4567e89b12d3a456426655440001")
|
||||||
val prio: Int,
|
val zoneId: String,
|
||||||
|
|
||||||
@Schema(description = "Changed date with Unix Timestamp")
|
@Schema(description = "Zone name(TLD)", example = "example.com")
|
||||||
val changeDate: Int,
|
val zoneName: String,
|
||||||
|
|
||||||
@Schema(description = "is disabled?", example = "false")
|
@Schema(description = "Record creation time", example = "2014-01-01T05:20:00.12345Z")
|
||||||
val disabled: Boolean,
|
val createdOn: String,
|
||||||
|
|
||||||
@Schema(description = "is authed", example = "true")
|
@Schema(description = "Record modification time", example = "2014-01-01T05:20:00.12345Z")
|
||||||
val auth: Boolean,
|
val modifiedOn: String,
|
||||||
|
|
||||||
|
@Schema(description = "Record priority", example = "0")
|
||||||
|
val priority: Int? = 0,
|
||||||
|
|
||||||
|
@Schema(description = "is proxyable: must false", example = "false")
|
||||||
|
val proxiable: Boolean = false,
|
||||||
|
|
||||||
|
@Schema(description = "is proxied: must false", example = "false")
|
||||||
|
val proxied: Boolean = false,
|
||||||
|
|
||||||
|
@Schema(description = "Record TTL", example = "300")
|
||||||
|
val ttl: Int = 300,
|
||||||
|
|
||||||
|
@Schema(description = "Record is locked: must false", example = "false")
|
||||||
|
val locked: Boolean = false,
|
||||||
|
|
||||||
|
@Schema(description = "Record comments", example = "")
|
||||||
|
val comment: String? = null,
|
||||||
)
|
)
|
@ -2,45 +2,160 @@ 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 space.mori.dnsapi.PowerDNSApiClient
|
||||||
import space.mori.dnsapi.db.DomainRepository
|
import space.mori.dnsapi.db.DomainRepository
|
||||||
import space.mori.dnsapi.db.RecordRepository
|
|
||||||
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.dto.DomainRequestDTO
|
||||||
import space.mori.dnsapi.dto.RecordRequestDTO
|
import space.mori.dnsapi.dto.RecordRequestDTO
|
||||||
|
import space.mori.dnsapi.dto.RecordResponseDTO
|
||||||
|
import space.mori.dnsapi.getISOFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class RecordService {
|
class RecordService(
|
||||||
@Autowired
|
@Autowired
|
||||||
private lateinit var domainRepository: DomainRepository
|
private val powerDNSApiClient: PowerDNSApiClient,
|
||||||
@Autowired
|
@Autowired
|
||||||
private val recordRepository: RecordRepository? = null
|
private val domainRepository: DomainRepository,
|
||||||
|
@Autowired
|
||||||
|
private val recordRepository: RecordRepository
|
||||||
|
) {
|
||||||
|
fun createRecord(domain_id: String, recordRequest: RecordRequestDTO): RecordResponseDTO {
|
||||||
|
val domain = domainRepository.findByCfid(domain_id)
|
||||||
|
if(domain.isEmpty) throw RuntimeException("Failed to find domain in API: $domain_id")
|
||||||
|
|
||||||
fun getAllRecords(cfid: String): List<DomainRecord?> {
|
val response = powerDNSApiClient.createRecord(domain.get().name, recordRequest)
|
||||||
return recordRepository!!.findByDomainCfid(cfid)
|
if (!response.statusCode.is2xxSuccessful) {
|
||||||
}
|
throw RuntimeException("Failed to create record in PowerDNS: ${response.body}")
|
||||||
|
}
|
||||||
fun getRecordById(cfid: String): Optional<DomainRecord> {
|
val record = DomainRecord(
|
||||||
return recordRepository!!.findByCfid(cfid)
|
domain = domain.get(),
|
||||||
}
|
name = recordRequest.name,
|
||||||
|
type = recordRequest.type,
|
||||||
fun createRecord(record: RecordRequestDTO): DomainRecord {
|
content = recordRequest.content,
|
||||||
val domain = domainRepository.findByCfid(record.cfid)
|
ttl = recordRequest.ttl,
|
||||||
.orElseThrow { IllegalArgumentException("Invalid domain CFID") }
|
prio = recordRequest.priority ?: 0,
|
||||||
val r = DomainRecord(
|
disabled = false,
|
||||||
name = record.host,
|
auth = true,
|
||||||
type = record.type,
|
createdOn = Date(),
|
||||||
content = record.data,
|
modifiedOn = Date(),
|
||||||
ttl = record.ttl,
|
comment = recordRequest.comment,
|
||||||
domain = domain,
|
)
|
||||||
comment = record.comment,
|
|
||||||
changeDate = java.util.Date().time.toInt(),
|
return RecordResponseDTO(
|
||||||
disabled = false
|
id = record.cfid,
|
||||||
|
type = record.type,
|
||||||
|
name = record.name,
|
||||||
|
content = record.content,
|
||||||
|
proxiable = false,
|
||||||
|
proxied = false,
|
||||||
|
ttl = record.ttl,
|
||||||
|
locked = false,
|
||||||
|
zoneId = record.cfid,
|
||||||
|
zoneName = domain.get().name,
|
||||||
|
createdOn = record.createdOn.getISOFormat(),
|
||||||
|
modifiedOn = record.modifiedOn.getISOFormat(),
|
||||||
|
priority = record.prio,
|
||||||
|
comment = record.comment
|
||||||
)
|
)
|
||||||
return recordRepository!!.save(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteRecord(cfid: String) {
|
fun getRecord(domain_id: String, record_id: String): RecordResponseDTO {
|
||||||
recordRepository!!.deleteByCfid(cfid)
|
val domain = domainRepository.findByCfid(domain_id)
|
||||||
|
if(domain.isEmpty) throw RuntimeException("Failed to find domain in API: $domain_id")
|
||||||
|
|
||||||
|
val record = domain.get().records.find { it.cfid == record_id }
|
||||||
|
if(record == null) throw RuntimeException("Failed to find record in API: $record_id")
|
||||||
|
|
||||||
|
return RecordResponseDTO(
|
||||||
|
id = record.cfid,
|
||||||
|
type = record.type,
|
||||||
|
name = record.name,
|
||||||
|
content = record.content,
|
||||||
|
ttl = record.ttl,
|
||||||
|
zoneId = record.domain.cfid,
|
||||||
|
zoneName = record.domain.name,
|
||||||
|
createdOn = record.createdOn.getISOFormat(),
|
||||||
|
modifiedOn = record.modifiedOn.getISOFormat(),
|
||||||
|
comment = record.comment,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecordsByDomain(domain_id: String): List<RecordResponseDTO>? {
|
||||||
|
val domain = domainRepository.findByCfid(domain_id).orElseThrow { RuntimeException("Failed to find domain in API: $domain_id") }
|
||||||
|
return domain?.records?.map { RecordResponseDTO(
|
||||||
|
id = it.cfid,
|
||||||
|
type = it.type,
|
||||||
|
name = it.name,
|
||||||
|
content = it.content,
|
||||||
|
zoneId = it.domain.cfid,
|
||||||
|
zoneName = it.domain.name,
|
||||||
|
priority = it.prio,
|
||||||
|
ttl = it.ttl,
|
||||||
|
createdOn = it.createdOn.getISOFormat(),
|
||||||
|
modifiedOn = it.modifiedOn.getISOFormat(),
|
||||||
|
comment = it.comment,
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
fun updateRecord(domainId: String, cfid: String, updatedRecord: RecordRequestDTO): RecordResponseDTO {
|
||||||
|
// 도메인 조회
|
||||||
|
val domain = domainRepository.findByCfid(domainId)
|
||||||
|
.orElseThrow { RuntimeException("Domain not found") }
|
||||||
|
|
||||||
|
// 레코드 조회
|
||||||
|
val record = recordRepository.findByDomainIdAndCfid(domain.id!!, cfid)
|
||||||
|
.orElseThrow { RuntimeException("Record not found") }
|
||||||
|
|
||||||
|
// 레코드 업데이트
|
||||||
|
record.name = updatedRecord.name
|
||||||
|
record.type = updatedRecord.type
|
||||||
|
record.content = updatedRecord.content
|
||||||
|
record.ttl = updatedRecord.ttl
|
||||||
|
record.prio = updatedRecord.priority ?: 0
|
||||||
|
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 savedRecord = recordRepository.save(record)
|
||||||
|
return RecordResponseDTO(
|
||||||
|
id = savedRecord.cfid,
|
||||||
|
type = savedRecord.type,
|
||||||
|
name = savedRecord.name,
|
||||||
|
content = savedRecord.content,
|
||||||
|
proxiable = true,
|
||||||
|
proxied = false,
|
||||||
|
ttl = savedRecord.ttl,
|
||||||
|
locked = false,
|
||||||
|
zoneId = domain.cfid,
|
||||||
|
zoneName = domain.name,
|
||||||
|
createdOn = savedRecord.createdOn.getISOFormat(),
|
||||||
|
modifiedOn = savedRecord.modifiedOn.getISOFormat(),
|
||||||
|
priority = savedRecord.prio
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteRecord(domain_id: String, record_id: String) {
|
||||||
|
val domain = domainRepository.findByCfid(domain_id).orElseThrow { RuntimeException("Failed to find domain in API: $domain_id") }
|
||||||
|
|
||||||
|
val deletedCount = recordRepository.deleteByDomainIdAndCfid(domain.id!!, record_id)
|
||||||
|
|
||||||
|
if(deletedCount == 0) throw RuntimeException("Failed to find record in API: $record_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteDomain(name: String) {
|
||||||
|
val response = powerDNSApiClient.deleteDomain(name)
|
||||||
|
if (!response.statusCode.is2xxSuccessful) {
|
||||||
|
throw RuntimeException("Failed to delete domain in PowerDNS: ${response.body}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,4 +11,6 @@ springdoc.swagger-ui.enabled=false
|
|||||||
springdoc.api-docs.path=/api-docs
|
springdoc.api-docs.path=/api-docs
|
||||||
springdoc.default-consumes-media-type= application/json
|
springdoc.default-consumes-media-type= application/json
|
||||||
springdoc.default-produces-media-type= application/json
|
springdoc.default-produces-media-type= application/json
|
||||||
springdoc.version= '@project.version@'
|
springdoc.version= '@project.version@'
|
||||||
|
pdns.api.key=${PDNS_API_KEY}
|
||||||
|
pdns.api.url=${PDNS_API_URL}
|
Loading…
x
Reference in New Issue
Block a user