aboutsummaryrefslogtreecommitdiff
path: root/server/src/main/scala/Routes.scala
blob: d39fa18722d73b611bd90eb90e05ac09f093d6c1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package triad

import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.marshalling.sse.EventStreamMarshalling._
import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller}
import akka.http.scaladsl.model.MediaTypes
import akka.http.scaladsl.model.sse.ServerSentEvent
import akka.http.scaladsl.server.Directives._
import akka.stream.scaladsl.Source
import spray.json._
import triad.ApiProtocol._

import scala.concurrent.duration._

class Routes(repository: Repository, liveMessages: LiveMessages) {
  import repository.profile.api._

  // allows using scalatags templates as HTTP responses
  implicit val tagMarshaller: ToEntityMarshaller[scalatags.Text.Tag] = {
    Marshaller.stringMarshaller(MediaTypes.`text/html`).compose {
      (tag: scalatags.Text.Tag) =>
        tag.render
    }
  }

  private val lastMessages = repository.Messages.take(100).result

  private val messageStream: Source[Message, _] = {
    val publisher = repository.database.stream(lastMessages)
    Source
      .fromPublisher(publisher)
      .concat(liveMessages.feed)
  }

  val messages = path("messages") {
    get {
      onSuccess(repository.database.run(lastMessages)) { messages =>
        complete(messages)
      }
    } ~ post {
      entity(as[Message]) { message =>
        extractExecutionContext { implicit ec =>
          val query = repository.Messages.insertOrUpdate(message)
          val action = repository.database.run(query).flatMap { _ =>
            liveMessages.push(message)
          }
          onSuccess(action) { _ =>
            complete(message)
          }
        }
      }
    }
  }

  val ui = pathEndOrSingleSlash {
    get {
      parameter("js".as[Boolean] ? true) { js =>
        onSuccess(repository.database.run(lastMessages)) { messages =>
          complete(TextTemplates.page(messages, js))
        }
      }
    }
  }

  val live = path("live") {
    get {
      val src = messageStream
        .map(msg => ServerSentEvent(msg.toJson.compactPrint))
        .keepAlive(10.seconds, () => ServerSentEvent.heartbeat)
      complete(src)
    }
  }

  val assets = pathPrefix("assets") {
    getFromResourceDirectory("assets")
  }

  def all = messages ~ ui ~ live ~ assets

}