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