Implemented server sent events

This commit is contained in:
Eduard Urbach 2018-11-07 05:40:03 +09:00
parent 61454b3e5b
commit e56782d5a3
6 changed files with 81 additions and 1 deletions

View File

@ -76,7 +76,7 @@ func logRequest(ctx *aero.Context, responseTime time.Duration) {
// Notify us about long requests. // Notify us about long requests.
// However ignore requests under /auth/ because those depend on 3rd party servers. // 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()) errorLog.Error("Long response time", nick, id, ip, responseTimeString, ctx.StatusCode, ctx.URI())
} }
} }

View File

@ -4,6 +4,7 @@ import (
"strings" "strings"
"github.com/animenotifier/notify.moe/pages/post" "github.com/animenotifier/notify.moe/pages/post"
"github.com/animenotifier/notify.moe/pages/sse"
"github.com/animenotifier/notify.moe/pages/thread" "github.com/animenotifier/notify.moe/pages/thread"
"github.com/aerogo/aero" "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/next/soundtrack", soundtrack.Next)
app.Get("/api/character/:id/ranking", character.Ranking) app.Get("/api/character/:id/ranking", character.Ranking)
// Live updates
app.Get("/api/sse/events", sse.Events)
// Thread // Thread
app.Get("/api/thread/:id/reply/ui", thread.ReplyUI) app.Get("/api/thread/:id/reply/ui", thread.ReplyUI)

42
pages/sse/sse.go Normal file
View File

@ -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)
}

View File

@ -9,6 +9,7 @@ import Analytics from "./Analytics"
import SideBar from "./SideBar" import SideBar from "./SideBar"
import InfiniteScroller from "./InfiniteScroller" import InfiniteScroller from "./InfiniteScroller"
import ServiceWorkerManager from "./ServiceWorkerManager" import ServiceWorkerManager from "./ServiceWorkerManager"
import ServerEvents from "./ServerEvents"
import { checkNewVersionDelayed } from "./NewVersionCheck" import { checkNewVersionDelayed } from "./NewVersionCheck"
import { displayAiringDate, displayDate, displayTime } from "./DateView" import { displayAiringDate, displayDate, displayTime } from "./DateView"
import { findAll, canUseWebP, requestIdleCallback, swapElements, delay, findAllInside } from "./Utils" import { findAll, canUseWebP, requestIdleCallback, swapElements, delay, findAllInside } from "./Utils"
@ -35,6 +36,7 @@ export default class AnimeNotifier {
diffCompletedForCurrentPath: boolean diffCompletedForCurrentPath: boolean
lastReloadContentPath: string lastReloadContentPath: string
currentSoundTrackId: string currentSoundTrackId: string
serverEvents: ServerEvents
constructor(app: Application) { constructor(app: Application) {
this.app = app this.app = app
@ -235,6 +237,11 @@ export default class AnimeNotifier {
window.resizeTo(finalWidth, finalHeight) window.resizeTo(finalWidth, finalHeight)
} }
// Server sent events
if(this.user && EventSource) {
this.serverEvents = new ServerEvents()
}
// // Download popular anime titles for the search // // Download popular anime titles for the search
// let response = await fetch("/api/popular/anime/titles/500") // let response = await fetch("/api/popular/anime/titles/500")
// let titles = await response.json() // let titles = await response.json()

26
scripts/ServerEvents.ts Normal file
View File

@ -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")
}
}

View File

@ -446,6 +446,7 @@ var routeTests = map[string][]string{
"/api/pushsubscriptions/:id/get/:item/:property": nil, "/api/pushsubscriptions/:id/get/:item/:property": nil,
"/api/count/notifications/unseen": nil, "/api/count/notifications/unseen": nil,
"/api/mark/notifications/seen": nil, "/api/mark/notifications/seen": nil,
"/api/sse/events": nil,
"/editor/kitsu/new/anime": nil, "/editor/kitsu/new/anime": nil,
"/paypal/success": nil, "/paypal/success": nil,
"/paypal/cancel": nil, "/paypal/cancel": nil,