frontend performance, rpc/rest jwt authentication
This commit is contained in:
@@ -13,6 +13,8 @@ type serverConfig struct {
|
||||
Port int `yaml:"port"`
|
||||
DownloadPath string `yaml:"downloadPath"`
|
||||
DownloaderPath string `yaml:"downloaderPath"`
|
||||
RequireAuth bool `yaml:"require_auth"`
|
||||
RPCSecret string `yaml:"rpc_secret"`
|
||||
}
|
||||
|
||||
type config struct {
|
||||
@@ -46,6 +48,13 @@ func (c *config) DownloaderPath(path string) {
|
||||
c.cfg.DownloaderPath = path
|
||||
}
|
||||
|
||||
func (c *config) RequireAuth(value bool) {
|
||||
c.cfg.RequireAuth = value
|
||||
}
|
||||
func (c *config) RPCSecret(secret string) {
|
||||
c.cfg.RPCSecret = secret
|
||||
}
|
||||
|
||||
var instance *config
|
||||
|
||||
func Instance() *config {
|
||||
|
||||
50
server/middleware/jwt.go
Normal file
50
server/middleware/jwt.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
||||
)
|
||||
|
||||
const (
|
||||
TOKEN_COOKIE_NAME = "jwt"
|
||||
)
|
||||
|
||||
var Authenticated = func(c *fiber.Ctx) error {
|
||||
if !config.Instance().GetConfig().RequireAuth {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
cookie := c.Cookies(TOKEN_COOKIE_NAME)
|
||||
|
||||
if cookie == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).SendString("invalid token")
|
||||
}
|
||||
|
||||
token, _ := jwt.Parse(cookie, func(t *jwt.Token) (interface{}, error) {
|
||||
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
|
||||
}
|
||||
return []byte(os.Getenv("JWTSECRET")), nil
|
||||
})
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
expiresAt, err := time.Parse(time.RFC3339, claims["expiresAt"].(string))
|
||||
|
||||
if err != nil {
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if time.Now().After(expiresAt) {
|
||||
return c.Status(fiber.StatusBadRequest).SendString("expired token")
|
||||
}
|
||||
} else {
|
||||
return c.Status(fiber.StatusUnauthorized).SendString("invalid token")
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
@@ -11,10 +11,15 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
TOKEN_COOKIE_NAME = "jwt"
|
||||
)
|
||||
|
||||
type DirectoryEntry struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
@@ -139,3 +144,52 @@ func SendFile(ctx *fiber.Ctx) error {
|
||||
|
||||
return ctx.SendStatus(fiber.StatusUnauthorized)
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
func Login(ctx *fiber.Ctx) error {
|
||||
req := new(LoginRequest)
|
||||
err := ctx.BodyParser(req)
|
||||
if err != nil {
|
||||
return ctx.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if config.Instance().GetConfig().RPCSecret != req.Secret {
|
||||
return ctx.SendStatus(fiber.StatusBadRequest)
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"expiresAt": time.Now().Add(time.Minute * 30),
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString([]byte(os.Getenv("JWTSECRET")))
|
||||
if err != nil {
|
||||
return ctx.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
ctx.Cookie(&fiber.Cookie{
|
||||
Name: TOKEN_COOKIE_NAME,
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
Expires: time.Now().Add(time.Hour * 24 * 30), // 30 days
|
||||
Value: tokenString,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func Logout(ctx *fiber.Ctx) error {
|
||||
ctx.Cookie(&fiber.Cookie{
|
||||
Name: TOKEN_COOKIE_NAME,
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
Expires: time.Now(),
|
||||
Value: "",
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
||||
"github.com/gofiber/websocket/v2"
|
||||
middlewares "github.com/marcopeocchi/yt-dlp-web-ui/server/middleware"
|
||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/rest"
|
||||
)
|
||||
|
||||
@@ -35,21 +36,32 @@ func RunBlocking(port int, frontend fs.FS) {
|
||||
Root: http.FS(frontend),
|
||||
}))
|
||||
|
||||
// Client side routes
|
||||
app.Get("/settings", func(c *fiber.Ctx) error {
|
||||
return c.Redirect("/")
|
||||
})
|
||||
app.Get("/archive", func(c *fiber.Ctx) error {
|
||||
return c.Redirect("/")
|
||||
})
|
||||
app.Get("/login", func(c *fiber.Ctx) error {
|
||||
return c.Redirect("/")
|
||||
})
|
||||
|
||||
app.Post("/downloaded", rest.ListDownloaded)
|
||||
// Archive routes
|
||||
archive := app.Group("archive", middlewares.Authenticated)
|
||||
archive.Post("/downloaded", rest.ListDownloaded)
|
||||
archive.Post("/delete", rest.DeleteFile)
|
||||
archive.Get("/d/:id", rest.SendFile)
|
||||
|
||||
app.Post("/delete", rest.DeleteFile)
|
||||
app.Get("/d/:id", rest.SendFile)
|
||||
// Authentication routes
|
||||
app.Post("/auth/login", rest.Login)
|
||||
app.Get("/auth/logout", rest.Logout)
|
||||
|
||||
// RPC handlers
|
||||
// websocket
|
||||
app.Get("/ws-rpc", websocket.New(func(c *websocket.Conn) {
|
||||
rpc := app.Group("/rpc", middlewares.Authenticated)
|
||||
|
||||
rpc.Get("/ws", websocket.New(func(c *websocket.Conn) {
|
||||
c.WriteMessage(websocket.TextMessage, []byte(`{
|
||||
"status": "connected"
|
||||
}`))
|
||||
@@ -69,7 +81,7 @@ func RunBlocking(port int, frontend fs.FS) {
|
||||
}
|
||||
}))
|
||||
// http-post
|
||||
app.Post("/http-rpc", func(c *fiber.Ctx) error {
|
||||
rpc.Post("/http", func(c *fiber.Ctx) error {
|
||||
reader := c.Context().RequestBodyStream()
|
||||
writer := c.Response().BodyWriter()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user