forked from SteamWar/SteamWar
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.routes
|
||||
|
||||
import de.steamwar.plugins.SWPermissionCheck
|
||||
import de.steamwar.sql.AuditLog
|
||||
import de.steamwar.sql.AuditLogTable
|
||||
import de.steamwar.sql.SteamwarUserTable
|
||||
import de.steamwar.sql.UserPerm
|
||||
import de.steamwar.sql.internal.useDb
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jetbrains.exposed.v1.core.JoinType
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
import org.jetbrains.exposed.v1.core.alias
|
||||
import org.jetbrains.exposed.v1.core.greater
|
||||
import org.jetbrains.exposed.v1.core.inList
|
||||
import org.jetbrains.exposed.v1.core.isNull
|
||||
import org.jetbrains.exposed.v1.core.less
|
||||
import org.jetbrains.exposed.v1.core.like
|
||||
import org.jetbrains.exposed.v1.core.or
|
||||
import org.jetbrains.exposed.v1.jdbc.andWhere
|
||||
import org.jetbrains.exposed.v1.jdbc.select
|
||||
import java.time.Instant
|
||||
|
||||
fun Route.configureAuditLog() {
|
||||
route("/auditlog") {
|
||||
install(SWPermissionCheck) {
|
||||
permission = UserPerm.MODERATION
|
||||
}
|
||||
|
||||
get {
|
||||
val text = call.request.queryParameters["fullText"]
|
||||
val actionText = call.request.queryParameters["actionText"]
|
||||
val serverText = call.request.queryParameters["server"]
|
||||
val actor = call.request.queryParameters.getAll("actor")?.toSet()
|
||||
val actionType =
|
||||
call.request.queryParameters.getAll("actionType")?.map { AuditLog.Type.valueOf(it) }?.toSet()
|
||||
val timeGreater = call.request.queryParameters["timeGreater"]?.let { Instant.ofEpochMilli(it.toLong()) }
|
||||
val timeLess = call.request.queryParameters["timeLess"]?.let { Instant.ofEpochMilli(it.toLong()) }
|
||||
|
||||
val serverOwner = call.request.queryParameters.getAll("serverOwner")?.toSet()
|
||||
val velocity = call.request.queryParameters["velocity"]?.toBoolean()
|
||||
|
||||
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 0
|
||||
val limit = call.request.queryParameters["limit"]?.toIntOrNull() ?: 100
|
||||
val sorting = call.request.queryParameters["sorting"] ?: "DESC"
|
||||
|
||||
call.respond(
|
||||
PagedAuditLog.filter(
|
||||
actionText,
|
||||
serverText,
|
||||
text,
|
||||
actor,
|
||||
actionType,
|
||||
timeGreater,
|
||||
timeLess,
|
||||
serverOwner,
|
||||
velocity,
|
||||
page,
|
||||
limit,
|
||||
sorting
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class PagedAuditLog(val rows: Long, val entries: List<AuditLogEntry>) {
|
||||
|
||||
companion object {
|
||||
fun filter(
|
||||
actionText: String? = null,
|
||||
serverText: String? = null,
|
||||
fullText: String? = null,
|
||||
actor: Set<String>? = null,
|
||||
actionType: Set<AuditLog.Type>? = null,
|
||||
timeGreater: Instant? = null,
|
||||
timeLess: Instant? = null,
|
||||
serverOwner: Set<String>? = null,
|
||||
velocity: Boolean? = null,
|
||||
page: Int = 0,
|
||||
limit: Int = 100,
|
||||
sorting: String = "DESC"
|
||||
) = useDb {
|
||||
val actorTable = SteamwarUserTable.alias("actor")
|
||||
val serverOwnerTable = SteamwarUserTable.alias("serverOwner")
|
||||
|
||||
val query = AuditLogTable.join(
|
||||
actorTable,
|
||||
JoinType.INNER,
|
||||
onColumn = actorTable[SteamwarUserTable.id],
|
||||
otherColumn = AuditLogTable.actor
|
||||
)
|
||||
.join(
|
||||
serverOwnerTable,
|
||||
JoinType.LEFT,
|
||||
onColumn = serverOwnerTable[SteamwarUserTable.id],
|
||||
otherColumn = AuditLogTable.serverOwner
|
||||
)
|
||||
.select(
|
||||
actorTable[SteamwarUserTable.username], serverOwnerTable[SteamwarUserTable.username],
|
||||
*AuditLogTable.columns.toTypedArray()
|
||||
)
|
||||
|
||||
|
||||
actionText?.let {
|
||||
query.andWhere { (AuditLogTable.actionText like "%$it%") }
|
||||
}
|
||||
|
||||
serverText?.let {
|
||||
query.andWhere { (AuditLogTable.server like "%$it%") }
|
||||
}
|
||||
|
||||
fullText?.let {
|
||||
query.andWhere { (AuditLogTable.actionText like "%$it%") or (AuditLogTable.server like "%$it%") }
|
||||
}
|
||||
|
||||
actor?.let { query.andWhere { actorTable[SteamwarUserTable.uuid] inList actor } }
|
||||
|
||||
actionType?.let { query.andWhere { AuditLogTable.action inList actionType } }
|
||||
|
||||
timeGreater?.let { query.andWhere { AuditLogTable.time greater timeGreater } }
|
||||
|
||||
timeLess?.let { query.andWhere { AuditLogTable.time less timeLess } }
|
||||
|
||||
serverOwner?.let { query.andWhere { serverOwnerTable[SteamwarUserTable.uuid] inList serverOwner } }
|
||||
|
||||
if (velocity == true) query.andWhere { AuditLogTable.serverOwner.isNull() }
|
||||
|
||||
PagedAuditLog(
|
||||
query.count(),
|
||||
query
|
||||
.limit(limit)
|
||||
.offset((page * limit).toLong())
|
||||
.orderBy(AuditLogTable.time, SortOrder.valueOf(sorting.uppercase()))
|
||||
.map {
|
||||
AuditLogEntry(
|
||||
it[AuditLogTable.id].value,
|
||||
it[AuditLogTable.time].toEpochMilli(),
|
||||
it[AuditLogTable.server],
|
||||
it[serverOwnerTable[SteamwarUserTable.username]],
|
||||
it[actorTable[SteamwarUserTable.username]],
|
||||
it[AuditLogTable.action],
|
||||
it[AuditLogTable.actionText]
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class AuditLogEntry(
|
||||
val id: Int,
|
||||
val time: Long,
|
||||
val server: String,
|
||||
val serverOwner: String?,
|
||||
val actor: String,
|
||||
val actionType: AuditLog.Type,
|
||||
val actionText: String
|
||||
)
|
||||
@@ -33,6 +33,7 @@ fun Application.configureRoutes() {
|
||||
configurePage()
|
||||
configureSchematic()
|
||||
configureAuth()
|
||||
configureAuditLog()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user