From ac9523c73c6b3b5e1646580145a9e04d29045fc7 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sun, 14 Jun 2026 07:04:08 +0000 Subject: [PATCH] Add repository overview endpoint --- src/main/kotlin/de/ruvnox/tactical/Main.kt | 2 ++ .../de/ruvnox/tactical/api/SystemDtos.kt | 20 +++++++++++ .../de/ruvnox/tactical/api/SystemRoutes.kt | 35 +++++++++++++++++++ .../repository/AuditEventRepository.kt | 7 ++++ .../tactical/repository/DeviceRepository.kt | 11 ++++++ .../tactical/repository/MessageRepository.kt | 7 ++++ .../repository/OperationRoomRepository.kt | 11 ++++++ .../tactical/repository/RepositorySupport.kt | 13 +++++++ .../tactical/repository/UserRepository.kt | 11 ++++++ 9 files changed, 117 insertions(+) create mode 100644 src/main/kotlin/de/ruvnox/tactical/api/SystemDtos.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/api/SystemRoutes.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/repository/AuditEventRepository.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/repository/DeviceRepository.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/repository/MessageRepository.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/repository/OperationRoomRepository.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/repository/RepositorySupport.kt create mode 100644 src/main/kotlin/de/ruvnox/tactical/repository/UserRepository.kt diff --git a/src/main/kotlin/de/ruvnox/tactical/Main.kt b/src/main/kotlin/de/ruvnox/tactical/Main.kt index 373ea88..aef60dc 100644 --- a/src/main/kotlin/de/ruvnox/tactical/Main.kt +++ b/src/main/kotlin/de/ruvnox/tactical/Main.kt @@ -1,6 +1,7 @@ package de.ruvnox.tactical import de.ruvnox.tactical.api.statusRoutes +import de.ruvnox.tactical.api.systemRoutes import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.engine.embeddedServer @@ -35,5 +36,6 @@ fun Application.module() { routing { statusRoutes() + systemRoutes() } } diff --git a/src/main/kotlin/de/ruvnox/tactical/api/SystemDtos.kt b/src/main/kotlin/de/ruvnox/tactical/api/SystemDtos.kt new file mode 100644 index 0000000..d20c1fa --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/api/SystemDtos.kt @@ -0,0 +1,20 @@ +package de.ruvnox.tactical.api + +import kotlinx.serialization.Serializable + +@Serializable +data class CountSummaryResponse( + val total: Long, + val active: Long +) + +@Serializable +data class SystemOverviewResponse( + val service: String, + val status: String, + val users: CountSummaryResponse, + val devices: CountSummaryResponse, + val operationRooms: CountSummaryResponse, + val messages: Long, + val auditEvents: Long +) diff --git a/src/main/kotlin/de/ruvnox/tactical/api/SystemRoutes.kt b/src/main/kotlin/de/ruvnox/tactical/api/SystemRoutes.kt new file mode 100644 index 0000000..9bc4043 --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/api/SystemRoutes.kt @@ -0,0 +1,35 @@ +package de.ruvnox.tactical.api + +import de.ruvnox.tactical.repository.AuditEventRepository +import de.ruvnox.tactical.repository.DeviceRepository +import de.ruvnox.tactical.repository.MessageRepository +import de.ruvnox.tactical.repository.OperationRoomRepository +import de.ruvnox.tactical.repository.UserRepository +import io.ktor.server.response.respond +import io.ktor.server.routing.Route +import io.ktor.server.routing.get + +fun Route.systemRoutes() { + get("/api/v1/system/overview") { + call.respond( + SystemOverviewResponse( + service = "ruvnox-tactical-server", + status = "online", + users = CountSummaryResponse( + total = UserRepository.countTotal(), + active = UserRepository.countActive() + ), + devices = CountSummaryResponse( + total = DeviceRepository.countTotal(), + active = DeviceRepository.countActive() + ), + operationRooms = CountSummaryResponse( + total = OperationRoomRepository.countTotal(), + active = OperationRoomRepository.countActive() + ), + messages = MessageRepository.countTotal(), + auditEvents = AuditEventRepository.countTotal() + ) + ) + } +} diff --git a/src/main/kotlin/de/ruvnox/tactical/repository/AuditEventRepository.kt b/src/main/kotlin/de/ruvnox/tactical/repository/AuditEventRepository.kt new file mode 100644 index 0000000..57daf7a --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/repository/AuditEventRepository.kt @@ -0,0 +1,7 @@ +package de.ruvnox.tactical.repository + +object AuditEventRepository { + fun countTotal(): Long { + return queryLong("select count(*) from audit_events") + } +} diff --git a/src/main/kotlin/de/ruvnox/tactical/repository/DeviceRepository.kt b/src/main/kotlin/de/ruvnox/tactical/repository/DeviceRepository.kt new file mode 100644 index 0000000..2a365fb --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/repository/DeviceRepository.kt @@ -0,0 +1,11 @@ +package de.ruvnox.tactical.repository + +object DeviceRepository { + fun countTotal(): Long { + return queryLong("select count(*) from devices") + } + + fun countActive(): Long { + return queryLong("select count(*) from devices where is_active = true") + } +} diff --git a/src/main/kotlin/de/ruvnox/tactical/repository/MessageRepository.kt b/src/main/kotlin/de/ruvnox/tactical/repository/MessageRepository.kt new file mode 100644 index 0000000..ad59a7b --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/repository/MessageRepository.kt @@ -0,0 +1,7 @@ +package de.ruvnox.tactical.repository + +object MessageRepository { + fun countTotal(): Long { + return queryLong("select count(*) from messages") + } +} diff --git a/src/main/kotlin/de/ruvnox/tactical/repository/OperationRoomRepository.kt b/src/main/kotlin/de/ruvnox/tactical/repository/OperationRoomRepository.kt new file mode 100644 index 0000000..69c69e0 --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/repository/OperationRoomRepository.kt @@ -0,0 +1,11 @@ +package de.ruvnox.tactical.repository + +object OperationRoomRepository { + fun countTotal(): Long { + return queryLong("select count(*) from operation_rooms") + } + + fun countActive(): Long { + return queryLong("select count(*) from operation_rooms where is_active = true") + } +} diff --git a/src/main/kotlin/de/ruvnox/tactical/repository/RepositorySupport.kt b/src/main/kotlin/de/ruvnox/tactical/repository/RepositorySupport.kt new file mode 100644 index 0000000..21154b2 --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/repository/RepositorySupport.kt @@ -0,0 +1,13 @@ +package de.ruvnox.tactical.repository + +import de.ruvnox.tactical.Database + +internal fun queryLong(sql: String): Long { + return Database.connection().use { connection -> + connection.createStatement().use { statement -> + statement.executeQuery(sql).use { result -> + if (result.next()) result.getLong(1) else 0L + } + } + } +} diff --git a/src/main/kotlin/de/ruvnox/tactical/repository/UserRepository.kt b/src/main/kotlin/de/ruvnox/tactical/repository/UserRepository.kt new file mode 100644 index 0000000..c529f8d --- /dev/null +++ b/src/main/kotlin/de/ruvnox/tactical/repository/UserRepository.kt @@ -0,0 +1,11 @@ +package de.ruvnox.tactical.repository + +object UserRepository { + fun countTotal(): Long { + return queryLong("select count(*) from app_users") + } + + fun countActive(): Long { + return queryLong("select count(*) from app_users where is_active = true") + } +}