feat: more reorganisation and keys endpoint
This commit is contained in:
@@ -19,14 +19,18 @@ func KickoffApi(logger *slog.Logger, env bootstrap.Environment, db *gorm.DB) {
|
|||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
// For a nice looking logger:
|
// For a nice looking logger:
|
||||||
// r := gin.Default()
|
// r := gin.Default() // default, this makes a nice looking log-trace but I want JSON
|
||||||
// JSON logger: https://gin-gonic.com/en/docs/logging/structured-logging/
|
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Use(middleware.SlogMiddleware(logger))
|
r.Use(middleware.SlogMiddleware(logger))
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
|
|
||||||
|
if env.AuthenticationEnabled {
|
||||||
|
slog.Debug("activating authentication middleware") // only log when actually doign the thing it logs to do
|
||||||
|
r.Use(middleware.AuthMiddleware())
|
||||||
|
}
|
||||||
|
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
routes.RegisterApiRoutes(api /*env,*/, db)
|
routes.RegisterApiRoutes(api, db)
|
||||||
|
|
||||||
file := r.Group("/file")
|
file := r.Group("/file")
|
||||||
routes.RegisterFileRoutes(file, env, db)
|
routes.RegisterFileRoutes(file, env, db)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func SlogMiddleware(logger *slog.Logger) gin.HandlerFunc {
|
func SlogMiddleware(logger *slog.Logger) gin.HandlerFunc {
|
||||||
|
// Make a slog-looking logger, inspired by the gin docs themself
|
||||||
|
// JSON logger: https://gin-gonic.com/en/docs/logging/structured-logging/
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
path := c.Request.URL.Path
|
path := c.Request.URL.Path
|
||||||
@@ -47,6 +49,8 @@ func AuthMiddleware() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
headerParts := strings.Split(authorizationHeader, " ")
|
headerParts := strings.Split(authorizationHeader, " ")
|
||||||
|
// The header must be a specific format, 0 being the bearer text and 1 being the token itself, making it 2 pieces total
|
||||||
|
// In the following if statement we verify both parts if the part after Bearer is empty its only 1 part for example
|
||||||
if len(headerParts) != 2 || headerParts[0] != "Bearer" {
|
if len(headerParts) != 2 || headerParts[0] != "Bearer" {
|
||||||
c.AbortWithStatusJSON(http.StatusUnauthorized, response.BasicResponse{
|
c.AbortWithStatusJSON(http.StatusUnauthorized, response.BasicResponse{
|
||||||
Msg: "Authorization header is invalid",
|
Msg: "Authorization header is invalid",
|
||||||
@@ -54,7 +58,6 @@ func AuthMiddleware() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
givenKey := headerParts[1]
|
//givenKey := headerParts[1]
|
||||||
slog.Info(givenKey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,19 @@ import (
|
|||||||
|
|
||||||
func RegisterApiRoutes(api *gin.RouterGroup /* env runtime.Environment,*/, db *gorm.DB) {
|
func RegisterApiRoutes(api *gin.RouterGroup /* env runtime.Environment,*/, db *gorm.DB) {
|
||||||
// prefix: api
|
// prefix: api
|
||||||
|
|
||||||
|
api.GET("/keys", func(c *gin.Context) {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
api.POST("/keys", func(c *gin.Context) {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
api.DELETE("/keys", func(c *gin.Context) {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
// Display the information on what is going on at the moment
|
// Display the information on what is going on at the moment
|
||||||
api.GET("/command", func(c *gin.Context) {
|
api.GET("/command", func(c *gin.Context) {
|
||||||
state, err := database.GetState(db)
|
state, err := database.GetState(db)
|
||||||
|
|||||||
@@ -3,68 +3,27 @@ package bootstrap
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Environment struct {
|
type Environment struct {
|
||||||
|
// Items made for the presentation of ORBITS
|
||||||
Version string
|
Version string
|
||||||
Codename string
|
Codename string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
|
|
||||||
DataDirectory string
|
// Items made for the configuration of the server parts
|
||||||
ContentDirectory string
|
DataDirectory string
|
||||||
Hostname string
|
ContentDirectory string
|
||||||
Port int
|
Hostname string
|
||||||
|
Port int
|
||||||
|
AuthenticationEnabled bool
|
||||||
|
|
||||||
|
// Items made for the server watchdog
|
||||||
WatchdogEnabled bool
|
WatchdogEnabled bool
|
||||||
WatchdogInterval int
|
WatchdogInterval int
|
||||||
WatchdogSyncMode string
|
WatchdogSyncMode string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
validSyncModes = []string{
|
|
||||||
"sync",
|
|
||||||
"strict",
|
|
||||||
"dry",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// part of environment checking
|
|
||||||
func safeStringGrab(key, fallback string) string {
|
|
||||||
if v, ok := os.LookupEnv(key); ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeIntGrab(key string, fallback int) int {
|
|
||||||
if v, ok := os.LookupEnv(key); ok {
|
|
||||||
if i, err := strconv.Atoi(v); err == nil {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeBoolGrab(key string, fallback bool) bool {
|
|
||||||
if v, ok := os.LookupEnv(key); ok {
|
|
||||||
if b, err := strconv.ParseBool(v); err == nil {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeSyncModeGrab(key, fallback string) string {
|
|
||||||
if v, ok := os.LookupEnv(key); ok {
|
|
||||||
if slices.Contains(validSyncModes, v) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func GrabEnvironment() Environment {
|
func GrabEnvironment() Environment {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -81,10 +40,11 @@ func GrabEnvironment() Environment {
|
|||||||
LogLevel: safeStringGrab("LOG_LEVEL", "debug"),
|
LogLevel: safeStringGrab("LOG_LEVEL", "debug"),
|
||||||
|
|
||||||
// GIN API configuration
|
// GIN API configuration
|
||||||
DataDirectory: safeStringGrab("DATA_DIR", fbBase),
|
DataDirectory: safeStringGrab("DATA_DIR", fbBase),
|
||||||
ContentDirectory: safeStringGrab("CONTENT_DIR", fbContent),
|
ContentDirectory: safeStringGrab("CONTENT_DIR", fbContent),
|
||||||
Hostname: safeStringGrab("HOSTNAME", "0.0.0.0"),
|
Hostname: safeStringGrab("HOSTNAME", "0.0.0.0"),
|
||||||
Port: safeIntGrab("PORT", 8080),
|
Port: safeIntGrab("PORT", 8080),
|
||||||
|
AuthenticationEnabled: safeBoolGrab("AUTH_ENABLED", true),
|
||||||
|
|
||||||
// watchdog configuration
|
// watchdog configuration
|
||||||
// watchdog is the integrity checker/steward
|
// watchdog is the integrity checker/steward
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
validSyncModes = []string{
|
||||||
|
"sync",
|
||||||
|
"strict",
|
||||||
|
"dry",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// part of environment checking
|
||||||
|
func safeStringGrab(key, fallback string) string {
|
||||||
|
if v, ok := os.LookupEnv(key); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeIntGrab(key string, fallback int) int {
|
||||||
|
if v, ok := os.LookupEnv(key); ok {
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeBoolGrab(key string, fallback bool) bool {
|
||||||
|
if v, ok := os.LookupEnv(key); ok {
|
||||||
|
if b, err := strconv.ParseBool(v); err == nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeSyncModeGrab(key, fallback string) string {
|
||||||
|
if v, ok := os.LookupEnv(key); ok {
|
||||||
|
if slices.Contains(validSyncModes, v) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"orbits-server/internal/server/bootstrap"
|
"orbits-server/internal/server/bootstrap"
|
||||||
|
"orbits-server/internal/shared/utility"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ func KickoffDatabase(workDir string) (*gorm.DB, error) {
|
|||||||
if err := db.FirstOrCreate(&Command{}, Command{
|
if err := db.FirstOrCreate(&Command{}, Command{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
State: "idle",
|
State: "idle",
|
||||||
MediaType: Unspecified,
|
MediaType: utility.Unspecified,
|
||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"orbits-server/internal/shared/utility"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MediaType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
Video MediaType = "video"
|
|
||||||
Presentation MediaType = "presentation"
|
|
||||||
Internet MediaType = "internet"
|
|
||||||
Unspecified MediaType = "unspecified"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Timestamps struct {
|
type Timestamps struct {
|
||||||
CreatedAt time.Time `gorm:"not null;"`
|
CreatedAt time.Time `gorm:"not null;"`
|
||||||
UpdatedAt time.Time `gorm:"not null;"`
|
UpdatedAt time.Time `gorm:"not null;"`
|
||||||
@@ -26,10 +18,10 @@ type Command struct {
|
|||||||
// video
|
// video
|
||||||
// presentation
|
// presentation
|
||||||
// internet URL
|
// internet URL
|
||||||
MediaType MediaType `gorm:"type:varchar(20);not null"` // Must specify what kind of file it is
|
MediaType utility.MediaType `gorm:"type:varchar(20);not null;"` // Must specify what kind of file it is
|
||||||
// Must be target list who are compelled to listen to the command
|
// Must be target list who are compelled to listen to the command
|
||||||
// can be none when there is no targets specified (init stage)
|
// can be none when there is no targets specified (init stage)
|
||||||
Targets []string `gorm:"type:json"`
|
Targets []string `gorm:"type:json;"`
|
||||||
// Must be the location where the file is downloadable on the API
|
// Must be the location where the file is downloadable on the API
|
||||||
// can be none when there is no media specified (init stage)
|
// can be none when there is no media specified (init stage)
|
||||||
Location string
|
Location string
|
||||||
@@ -64,7 +56,7 @@ type File struct {
|
|||||||
// video
|
// video
|
||||||
// presentation
|
// presentation
|
||||||
// internet URL
|
// internet URL
|
||||||
MediaType MediaType `gorm:"type:varchar(20);not null;"`
|
MediaType utility.MediaType `gorm:"type:varchar(20);not null;"`
|
||||||
// the name given by the user
|
// the name given by the user
|
||||||
MetaName string
|
MetaName string
|
||||||
FileName string `gorm:"not null;"`
|
FileName string `gorm:"not null;"`
|
||||||
|
|||||||
@@ -7,36 +7,14 @@ import (
|
|||||||
"orbits-server/internal/shared/utility"
|
"orbits-server/internal/shared/utility"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 0: unspecified
|
|
||||||
// 1: video
|
|
||||||
// 2: presentation
|
|
||||||
// 3: internet URL
|
|
||||||
func CategorizeMediaType(ext string) (MediaType, bool) {
|
|
||||||
|
|
||||||
switch ext {
|
|
||||||
case ".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4a":
|
|
||||||
return Video, true
|
|
||||||
case ".pptx", ".ppt", ".key", ".odp":
|
|
||||||
return Presentation, true
|
|
||||||
default:
|
|
||||||
slog.Debug("marking file as invalid due to its undefined extension")
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateSafeName(category MediaType, ext string) string {
|
|
||||||
return uuid.New().String() + "_" + string(category) + ext
|
|
||||||
}
|
|
||||||
|
|
||||||
// it has been made more general for DRY purposes
|
// it has been made more general for DRY purposes
|
||||||
// this function should only be called after manually checking the filetype
|
// this function should only be called after manually checking the filetype
|
||||||
func BuildFileRecord(r io.Reader, origName string, contentDirectory string) (File, error) {
|
func BuildFileRecord(r io.Reader, origName string, contentDirectory string) (File, error) {
|
||||||
ext := filepath.Ext(origName)
|
ext := filepath.Ext(origName)
|
||||||
category, ok := CategorizeMediaType(ext)
|
category, ok := utility.CategorizeMediaType(ext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return File{}, fmt.Errorf("unsupported filetype")
|
return File{}, fmt.Errorf("unsupported filetype")
|
||||||
}
|
}
|
||||||
@@ -47,7 +25,7 @@ func BuildFileRecord(r io.Reader, origName string, contentDirectory string) (Fil
|
|||||||
return File{}, err
|
return File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
safeName := GenerateSafeName(category, ext)
|
safeName := utility.GenerateSafeName(category, ext)
|
||||||
destPath := filepath.Join(contentDirectory, safeName)
|
destPath := filepath.Join(contentDirectory, safeName)
|
||||||
|
|
||||||
fData := File{
|
fData := File{
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package utility
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateSafeName(category MediaType, ext string) string {
|
||||||
|
return uuid.New().String() + "_" + string(category) + ext
|
||||||
|
}
|
||||||
@@ -6,6 +6,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MediaType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Video MediaType = "video"
|
||||||
|
Presentation MediaType = "presentation"
|
||||||
|
Internet MediaType = "internet"
|
||||||
|
Unspecified MediaType = "unspecified"
|
||||||
|
)
|
||||||
|
|
||||||
func RemoveFile(p string) error {
|
func RemoveFile(p string) error {
|
||||||
err := os.Remove(p)
|
err := os.Remove(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -15,6 +24,23 @@ func RemoveFile(p string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0: unspecified
|
||||||
|
// 1: video
|
||||||
|
// 2: presentation
|
||||||
|
// 3: internet URL
|
||||||
|
func CategorizeMediaType(ext string) (MediaType, bool) {
|
||||||
|
|
||||||
|
switch ext {
|
||||||
|
case ".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4a":
|
||||||
|
return Video, true
|
||||||
|
case ".pptx", ".ppt", ".key", ".odp":
|
||||||
|
return Presentation, true
|
||||||
|
default:
|
||||||
|
slog.Debug("marking file as invalid due to its undefined extension")
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ParseSlogLevel(s string) slog.Level {
|
func ParseSlogLevel(s string) slog.Level {
|
||||||
switch strings.ToLower(s) {
|
switch strings.ToLower(s) {
|
||||||
case "debug":
|
case "debug":
|
||||||
|
|||||||
Reference in New Issue
Block a user