Merge pull request #2119 from bradrydzewski/master
add SSE for user event feed
This commit is contained in:
commit
cdb70a6d6a
4 changed files with 107 additions and 16 deletions
|
@ -119,6 +119,11 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sse := e.Group("/stream")
|
||||||
|
{
|
||||||
|
sse.GET("/events", server.EventStreamSSE)
|
||||||
|
}
|
||||||
|
|
||||||
info := e.Group("/api/info")
|
info := e.Group("/api/info")
|
||||||
{
|
{
|
||||||
info.GET("/queue",
|
info.GET("/queue",
|
||||||
|
|
|
@ -3,6 +3,7 @@ package server
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -204,3 +205,72 @@ func EventStream(c *gin.Context) {
|
||||||
}()
|
}()
|
||||||
reader(ws)
|
reader(ws)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EventStreamSSE(c *gin.Context) {
|
||||||
|
c.Header("Content-Type", "text/event-stream")
|
||||||
|
c.Header("Cache-Control", "no-cache")
|
||||||
|
c.Header("Connection", "keep-alive")
|
||||||
|
|
||||||
|
rw := c.Writer
|
||||||
|
|
||||||
|
flusher, ok := rw.(http.Flusher)
|
||||||
|
if !ok {
|
||||||
|
c.String(500, "Streaming not supported")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("user feed: connection opened")
|
||||||
|
|
||||||
|
user := session.User(c)
|
||||||
|
repo := map[string]bool{}
|
||||||
|
if user != nil {
|
||||||
|
repos, _ := store.FromContext(c).RepoList(user)
|
||||||
|
for _, r := range repos {
|
||||||
|
repo[r.FullName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventc := make(chan []byte, 10)
|
||||||
|
ctx, cancel := context.WithCancel(
|
||||||
|
context.Background(),
|
||||||
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
close(eventc)
|
||||||
|
logrus.Debugf("user feed: connection closed")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// TODO remove this from global config
|
||||||
|
Config.Services.Pubsub.Subscribe(c, "topic/events", func(m pubsub.Message) {
|
||||||
|
name := m.Labels["repo"]
|
||||||
|
priv := m.Labels["private"]
|
||||||
|
if repo[name] || priv == "false" {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
eventc <- m.Data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-rw.CloseNotify():
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case buf, ok := <-eventc:
|
||||||
|
if ok {
|
||||||
|
io.WriteString(rw, "data: ")
|
||||||
|
rw.Write(buf)
|
||||||
|
io.WriteString(rw, "\n\n")
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,36 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8">
|
||||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
<meta name="author" content="bradrydzewski">
|
||||||
<meta content="ie=edge" http-equiv="x-ua-compatible"/>
|
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
||||||
{{ if .csrf }}<meta name="csrf-token" content="{{ .csrf }}" />{{ end }}
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/>
|
<link rel="shortcut icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/>
|
<link rel="shortcut icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
|
|
||||||
<link href="/static/app.css" rel="stylesheet"/>
|
<title></title>
|
||||||
<link href="/static/favicon.ico" rel="icon" type="image/x-icon"/>
|
<script>
|
||||||
|
window.ENV = {};
|
||||||
|
window.ENV.server = window.location.protocol+"//"+window.location.host;
|
||||||
|
{{ if .csrf }}window.ENV.csrf = "{{ .csrf }}"{{ end }}
|
||||||
|
{{ if .user }}
|
||||||
|
window.USER = {{ json .user }};
|
||||||
|
{{ end }}
|
||||||
|
</script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-loader.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Mono">
|
||||||
|
<link rel="import" href="/src/drone/drone-app.html">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
padding:0px;
|
||||||
|
margin:0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<drone-app></drone-app>
|
||||||
<script>
|
|
||||||
window.STATE_FROM_SERVER={{ . | json }};
|
|
||||||
</script>
|
|
||||||
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
|
|
||||||
<script src="/static/app.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -60,6 +60,8 @@ func (w *local) File(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func (w *local) Routes() []string {
|
func (w *local) Routes() []string {
|
||||||
return []string{
|
return []string{
|
||||||
|
"/favicon-32x32.png",
|
||||||
|
"/favicon-16x16.png",
|
||||||
"/src/*filepath",
|
"/src/*filepath",
|
||||||
"/bower_components/*filepath",
|
"/bower_components/*filepath",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue