emissary/internal/agent/controller/app/server.go

167 lines
3.5 KiB
Go

package app
import (
"context"
"database/sql"
"net/http"
"sync"
"forge.cadoles.com/arcad/edge/pkg/app"
"forge.cadoles.com/arcad/edge/pkg/bus"
"forge.cadoles.com/arcad/edge/pkg/bus/memory"
edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http"
"forge.cadoles.com/arcad/edge/pkg/module"
"forge.cadoles.com/arcad/edge/pkg/module/auth"
"forge.cadoles.com/arcad/edge/pkg/module/cast"
"forge.cadoles.com/arcad/edge/pkg/module/net"
"forge.cadoles.com/arcad/edge/pkg/storage"
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
"gitlab.com/wpetit/goweb/logger"
"forge.cadoles.com/arcad/edge/pkg/bundle"
"github.com/dop251/goja"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/chi/v5"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/pkg/errors"
)
type Server struct {
bundle bundle.Bundle
db *sql.DB
server *http.Server
serverMutex sync.RWMutex
key jwk.Key
}
func (s *Server) Start(ctx context.Context, addr string) (err error) {
if s.server != nil {
if err := s.Stop(); err != nil {
return errors.WithStack(err)
}
}
router := chi.NewRouter()
router.Use(middleware.Logger)
bus := memory.NewBus()
ds := sqlite.NewDocumentStoreWithDB(s.db)
bs := sqlite.NewBlobStoreWithDB(s.db)
handler := edgeHTTP.NewHandler(
edgeHTTP.WithBus(bus),
edgeHTTP.WithServerModules(s.getAppModules(bus, ds, bs)...),
)
if err := handler.Load(s.bundle); err != nil {
return errors.Wrap(err, "could not load app bundle")
}
router.Handle("/*", handler)
server := &http.Server{
Addr: addr,
Handler: router,
}
go func() {
defer func() {
if recovered := recover(); recovered != nil {
if err, ok := recovered.(error); ok {
logger.Error(ctx, err.Error(), logger.E(errors.WithStack(err)))
return
}
panic(recovered)
}
}()
defer func() {
if err := s.Stop(); err != nil {
panic(errors.WithStack(err))
}
}()
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
panic(errors.WithStack(err))
}
}()
s.serverMutex.Lock()
s.server = server
s.serverMutex.Unlock()
return nil
}
func (s *Server) Running() bool {
s.serverMutex.RLock()
defer s.serverMutex.RUnlock()
return s.server != nil
}
func (s *Server) Stop() error {
if s.server == nil {
return nil
}
defer func() {
s.serverMutex.Lock()
s.server = nil
s.serverMutex.Unlock()
}()
if err := s.server.Close(); err != nil {
panic(errors.WithStack(err))
}
return nil
}
func (s *Server) getAppModules(bus bus.Bus, ds storage.DocumentStore, bs storage.BlobStore) []app.ServerModuleFactory {
return []app.ServerModuleFactory{
module.ContextModuleFactory(),
module.ConsoleModuleFactory(),
cast.CastModuleFactory(),
module.LifecycleModuleFactory(),
net.ModuleFactory(bus),
module.RPCModuleFactory(bus),
module.StoreModuleFactory(ds),
module.BlobModuleFactory(bus, bs),
module.Extends(
auth.ModuleFactory(
auth.WithJWT(s.getJWTKeySet),
),
func(o *goja.Object) {
if err := o.Set("CLAIM_ROLE", "role"); err != nil {
panic(errors.New("could not set 'CLAIM_ROLE' property"))
}
if err := o.Set("CLAIM_PREFERRED_USERNAME", "preferred_username"); err != nil {
panic(errors.New("could not set 'CLAIM_PREFERRED_USERNAME' property"))
}
},
),
}
}
func (s *Server) getJWTKeySet() (jwk.Set, error) {
set := jwk.NewSet()
if err := set.AddKey(s.key); err != nil {
return nil, errors.WithStack(err)
}
return set, nil
}
func NewServer(bundle bundle.Bundle, db *sql.DB, key jwk.Key) *Server {
return &Server{
bundle: bundle,
db: db,
key: key,
}
}