📮 Adds action routes

This commit is contained in:
Daniel Svitan 2025-05-11 18:20:52 +02:00
parent de3bc8897e
commit 2fa523b814
6 changed files with 132 additions and 28 deletions

View File

@ -0,0 +1,38 @@
package dev.svitan.plugins
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.NotFoundException
import io.ktor.server.plugins.statuspages.StatusPages
import io.ktor.server.response.respond
class AuthorizationException(message: String) : Exception(message)
fun Application.configureErrors() {
install(StatusPages) {
exception<Throwable> { call, cause ->
when (cause) {
is IllegalArgumentException -> call.respond(
status = HttpStatusCode.BadRequest,
message = cause.message!!
)
is NotFoundException -> call.respond(
status = HttpStatusCode.NotFound,
message = cause.message ?: ""
)
is AuthorizationException -> call.respond(
status = HttpStatusCode.Unauthorized,
message = cause.message ?: ""
)
else -> call.respond(
status = HttpStatusCode.InternalServerError,
message = cause.message ?: ""
)
}
}
}
}

View File

@ -1,14 +1,11 @@
package dev.svitan.plugins
import dev.svitan.routes.routeAction
import dev.svitan.routes.routeAuth
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.plugins.NotFoundException
import io.ktor.server.plugins.requestvalidation.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.response.*
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import io.ktor.server.routing.*
fun Application.configureRouting() {
install(RequestValidation) {
@ -19,27 +16,6 @@ fun Application.configureRouting() {
}
}
install(StatusPages) {
exception<Throwable> { call, cause ->
when (cause) {
is IllegalArgumentException -> call.respond(
status = HttpStatusCode.BadRequest,
message = cause.message!!
)
is NotFoundException -> call.respond(
status = HttpStatusCode.NotFound,
message = cause.message ?: ""
)
else -> call.respond(
status = HttpStatusCode.InternalServerError,
message = cause.message ?: ""
)
}
}
}
routing {
get("/") {
call.respond("Hello World!")
@ -47,4 +23,5 @@ fun Application.configureRouting() {
}
routeAuth()
routeAction()
}

View File

@ -0,0 +1,67 @@
package dev.svitan.routes
import dev.svitan.plugins.AuthorizationException
import dev.svitan.schemas.ActionService
import dev.svitan.schemas.AuthService
import dev.svitan.schemas.NewActionDTO
import dev.svitan.schemas.RunActionDTO
import dev.svitan.schemas.sha256
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.plugins.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*
fun Application.routeAction() {
routing {
authentication {
get("/action") {
call.respond(ActionService.readAll())
}
post("/action") {
val action = call.receive<NewActionDTO>()
call.respond(HttpStatusCode.Created, ActionService.create(action))
}
get("/action/{id}") {
val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id")
val id = UUID.fromString(idRaw)
val action = ActionService.read(id) ?: throw NotFoundException()
call.respond(action)
}
post("/action/{id}") {
val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id")
val id = UUID.fromString(idRaw)
}
put("/action/{id}") {
val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id")
val id = UUID.fromString(idRaw)
val action = ActionService.read(id) ?: throw NotFoundException()
val run = call.receive<RunActionDTO>()
val auth = AuthService.validate(
UUID.fromString(action.authId),
run.pin,
run.key
) ?: throw AuthorizationException("Invalid pin or key")
// TODO: run the action
call.respond(HttpStatusCode.NoContent)
}
delete("/action/{id}") {
val idRaw = call.parameters["id"] ?: throw BadRequestException("Invalid id")
val id = UUID.fromString(idRaw)
ActionService.delete(id)
call.respond(HttpStatusCode.NoContent)
}
}
}
}

View File

@ -19,7 +19,7 @@ fun Application.routeAuth() {
call.respond(AuthService.readAll())
}
put("/auth") {
post("/auth") {
val auth = call.receive<NewAuthDTO>()
call.respond(HttpStatusCode.Created, AuthService.create(auth))
}

View File

@ -40,7 +40,16 @@ class ActionDTO(
@Serializable
class NewActionDTO(
val name: String, val kind: String, val source: String, val authId: String
val name: String,
val kind: String,
val source: String,
val authId: String
)
@Serializable
class RunActionDTO(
val pin: String,
val key: String
)
class ActionService {

View File

@ -73,6 +73,19 @@ class AuthService {
}
}
fun validate(id: UUID, pin: String, key: String): AuthDTO? = transaction {
Auths.selectAll()
.where { (Auths.id eq id) and (Auths.pin eq sha256(pin)) and (Auths.key eq sha256(key)) }
.map {
AuthDTO(
it[Auths.id].toString(),
it[Auths.name],
it[Auths.createdAt]
)
}
.singleOrNull()
}
fun delete(id: UUID) {
transaction {
Auths.deleteWhere { Auths.id eq id }