MWSE/internal/services/services.go

121 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package services ports the message handlers and lifecycle hooks that lived in
// Source/Services/* of the Node.js engine: YourID, Session, Auth, Room,
// IPPressure and DataTransfer. Each is registered onto a *ws.Hub, which owns the
// router and the connect/disconnect event bus.
//
// Where the original handlers contained outright bugs (a Set used as if it were an
// Array, comparing a Client object to a string id, validating against an
// undefined schema variable, ...) this port implements the *intended* behaviour
// and records the deviation in decisions.md. The on-the-wire message shapes the
// SDK depends on are preserved.
package services
import (
"crypto/sha256"
"encoding/hex"
"git.saqut.com/saqut/mwse/internal/datastore"
"git.saqut.com/saqut/mwse/internal/notify"
"git.saqut.com/saqut/mwse/internal/protocol"
"git.saqut.com/saqut/mwse/internal/ws"
)
// NotifyTrigger is invoked when a suit notification receives a reply, so MWSE can
// push the result to a 3rd-party application server (#44) instead of that server
// polling for it. The default is a no-op; a real HTTP-backed implementation lives
// in the bridge wiring (#46).
type NotifyTrigger interface {
NotifyReplied(n notify.Notification)
}
type noopTrigger struct{}
func (noopTrigger) NotifyReplied(notify.Notification) {}
// options collects the externally-wired integrations a deployment may supply.
type options struct {
notifyTrigger NotifyTrigger
}
// Option configures Register.
type Option func(*options)
// WithNotifyTrigger sets the 3rd-party trigger fired on suit replies (#44).
func WithNotifyTrigger(t NotifyTrigger) Option {
return func(o *options) {
if t != nil {
o.notifyTrigger = t
}
}
}
// Registry exposes the long-lived stores the services own so the caller (main)
// can manage their lifecycle — for example start the notify/datastore janitors
// that reclaim expired entries.
type Registry struct {
Notify *notify.Store
Data *datastore.Store
}
// Register wires every service onto the hub and returns a Registry of the stores
// that need lifecycle management. Call once during startup, before the server
// begins accepting connections. The order mirrors the Node require() order so
// connect-time side effects (id message, private room, session defaults) happen
// in the same sequence; the data-sync services (#43#45) are appended after.
func Register(hub *ws.Hub, opts ...Option) *Registry {
o := options{notifyTrigger: noopTrigger{}}
for _, apply := range opts {
apply(&o)
}
registerYourID(hub)
registerAuth(hub)
registerRoom(hub)
registerDataTransfer(hub)
registerIPPressure(hub, nil)
registerSession(hub)
return &Registry{
Notify: registerNotify(hub, o.notifyTrigger),
Data: registerDatastore(hub),
}
}
// ---- small response helpers ---------------------------------------------
// success is the ubiquitous {status:"success"} reply.
func success() map[string]any { return map[string]any{"status": "success"} }
// fail is the ubiquitous {status:"fail", message:...} reply.
func fail(message string) map[string]any {
return map[string]any{"status": "fail", "message": message}
}
// toMap coerces an arbitrary decoded JSON value to an object, returning an empty
// map when it is not one (e.g. a missing "filter" field).
func toMap(v any) map[string]any {
if m, ok := v.(map[string]any); ok {
return m
}
return map[string]any{}
}
// sha256hex hashes credentials the same way the original Room service did.
func sha256hex(s string) string {
sum := sha256.Sum256([]byte(s))
return hex.EncodeToString(sum[:])
}
// ids extracts the client ids from a slice of clients.
func ids(clients []*ws.Client) []string {
out := make([]string, 0, len(clients))
for _, c := range clients {
out = append(out, c.ID)
}
return out
}
// handler is a convenience alias matching ws.Handler's shape, used to keep the
// per-service files concise.
type handler = func(c *ws.Client, m protocol.Message) any