package net import ( "context" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bus" edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http" "forge.cadoles.com/arcad/edge/pkg/module" "forge.cadoles.com/arcad/edge/pkg/module/util" "github.com/dop251/goja" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) type Module struct { server *app.Server bus bus.Bus } func (m *Module) Name() string { return "net" } func (m *Module) Export(export *goja.Object) { if err := export.Set("broadcast", m.broadcast); err != nil { panic(errors.Wrap(err, "could not set 'broadcast' function")) } if err := export.Set("send", m.send); err != nil { panic(errors.Wrap(err, "could not set 'send' function")) } } func (m *Module) broadcast(call goja.FunctionCall, rt *goja.Runtime) goja.Value { if len(call.Arguments) < 1 { panic(rt.ToValue(errors.New("invalid number of argument"))) } data := call.Argument(0).Export() ctx := context.Background() msg := module.NewServerMessage(ctx, data) if err := m.bus.Publish(ctx, msg); err != nil { panic(rt.ToValue(errors.WithStack(err))) } return nil } func (m *Module) send(call goja.FunctionCall, rt *goja.Runtime) goja.Value { if len(call.Arguments) < 2 { panic(rt.ToValue(errors.New("invalid number of argument"))) } var ctx context.Context firstArg := call.Argument(0) sessionID, ok := firstArg.Export().(string) if ok { ctx = module.WithContext(context.Background(), map[module.ContextKey]any{ edgeHTTP.ContextKeySessionID: sessionID, }) } else { ctx = util.AssertContext(firstArg, rt) } data := call.Argument(1).Export() msg := module.NewServerMessage(ctx, data) if err := m.bus.Publish(ctx, msg); err != nil { panic(rt.ToValue(errors.WithStack(err))) } return nil } func (m *Module) handleClientMessages() { ctx := context.Background() logger.Debug( ctx, "subscribing to bus messages", ) clientMessages, err := m.bus.Subscribe(ctx, module.MessageNamespaceClient) if err != nil { panic(errors.WithStack(err)) } defer func() { logger.Debug( ctx, "unsubscribing from bus messages", ) m.bus.Unsubscribe(ctx, module.MessageNamespaceClient, clientMessages) }() for { logger.Debug( ctx, "waiting for next message", ) select { case <-ctx.Done(): logger.Debug( ctx, "context done", ) return case msg := <-clientMessages: clientMessage, ok := msg.(*module.ClientMessage) if !ok { logger.Warn( ctx, "unexpected message type", logger.F("message", msg), ) continue } logger.Debug( ctx, "received client message", logger.F("message", clientMessage), ) if _, err := m.server.ExecFuncByName(clientMessage.Context, "onClientMessage", clientMessage.Context, clientMessage.Data); err != nil { if errors.Is(err, app.ErrFuncDoesNotExist) { continue } logger.Error( ctx, "on client message error", logger.CapturedE(errors.WithStack(err)), ) } } } } func ModuleFactory(bus bus.Bus) app.ServerModuleFactory { return func(server *app.Server) app.ServerModule { module := &Module{ server: server, bus: bus, } go module.handleClientMessages() return module } }