Reload server and edit configuration. ()

The first iteration for config editor and server reload from the web interface.
This commit is contained in:
Antonio Navarro Perez 2020-11-08 18:19:25 +01:00 committed by GitHub
parent 56d32fd1f4
commit 0e2288565d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 882 additions and 178 deletions

87
http/api.go Normal file
View file

@ -0,0 +1,87 @@
package http
import (
"errors"
"io"
"io/ioutil"
"github.com/ajnavarro/distribyted/config"
"github.com/ajnavarro/distribyted/stats"
"github.com/anacrolix/missinggo/v2/filecache"
"github.com/gin-gonic/gin"
)
var apiStatusHandler = func(fc *filecache.Cache, ss *stats.Torrent) gin.HandlerFunc {
return func(ctx *gin.Context) {
// TODO move to a struct
ctx.JSON(200, gin.H{
"cacheItems": fc.Info().NumItems,
"cacheFilled": fc.Info().Filled / 1024 / 1024,
"cacheCapacity": fc.Info().Capacity / 1024 / 1024,
"torrentStats": ss.GlobalStats(),
})
}
}
var apiRoutesHandler = func(ss *stats.Torrent) gin.HandlerFunc {
return func(ctx *gin.Context) {
stats := ss.RoutesStats()
ctx.JSON(200, stats)
}
}
var apiGetConfigFile = func(ch *config.Handler) gin.HandlerFunc {
return func(ctx *gin.Context) {
rc, err := ch.GetRaw()
if err != nil {
ctx.AbortWithError(500, err)
return
}
ctx.Data(200, "text/x-yaml", rc)
}
}
var apiSetConfigFile = func(ch *config.Handler) gin.HandlerFunc {
return func(ctx *gin.Context) {
if ctx.Request.Body == nil {
ctx.AbortWithError(500, errors.New("no config file sent"))
return
}
c, err := ioutil.ReadAll(ctx.Request.Body)
if err != nil {
ctx.AbortWithError(500, err)
return
}
if len(c) == 0 {
ctx.AbortWithError(500, errors.New("no config file sent"))
return
}
if err := ch.Set(c); err != nil {
ctx.AbortWithError(500, err)
return
}
// TODO return something?
ctx.JSON(200, nil)
}
}
var apiReloadServer = func(ch *config.Handler) gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Stream(func(w io.Writer) bool {
err := ch.Reload(
func(m string) {
ctx.SSEvent("reload", m)
})
if err != nil {
ctx.AbortWithError(500, err)
}
return false
})
}
}

52
http/http.go Normal file
View file

@ -0,0 +1,52 @@
package http
import (
"fmt"
"github.com/ajnavarro/distribyted"
"github.com/ajnavarro/distribyted/config"
"github.com/ajnavarro/distribyted/stats"
"github.com/anacrolix/missinggo/v2/filecache"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/shurcooL/httpfs/html/vfstemplate"
"github.com/sirupsen/logrus"
)
func New(fc *filecache.Cache, ss *stats.Torrent, ch *config.Handler, port int) error {
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.ErrorLogger())
assets := distribyted.NewBinaryFileSystem(distribyted.HttpFS, "/assets")
r.Use(static.Serve("/assets", assets))
t, err := vfstemplate.ParseGlob(distribyted.HttpFS, nil, "/templates/*")
if err != nil {
return fmt.Errorf("error parsing html: %w", err)
}
r.SetHTMLTemplate(t)
r.GET("/", indexHandler)
r.GET("/routes", routesHandler(ss))
r.GET("/config", configHandler)
api := r.Group("/api")
{
api.GET("/status", apiStatusHandler(fc, ss))
api.GET("/routes", apiRoutesHandler(ss))
api.GET("/config", apiGetConfigFile(ch))
api.POST("/config", apiSetConfigFile(ch))
api.POST("/reload", apiReloadServer(ch))
}
logrus.WithField("host", fmt.Sprintf("0.0.0.0:%d", port)).Info("starting webserver")
if err := r.Run(fmt.Sprintf(":%d", port)); err != nil {
return fmt.Errorf("error initializing server: %w", err)
}
return nil
}

22
http/web.go Normal file
View file

@ -0,0 +1,22 @@
package http
import (
"net/http"
"github.com/ajnavarro/distribyted/stats"
"github.com/gin-gonic/gin"
)
var indexHandler = func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
}
var routesHandler = func(ss *stats.Torrent) gin.HandlerFunc {
return func(c *gin.Context) {
c.HTML(http.StatusOK, "routes.html", ss.RoutesStats())
}
}
var configHandler = func(c *gin.Context) {
c.HTML(http.StatusOK, "config.html", nil)
}