feat: add plaintext admin message endpoint for testing

- New POST /api/admin/send-message endpoint (admin-only)
- Messages prefixed with [PLAINTEXT] bypass E2EE decryption
- App recognizes [PLAINTEXT] marker and strips it before display
- Allows easy chat testing without E2EE key management
This commit is contained in:
Jens Reinemann 2026-05-18 13:56:15 +02:00
parent 30e86bb7e0
commit ca6cfbfad9
2 changed files with 64 additions and 5 deletions

View file

@ -70,11 +70,20 @@ internal class MessageRepositoryImpl @Inject constructor(
when (event) { when (event) {
is WebSocketEvent.NewMessage -> { is WebSocketEvent.NewMessage -> {
val msg = event.message val msg = event.message
val decryptedBody = try { val decryptedBody = when {
e2eeKeyManager.decryptMessage(msg.body) msg.body.startsWith("[PLAINTEXT] ") -> {
} catch (e: Exception) { // Admin test messages remove prefix
Log.e(TAG, "E2EE: Failed to decrypt message ${msg.id}", e) msg.body.removePrefix("[PLAINTEXT] ")
"[Entschlüsselung fehlgeschlagen]" }
else -> {
// E2EE messages decrypt
try {
e2eeKeyManager.decryptMessage(msg.body)
} catch (e: Exception) {
Log.e(TAG, "E2EE: Failed to decrypt message ${msg.id}", e)
"[Entschlüsselung fehlgeschlagen]"
}
}
} }
dao.upsert( dao.upsert(
MessageEntity( MessageEntity(

View file

@ -95,4 +95,54 @@ internal fun Route.messageRoutes(
call.respond(HttpStatusCode.OK, messages) call.respond(HttpStatusCode.OK, messages)
} }
} }
route("/api/admin/send-message") {
post {
val principal = call.principal<UserPrincipal>()
?: return@post call.respond(
HttpStatusCode.Unauthorized,
ErrorResponse(status = 401, message = "Unauthorized")
)
// Only admins can send test messages
if (!principal.isAdmin) {
return@post call.respond(
HttpStatusCode.Forbidden,
ErrorResponse(status = 403, message = "Admin access required")
)
}
val request = call.receive<SendMessageRequest>()
if (request.body.isBlank()) {
call.respond(
HttpStatusCode.BadRequest,
ErrorResponse(status = 400, message = "Body must not be empty")
)
return@post
}
if (userRepository.findById(request.receiverId) == null) {
call.respond(
HttpStatusCode.NotFound,
ErrorResponse(status = 404, message = "Receiver not found")
)
return@post
}
val msgId = request.id ?: UUID.randomUUID().toString()
// Wrap body with [PLAINTEXT] marker so app doesn't try to decrypt
val plaintext = "[PLAINTEXT] ${request.body}"
val message = messageRepository.save(
id = msgId,
senderId = principal.userId,
senderUsername = principal.username,
receiverId = request.receiverId,
body = plaintext,
sentAt = request.sentAt
)
wsManager.notifyNewMessage(request.receiverId, message)
if (wsManager.isOnline(request.receiverId)) {
messageRepository.markDelivered(msgId)
}
call.respond(HttpStatusCode.Created, SendMessageResponse(message = message))
}
}
} }