create v2 first version WIP
This commit is contained in:
parent
a161b0d68e
commit
699d9e2fc2
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
89
v2/emitter.go
Normal file
89
v2/emitter.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package serverevents
|
||||||
|
|
||||||
|
import (
|
||||||
|
di "git.apihub24.de/admin/generic-di"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"slices"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
di.Injectable(newEventEmitter)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IEventEmitter interface {
|
||||||
|
On(eventType string, do func(Event)) Subscription
|
||||||
|
OnAll(do func(Event)) Subscription
|
||||||
|
Emit(event Event)
|
||||||
|
Unsubscribe(Subscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventEmitter struct {
|
||||||
|
subscribers map[string][]Subscription
|
||||||
|
onAllSubscribers []Subscription
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEventEmitter() IEventEmitter {
|
||||||
|
return &eventEmitter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emitter *eventEmitter) On(eventType string, do func(Event)) Subscription {
|
||||||
|
emitter.mu.Lock()
|
||||||
|
defer emitter.mu.Unlock()
|
||||||
|
|
||||||
|
if emitter.subscribers[eventType] == nil {
|
||||||
|
emitter.subscribers[eventType] = make([]Subscription, 0)
|
||||||
|
}
|
||||||
|
sub := Subscription{
|
||||||
|
id: uuid.NewString(),
|
||||||
|
eventType: eventType,
|
||||||
|
todo: do,
|
||||||
|
emitter: emitter,
|
||||||
|
}
|
||||||
|
emitter.subscribers[eventType] = append(emitter.subscribers[eventType], sub)
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emitter *eventEmitter) OnAll(do func(Event)) Subscription {
|
||||||
|
emitter.mu.Lock()
|
||||||
|
defer emitter.mu.Unlock()
|
||||||
|
|
||||||
|
sub := Subscription{
|
||||||
|
id: uuid.NewString(),
|
||||||
|
todo: do,
|
||||||
|
emitter: emitter,
|
||||||
|
}
|
||||||
|
emitter.onAllSubscribers = append(emitter.onAllSubscribers, sub)
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emitter *eventEmitter) Emit(event Event) {
|
||||||
|
emitter.executeSubscribers(emitter.onAllSubscribers, event)
|
||||||
|
eventSubscribers := emitter.subscribers[event.Type]
|
||||||
|
if eventSubscribers != nil {
|
||||||
|
emitter.executeSubscribers(eventSubscribers, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emitter *eventEmitter) Unsubscribe(subscription Subscription) {
|
||||||
|
if len(subscription.eventType) < 1 {
|
||||||
|
emitter.onAllSubscribers = slices.DeleteFunc(emitter.onAllSubscribers, func(existSub Subscription) bool {
|
||||||
|
return existSub.id == subscription.id
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eventSubscribers := emitter.subscribers[subscription.eventType]
|
||||||
|
if eventSubscribers == nil || len(eventSubscribers) < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emitter.subscribers[subscription.eventType] = slices.DeleteFunc(emitter.subscribers[subscription.eventType], func(existSub Subscription) bool {
|
||||||
|
return existSub.id == subscription.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emitter *eventEmitter) executeSubscribers(toDos []Subscription, event Event) {
|
||||||
|
for _, sub := range toDos {
|
||||||
|
sub.todo(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
v2/event.go
Normal file
7
v2/event.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package serverevents
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
IsBackendOnly bool `json:"-"`
|
||||||
|
}
|
||||||
9
v2/go.mod
Normal file
9
v2/go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module git.apihub24.de/admin/server_events/v2
|
||||||
|
|
||||||
|
go 1.22.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.apihub24.de/admin/generic-di v1.4.0
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/gorilla/websocket v1.5.3
|
||||||
|
)
|
||||||
6
v2/go.sum
Normal file
6
v2/go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
git.apihub24.de/admin/generic-di v1.4.0 h1:0mQnpAcavMLBcnF5UO+tUI7abZ6zQPleqPsjEk3WIaU=
|
||||||
|
git.apihub24.de/admin/generic-di v1.4.0/go.mod h1:VcHV8MOb1qhwabHdO09CpjEg2VaDesehul86g1iyOxY=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
95
v2/middleware.go
Normal file
95
v2/middleware.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package serverevents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
di "git.apihub24.de/admin/generic-di"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
di.Injectable(newServerEventsMiddleware)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiddlewareOptions struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IServerEventsMiddleware interface {
|
||||||
|
Use(MiddlewareOptions, *http.ServeMux)
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverEventsMiddleware struct {
|
||||||
|
upgrader websocket.Upgrader
|
||||||
|
streamSubscribers map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newServerEventsMiddleware() IServerEventsMiddleware {
|
||||||
|
return &serverEventsMiddleware{
|
||||||
|
upgrader: websocket.Upgrader{
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
streamSubscribers: make(map[string]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (middleware *serverEventsMiddleware) Use(options MiddlewareOptions, muxer *http.ServeMux) {
|
||||||
|
middleware.selectMethod(options.Path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
middleware.handleEventStream(w, r)
|
||||||
|
}, muxer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (middleware *serverEventsMiddleware) handleEventStream(w http.ResponseWriter, r *http.Request) {
|
||||||
|
emitter := di.Inject[IEventEmitter]()
|
||||||
|
conn, err := middleware.upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error upgrading:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// do not handle Error
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
id := r.URL.Query().Get("id")
|
||||||
|
|
||||||
|
if _, ok := middleware.streamSubscribers[id]; !ok {
|
||||||
|
subscription := emitter.OnAll(func(event Event) {
|
||||||
|
if !event.IsBackendOnly {
|
||||||
|
jsonData, jsonErr := json.Marshal(event)
|
||||||
|
if jsonErr != nil {
|
||||||
|
fmt.Println("Error make json string", jsonErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = conn.WriteMessage(websocket.TextMessage, jsonData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer func() {
|
||||||
|
subscription.Unsubscribe()
|
||||||
|
delete(middleware.streamSubscribers, id)
|
||||||
|
}()
|
||||||
|
middleware.streamSubscribers[id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
for {
|
||||||
|
_, _, err := conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (middleware *serverEventsMiddleware) selectMethod(path string, todo http.HandlerFunc, muxer *http.ServeMux) {
|
||||||
|
if muxer == nil {
|
||||||
|
http.HandleFunc(path, todo)
|
||||||
|
} else {
|
||||||
|
muxer.HandleFunc(path, todo)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
v2/subscription.go
Normal file
12
v2/subscription.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package serverevents
|
||||||
|
|
||||||
|
type Subscription struct {
|
||||||
|
id string
|
||||||
|
eventType string
|
||||||
|
todo func(Event)
|
||||||
|
emitter IEventEmitter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (subscription Subscription) Unsubscribe() {
|
||||||
|
subscription.emitter.Unsubscribe(subscription)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user