Remove websockets
This commit is contained in:
parent
8f8242a97a
commit
7b7a9b0fc2
|
@ -83,7 +83,6 @@ kotlin {
|
|||
implementation "io.ktor:ktor-server-netty:$ktor_version"
|
||||
implementation "io.ktor:ktor-auth:$ktor_version"
|
||||
implementation "io.ktor:ktor-server-sessions:$ktor_version"
|
||||
implementation "io.ktor:ktor-websockets:$ktor_version"
|
||||
implementation "io.ktor:ktor-jackson:$ktor_version"
|
||||
implementation 'org.jetbrains:kotlin-css-jvm:1.0.0-pre.70-kotlin-1.3.21'
|
||||
|
||||
|
|
|
@ -1,30 +1,24 @@
|
|||
package de.kif.common
|
||||
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
|
||||
@Serializable
|
||||
data class MessageBox(
|
||||
val timestamp: Long,
|
||||
val valid: Boolean,
|
||||
val messages: List<Message>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Message(
|
||||
val type: MessageType,
|
||||
val repository: RepositoryType,
|
||||
val id: Long
|
||||
) {
|
||||
|
||||
fun stringify(): String {
|
||||
return json.stringify(serializer(), this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val jsonContext = SerializersModule {}
|
||||
|
||||
val json = Json(context = jsonContext)
|
||||
|
||||
fun parse(data: String): Message {
|
||||
return json.parse(serializer(), data)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
enum class MessageType {
|
||||
CREATE, UPDATE, DELETE
|
||||
|
@ -33,3 +27,12 @@ enum class MessageType {
|
|||
enum class RepositoryType {
|
||||
ROOM, SCHEDULE, TRACK, USER, WORK_GROUP, POST
|
||||
}
|
||||
|
||||
object Serialization {
|
||||
private val jsonContext = SerializersModule {}
|
||||
|
||||
val json = Json(context = jsonContext)
|
||||
|
||||
fun <T> stringify(serializer: SerializationStrategy<T>, obj: T) = json.stringify(serializer, obj)
|
||||
fun <T> parse(serializer: DeserializationStrategy<T>, str: String) = json.parse(serializer, str)
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ data class SearchElement(
|
|||
) {
|
||||
|
||||
fun stringify(): String {
|
||||
return Message.json.stringify(serializer(), this)
|
||||
return Serialization.stringify(serializer(), this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parse(data: String): SearchElement {
|
||||
return Message.json.parse(serializer(), data)
|
||||
return Serialization.parse(serializer(), data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +1,92 @@
|
|||
package de.kif.frontend
|
||||
|
||||
import de.kif.common.Message
|
||||
import de.kif.common.MessageBox
|
||||
import de.kif.common.MessageType
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.frontend.repository.*
|
||||
import de.westermann.kwebview.async
|
||||
import org.w3c.dom.MessageEvent
|
||||
import org.w3c.dom.WebSocket
|
||||
import org.w3c.dom.events.Event
|
||||
import de.westermann.kwebview.clearInterval
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import de.westermann.kwebview.interval
|
||||
import kotlinx.serialization.DynamicObjectParser
|
||||
import org.w3c.dom.get
|
||||
import org.w3c.xhr.XMLHttpRequest
|
||||
import kotlin.browser.document
|
||||
import kotlin.browser.window
|
||||
|
||||
class WebSocketClient() {
|
||||
class WebSocketClient {
|
||||
private val prefix = js("prefix")
|
||||
private val url = "$prefix/api/updates"
|
||||
private val body = document.body ?: createHtmlView()
|
||||
private val parser = DynamicObjectParser()
|
||||
|
||||
private val useSsl = "https" in window.location.protocol
|
||||
private val wsProtocol = if (useSsl) "wss" else "ws"
|
||||
private val url = "$wsProtocol://${window.location.host}$prefix/websocket"
|
||||
private var timestamp = body.dataset["timestamp"]?.toLongOrNull() ?: 0L
|
||||
private var intervalId: Int? = null
|
||||
|
||||
private lateinit var ws: WebSocket
|
||||
private var reconnect = false
|
||||
private fun reload() {
|
||||
val id = intervalId ?: return
|
||||
clearInterval(id)
|
||||
intervalId = null
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onOpen(event: Event) {
|
||||
console.log("Connected!")
|
||||
if (reconnect) {
|
||||
window.location.reload()
|
||||
}
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
private fun onMessage(messageEvent: MessageEvent) {
|
||||
val message = Message.parse(messageEvent.data?.toString() ?: "")
|
||||
private fun onMessage(messageBox: MessageBox) {
|
||||
body.classList.remove("offline")
|
||||
|
||||
for (handler in messageHandlers) {
|
||||
if (handler.repository == message.repository) {
|
||||
when (message.type) {
|
||||
MessageType.CREATE -> handler.onCreate(message.id)
|
||||
MessageType.UPDATE -> handler.onUpdate(message.id)
|
||||
MessageType.DELETE -> handler.onDelete(message.id)
|
||||
if (messageBox.valid) {
|
||||
timestamp = messageBox.timestamp
|
||||
|
||||
for (message in messageBox.messages) {
|
||||
for (handler in messageHandlers) {
|
||||
if (handler.repository == message.repository) {
|
||||
when (message.type) {
|
||||
MessageType.CREATE -> handler.onCreate(message.id)
|
||||
MessageType.UPDATE -> handler.onUpdate(message.id)
|
||||
MessageType.DELETE -> handler.onDelete(message.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reload()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onClose(event: Event) {
|
||||
console.log("Disconnected!")
|
||||
reconnect = true
|
||||
async(1000) {
|
||||
connect()
|
||||
private fun onError(code: Int) {
|
||||
if (!body.classList.contains("offline")) {
|
||||
console.log("Offline reason: $code")
|
||||
}
|
||||
body.classList.add("offline")
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun onError(event: Event) {
|
||||
console.log("An error occurred!")
|
||||
}
|
||||
private fun request() {
|
||||
val xmlHttpRequest = XMLHttpRequest()
|
||||
|
||||
private fun connect() {
|
||||
ws = WebSocket(url)
|
||||
xmlHttpRequest.onreadystatechange = {
|
||||
try {
|
||||
if (xmlHttpRequest.readyState == 4.toShort()) {
|
||||
if (xmlHttpRequest.status == 200.toShort() || xmlHttpRequest.status == 304.toShort()) {
|
||||
val json = JSON.parse<JsonResponse>(xmlHttpRequest.responseText)
|
||||
|
||||
ws.onopen = this::onOpen
|
||||
ws.onmessage = this::onMessage
|
||||
ws.onclose = this::onClose
|
||||
ws.onerror = this::onError
|
||||
if (json.OK) {
|
||||
val message = parser.parse(json.data, MessageBox.serializer())
|
||||
onMessage(message)
|
||||
} else {
|
||||
onError(-1)
|
||||
}
|
||||
|
||||
} else {
|
||||
onError(xmlHttpRequest.status.toInt())
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
console.error(e)
|
||||
onError(-2)
|
||||
}
|
||||
}
|
||||
xmlHttpRequest.open("GET", "$url?timestamp=$timestamp", true)
|
||||
xmlHttpRequest.overrideMimeType("application/json")
|
||||
xmlHttpRequest.send()
|
||||
}
|
||||
|
||||
private val messageHandlers: List<MessageHandler> = listOf(
|
||||
|
@ -75,7 +99,9 @@ class WebSocketClient() {
|
|||
)
|
||||
|
||||
init {
|
||||
connect()
|
||||
intervalId = interval(500) {
|
||||
request()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,15 @@ import kotlin.coroutines.resumeWithException
|
|||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.js.Promise
|
||||
|
||||
interface JsonResponse {
|
||||
val OK: Boolean
|
||||
val data: dynamic
|
||||
}
|
||||
|
||||
suspend fun repositoryGet(
|
||||
url: String
|
||||
): dynamic {
|
||||
val promise = Promise<dynamic> { resolve, reject ->
|
||||
val promise = Promise<JsonResponse> { resolve, reject ->
|
||||
val xhttp = XMLHttpRequest()
|
||||
|
||||
xhttp.onreadystatechange = {
|
||||
|
@ -47,7 +52,7 @@ suspend fun repositoryPost(
|
|||
url: String,
|
||||
data: String? = null
|
||||
): dynamic {
|
||||
val promise = Promise<dynamic> { resolve, reject ->
|
||||
val promise = Promise<JsonResponse> { resolve, reject ->
|
||||
val xhttp = XMLHttpRequest()
|
||||
|
||||
xhttp.onreadystatechange = {
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.kif.frontend.repository
|
|||
import de.kif.common.Message
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.Serialization
|
||||
import de.kif.common.model.Post
|
||||
import de.kif.frontend.MessageHandler
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -25,13 +26,13 @@ object PostRepository : Repository<Post> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: Post): Long {
|
||||
return repositoryPost("$prefix/api/posts", Message.json.stringify(Post.serializer(), model))
|
||||
return repositoryPost("$prefix/api/posts", Serialization.stringify(Post.serializer(), model))
|
||||
?: throw IllegalStateException("Cannot create model!")
|
||||
}
|
||||
|
||||
override suspend fun update(model: Post) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
repositoryPost("$prefix/api/post/${model.id}", Message.json.stringify(Post.serializer(), model))
|
||||
repositoryPost("$prefix/api/post/${model.id}", Serialization.stringify(Post.serializer(), model))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.kif.frontend.repository
|
|||
import de.kif.common.Message
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.Serialization
|
||||
import de.kif.common.model.Room
|
||||
import de.kif.frontend.MessageHandler
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -25,13 +26,13 @@ object RoomRepository : Repository<Room> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: Room): Long {
|
||||
return repositoryPost("$prefix/api/rooms", Message.json.stringify(Room.serializer(), model))
|
||||
return repositoryPost("$prefix/api/rooms", Serialization.stringify(Room.serializer(), model))
|
||||
?: throw IllegalStateException("Cannot create model!")
|
||||
}
|
||||
|
||||
override suspend fun update(model: Room) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
repositoryPost("$prefix/api/room/${model.id}", Message.json.stringify(Room.serializer(), model))
|
||||
repositoryPost("$prefix/api/room/${model.id}", Serialization.stringify(Room.serializer(), model))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package de.kif.frontend.repository
|
||||
|
||||
import de.kif.common.ConstraintMap
|
||||
import de.kif.common.Message
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.*
|
||||
import de.kif.common.model.Schedule
|
||||
import de.kif.frontend.MessageHandler
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -31,13 +28,13 @@ object ScheduleRepository : Repository<Schedule> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: Schedule): Long {
|
||||
return repositoryPost("$prefix/api/schedules", Message.json.stringify(Schedule.serializer(), model))
|
||||
return repositoryPost("$prefix/api/schedules", Serialization.stringify(Schedule.serializer(), model))
|
||||
?: throw IllegalStateException("Cannot create model!")
|
||||
}
|
||||
|
||||
override suspend fun update(model: Schedule) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
repositoryPost("$prefix/api/schedule/${model.id}", Message.json.stringify(Schedule.serializer(), model))
|
||||
repositoryPost("$prefix/api/schedule/${model.id}", Serialization.stringify(Schedule.serializer(), model))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.kif.frontend.repository
|
|||
import de.kif.common.Message
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.Serialization
|
||||
import de.kif.common.model.Track
|
||||
import de.kif.frontend.MessageHandler
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -25,13 +26,13 @@ object TrackRepository : Repository<Track> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: Track): Long {
|
||||
return repositoryPost("$prefix/api/tracks", Message.json.stringify(Track.serializer(), model))
|
||||
return repositoryPost("$prefix/api/tracks", Serialization.stringify(Track.serializer(), model))
|
||||
?: throw IllegalStateException("Cannot create model!")
|
||||
}
|
||||
|
||||
override suspend fun update(model: Track) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
repositoryPost("$prefix/api/track/${model.id}", Message.json.stringify(Track.serializer(), model))
|
||||
repositoryPost("$prefix/api/track/${model.id}", Serialization.stringify(Track.serializer(), model))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.kif.frontend.repository
|
|||
import de.kif.common.Message
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.Serialization
|
||||
import de.kif.common.model.User
|
||||
import de.kif.frontend.MessageHandler
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -25,13 +26,13 @@ object UserRepository : Repository<User> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: User): Long {
|
||||
return repositoryPost("$prefix/api/users", Message.json.stringify(User.serializer(), model))
|
||||
return repositoryPost("$prefix/api/users", Serialization.stringify(User.serializer(), model))
|
||||
?: throw IllegalStateException("Cannot create model!")
|
||||
}
|
||||
|
||||
override suspend fun update(model: User) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
repositoryPost("$prefix/api/user/${model.id}", Message.json.stringify(User.serializer(), model))
|
||||
repositoryPost("$prefix/api/user/${model.id}", Serialization.stringify(User.serializer(), model))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.kif.frontend.repository
|
|||
import de.kif.common.Message
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.Serialization
|
||||
import de.kif.common.model.WorkGroup
|
||||
import de.kif.frontend.MessageHandler
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -25,13 +26,13 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
|||
}
|
||||
|
||||
override suspend fun create(model: WorkGroup): Long {
|
||||
return repositoryPost("$prefix/api/workgroups", Message.json.stringify(WorkGroup.serializer(), model))
|
||||
return repositoryPost("$prefix/api/workgroups", Serialization.stringify(WorkGroup.serializer(), model))
|
||||
?: throw IllegalStateException("Cannot create model!")
|
||||
}
|
||||
|
||||
override suspend fun update(model: WorkGroup) {
|
||||
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||
repositoryPost("$prefix/api/workgroup/${model.id}", Message.json.stringify(WorkGroup.serializer(), model))
|
||||
repositoryPost("$prefix/api/workgroup/${model.id}", Serialization.stringify(WorkGroup.serializer(), model))
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) {
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
border-bottom: solid 1px var(--table-border-color);
|
||||
box-shadow: 0 0 4px black;
|
||||
box-shadow: 0 0 4px var(--shadow-color);
|
||||
top: -1rem;
|
||||
padding-top: 1.2rem;
|
||||
}
|
||||
|
|
|
@ -160,3 +160,25 @@ a {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.offline-banner {
|
||||
position: fixed;
|
||||
bottom: 3rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
text-align: center;
|
||||
font-size: 1.2rem;
|
||||
background-color: var(--primary-color);
|
||||
color: var(--primary-text-color);
|
||||
z-index: 12;
|
||||
box-shadow: 0 1px 4px var(--shadow-color);
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.offline {
|
||||
.offline-banner {
|
||||
display: block;
|
||||
}
|
||||
}
|
|
@ -3,10 +3,7 @@ package de.kif.backend.repository
|
|||
import de.kif.backend.database.DbWorkGroup
|
||||
import de.kif.backend.database.dbQuery
|
||||
import de.kif.backend.util.PushService
|
||||
import de.kif.common.Message
|
||||
import de.kif.common.MessageType
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.*
|
||||
import de.kif.common.model.Constraint
|
||||
import de.kif.common.model.WorkGroup
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
|
@ -36,8 +33,8 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
|||
val accessible = row[DbWorkGroup.accessible]
|
||||
val length = row[DbWorkGroup.length]
|
||||
val language = row[DbWorkGroup.language]
|
||||
val leader = Message.json.parse(String.serializer().list, row[DbWorkGroup.leader])
|
||||
val constraints = Message.json.parse(Constraint.serializer().list, row[DbWorkGroup.constraints])
|
||||
val leader = Serialization.parse(String.serializer().list, row[DbWorkGroup.leader])
|
||||
val constraints = Serialization.parse(Constraint.serializer().list, row[DbWorkGroup.constraints])
|
||||
|
||||
val createdAt = row[DbWorkGroup.createdAt]
|
||||
val updatedAt = row[DbWorkGroup.updatedAt]
|
||||
|
@ -92,8 +89,8 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
|||
it[accessible] = model.accessible
|
||||
it[length] = model.length
|
||||
it[language] = model.language
|
||||
it[leader] = Message.json.stringify(String.serializer().list, model.leader)
|
||||
it[constraints] = Message.json.stringify(Constraint.serializer().list, model.constraints)
|
||||
it[leader] = Serialization.stringify(String.serializer().list, model.leader)
|
||||
it[constraints] = Serialization.stringify(Constraint.serializer().list, model.constraints)
|
||||
|
||||
it[createdAt] = now
|
||||
it[updatedAt] = now
|
||||
|
@ -124,8 +121,8 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
|||
it[accessible] = model.accessible
|
||||
it[length] = model.length
|
||||
it[language] = model.language
|
||||
it[leader] = Message.json.stringify(String.serializer().list, model.leader)
|
||||
it[constraints] = Message.json.stringify(Constraint.serializer().list, model.constraints)
|
||||
it[leader] = Serialization.stringify(String.serializer().list, model.leader)
|
||||
it[constraints] = Serialization.stringify(Constraint.serializer().list, model.constraints)
|
||||
|
||||
it[updatedAt] = now
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import de.kif.backend.database.Connection
|
|||
import de.kif.backend.repository.*
|
||||
import de.kif.common.Message
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.Serialization
|
||||
import de.kif.common.model.*
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
@ -42,12 +43,12 @@ data class Backup(
|
|||
}
|
||||
}
|
||||
|
||||
return Message.json.stringify(serializer(), backup)
|
||||
return Serialization.stringify(serializer(), backup)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
suspend fun import(data: String) {
|
||||
val backup = Message.json.parse(serializer(), data)
|
||||
val backup = Serialization.parse(serializer(), data)
|
||||
|
||||
backup.users.forEach { UserRepository.create(it); println("Import user ${it.username}") }
|
||||
backup.posts.forEach { PostRepository.create(it); println("Import post") }
|
||||
|
|
|
@ -1,48 +1,60 @@
|
|||
package de.kif.backend.util
|
||||
|
||||
import de.kif.backend.prefix
|
||||
import de.kif.backend.repository.*
|
||||
import de.kif.common.*
|
||||
import de.kif.backend.route.api.error
|
||||
import de.kif.backend.route.api.success
|
||||
import de.kif.common.Message
|
||||
import de.kif.common.MessageBox
|
||||
import de.kif.common.MessageType
|
||||
import de.kif.common.RepositoryType
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readText
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.websocket.WebSocketServerSession
|
||||
import io.ktor.websocket.webSocket
|
||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||
import io.ktor.routing.get
|
||||
|
||||
object PushService {
|
||||
|
||||
var clients: List<WebSocketServerSession> = emptyList()
|
||||
var leastValidTimestamp = System.currentTimeMillis()
|
||||
|
||||
suspend fun notify(type: MessageType, repository: RepositoryType, id: Long) {
|
||||
try {
|
||||
val data = Message(type, repository, id).stringify()
|
||||
for (client in clients) {
|
||||
client.outgoing.send(Frame.Text(data))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
/**
|
||||
* Save the message with the current timestamp
|
||||
*/
|
||||
fun notify(type: MessageType, repository: RepositoryType, id: Long) {
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val message = Message(type, repository, id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all messages created after the given timestamp.
|
||||
* The return Box has the current timestamp. If the leastValidTimestamp is less then the current timestamp set the
|
||||
* valid flag to false and return an empty message list.
|
||||
*/
|
||||
fun getMessages(timestamp: Long?): MessageBox {
|
||||
return MessageBox(
|
||||
System.currentTimeMillis(),
|
||||
true,
|
||||
emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all messages before the given timestamp
|
||||
*/
|
||||
fun gc(timestamp: Long) {
|
||||
leastValidTimestamp = timestamp
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.pushService() {
|
||||
webSocket("/websocket") {
|
||||
PushService.clients += this
|
||||
|
||||
get("/api/updates") {
|
||||
try {
|
||||
while (true) {
|
||||
val text = (incoming.receive() as Frame.Text).readText()
|
||||
println("onMessage($text)")
|
||||
outgoing.send(Frame.Text(text))
|
||||
}
|
||||
} catch (_: ClosedReceiveChannelException) {
|
||||
PushService.clients -= this
|
||||
} catch (e: Throwable) {
|
||||
println("onFailure ${closeReason.await()}")
|
||||
e.printStackTrace()
|
||||
PushService.clients -= this
|
||||
val timestamp = call.request.queryParameters["timestamp"]?.toLongOrNull()
|
||||
|
||||
val messageBox = PushService.getMessages(timestamp)
|
||||
|
||||
call.success(messageBox)
|
||||
} catch (_: Exception) {
|
||||
call.error(HttpStatusCode.InternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ class MainTemplate(
|
|||
}
|
||||
}
|
||||
body {
|
||||
attributes["data-timestamp"] = currentTimeMillis().toString()
|
||||
|
||||
if (!noMenu) {
|
||||
insert(MenuTemplate(url, user)) {}
|
||||
}
|
||||
|
@ -84,6 +86,10 @@ class MainTemplate(
|
|||
}
|
||||
}
|
||||
|
||||
div("offline-banner") {
|
||||
span { +"Die Verbindung zum Server ist unterbrochen" }
|
||||
}
|
||||
|
||||
if (!noMenu) {
|
||||
div("footer") {
|
||||
div("container") {
|
||||
|
|
Loading…
Reference in a new issue