Compare commits

..
Author SHA1 Message Date
artemis 9bfee2978e Added MQTT actor 2026-07-19 01:45:49 +03:00
7 changed files with 106 additions and 20 deletions
@@ -5,7 +5,7 @@ import jakarta.inject.{Inject, Singleton}
import models.AuthToken import models.AuthToken
import play.api.libs.json.* import play.api.libs.json.*
import play.api.libs.functional.syntax.* import play.api.libs.functional.syntax.*
import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents} import play.api.mvc.{AbstractController, ControllerComponents}
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
@@ -22,10 +22,7 @@ implicit val tokenWrites: Writes[AuthToken] = (token: AuthToken) => Json.obj(
@Singleton @Singleton
class SessionsController @Inject()(controllerComponents: ControllerComponents, class SessionsController @Inject()(controllerComponents: ControllerComponents, usersDAO: UsersDAO, authTokensDAO: AuthTokensDAO, implicit val ec: ExecutionContext) extends AbstractController(controllerComponents) {
usersDAO: UsersDAO, authTokensDAO: AuthTokensDAO,
authenticatedAction: AuthenticatedAction,
implicit val ec: ExecutionContext) extends AbstractController(controllerComponents) {
def login() = Action.async(parse.json) { implicit request => def login() = Action.async(parse.json) { implicit request =>
val loginDataResult = request.body.validate[LoginData] val loginDataResult = request.body.validate[LoginData]
loginDataResult.fold( loginDataResult.fold(
@@ -44,7 +41,7 @@ class SessionsController @Inject()(controllerComponents: ControllerComponents,
) )
} }
def logout = (Action andThen authenticatedAction).async { request => def logout = Action.andThen(new AuthenticatedAction(authTokensDAO)).async { request =>
authTokensDAO.destroy(request.token).map{ _ => Ok("") } authTokensDAO.destroy(request.token).map{ _ => Ok("") }
} }
} }
@@ -0,0 +1,46 @@
package mqttClient
import com.google.inject.Provides
import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient
import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish
import jakarta.inject.{Inject, Singleton}
import org.apache.pekko.actor.typed.{ActorRef, Behavior}
import org.apache.pekko.actor.typed.scaladsl.Behaviors
import play.api.Logging
import play.api.libs.concurrent.ActorModule
import java.nio.charset.StandardCharsets
object MessageProcessorActor extends ActorModule with Logging {
sealed trait Command
case object Stop extends Command
private case class MqttMessageReceived(publish: Mqtt5Publish) extends Command
override type Message = Command
@Singleton
class Reg @Inject()(private val mqttMessageProcessorActor: ActorRef[Command])
@Provides
def create(client: Mqtt5AsyncClient): Behavior[Command] = Behaviors.setup { context =>
client.connect().whenComplete { (_action, _throwable) =>
client.subscribeWith()
.topicFilter("test/+")
.callback{ (publish) =>
context.self ! MqttMessageReceived(publish)
}
.send()
}
Behaviors.receiveMessage[Command] {
case MqttMessageReceived(publish) =>
val topic = publish.getTopic
val payload = String(publish.getPayloadAsBytes, StandardCharsets.UTF_8)
logger.info(s"Received message $topic $payload")
Behaviors.same
case Stop => Behaviors.stopped{ () =>
client.disconnect()
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
package mqttClient
import com.google.inject.{AbstractModule, Provides}
import com.hivemq.client.mqtt.mqtt5.{Mqtt5AsyncClient, Mqtt5Client}
import jakarta.inject.Inject
import play.api.libs.concurrent.PekkoGuiceSupport
import play.api.{Configuration, Logging}
import java.nio.charset.StandardCharsets
class Module extends AbstractModule with PekkoGuiceSupport with Logging {
override def configure(): Unit = {
bindTypedActor(MessageProcessorActor, "mqtt-message-processor-actor")
bind(classOf[MessageProcessorActor.Reg]).asEagerSingleton()
}
@Provides
def mqtt5Client(config: Configuration): Mqtt5AsyncClient = {
val mqttConfig = config.get[Configuration]("mqtt")
var builder = Mqtt5Client.builder()
.identifier(mqttConfig.get[String]("clientId"))
.serverHost(mqttConfig.get[String]("host"))
.serverPort(mqttConfig.get("port"))
mqttConfig.getOptional[String]("username").foreach{username =>
val password = mqttConfig.get[String]("password").getBytes(StandardCharsets.US_ASCII)
builder = builder.simpleAuth()
.username(username)
.password(password)
.applySimpleAuth()
}
builder.buildAsync()
}
}
+3
View File
@@ -21,6 +21,9 @@ libraryDependencies += "org.playframework" %% "play-slick-evolutions" % "6.2.0"
// Source: https://mvnrepository.com/artifact/de.mkammerer/argon2-jvm // Source: https://mvnrepository.com/artifact/de.mkammerer/argon2-jvm
libraryDependencies += "de.mkammerer" % "argon2-jvm" % "2.12" libraryDependencies += "de.mkammerer" % "argon2-jvm" % "2.12"
// Source: https://mvnrepository.com/artifact/com.hivemq/hivemq-mqtt-client
libraryDependencies += "com.hivemq" % "hivemq-mqtt-client" % "1.3.17"
// Adds additional packages into Twirl // Adds additional packages into Twirl
//TwirlKeys.templateImports += "me.artemis.controllers._" //TwirlKeys.templateImports += "me.artemis.controllers._"
+17
View File
@@ -2,6 +2,8 @@
play.http.filters=controllers.filters.Filters play.http.filters=controllers.filters.Filters
play.modules.enabled += "mqttClient.Module"
slick.dbs.default = { slick.dbs.default = {
profile = "slick.jdbc.PostgresProfile$" profile = "slick.jdbc.PostgresProfile$"
db = { db = {
@@ -26,5 +28,20 @@ pekko {
} }
throughput = 1 throughput = 1
} }
mqtt-dispatcher {
type = Dispatcher
executor = "fork-join-executor"
fork-join-executor {
}
}
} }
}
mqtt {
host = "localhost"
port = 1883
clientId = "neon-vertigo-api"
username = "artemis"
password = "artemis"
} }
-14
View File
@@ -1,14 +0,0 @@
-- !Ups
CREATE TABLE auctions
(
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
gid VARCHAR UNIQUE NOT NULL,
author_id BIGINT NOT NULL REFERENCES users (id) ON DELETE RESTRICT,
description VARCHAR,
starting_price DECIMAL(12, 3) NOT NULL,
current_bid DECIMAL(12, 3),
current_bidder_id BIGINT REFERENCES users (id) ON DELETE SET NULL
);
-- !Downs
DROP TABLE auctions;
+1
View File
@@ -41,6 +41,7 @@
<logger name="play" level="INFO"/> <logger name="play" level="INFO"/>
<logger name="application" level="DEBUG"/> <logger name="application" level="DEBUG"/>
<logger name="mqttClient" level="DEBUG"/>
<root level="WARN"> <root level="WARN">
<appender-ref ref="ASYNCFILE"/> <appender-ref ref="ASYNCFILE"/>