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()
|
configurePage()
|
||||||
configureSchematic()
|
configureSchematic()
|
||||||
configureAuth()
|
configureAuth()
|
||||||
|
configureAuditLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user