mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
briar-headless: Add a websocket controller for private message events
Also version API URLs
This commit is contained in:
@@ -22,6 +22,7 @@ import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
||||
import org.briarproject.bramble.plugin.tor.LinuxTorPluginFactory;
|
||||
import org.briarproject.bramble.system.JavaSystemModule;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.headless.messaging.MessagingModule;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.GeneralSecurityException;
|
||||
@@ -42,7 +43,8 @@ import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBL
|
||||
@Module(includes = {
|
||||
JavaNetworkModule.class,
|
||||
JavaSystemModule.class,
|
||||
CircumventionModule.class
|
||||
CircumventionModule.class,
|
||||
MessagingModule.class
|
||||
})
|
||||
public class HeadlessModule {
|
||||
|
||||
@@ -119,6 +121,14 @@ public class HeadlessModule {
|
||||
return devConfig;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
WebSocketController provideWebSocketHandler(
|
||||
WebSocketControllerImpl webSocketController) {
|
||||
return webSocketController;
|
||||
}
|
||||
|
||||
|
||||
private File appDir(ConfigurationManager configurationManager,
|
||||
String file) {
|
||||
return new File(configurationManager.getAppDir(), file);
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.javalin.JavalinEvent.SERVER_START_FAILED
|
||||
import io.javalin.JavalinEvent.SERVER_STOPPED
|
||||
import io.javalin.apibuilder.ApiBuilder.*
|
||||
import org.briarproject.briar.headless.blogs.BlogController
|
||||
import org.briarproject.briar.headless.contact.ContactController
|
||||
import org.briarproject.briar.headless.forums.ForumController
|
||||
import org.briarproject.briar.headless.messaging.MessagingController
|
||||
import java.lang.Runtime.getRuntime
|
||||
@@ -19,7 +18,8 @@ import kotlin.system.exitProcess
|
||||
class Router @Inject
|
||||
constructor(
|
||||
private val briarService: BriarService,
|
||||
private val contactController: ContactController,
|
||||
private val webSocketController: WebSocketController,
|
||||
private val contactController: MessagingController,
|
||||
private val messagingController: MessagingController,
|
||||
private val forumController: ForumController,
|
||||
private val blogController: BlogController
|
||||
@@ -40,24 +40,30 @@ constructor(
|
||||
.start()
|
||||
|
||||
app.routes {
|
||||
path("/contacts") {
|
||||
get { ctx -> contactController.list(ctx) }
|
||||
}
|
||||
path("/messages/:contactId") {
|
||||
get { ctx -> messagingController.list(ctx) }
|
||||
post { ctx -> messagingController.write(ctx) }
|
||||
}
|
||||
path("/forums") {
|
||||
get { ctx -> forumController.list(ctx) }
|
||||
post { ctx -> forumController.create(ctx) }
|
||||
}
|
||||
path("/blogs") {
|
||||
path("/posts") {
|
||||
get { ctx -> blogController.listPosts(ctx) }
|
||||
post { ctx -> blogController.createPost(ctx) }
|
||||
path("/v1") {
|
||||
path("/contacts") {
|
||||
get { ctx -> contactController.list(ctx) }
|
||||
}
|
||||
path("/messages/:contactId") {
|
||||
get { ctx -> messagingController.list(ctx) }
|
||||
post { ctx -> messagingController.write(ctx) }
|
||||
}
|
||||
path("/forums") {
|
||||
get { ctx -> forumController.list(ctx) }
|
||||
post { ctx -> forumController.create(ctx) }
|
||||
}
|
||||
path("/blogs") {
|
||||
path("/posts") {
|
||||
get { ctx -> blogController.listPosts(ctx) }
|
||||
post { ctx -> blogController.createPost(ctx) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
app.ws("/v1/ws") { ws ->
|
||||
ws.onConnect { session -> webSocketController.sessions.add(session) }
|
||||
ws.onClose { session, _, _ -> webSocketController.sessions.remove(session) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun stop() {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.briarproject.briar.headless
|
||||
|
||||
import io.javalin.websocket.WsSession
|
||||
|
||||
interface WebSocketController {
|
||||
|
||||
val sessions: MutableSet<WsSession>
|
||||
|
||||
fun sendEvent(name: String, obj: Any)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.briarproject.briar.headless
|
||||
|
||||
import io.javalin.json.JavalinJson
|
||||
import io.javalin.websocket.WsSession
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.annotation.concurrent.Immutable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Immutable
|
||||
@Singleton
|
||||
internal class WebSocketControllerImpl @Inject constructor() : WebSocketController {
|
||||
|
||||
override val sessions: MutableSet<WsSession> = ConcurrentHashMap.newKeySet<WsSession>()
|
||||
|
||||
override fun sendEvent(name: String, obj: Any) {
|
||||
sessions.forEach { session ->
|
||||
val event = OutputEvent(name, obj)
|
||||
val json = JavalinJson.toJsonMapper.map(event)
|
||||
session.send(json)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Suppress("unused")
|
||||
internal class OutputEvent(val name: String, val data: Any) {
|
||||
val type = "event"
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
package org.briarproject.briar.headless.messaging
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.briar.api.messaging.PrivateMessage
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
||||
|
||||
internal fun PrivateMessageHeader.output(body: String) = OutputPrivateMessage(this, body)
|
||||
internal fun PrivateMessageHeader.output(contactId: ContactId, body: String) =
|
||||
OutputPrivateMessage(this, contactId, body)
|
||||
|
||||
internal fun PrivateMessage.output(body: String) = OutputPrivateMessage(this, body)
|
||||
internal fun PrivateMessage.output(contactId: ContactId, body: String) =
|
||||
OutputPrivateMessage(this, contactId, body)
|
||||
|
||||
internal fun PrivateMessageReceivedEvent.output(body: String) =
|
||||
messageHeader.output(contactId, body)
|
||||
|
||||
@@ -1,66 +1,11 @@
|
||||
package org.briarproject.briar.headless.messaging
|
||||
|
||||
import io.javalin.BadRequestResponse
|
||||
import io.javalin.Context
|
||||
import io.javalin.NotFoundResponse
|
||||
import org.briarproject.bramble.api.contact.Contact
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.bramble.api.contact.ContactManager
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||
import org.briarproject.bramble.api.system.Clock
|
||||
import org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH
|
||||
import org.briarproject.briar.api.messaging.MessagingManager
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory
|
||||
import javax.annotation.concurrent.Immutable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Immutable
|
||||
@Singleton
|
||||
class MessagingController @Inject
|
||||
constructor(
|
||||
private val messagingManager: MessagingManager,
|
||||
private val privateMessageFactory: PrivateMessageFactory,
|
||||
private val contactManager: ContactManager,
|
||||
private val clock: Clock
|
||||
) {
|
||||
interface MessagingController {
|
||||
|
||||
fun list(ctx: Context): Context {
|
||||
val contact = getContact(ctx)
|
||||
fun list(ctx: Context): Context
|
||||
|
||||
val messages = messagingManager.getMessageHeaders(contact.id).map { header ->
|
||||
val body = messagingManager.getMessageBody(header.id)
|
||||
header.output(body)
|
||||
}
|
||||
return ctx.json(messages)
|
||||
}
|
||||
|
||||
fun write(ctx: Context): Context {
|
||||
val contact = getContact(ctx)
|
||||
|
||||
val message = ctx.formParam("message")
|
||||
if (message == null || message.isEmpty())
|
||||
throw BadRequestResponse("Expecting Message text")
|
||||
if (message.length > MAX_PRIVATE_MESSAGE_BODY_LENGTH)
|
||||
throw BadRequestResponse("Message text too large")
|
||||
|
||||
val group = messagingManager.getContactGroup(contact)
|
||||
val now = clock.currentTimeMillis()
|
||||
val m = privateMessageFactory.createPrivateMessage(group.id, now, message)
|
||||
|
||||
messagingManager.addLocalMessage(m)
|
||||
return ctx.json(m.output(message))
|
||||
}
|
||||
|
||||
private fun getContact(ctx: Context): Contact {
|
||||
val contactString = ctx.pathParam("contactId")
|
||||
val contactInt = Integer.parseInt(contactString)
|
||||
val contactId = ContactId(contactInt)
|
||||
return try {
|
||||
contactManager.getContact(contactId)
|
||||
} catch (e: NoSuchContactException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
}
|
||||
fun write(ctx: Context): Context
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.briarproject.briar.headless.messaging
|
||||
|
||||
import io.javalin.BadRequestResponse
|
||||
import io.javalin.Context
|
||||
import io.javalin.NotFoundResponse
|
||||
import org.briarproject.bramble.api.contact.Contact
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.bramble.api.contact.ContactManager
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||
import org.briarproject.bramble.api.event.Event
|
||||
import org.briarproject.bramble.api.event.EventListener
|
||||
import org.briarproject.bramble.api.system.Clock
|
||||
import org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH
|
||||
import org.briarproject.briar.api.messaging.MessagingManager
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory
|
||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
||||
import org.briarproject.briar.headless.WebSocketController
|
||||
import java.util.concurrent.Executor
|
||||
import javax.annotation.concurrent.Immutable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Immutable
|
||||
@Singleton
|
||||
internal class MessagingControllerImpl @Inject
|
||||
constructor(
|
||||
private val messagingManager: MessagingManager,
|
||||
private val privateMessageFactory: PrivateMessageFactory,
|
||||
private val contactManager: ContactManager,
|
||||
private val webSocketController: WebSocketController,
|
||||
@DatabaseExecutor private val dbExecutor: Executor,
|
||||
private val clock: Clock
|
||||
) : MessagingController, EventListener {
|
||||
|
||||
override fun list(ctx: Context): Context {
|
||||
val contact = getContact(ctx)
|
||||
|
||||
val messages = messagingManager.getMessageHeaders(contact.id).map { header ->
|
||||
val body = messagingManager.getMessageBody(header.id)
|
||||
header.output(contact.id, body)
|
||||
}
|
||||
return ctx.json(messages)
|
||||
}
|
||||
|
||||
override fun write(ctx: Context): Context {
|
||||
val contact = getContact(ctx)
|
||||
|
||||
val message = ctx.formParam("message")
|
||||
if (message == null || message.isEmpty())
|
||||
throw BadRequestResponse("Expecting Message text")
|
||||
if (message.length > MAX_PRIVATE_MESSAGE_BODY_LENGTH)
|
||||
throw BadRequestResponse("Message text too large")
|
||||
|
||||
val group = messagingManager.getContactGroup(contact)
|
||||
val now = clock.currentTimeMillis()
|
||||
val m = privateMessageFactory.createPrivateMessage(group.id, now, message)
|
||||
|
||||
messagingManager.addLocalMessage(m)
|
||||
return ctx.json(m.output(contact.id, message))
|
||||
}
|
||||
|
||||
override fun eventOccurred(e: Event) {
|
||||
when (e) {
|
||||
is PrivateMessageReceivedEvent -> dbExecutor.run {
|
||||
val name =
|
||||
"org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent"
|
||||
val body = messagingManager.getMessageBody(e.messageHeader.id)
|
||||
webSocketController.sendEvent(name, e.output(body))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContact(ctx: Context): Contact {
|
||||
val contactString = ctx.pathParam("contactId")
|
||||
val contactInt = Integer.parseInt(contactString)
|
||||
val contactId = ContactId(contactInt)
|
||||
return try {
|
||||
contactManager.getContact(contactId)
|
||||
} catch (e: NoSuchContactException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.briarproject.briar.headless.messaging
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.briarproject.bramble.api.event.EventBus
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class MessagingModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideMessagingController(
|
||||
eventBus: EventBus, messagingController: MessagingControllerImpl
|
||||
): MessagingController {
|
||||
eventBus.addListener(messagingController)
|
||||
return messagingController
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.headless.messaging
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.briar.api.messaging.PrivateMessage
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
||||
import javax.annotation.concurrent.Immutable
|
||||
@@ -16,8 +17,9 @@ internal class OutputPrivateMessage {
|
||||
val local: Boolean
|
||||
val id: ByteArray
|
||||
val groupId: ByteArray
|
||||
val contactId: Int
|
||||
|
||||
internal constructor(header: PrivateMessageHeader, body: String) {
|
||||
internal constructor(header: PrivateMessageHeader, contactId: ContactId, body: String) {
|
||||
this.body = body
|
||||
this.timestamp = header.timestamp
|
||||
this.read = header.isRead
|
||||
@@ -26,12 +28,13 @@ internal class OutputPrivateMessage {
|
||||
this.local = header.isLocal
|
||||
this.id = header.id.bytes
|
||||
this.groupId = header.groupId.bytes
|
||||
this.contactId = contactId.int
|
||||
}
|
||||
|
||||
/**
|
||||
* Only meant for own [PrivateMessage]s directly after creation.
|
||||
*/
|
||||
internal constructor(m: PrivateMessage, body: String) {
|
||||
internal constructor(m: PrivateMessage, contactId: ContactId, body: String) {
|
||||
this.body = body
|
||||
this.timestamp = m.message.timestamp
|
||||
this.read = true
|
||||
@@ -40,5 +43,6 @@ internal class OutputPrivateMessage {
|
||||
this.local = true
|
||||
this.id = m.message.id.bytes
|
||||
this.groupId = m.message.groupId.bytes
|
||||
this.contactId = contactId.int
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user