SEBRAUC/src/server/server.go

172 lines
3.6 KiB
Go

package server
import (
"errors"
"fmt"
"net/http"
"strings"
"time"
"code.thetadev.de/TSGRain/SEBRAUC/src/rauc"
"code.thetadev.de/TSGRain/SEBRAUC/src/sysinfo"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"code.thetadev.de/TSGRain/SEBRAUC/ui"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/filesystem"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/websocket/v2"
"github.com/google/uuid"
)
type SEBRAUCServer struct {
address string
raucUpdater *rauc.Rauc
hub *MessageHub
tmpdir string
}
type statusMessage struct {
Success bool `json:"success"`
Msg string `json:"msg"`
}
func NewServer(address string) *SEBRAUCServer {
hub := NewHub()
raucUpdater := rauc.NewRauc(hub.Broadcast)
tmpdir, err := util.GetTmpdir()
if err != nil {
panic(err)
}
return &SEBRAUCServer{
address: address,
raucUpdater: raucUpdater,
hub: hub,
tmpdir: tmpdir,
}
}
func (srv *SEBRAUCServer) Run() error {
app := fiber.New(fiber.Config{
AppName: "SEBRAUC",
BodyLimit: 1024 * 1024 * 1024,
ErrorHandler: errorHandler,
DisableStartupMessage: true,
})
app.Use(logger.New())
app.Use(compress.New(compress.Config{
Next: func(c *fiber.Ctx) bool {
return strings.HasPrefix(c.Path(), "/api")
},
}))
// just for testing
app.Use("/api", cors.New())
app.Use("/api/ws", func(c *fiber.Ctx) error {
// IsWebSocketUpgrade returns true if the client
// requested upgrade to the WebSocket protocol.
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
app.Use("/", filesystem.New(filesystem.Config{
Root: http.FS(ui.Assets),
PathPrefix: ui.AssetsDir,
MaxAge: 7200,
}))
// ROUTES
app.Get("/api/ws", websocket.New(srv.hub.Handler))
app.Post("/api/update", srv.controllerUpdate)
app.Get("/api/status", srv.controllerStatus)
app.Get("/api/info", srv.controllerInfo)
app.Post("/api/reboot", srv.controllerReboot)
// Start messaging hub
go srv.hub.Run()
return app.Listen(srv.address)
}
func (srv *SEBRAUCServer) controllerUpdate(c *fiber.Ctx) error {
file, err := c.FormFile("updateFile")
if err != nil {
return err
}
uid, err := uuid.NewRandom()
if err != nil {
return err
}
updateFile := fmt.Sprintf("%s/update_%s.raucb", srv.tmpdir, uid.String())
err = c.SaveFile(file, updateFile)
if err != nil {
return err
}
err = srv.raucUpdater.RunRauc(updateFile)
if err == nil {
writeStatus(c, true, "Update started")
} else if errors.Is(err, util.ErrAlreadyRunning) {
return fiber.NewError(fiber.StatusConflict, "already running")
} else {
return err
}
return nil
}
func (srv *SEBRAUCServer) controllerStatus(c *fiber.Ctx) error {
c.Context().SetStatusCode(200)
_ = c.JSON(srv.raucUpdater.GetStatus())
return nil
}
func (srv *SEBRAUCServer) controllerInfo(c *fiber.Ctx) error {
info, err := sysinfo.GetSysinfo()
if err != nil {
return err
}
c.Context().SetStatusCode(200)
_ = c.JSON(info)
return nil
}
func (srv *SEBRAUCServer) controllerReboot(c *fiber.Ctx) error {
go util.Reboot(5 * time.Second)
writeStatus(c, true, "System is rebooting")
return nil
}
func errorHandler(c *fiber.Ctx, err error) error {
// API error handling
if strings.HasPrefix(c.Path(), "/api") {
writeStatus(c, false, err.Error())
}
return err
}
func writeStatus(c *fiber.Ctx, success bool, msg string) {
_ = c.JSON(statusMessage{
Success: success,
Msg: msg,
})
if success {
c.Context().SetStatusCode(200)
}
}