wip: prepare to implement server-sent events for image download progress
This commit is contained in:
parent
0c784b4cc9
commit
cb74b0d817
18
api/events/events.go
Normal file
18
api/events/events.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
type Event interface {
|
||||
templ.Component
|
||||
// Event returns the event name
|
||||
Event() string
|
||||
// SerializeTo writes the event data to the writer.
|
||||
//
|
||||
// SerializeTo must not write multiple linebreaks (single linebreak is fine)
|
||||
// in succession to the writer since it will mess up SSE events.
|
||||
SerializeTo(w io.Writer) error
|
||||
}
|
56
api/events/image_download_start.go
Normal file
56
api/events/image_download_start.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ImageDownloadEvent string
|
||||
|
||||
const (
|
||||
ImageDownloadStart ImageDownloadEvent = "image.download.start"
|
||||
ImageDownloadEnd ImageDownloadEvent = "image.download.end"
|
||||
ImageDownloadError ImageDownloadEvent = "image.download.error"
|
||||
ImageDownloadProgress ImageDownloadEvent = "image.download.progress"
|
||||
)
|
||||
|
||||
type ImageDownload struct {
|
||||
EventKind ImageDownloadEvent `json:"event"`
|
||||
ImageURL string `json:"image_url"`
|
||||
ImageHeight int64 `json:"image_height"`
|
||||
ImageWidth int64 `json:"image_width"`
|
||||
ContentLength int64 `json:"content_length"`
|
||||
Downloaded int64 `json:"downloaded"`
|
||||
Subreddit string `json:"subreddit"`
|
||||
PostURL string `json:"post_url"`
|
||||
PostName string `json:"post_name"`
|
||||
PostTitle string `json:"post_title"`
|
||||
Error error `json:"error"`
|
||||
}
|
||||
|
||||
// Render the template.
|
||||
func (im ImageDownload) Render(ctx context.Context, w io.Writer) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Event returns the event name
|
||||
func (im ImageDownload) Event() string {
|
||||
return "image.download"
|
||||
}
|
||||
|
||||
// SerializeTo writes the event data to the writer.
|
||||
//
|
||||
// SerializeTo must not write multiple linebreaks (single linebreak is fine)
|
||||
// in succession to the writer since it will mess up SSE events.
|
||||
func (im ImageDownload) SerializeTo(w io.Writer) error {
|
||||
return json.NewEncoder(w).Encode(im)
|
||||
}
|
||||
|
||||
type ImageDownloadSubreddit struct {
|
||||
ImageDownload
|
||||
}
|
||||
|
||||
func (im ImageDownloadSubreddit) Event() string {
|
||||
return string(im.EventKind) + "." + im.Subreddit
|
||||
}
|
46
server/routes/events/download_simple.go
Normal file
46
server/routes/events/download_simple.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/tigorlazuardi/redmage/pkg/log"
|
||||
)
|
||||
|
||||
func (handler *Handler) SimpleDownloadEvent(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := tracer.Start(r.Context(), "*Routes.EventsAPI")
|
||||
defer span.End()
|
||||
|
||||
flush, ok := rw.(http.Flusher)
|
||||
if !ok {
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
_ = json.NewEncoder(rw).Encode(map[string]string{"error": "response writer does not support streaming"})
|
||||
return
|
||||
}
|
||||
|
||||
log.New(ctx).Info("new simple event stream connection", "user_agent", r.UserAgent())
|
||||
|
||||
rw.Header().Set("Content-Type", "text/event-stream")
|
||||
rw.Header().Set("Cache-Control", "no-cache")
|
||||
rw.Header().Set("Connection", "keep-alive")
|
||||
rw.WriteHeader(200)
|
||||
flush.Flush()
|
||||
|
||||
ev, close := handler.Subscribe()
|
||||
defer close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-r.Context().Done():
|
||||
log.New(ctx).Info("simple event stream connection closed", "user_agent", r.UserAgent())
|
||||
return
|
||||
case event := <-ev:
|
||||
msg := event.Event()
|
||||
if _, err := fmt.Fprintf(rw, "event: %s\ndata: %s\n\n", msg, msg); err != nil {
|
||||
return
|
||||
}
|
||||
flush.Flush()
|
||||
}
|
||||
}
|
||||
}
|
17
server/routes/events/events.go
Normal file
17
server/routes/events/events.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"github.com/teivah/broadcast"
|
||||
"github.com/tigorlazuardi/redmage/api/events"
|
||||
"github.com/tigorlazuardi/redmage/config"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
Config *config.Config
|
||||
Broadcast *broadcast.Relay[events.Event]
|
||||
}
|
||||
|
||||
func (handler *Handler) Subscribe() (<-chan events.Event, func()) {
|
||||
listener := handler.Broadcast.Listener(10)
|
||||
return listener.Ch(), listener.Close
|
||||
}
|
5
server/routes/events/trace.go
Normal file
5
server/routes/events/trace.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package events
|
||||
|
||||
import "go.opentelemetry.io/otel"
|
||||
|
||||
var tracer = otel.Tracer("server/routes/events")
|
23
views/components/progress/image_download.templ
Normal file
23
views/components/progress/image_download.templ
Normal file
|
@ -0,0 +1,23 @@
|
|||
package progress
|
||||
|
||||
type ImageDownloadStartData struct {
|
||||
ID string
|
||||
Subreddit string
|
||||
PostURL string
|
||||
PostName string
|
||||
PostTitle string
|
||||
}
|
||||
|
||||
templ ImageDownloadStart(data ImageDownloadStartData) {
|
||||
}
|
||||
|
||||
type ImageDownloadEndData struct {
|
||||
ID string
|
||||
Subreddit string
|
||||
PostURL string
|
||||
PostName string
|
||||
PostTitle string
|
||||
}
|
||||
|
||||
templ ImageDownloadEnd(data ImageDownloadEndData) {
|
||||
}
|
Loading…
Reference in a new issue