initialized

This commit is contained in:
dalbodeule
2024-06-05 08:02:06 +09:00
commit 2dbaf1317d
37 changed files with 2112 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
package space.mori.dnsapi
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import io.github.cdimascio.dotenv.dotenv
@SpringBootApplication
class DnsapiApplication
fun main(args: Array<String>) {
val envVars = mapOf(
"DB_HOST" to dotenv["DB_HOST"],
"DB_PORT" to dotenv["DB_PORT"],
"DB_NAME" to dotenv["DB_NAME"],
"DB_USER" to dotenv["DB_USER"],
"DB_PASSWORD" to dotenv["DB_PASSWORD"]
)
runApplication<DnsapiApplication>(*args) {
setDefaultProperties(envVars)
}
}
val dotenv = dotenv {
ignoreIfMissing = true
}

View File

@@ -0,0 +1,25 @@
package space.mori.dnsapi
import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Info
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class SwaggerConfig {
@Bean
fun openAPI(): OpenAPI {
return OpenAPI()
.components(Components())
.info(apiInfo())
}
private fun apiInfo(): Info {
return Info()
.title("Spring Boot REST API Specifications")
.description("Specification")
.version("1.0.0")
}
}

View File

@@ -0,0 +1,72 @@
package space.mori.dnsapi.controller
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.ArraySchema
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.ErrorResponse
import org.springframework.web.bind.annotation.*
import space.mori.dnsapi.db.Domain
import space.mori.dnsapi.dto.DomainResponseDTO
import space.mori.dnsapi.service.DomainService
import java.util.*
@RestController
@RequestMapping("/domain")
class DomainController {
@Autowired
private val domainService: DomainService? = null
@get:GetMapping
@get:Operation(summary = "Get all domains", tags = ["domain"])
@get:ApiResponse(responseCode = "200", description = "Returns all domains",
content = [Content(array = ArraySchema(schema = Schema(implementation = DomainResponseDTO::class)))])
val allDomains: List<DomainResponseDTO?>
get() = domainService!!.getAllDomains().map { it?.toDTO() }
@Operation(summary = "Get domain", tags = ["domain"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Returns domain",
content = [Content(schema = Schema(implementation = DomainResponseDTO::class))]),
ApiResponse(responseCode = "404", description = "Returns not found",
content = [Content(schema = Schema(implementation = Void::class))])
])
@GetMapping("/{cfid}")
fun getDomainByCfid(
@Parameter(description = "CFID", required = true)
@PathVariable cfid: String?
): Optional<DomainResponseDTO> {
return domainService!!.getDomainById(cfid!!).map { it.toDTO() }
}
@Operation(summary = "Create domain", tags = ["domain"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Created domain",
content = [Content(schema = Schema(implementation = DomainResponseDTO::class))]),
ApiResponse(responseCode = "400", description = "Bad request",
content = [Content(schema = Schema(implementation = Void::class))])
])
@PostMapping
fun createDomain(@RequestBody domain: Domain?): DomainResponseDTO {
return domainService!!.createDomain(domain!!).toDTO()
}
@Operation(summary = "Delete domain", tags = ["domain"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Deleted domain",
content = [Content(schema = Schema(implementation = Void::class))]),
ApiResponse(responseCode = "400", description = "Bad request",
content = [Content(schema = Schema(implementation = Void::class))])
])
@DeleteMapping("/{cfid}")
fun deleteDomain(@PathVariable cfid: String?) {
domainService!!.deleteDomain(cfid!!)
}
private fun Domain.toDTO() = DomainResponseDTO(cfid = cfid!!, domainName = domainName!!)
}

View File

