1
0
Fork 0
mirror of https://git.sr.ht/~ashkeel/strimertul synced 2024-09-20 02:00:49 +00:00
strimertul/modules/http/server.go

185 lines
4 KiB
Go
Raw Normal View History

package http
import (
"context"
"errors"
"fmt"
"io/fs"
"net/http"
2022-02-01 11:35:34 +00:00
jsoniter "github.com/json-iterator/go"
"github.com/strimertul/strimertul/modules/database"
2022-01-27 15:49:18 +00:00
"go.uber.org/zap"
2022-02-01 11:35:34 +00:00
kv "github.com/strimertul/kilovolt/v8"
"github.com/strimertul/strimertul/modules"
)
type Server struct {
Config ServerConfig
2022-02-01 11:35:34 +00:00
db *database.DBModule
2022-01-27 15:49:18 +00:00
logger *zap.Logger
server *http.Server
frontend fs.FS
hub *kv.Hub
mux *http.ServeMux
}
func NewServer(manager *modules.Manager) (*Server, error) {
2022-02-01 11:35:34 +00:00
db, ok := manager.Modules["db"].(*database.DBModule)
if !ok {
return nil, errors.New("db module not found")
}
2022-01-27 15:49:18 +00:00
logger := manager.Logger(modules.ModuleHTTP)
server := &Server{
2022-01-27 15:49:18 +00:00
logger: logger,
db: db,
server: &http.Server{},
}
err := db.GetJSON(ServerConfigKey, &server.Config)
2021-11-21 21:36:48 +00:00
if err != nil {
2021-12-09 12:32:58 +00:00
// Initialize with default config
server.Config = ServerConfig{
Bind: "localhost:4337",
EnableStaticServer: false,
KVPassword: "",
}
// Save
err = db.PutJSON(ServerConfigKey, server.Config)
if err != nil {
return nil, err
}
2021-11-21 21:36:48 +00:00
}
2022-02-01 11:35:34 +00:00
// Set hub
server.hub = db.Hub()
// Set password
server.hub.SetOptions(kv.HubOptions{
2021-11-21 21:36:48 +00:00
Password: server.Config.KVPassword,
2022-02-01 11:35:34 +00:00
})
// Register module
manager.Modules[modules.ModuleHTTP] = server
2021-11-21 21:36:48 +00:00
return server, nil
}
2021-12-09 09:46:14 +00:00
func (s *Server) Status() modules.ModuleStatus {
return modules.ModuleStatus{
Enabled: true,
Working: s.server != nil,
Data: struct {
Bind string
}{
s.server.Addr,
},
StatusString: s.server.Addr,
}
}
func (s *Server) Close() error {
return s.server.Close()
}
func (s *Server) SetFrontend(files fs.FS) {
s.frontend = files
}
func (s *Server) makeMux() *http.ServeMux {
mux := http.NewServeMux()
if s.frontend != nil {
mux.Handle("/ui/", http.StripPrefix("/ui/", FileServerWithDefault(http.FS(s.frontend))))
}
if s.hub != nil {
mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
kv.ServeWs(s.hub, w, r)
})
}
if s.Config.EnableStaticServer {
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(s.Config.Path))))
}
return mux
}
func (s *Server) Listen() error {
// Start HTTP server
restart := newSafeBool(false)
exit := make(chan error)
go func() {
2022-02-01 11:35:34 +00:00
err := s.db.Subscribe(func(key, value string) {
if key == ServerConfigKey {
oldBind := s.Config.Bind
oldPassword := s.Config.KVPassword
err := jsoniter.ConfigFastest.Unmarshal([]byte(value), &s.Config)
if err != nil {
s.logger.Error("Failed to unmarshal config", zap.Error(err))
return
}
s.mux = s.makeMux()
// Restart hub if password changed
if oldPassword != s.Config.KVPassword {
s.hub.SetOptions(kv.HubOptions{
Password: s.Config.KVPassword,
})
}
// Restart server if bind changed
if oldBind != s.Config.Bind {
restart.Set(true)
err = s.server.Shutdown(context.Background())
if err != nil {
2022-02-01 11:35:34 +00:00
s.logger.Error("Failed to shutdown server", zap.Error(err))
return
}
}
}
}, ServerConfigKey)
if err != nil {
exit <- fmt.Errorf("error while handling subscription to HTTP config changes: %w", err)
}
}()
go func() {
for {
2022-01-27 15:49:18 +00:00
s.logger.Info("Starting HTTP server", zap.String("bind", s.Config.Bind))
s.mux = s.makeMux()
s.server = &http.Server{
Handler: s,
Addr: s.Config.Bind,
}
2022-01-27 15:49:18 +00:00
s.logger.Info("HTTP server started", zap.String("bind", s.Config.Bind))
err := s.server.ListenAndServe()
2022-01-27 15:49:18 +00:00
s.logger.Debug("HTTP server died", zap.Error(err))
if err != nil && !errors.Is(err, http.ErrServerClosed) {
exit <- err
return
}
// Are we trying to close or restart?
2022-01-27 15:49:18 +00:00
s.logger.Debug("HTTP server stopped", zap.Bool("restart", restart.Get()))
if restart.Get() {
restart.Set(false)
continue
}
break
}
s.logger.Debug("HTTP server stalled")
exit <- nil
}()
return <-exit
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Redirect to /ui/ if root
if r.URL.Path == "/" {
http.Redirect(w, r, "/ui/", http.StatusFound)
return
}
s.mux.ServeHTTP(w, r)
}