diff --git a/app/src/main/java/de/bollwerk/app/data/repository/MessageRepositoryImpl.kt b/app/src/main/java/de/bollwerk/app/data/repository/MessageRepositoryImpl.kt index 2ff6ec5..68337bb 100644 --- a/app/src/main/java/de/bollwerk/app/data/repository/MessageRepositoryImpl.kt +++ b/app/src/main/java/de/bollwerk/app/data/repository/MessageRepositoryImpl.kt @@ -70,11 +70,20 @@ internal class MessageRepositoryImpl @Inject constructor( when (event) { is WebSocketEvent.NewMessage -> { val msg = event.message - val decryptedBody = try { - e2eeKeyManager.decryptMessage(msg.body) - } catch (e: Exception) { - Log.e(TAG, "E2EE: Failed to decrypt message ${msg.id}", e) - "[Entschlüsselung fehlgeschlagen]" + val decryptedBody = when { + msg.body.startsWith("[PLAINTEXT] ") -> { + // Admin test messages – remove prefix + msg.body.removePrefix("[PLAINTEXT] ") + } + 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( MessageEntity( diff --git a/server/src/main/kotlin/de/bollwerk/server/routes/MessageRoutes.kt b/server/src/main/kotlin/de/bollwerk/server/routes/MessageRoutes.kt index 7e6b519..57845fc 100644 --- a/server/src/main/kotlin/de/bollwerk/server/routes/MessageRoutes.kt +++ b/server/src/main/kotlin/de/bollwerk/server/routes/MessageRoutes.kt @@ -95,4 +95,54 @@ internal fun Route.messageRoutes( call.respond(HttpStatusCode.OK, messages) } } + + route("/api/admin/send-message") { + post { + val principal = call.principal() + ?: 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() + 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)) + } + } }