diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c597da4..cc4ae18 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,14 +9,6 @@ repos:
- repo: local
hooks:
- - id: check-swagger
- name: check-swagger
- language: system
- files: swagger.yaml
- entry: swagger
- args: ["validate", "swagger.yaml"]
- pass_filenames: false
-
- id: tsc
name: tsc
entry: tsc
diff --git a/Makefile b/Makefile
index 36c51f6..2a1cbee 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,8 @@
SRC_DIR=./src
UI_DIR=./ui
+APIDOC_FILE=./src/server/swagger/swagger.yaml
+
VERSION=$(shell git tag --sort=-version:refname | head -n 1)
setup:
@@ -22,10 +24,10 @@ build-server:
build: build-ui build-server
generate-apidoc:
- SWAGGER_GENERATE_EXTENSION=false swagger generate spec --scan-models -o swagger.yaml
+ SWAGGER_GENERATE_EXTENSION=false swagger generate spec --scan-models -o ${APIDOC_FILE}
generate-apiclient:
- openapi-generator generate -i swagger.yaml -g typescript-axios -o ${UI_DIR}/src/sebrauc-client -p "supportsES6=true"
+ openapi-generator generate -i ${APIDOC_FILE} -g typescript-axios -o ${UI_DIR}/src/sebrauc-client -p "supportsES6=true"
cd ${UI_DIR} && npm run format
clean:
diff --git a/go.mod b/go.mod
index 36fe1e4..2090ee2 100644
--- a/go.mod
+++ b/go.mod
@@ -4,9 +4,11 @@ go 1.16
require (
code.thetadev.de/TSGRain/ginzip v0.1.1
+ github.com/ekyoung/gin-nice-recovery v0.0.0-20160510022553-1654dca486db
github.com/fortytw2/leaktest v1.3.0
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.7
+ github.com/go-errors/errors v1.4.1 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.2
github.com/stretchr/testify v1.7.0
diff --git a/go.sum b/go.sum
index c2354c4..7011440 100644
--- a/go.sum
+++ b/go.sum
@@ -5,6 +5,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/ekyoung/gin-nice-recovery v0.0.0-20160510022553-1654dca486db h1:oZ4U9IqO8NS+61OmGTBi8vopzqTRxwQeogyBHdrhjbc=
+github.com/ekyoung/gin-nice-recovery v0.0.0-20160510022553-1654dca486db/go.mod h1:Pk7/9x6tyChFTkahDvLBQMlvdsWvfC+yU8HTT5VD314=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
@@ -14,6 +16,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
+github.com/go-errors/errors v1.4.1 h1:IvVlgbzSsaUNudsw5dcXSzF3EWyXTi5XrAdngnuhRyg=
+github.com/go-errors/errors v1.4.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
diff --git a/src/assets/assets.go b/src/assets/assets.go
deleted file mode 100644
index 8f2c9de..0000000
--- a/src/assets/assets.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package assets
-
-import (
- "embed"
-)
-
-//go:embed files/**
-var Assets embed.FS
diff --git a/src/assets/files/index.html b/src/assets/files/index.html
deleted file mode 100644
index 0554e60..0000000
--- a/src/assets/files/index.html
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
- Chat Example
-
-
-
-
-
-
-
-
-
diff --git a/src/main.go b/src/main.go
index 2e83122..0bbf0f0 100644
--- a/src/main.go
+++ b/src/main.go
@@ -3,20 +3,28 @@ package main
import (
"fmt"
"log"
+ "time"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/rauc"
"code.thetadev.de/TSGRain/SEBRAUC/src/server"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/server/stream"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
)
func main() {
fmt.Println("SEBRAUC " + util.Version())
+ mode.Set(util.Mode)
- if util.TestMode {
+ if mode.IsDev() {
fmt.Println("Test mode active - no update operations are executed.")
fmt.Println("Build with -tags prod to enable live mode.")
}
- srv := server.NewServer(":8080")
+ streamer := stream.New(10*time.Second, 1*time.Second, []string{})
+ raucUpdater := &rauc.Rauc{}
+
+ srv := server.NewServer(":8080", streamer, raucUpdater)
err := srv.Run()
if err != nil {
log.Fatalln(err)
diff --git a/src/model/error.go b/src/model/error.go
new file mode 100644
index 0000000..b2f1396
--- /dev/null
+++ b/src/model/error.go
@@ -0,0 +1,24 @@
+package model
+
+// Error model
+//
+// The Error contains error relevant information.
+//
+//swagger:model Error
+type Error struct {
+ // The general error message according to HTTP specification.
+ //
+ // required: true
+ // example: Unauthorized
+ Error string `json:"error"`
+ // The http error code.
+ //
+ // required: true
+ // example: 500
+ StatusCode int `json:"status_code"`
+ // Concrete error message.
+ //
+ // required: true
+ // example: already running
+ Message string `json:"msg"`
+}
diff --git a/src/model/rauc_status.go b/src/model/rauc_status.go
index 52eb88b..3a48b53 100644
--- a/src/model/rauc_status.go
+++ b/src/model/rauc_status.go
@@ -1,5 +1,9 @@
package model
+// RaucStatus model
+//
+// RaucStatus contains information about the current RAUC updater status.
+//
//swagger:model RaucStatus
type RaucStatus struct {
// True if the installer is running
@@ -19,13 +23,11 @@ type RaucStatus struct {
// Installation error message
// required: true
- //nolint:lll
- // example: Failed to check bundle identifier: Invalid identifier. Did you pass a valid RAUC bundle?
+ // example: Failed to check bundle identifier: Invalid identifier.
LastError string `json:"last_error"`
// Full command line output of the current installation
// required: true
- //nolint:lll
- // example: 0% Installing\n0% Determining slot states\n20% Determining slot states done\n
+ // example: 0% Installing 0% Determining slot states 20% Determining slot states done
Log string `json:"log"`
}
diff --git a/src/model/status_message.go b/src/model/status_message.go
index 256b004..d01fe03 100644
--- a/src/model/status_message.go
+++ b/src/model/status_message.go
@@ -1,5 +1,9 @@
package model
+// StatusMessage model
+//
+// StatusMessage contains the status of an operation.
+//
//swagger:model StatusMessage
type StatusMessage struct {
// Is operation successful?
diff --git a/src/model/system_info.go b/src/model/system_info.go
index ec02bbb..6c923d7 100644
--- a/src/model/system_info.go
+++ b/src/model/system_info.go
@@ -1,5 +1,9 @@
package model
+// SystemInfo model
+//
+// SystemInfo contains information about the running system.
+//
//swagger:model SystemInfo
type SystemInfo struct {
// Hostname of the system
diff --git a/src/rauc/rauc.go b/src/rauc/rauc.go
index 5c1b4be..e84b142 100644
--- a/src/rauc/rauc.go
+++ b/src/rauc/rauc.go
@@ -19,23 +19,14 @@ var (
)
type Rauc struct {
- bc broadcaster
+ bc util.Broadcaster
status model.RaucStatus
runningMtx sync.Mutex
}
-type broadcaster interface {
- Broadcast(msg []byte)
-}
-
-func NewRauc(bc broadcaster) *Rauc {
- r := &Rauc{
- bc: bc,
- }
-
+func (r *Rauc) SetBroadcaster(bc util.Broadcaster) {
+ r.bc = bc
r.bc.Broadcast(r.GetStatusJson())
-
- return r
}
func (r *Rauc) completed(updateFile string) {
diff --git a/src/server/docs.go b/src/server/docs.go
deleted file mode 100644
index 1704805..0000000
--- a/src/server/docs.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// SEBRAUC
-//
-// REST API for the SEBRAUC firmware updater
-//
-// ---
-// Schemes: http, https
-// Version: 0.2.0
-// License: MIT
-//
-// Consumes:
-// - application/json
-//
-// Produces:
-// - application/json
-//
-// swagger:meta
-package server
diff --git a/src/server/middleware/cache.go b/src/server/middleware/cache.go
new file mode 100644
index 0000000..8c97b26
--- /dev/null
+++ b/src/server/middleware/cache.go
@@ -0,0 +1,7 @@
+package middleware
+
+import "github.com/gin-gonic/gin"
+
+func Cache(c *gin.Context) {
+ c.Writer.Header().Set("Cache-Control", "public, max-age=604800, immutable")
+}
diff --git a/src/server/middleware/error_handler.go b/src/server/middleware/error_handler.go
new file mode 100644
index 0000000..e774c66
--- /dev/null
+++ b/src/server/middleware/error_handler.go
@@ -0,0 +1,58 @@
+package middleware
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+
+ "code.thetadev.de/TSGRain/SEBRAUC/src/model"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/util"
+ nice "github.com/ekyoung/gin-nice-recovery"
+ "github.com/gin-gonic/gin"
+)
+
+// ErrorHandler creates a gin middleware for handling errors.
+func ErrorHandler(isApi bool) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ c.Next()
+
+ if len(c.Errors) > 0 {
+ for _, e := range c.Errors {
+ writeError(c, e.Err, isApi)
+ }
+ }
+ }
+}
+
+func PanicHandler(isApi bool) gin.HandlerFunc {
+ return nice.Recovery(func(c *gin.Context, err interface{}) {
+ writeError(c, fmt.Errorf("panic: %s", err), isApi)
+ })
+}
+
+func writeError(c *gin.Context, err error, isApi bool) {
+ status := http.StatusInternalServerError
+
+ var httpErr util.HttpError
+ if errors.As(err, &httpErr) {
+ status = httpErr.StatusCode()
+ }
+
+ // only write error message if there is no content
+ if c.Writer.Size() != -1 {
+ c.Status(status)
+ return
+ }
+
+ if isApi {
+ // Machine-readable JSON error message
+ c.JSON(status, &model.Error{
+ Error: http.StatusText(status),
+ StatusCode: status,
+ Message: err.Error(),
+ })
+ } else {
+ // Human-readable error message
+ c.String(status, "%d %s: %s", status, http.StatusText(status), err.Error())
+ }
+}
diff --git a/src/server/server.go b/src/server/server.go
index 7452edd..3f1eafb 100644
--- a/src/server/server.go
+++ b/src/server/server.go
@@ -1,15 +1,25 @@
+// SEBRAUC
+//
+// REST API for the SEBRAUC firmware updater
+//
+// ---
+// Schemes: http, https
+// Version: 0.2.0
+// License: MIT
+//
+// swagger:meta
package server
import (
"errors"
"fmt"
- "strings"
+ "net/http"
"time"
"code.thetadev.de/TSGRain/SEBRAUC/src/model"
- "code.thetadev.de/TSGRain/SEBRAUC/src/rauc"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/server/middleware"
"code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
- "code.thetadev.de/TSGRain/SEBRAUC/src/server/stream"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/server/swagger"
"code.thetadev.de/TSGRain/SEBRAUC/src/sysinfo"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"code.thetadev.de/TSGRain/SEBRAUC/ui"
@@ -18,17 +28,28 @@ import (
"github.com/google/uuid"
)
-type SEBRAUCServer struct {
- address string
- raucUpdater *rauc.Rauc
- streamer *stream.API
- tmpdir string
+type serverStreamer interface {
+ util.Broadcaster
+ Handle(ctx *gin.Context)
+ Close()
}
-func NewServer(address string) *SEBRAUCServer {
- streamer := stream.New(10*time.Second, 1*time.Second, []string{})
+type serverUpdater interface {
+ GetStatus() model.RaucStatus
+ RunRauc(updateFile string) error
+ SetBroadcaster(bc util.Broadcaster)
+}
- raucUpdater := rauc.NewRauc(streamer)
+type SEBRAUCServer struct {
+ address string
+ streamer serverStreamer
+ udater serverUpdater
+ tmpdir string
+}
+
+func NewServer(address string, streamer serverStreamer,
+ updater serverUpdater) *SEBRAUCServer {
+ updater.SetBroadcaster(streamer)
tmpdir, err := util.GetTmpdir()
if err != nil {
@@ -36,68 +57,80 @@ func NewServer(address string) *SEBRAUCServer {
}
return &SEBRAUCServer{
- address: address,
- raucUpdater: raucUpdater,
- streamer: streamer,
- tmpdir: tmpdir,
+ address: address,
+ udater: updater,
+ streamer: streamer,
+ tmpdir: tmpdir,
}
}
func (srv *SEBRAUCServer) Run() error {
- router := gin.Default()
+ router := gin.New()
+ router.Use(gin.Logger())
+ _ = router.SetTrustedProxies(nil)
if mode.IsDev() {
router.Use(cors.Default())
}
+ router.Use(middleware.ErrorHandler(false), middleware.PanicHandler(false))
+ router.NoRoute(func(c *gin.Context) { c.Error(util.ErrPageNotFound) })
+
+ api := router.Group("/api",
+ middleware.ErrorHandler(true), middleware.PanicHandler(true))
+
// ROUTES
- router.GET("/api/ws", srv.streamer.Handle)
- router.GET("/api/status", srv.controllerStatus)
- router.GET("/api/info", srv.controllerInfo)
+ api.GET("/ws", srv.streamer.Handle)
+ api.GET("/status", srv.controllerStatus)
+ api.GET("/info", srv.controllerInfo)
- router.POST("/api/update", srv.controllerUpdate)
- router.POST("/api/reboot", srv.controllerReboot)
+ api.POST("/update", srv.controllerUpdate)
+ api.POST("/reboot", srv.controllerReboot)
+
+ // Error routes for testing
+ if mode.IsDev() {
+ router.GET("/error", srv.controllerError)
+ router.GET("/panic", srv.controllerPanic)
+
+ api.GET("/error", srv.controllerError)
+ api.GET("/panic", srv.controllerPanic)
+ }
- // router.StaticFS("/", ui.GetFS())
ui.Register(router)
+ swagger.Register(router)
+
return router.Run(srv.address)
}
-// @Description Start the update process
-// @Route /update
+// swagger:operation POST /update startUpdate
//
-// _Param {name} {in} {goType} {required} {description}
-// @Param updateFile form file true "Rauc firmware image file (*.raucb)"
+// Start the update process
//
-// _Success {status} {jsonType} {goType} {description}
-// @Success 200 object statusMessage
-// @Failure 500 object statusMessage "Server error"
+// ---
+// consumes:
+// - multipart/form-data
+// produces: [application/json]
+// parameters:
+// - name: updateFile
+// in: formData
+// description: RAUC firmware image file (*.raucb)
+// required: true
+// type: file
+// responses:
+// 200:
+// description: Ok
+// schema:
+// $ref: "#/definitions/StatusMessage"
+// 409:
+// description: already running
+// schema:
+// $ref: "#/definitions/Error"
+// 500:
+// description: Server Error
+// schema:
+// $ref: "#/definitions/Error"
func (srv *SEBRAUCServer) controllerUpdate(c *gin.Context) {
- // swagger:operation POST /update startUpdate
- //
- // Start the update process
- //
- // ---
- // consumes:
- // - multipart/form-data
- // produces: [application/json]
- // parameters:
- // - name: updateFile
- // in: formData
- // description: Rauc firmware image file (*.raucb)
- // required: true
- // type: file
- // responses:
- // 200:
- // description: Ok
- // schema:
- // $ref: "#/definitions/StatusMessage"
- // 500:
- // description: Server Error
- // schema:
- // $ref: "#/definitions/StatusMessage"
-
file, err := c.FormFile("updateFile")
if err != nil {
c.Error(err)
@@ -118,100 +151,93 @@ func (srv *SEBRAUCServer) controllerUpdate(c *gin.Context) {
return
}
- err = srv.raucUpdater.RunRauc(updateFile)
+ err = srv.udater.RunRauc(updateFile)
if err == nil {
writeStatus(c, true, "Update started")
} else if errors.Is(err, util.ErrAlreadyRunning) {
- c.AbortWithError(409, errors.New("already running"))
+ writeStatus(c, false, "Updater already running")
} else {
c.Error(err)
return
}
}
+// swagger:operation GET /status getStatus
+//
+// Get the current status of the RAUC updater
+//
+// ---
+// produces: [application/json]
+// responses:
+// 200:
+// description: Ok
+// schema:
+// $ref: "#/definitions/RaucStatus"
+// 500:
+// description: Server Error
+// schema:
+// $ref: "#/definitions/Error"
func (srv *SEBRAUCServer) controllerStatus(c *gin.Context) {
- // swagger:operation GET /status getStatus
- //
- // Get the current status of the RAUC updater
- //
- // ---
- // produces: [application/json]
- // responses:
- // 200:
- // description: Ok
- // schema:
- // $ref: "#/definitions/RaucStatus"
- // 500:
- // description: Server Error
- // schema:
- // $ref: "#/definitions/StatusMessage"
-
- c.JSON(200, srv.raucUpdater.GetStatus())
+ c.JSON(http.StatusOK, srv.udater.GetStatus())
}
+// swagger:operation GET /info getInfo
+//
+// Get the current system info
+//
+// ---
+// produces: [application/json]
+// responses:
+// 200:
+// description: Ok
+// schema:
+// $ref: "#/definitions/SystemInfo"
+// 500:
+// description: Server Error
+// schema:
+// $ref: "#/definitions/Error"
func (srv *SEBRAUCServer) controllerInfo(c *gin.Context) {
- // swagger:operation GET /info getInfo
- //
- // Get the current system info
- //
- // ---
- // produces: [application/json]
- // responses:
- // 200:
- // description: Ok
- // schema:
- // $ref: "#/definitions/SystemInfo"
- // 500:
- // description: Server Error
- // schema:
- // $ref: "#/definitions/StatusMessage"
-
info, err := sysinfo.GetSysinfo()
if err != nil {
c.Error(err)
} else {
- c.JSON(200, info)
+ c.JSON(http.StatusOK, info)
}
}
+// swagger:operation GET /reboot startReboot
+//
+// Reboot the system
+//
+// ---
+// produces: [application/json]
+// responses:
+// 200:
+// description: Ok
+// schema:
+// $ref: "#/definitions/StatusMessage"
+// 500:
+// description: Server Error
+// schema:
+// $ref: "#/definitions/Error"
func (srv *SEBRAUCServer) controllerReboot(c *gin.Context) {
- // swagger:operation GET /reboot startReboot
- //
- // Reboot the system
- //
- // ---
- // produces: [application/json]
- // responses:
- // 200:
- // description: Ok
- // schema:
- // $ref: "#/definitions/StatusMessage"
- // 500:
- // description: Server Error
- // schema:
- // $ref: "#/definitions/StatusMessage"
-
go util.Reboot(5 * time.Second)
writeStatus(c, true, "System is rebooting")
}
-func errorHandler(c *gin.Context, err error) error {
- // API error handling
- if strings.HasPrefix(c.FullPath(), "/api") {
- writeStatus(c, false, err.Error())
- }
- return err
+// controllerError throws an error for testing
+func (srv *SEBRAUCServer) controllerError(c *gin.Context) {
+ c.Error(util.HttpErrNew("error test", http.StatusBadRequest))
+}
+
+// controllerPanic panics for testing
+func (srv *SEBRAUCServer) controllerPanic(c *gin.Context) {
+ panic(errors.New("panic message"))
}
func writeStatus(c *gin.Context, success bool, msg string) {
- status := 200
-
- if !success {
- status = 500
- }
-
- c.JSON(status, model.StatusMessage{
+ c.JSON(http.StatusOK, model.StatusMessage{
Success: success,
Msg: msg,
})
diff --git a/src/server/swagger/swagger.go b/src/server/swagger/swagger.go
new file mode 100644
index 0000000..e48ca00
--- /dev/null
+++ b/src/server/swagger/swagger.go
@@ -0,0 +1,25 @@
+package swagger
+
+import (
+ _ "embed"
+
+ "code.thetadev.de/TSGRain/SEBRAUC/src/server/middleware"
+ "github.com/gin-gonic/gin"
+)
+
+//go:embed swagger.html
+var swaggerHtml []byte
+
+//go:embed swagger.yaml
+var swaggerYaml []byte
+
+func Register(r *gin.Engine) {
+ swg := r.Group("/api/swagger", middleware.Cache)
+
+ swg.GET("/", func(c *gin.Context) {
+ c.Data(200, "text/html", swaggerHtml)
+ })
+ swg.GET("/swagger.yaml", func(c *gin.Context) {
+ c.Data(200, "text/yaml", swaggerYaml)
+ })
+}
diff --git a/src/server/swagger/swagger.html b/src/server/swagger/swagger.html
new file mode 100644
index 0000000..cc6855f
--- /dev/null
+++ b/src/server/swagger/swagger.html
@@ -0,0 +1,20 @@
+
+
+
+ SEBRAUC API documentation
+
+
+
+
+
+
+
+
+
+
+
diff --git a/swagger.yaml b/src/server/swagger/swagger.yaml
similarity index 77%
rename from swagger.yaml
rename to src/server/swagger/swagger.yaml
index 14afa86..3d85982 100644
--- a/swagger.yaml
+++ b/src/server/swagger/swagger.yaml
@@ -1,20 +1,40 @@
-consumes:
- - application/json
definitions:
+ Error:
+ description: The Error contains error relevant information.
+ properties:
+ error:
+ description: The general error message according to HTTP specification.
+ example: Unauthorized
+ type: string
+ msg:
+ description: Concrete error message.
+ example: already running
+ type: string
+ status_code:
+ description: The http error code.
+ example: 500
+ format: int64
+ type: integer
+ required:
+ - error
+ - status_code
+ - msg
+ title: Error model
+ type: object
RaucStatus:
+ description: RaucStatus contains information about the current RAUC updater status.
properties:
installing:
description: True if the installer is running
type: boolean
last_error:
description: Installation error message
- example: "Failed to check bundle identifier: Invalid identifier. Did you pass
- a valid RAUC bundle?"
+ example: "Failed to check bundle identifier: Invalid identifier."
type: string
log:
description: Full command line output of the current installation
- example: 0% Installing\n0% Determining slot states\n20% Determining slot states
- done\n
+ example: 0% Installing 0% Determining slot states 20% Determining slot states
+ done
type: string
message:
description: Current installation step
@@ -32,6 +52,7 @@ definitions:
- message
- last_error
- log
+ title: RaucStatus model
type: object
Rootfs:
properties:
@@ -65,6 +86,7 @@ definitions:
- primary
type: object
StatusMessage:
+ description: StatusMessage contains the status of an operation.
properties:
msg:
description: Status message text
@@ -76,8 +98,10 @@ definitions:
required:
- success
- msg
+ title: StatusMessage model
type: object
SystemInfo:
+ description: SystemInfo contains information about the running system.
properties:
hostname:
description: Hostname of the system
@@ -117,6 +141,7 @@ definitions:
- rauc_compatible
- rauc_variant
- rauc_rootfs
+ title: SystemInfo model
type: object
info:
description: REST API for the SEBRAUC firmware updater
@@ -139,7 +164,7 @@ paths:
"500":
description: Server Error
schema:
- $ref: "#/definitions/StatusMessage"
+ $ref: "#/definitions/Error"
/reboot:
get:
description: Reboot the system
@@ -154,7 +179,7 @@ paths:
"500":
description: Server Error
schema:
- $ref: "#/definitions/StatusMessage"
+ $ref: "#/definitions/Error"
/status:
get:
description: Get the current status of the RAUC updater
@@ -169,7 +194,7 @@ paths:
"500":
description: Server Error
schema:
- $ref: "#/definitions/StatusMessage"
+ $ref: "#/definitions/Error"
/update:
post:
consumes:
@@ -177,7 +202,7 @@ paths:
description: Start the update process
operationId: startUpdate
parameters:
- - description: Rauc firmware image file (*.raucb)
+ - description: RAUC firmware image file (*.raucb)
in: formData
name: updateFile
required: true
@@ -189,12 +214,12 @@ paths:
description: Ok
schema:
$ref: "#/definitions/StatusMessage"
+ "409":
+ description: already running
"500":
description: Server Error
schema:
- $ref: "#/definitions/StatusMessage"
-produces:
- - application/json
+ $ref: "#/definitions/Error"
schemes:
- http
- https
diff --git a/src/util/commands.go b/src/util/commands.go
index a157fa0..78fb45d 100644
--- a/src/util/commands.go
+++ b/src/util/commands.go
@@ -3,9 +3,11 @@
package util
+import "code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
+
const (
RebootCmd = "shutdown -r 0"
RaucCmd = "rauc"
- TestMode = false
+ Mode = mode.Prod
)
diff --git a/src/util/commands_mock.go b/src/util/commands_mock.go
index d4d1392..b447055 100644
--- a/src/util/commands_mock.go
+++ b/src/util/commands_mock.go
@@ -3,9 +3,11 @@
package util
+import "code.thetadev.de/TSGRain/SEBRAUC/src/server/mode"
+
const (
RebootCmd = "touch /tmp/sebrauc_reboot_test"
RaucCmd = "go run code.thetadev.de/TSGRain/SEBRAUC/src/fixtures/rauc_mock"
- TestMode = true
+ Mode = mode.Dev
)
diff --git a/src/util/errors.go b/src/util/errors.go
index 20ee9e4..1d3fc3b 100644
--- a/src/util/errors.go
+++ b/src/util/errors.go
@@ -1,8 +1,12 @@
package util
-import "errors"
+import (
+ "errors"
+ "net/http"
+)
var (
ErrAlreadyRunning = errors.New("rauc already running")
ErrFileDoesNotExist = errors.New("file does not exist")
+ ErrPageNotFound = HttpErrNew("page not found", http.StatusNotFound)
)
diff --git a/src/util/http_error.go b/src/util/http_error.go
new file mode 100644
index 0000000..c5d7cf8
--- /dev/null
+++ b/src/util/http_error.go
@@ -0,0 +1,39 @@
+package util
+
+import "errors"
+
+type HttpError interface {
+ error
+ StatusCode() int
+}
+
+type httpErr struct {
+ err error
+ statusCode int
+}
+
+func HttpErrWrap(e error, statusCode int) HttpError {
+ return &httpErr{
+ err: e,
+ statusCode: statusCode,
+ }
+}
+
+func HttpErrNew(msg string, statusCode int) HttpError {
+ return HttpErrWrap(errors.New(msg), statusCode)
+}
+
+func (e *httpErr) Error() string {
+ if e.err == nil {
+ return ""
+ }
+ return e.err.Error()
+}
+
+func (e *httpErr) Unwrap() error {
+ return e.err
+}
+
+func (e *httpErr) StatusCode() int {
+ return e.statusCode
+}
diff --git a/src/util/types.go b/src/util/types.go
new file mode 100644
index 0000000..afd106b
--- /dev/null
+++ b/src/util/types.go
@@ -0,0 +1,5 @@
+package util
+
+type Broadcaster interface {
+ Broadcast(msg []byte)
+}
diff --git a/ui/src/sebrauc-client/api.ts b/ui/src/sebrauc-client/api.ts
index 015ad91..c98ab0a 100644
--- a/ui/src/sebrauc-client/api.ts
+++ b/ui/src/sebrauc-client/api.ts
@@ -38,7 +38,32 @@ import {
} from "./base"
/**
- *
+ * The Error contains error relevant information.
+ * @export
+ * @interface ModelError
+ */
+export interface ModelError {
+ /**
+ * The general error message according to HTTP specification.
+ * @type {string}
+ * @memberof ModelError
+ */
+ error: string
+ /**
+ * Concrete error message.
+ * @type {string}
+ * @memberof ModelError
+ */
+ msg: string
+ /**
+ * The http error code.
+ * @type {number}
+ * @memberof ModelError
+ */
+ status_code: number
+}
+/**
+ * RaucStatus contains information about the current RAUC updater status.
* @export
* @interface RaucStatus
*/
@@ -118,7 +143,7 @@ export interface Rootfs {
type: string
}
/**
- *
+ * StatusMessage contains the status of an operation.
* @export
* @interface StatusMessage
*/
@@ -137,7 +162,7 @@ export interface StatusMessage {
success: boolean
}
/**
- *
+ * SystemInfo contains information about the running system.
* @export
* @interface SystemInfo
*/
@@ -290,7 +315,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati
},
/**
* Start the update process
- * @param {any} updateFile Rauc firmware image file (*.raucb)
+ * @param {any} updateFile RAUC firmware image file (*.raucb)
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
@@ -404,7 +429,7 @@ export const DefaultApiFp = function (configuration?: Configuration) {
},
/**
* Start the update process
- * @param {any} updateFile Rauc firmware image file (*.raucb)
+ * @param {any} updateFile RAUC firmware image file (*.raucb)
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
@@ -471,7 +496,7 @@ export const DefaultApiFactory = function (
},
/**
* Start the update process
- * @param {any} updateFile Rauc firmware image file (*.raucb)
+ * @param {any} updateFile RAUC firmware image file (*.raucb)
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
@@ -528,7 +553,7 @@ export class DefaultApi extends BaseAPI {
/**
* Start the update process
- * @param {any} updateFile Rauc firmware image file (*.raucb)
+ * @param {any} updateFile RAUC firmware image file (*.raucb)
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof DefaultApi
diff --git a/ui/ui.go b/ui/ui.go
index a10fda6..66329e8 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -7,6 +7,7 @@ import (
"io/fs"
"net/http"
+ "code.thetadev.de/TSGRain/SEBRAUC/src/server/middleware"
"code.thetadev.de/TSGRain/SEBRAUC/src/util"
"code.thetadev.de/TSGRain/ginzip"
"github.com/gin-gonic/gin"
@@ -36,7 +37,7 @@ func distFS() fs.FS {
func Register(r *gin.Engine) {
indexHandler := getIndexHandler()
- ui := r.Group("/", ginzip.New(ginzip.DefaultOptions()))
+ ui := r.Group("/", ginzip.New(ginzip.DefaultOptions()), middleware.Cache)
ui.GET("/", indexHandler)
ui.GET("/index.html", indexHandler)