From e56782d5a33070a4082f458e620c061312eb4cf9 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 7 Nov 2018 05:40:03 +0900 Subject: [PATCH] Implemented server sent events --- middleware/Log.go | 2 +- pages/index/apiroutes/apiroutes.go | 4 +++ pages/sse/sse.go | 42 ++++++++++++++++++++++++++++++ scripts/AnimeNotifier.ts | 7 +++++ scripts/ServerEvents.ts | 26 ++++++++++++++++++ utils/routetests/All.go | 1 + 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 pages/sse/sse.go create mode 100644 scripts/ServerEvents.ts diff --git a/middleware/Log.go b/middleware/Log.go index c755c33a..da79ccbc 100644 --- a/middleware/Log.go +++ b/middleware/Log.go @@ -76,7 +76,7 @@ func logRequest(ctx *aero.Context, responseTime time.Duration) { // Notify us about long requests. // However ignore requests under /auth/ because those depend on 3rd party servers. - if responseTime >= 300*time.Millisecond && !strings.HasPrefix(ctx.URI(), "/auth/") && !strings.HasPrefix(ctx.URI(), "/sitemap/") { + if responseTime >= 300*time.Millisecond && !strings.HasPrefix(ctx.URI(), "/auth/") && !strings.HasPrefix(ctx.URI(), "/sitemap/") && !strings.HasPrefix(ctx.URI(), "/api/sse/") { errorLog.Error("Long response time", nick, id, ip, responseTimeString, ctx.StatusCode, ctx.URI()) } } diff --git a/pages/index/apiroutes/apiroutes.go b/pages/index/apiroutes/apiroutes.go index d615ee43..c9140c92 100644 --- a/pages/index/apiroutes/apiroutes.go +++ b/pages/index/apiroutes/apiroutes.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/animenotifier/notify.moe/pages/post" + "github.com/animenotifier/notify.moe/pages/sse" "github.com/animenotifier/notify.moe/pages/thread" "github.com/aerogo/aero" @@ -42,6 +43,9 @@ func Register(l *layout.Layout, app *aero.Application) { app.Get("/api/next/soundtrack", soundtrack.Next) app.Get("/api/character/:id/ranking", character.Ranking) + // Live updates + app.Get("/api/sse/events", sse.Events) + // Thread app.Get("/api/thread/:id/reply/ui", thread.ReplyUI) diff --git a/pages/sse/sse.go b/pages/sse/sse.go new file mode 100644 index 00000000..0e231c60 --- /dev/null +++ b/pages/sse/sse.go @@ -0,0 +1,42 @@ +package sse + +import ( + "fmt" + "net/http" + + "github.com/aerogo/aero" + "github.com/animenotifier/notify.moe/utils" +) + +// Events streams server events to the client. +func Events(ctx *aero.Context) string { + user := utils.GetUser(ctx) + + if user == nil { + return ctx.Error(http.StatusUnauthorized, "Not logged in") + } + + fmt.Println(user.Nick, "receiving live events") + + events := make(chan *aero.Event) + disconnected := make(chan struct{}) + + go func() { + defer fmt.Println(user.Nick, "disconnected, stop sending events") + + for { + select { + case <-disconnected: + close(events) + return + + // case <-time.After(10 * time.Second): + // events <- &aero.Event{ + // Name: "ping", + // } + } + } + }() + + return ctx.EventStream(events, disconnected) +} diff --git a/scripts/AnimeNotifier.ts b/scripts/AnimeNotifier.ts index af3a2da4..41e01485 100644 --- a/scripts/AnimeNotifier.ts +++ b/scripts/AnimeNotifier.ts @@ -9,6 +9,7 @@ import Analytics from "./Analytics" import SideBar from "./SideBar" import InfiniteScroller from "./InfiniteScroller" import ServiceWorkerManager from "./ServiceWorkerManager" +import ServerEvents from "./ServerEvents" import { checkNewVersionDelayed } from "./NewVersionCheck" import { displayAiringDate, displayDate, displayTime } from "./DateView" import { findAll, canUseWebP, requestIdleCallback, swapElements, delay, findAllInside } from "./Utils" @@ -35,6 +36,7 @@ export default class AnimeNotifier { diffCompletedForCurrentPath: boolean lastReloadContentPath: string currentSoundTrackId: string + serverEvents: ServerEvents constructor(app: Application) { this.app = app @@ -235,6 +237,11 @@ export default class AnimeNotifier { window.resizeTo(finalWidth, finalHeight) } + // Server sent events + if(this.user && EventSource) { + this.serverEvents = new ServerEvents() + } + // // Download popular anime titles for the search // let response = await fetch("/api/popular/anime/titles/500") // let titles = await response.json() diff --git a/scripts/ServerEvents.ts b/scripts/ServerEvents.ts new file mode 100644 index 00000000..60c2bc77 --- /dev/null +++ b/scripts/ServerEvents.ts @@ -0,0 +1,26 @@ +class ServerEvent { + data: string +} + +export default class ServerEvents { + supported: boolean + eventSource: EventSource + + constructor() { + this.supported = ("EventSource" in window) + + if(!this.supported) { + return + } + + this.eventSource = new EventSource("/api/sse/events", { + withCredentials: true + }) + + this.eventSource.addEventListener("ping", (e: any) => this.ping(e)) + } + + ping(e: ServerEvent) { + console.log("sse: ping") + } +} \ No newline at end of file diff --git a/utils/routetests/All.go b/utils/routetests/All.go index bb3ff6ff..5ddc387d 100644 --- a/utils/routetests/All.go +++ b/utils/routetests/All.go @@ -446,6 +446,7 @@ var routeTests = map[string][]string{ "/api/pushsubscriptions/:id/get/:item/:property": nil, "/api/count/notifications/unseen": nil, "/api/mark/notifications/seen": nil, + "/api/sse/events": nil, "/editor/kitsu/new/anime": nil, "/paypal/success": nil, "/paypal/cancel": nil,