@@ -0,0 +1,82 @@
package space.mori.dnsapi.controller
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.ArraySchema
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.*
import space.mori.dnsapi.service.RecordService
import space.mori.dnsapi.db.Record
import space.mori.dnsapi.dto.RecordRequestDTO
import space.mori.dnsapi.dto.RecordResponseDTO
import java.util.*
@RestController
@RequestMapping("/record")
class RecordController {
@Autowired
private val recordService: RecordService? = null
@GetMapping
@Operation(summary = "Get all records", tags=["record"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Return All Records",
content = [Content(array = ArraySchema(schema = Schema(implementation = RecordResponseDTO::class)))]),
ApiResponse(responseCode = "400", description = "Bad request",
content = [Content(schema = Schema(implementation = Void::class))]),
])
fun allRecords(@PathVariable cfid: String?): List<RecordResponseDTO?> {
return recordService!!.getAllRecords(cfid!!).map{ it?.toDTO() }
}
@GetMapping("/{cfid}")
@Operation(summary = "Get Record by ID", tags=["record"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Return Record",
content = [Content(schema = Schema(implementation = RecordResponseDTO::class))]),
ApiResponse(responseCode = "400", description = "Bad request",
content = [Content(schema = Schema(implementation = Void::class))])
])
fun getRecordByCfid(@PathVariable cfid: String?): Optional<RecordResponseDTO> {
return recordService!!.getRecordById(cfid!!).map { it.toDTO() }
}
@PostMapping
@Operation(summary = "Add Record by ID", tags=["record"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Return Record",
content = [Content(schema = Schema(implementation = RecordResponseDTO::class))]),
ApiResponse(responseCode = "400", description = "Bad request",
content = [Content(schema = Schema(implementation = Void::class))]),
])
fun createRecord(@RequestBody record: RecordRequestDTO): RecordResponseDTO {
return recordService!!.createRecord(record).toDTO()
}
@DeleteMapping("/{cfid}")
@Operation(summary = "Remove Record by ID", tags=["record"])
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Return Record",
content = [Content(schema = Schema(implementation = Void::class))]),
ApiResponse(responseCode = "400", description = "Bad request",
content = [Content(schema = Schema(implementation = Void::class))]),
])
fun deleteRecord(@PathVariable cfid: String?) {
recordService!!.deleteRecord(cfid!!)
}
private fun Record.toDTO() = RecordResponseDTO(
cfid = cfid!!,
name = name!!,
type = type!!,
content = content!!,
prio = prio!!,
ttl = ttl!!,
changeDate = changeDate!!,
auth = auth,
disabled = disabled
)
}

View File

@@ -0,0 +1,43 @@
package space.mori.dnsapi.db
import io.swagger.v3.oas.annotations.media.Schema
import jakarta.persistence.*
import java.util.*
@Entity
@Table(name = "domains")
class Domain {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private val id: Long? = null
@Column(nullable = false, length = 255)
val domainName: String? = null
@Column(nullable = true, length = 128)
val master: String? = null
@Column(nullable = true, name = "last_check")
val lastCheck: Int? = null
@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
}

View File

@@ -0,0 +1,14 @@
package space.mori.dnsapi.db
import jakarta.transaction.Transactional
import org.springframework.data.jpa.repository.JpaRepository
import java.util.*
interface DomainRepository : JpaRepository<Domain?, Long?> {
@Transactional
fun findByCfid(cfid: String): Optional<Domain>
@Transactional
fun deleteByCfid(cfid: String)
}

View File

@@ -0,0 +1,73 @@
package space.mori.dnsapi.db
import jakarta.persistence.*
import java.util.*
@Entity
@Table(name = "records")
class Record {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private val id: Long? = null
@ManyToOne
@JoinColumn(name = "domain_id", nullable = false)
var domain: Domain? = null
@Column(nullable = false, length = 255)
var name: String? = null
@Column(nullable = false, length = 10)
var type: String? = null
@Column(nullable = false, length = 64000)
var content: String? = null
@Column(nullable = true)
var ttl: Int?
@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
}
}

View File

@@ -0,0 +1,19 @@
package space.mori.dnsapi.db
import jakarta.transaction.Transactional
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import java.util.*
interface RecordRepository : JpaRepository<Record?, Long?> {
@Transactional
fun findByCfid(cfid: String): Optional<Record>
@Transactional
fun deleteByCfid(cfid: String)
@Query("SELECT r FROM Record r WHERE r.domain.cfid = :domainCfid")
fun findByDomainCfid(@Param("domainCfid") cfid: String): List<Record>
}

View File

@@ -0,0 +1,9 @@
package space.mori.dnsapi.dto
import io.swagger.v3.oas.annotations.media.Schema
@Schema(description = "Request DTO for Domain")
data class DomainRequestDTO(
@Schema(description = "Domain name(TLD)", example = "example.com")
val domainName: String
)

View File

@@ -0,0 +1,12 @@
package space.mori.dnsapi.dto
import io.swagger.v3.oas.annotations.media.Schema
@Schema(description = "Response DTO for Domain")
data class DomainResponseDTO(
@Schema(description = "Domain CFID", example = "123e4567e89b12d3a456426655440000")
val cfid: String,
@Schema(description = "Domain name(TLD)", example = "example.com")
val domainName: String
)

View File

@@ -0,0 +1,24 @@
package space.mori.dnsapi.dto
import io.swagger.v3.oas.annotations.media.Schema
@Schema(description = "Request DTO for Record")
data class RecordRequestDTO(
@Schema(description = "Host name", example = "www")
val host: String,
@Schema(description = "Record type", example = "A")
val type: String,
@Schema(description = "Record data", example = "192.0.2.1")
val data: String,
@Schema(description = "TTL (Time to Live)", example = "3600")
val ttl: Int,
@Schema(description = "Domain CFID", example = "123e4567e89b12d3a456426655440000")
val cfid: String,
@Schema(description = "comment", example="")
val comment: String
)

View File

@@ -0,0 +1,34 @@
package space.mori.dnsapi.dto
import io.swagger.v3.oas.annotations.media.Schema
import java.util.*
@Schema(description = "Response DTO for Record")
data class RecordResponseDTO(
@Schema(description = "Record CFID", example = "123e4567e89b12d3a456426655440001")
val cfid: String,
@Schema(description = "Host name", example = "www.domain.tld")
val name: String,
@Schema(description = "Record type", example = "A")
val type: String,
@Schema(description = "Record data", example = "192.0.2.1")
val content: String,
@Schema(description = "TTL (Time to Live)", example = "3600")
val ttl: Int,
@Schema(description = "TTL per second", example = "300s")
val prio: Int,
@Schema(description = "Changed date with Unix Timestamp")
val changeDate: Int,
@Schema(description = "is disabled?", example = "false")
val disabled: Boolean,
@Schema(description = "is authed", example = "true")
val auth: Boolean,
)

View File

@@ -0,0 +1,30 @@
package space.mori.dnsapi.service
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import space.mori.dnsapi.db.Domain
import space.mori.dnsapi.db.DomainRepository
import java.util.*
@Service
class DomainService {
@Autowired
private val domainRepository: DomainRepository? = null
fun getAllDomains(): List<Domain?> {
return domainRepository!!.findAll()
}
fun getDomainById(cfid: String): Optional<Domain> {
return domainRepository!!.findByCfid(cfid)
}
fun createDomain(domain: Domain): Domain {
return domainRepository!!.save<Domain>(domain)
}
fun deleteDomain(cfid: String) {
domainRepository!!.deleteByCfid(cfid)
}
}

View File

@@ -0,0 +1,46 @@
package space.mori.dnsapi.service
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import space.mori.dnsapi.db.DomainRepository
import space.mori.dnsapi.db.RecordRepository
import space.mori.dnsapi.db.Record as DomainRecord
import space.mori.dnsapi.dto.RecordRequestDTO
import java.util.*
@Service
class RecordService {
@Autowired
private lateinit var domainRepository: DomainRepository
@Autowired
private val recordRepository: RecordRepository? = null
fun getAllRecords(cfid: String): List<DomainRecord?> {
return recordRepository!!.findByDomainCfid(cfid)
}
fun getRecordById(cfid: String): Optional<DomainRecord> {
return recordRepository!!.findByCfid(cfid)
}
fun createRecord(record: RecordRequestDTO): DomainRecord {
val domain = domainRepository.findByCfid(record.cfid)
.orElseThrow { IllegalArgumentException("Invalid domain CFID") }
val r = DomainRecord(
name = record.host,
type = record.type,
content = record.data,
ttl = record.ttl,
domain = domain,
comment = record.comment,
changeDate = java.util.Date().time.toInt(),
disabled = false
)
return recordRepository!!.save(r)
}
fun deleteRecord(cfid: String) {
recordRepository!!.deleteByCfid(cfid)
}
}

View File

@@ -0,0 +1,3 @@
springdoc.api-docs.enabled=true
springdoc.swagger-ui.enabled=true
spring.jpa.show-sql=true

View File

@@ -0,0 +1,14 @@
spring.application.name=dnsapi
spring.datasource.url=jdbc:mariadb://${DB_HOST}:${DB_PORT}/${DB_NAME}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
springdoc.api-docs.enabled=false
springdoc.swagger-ui.enabled=false
springdoc.api-docs.path=/api-docs
springdoc.default-consumes-media-type= application/json
springdoc.default-produces-media-type= application/json
springdoc.version= '@project.version@'

View File

@@ -0,0 +1,13 @@
package space.mori.dnsapi
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class DnsapiApplicationTests {
@Test
fun contextLoads() {
}
}