diff --git a/internal/server/api/api.go b/internal/server/api/api.go index 1d067a1..c925a15 100644 --- a/internal/server/api/api.go +++ b/internal/server/api/api.go @@ -19,14 +19,18 @@ func KickoffApi(logger *slog.Logger, env bootstrap.Environment, db *gorm.DB) { gin.SetMode(gin.ReleaseMode) // For a nice looking logger: - // r := gin.Default() - // JSON logger: https://gin-gonic.com/en/docs/logging/structured-logging/ + // r := gin.Default() // default, this makes a nice looking log-trace but I want JSON r := gin.New() r.Use(middleware.SlogMiddleware(logger)) 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") - routes.RegisterApiRoutes(api /*env,*/, db) + routes.RegisterApiRoutes(api, db) file := r.Group("/file") routes.RegisterFileRoutes(file, env, db) diff --git a/internal/server/api/middleware/middleware.go b/internal/server/api/middleware/middleware.go index 3f36593..c93cf98 100644 --- a/internal/server/api/middleware/middleware.go +++ b/internal/server/api/middleware/middleware.go @@ -11,6 +11,8 @@ import ( ) 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) { start := time.Now() path := c.Request.URL.Path @@ -47,6 +49,8 @@ func AuthMiddleware() gin.HandlerFunc { } 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" { c.AbortWithStatusJSON(http.StatusUnauthorized, response.BasicResponse{ Msg: "Authorization header is invalid", @@ -54,7 +58,6 @@ func AuthMiddleware() gin.HandlerFunc { return } - givenKey := headerParts[1] - slog.Info(givenKey) + //givenKey := headerParts[1] } } diff --git a/internal/server/api/routes/api.go b/internal/server/api/routes/api.go index f027151..85eccfc 100644 --- a/internal/server/api/routes/api.go +++ b/internal/server/api/routes/api.go @@ -12,6 +12,19 @@ import ( func RegisterApiRoutes(api *gin.RouterGroup /* env runtime.Environment,*/, db *gorm.DB) { // 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 api.GET("/command", func(c *gin.Context) { state, err := database.GetState(db) diff --git a/internal/server/bootstrap/environment.go b/internal/server/bootstrap/environment.go index 1e71374..15a22d9 100644 --- a/internal/server/bootstrap/environment.go +++ b/internal/server/bootstrap/environment.go @@ -3,68 +3,27 @@ package bootstrap import ( "os" "path/filepath" - "slices" - "strconv" ) type Environment struct { + // Items made for the presentation of ORBITS Version string Codename string LogLevel string - DataDirectory string - ContentDirectory string - Hostname string - Port int + // Items made for the configuration of the server parts + DataDirectory string + ContentDirectory string + Hostname string + Port int + AuthenticationEnabled bool + // Items made for the server watchdog WatchdogEnabled bool WatchdogInterval int 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 { cwd, err := os.Getwd() if err != nil { @@ -81,10 +40,11 @@ func GrabEnvironment() Environment { LogLevel: safeStringGrab("LOG_LEVEL", "debug"), // GIN API configuration - DataDirectory: safeStringGrab("DATA_DIR", fbBase), - ContentDirectory: safeStringGrab("CONTENT_DIR", fbContent), - Hostname: safeStringGrab("HOSTNAME", "0.0.0.0"), - Port: safeIntGrab("PORT", 8080), + DataDirectory: safeStringGrab("DATA_DIR", fbBase), + ContentDirectory: safeStringGrab("CONTENT_DIR", fbContent), + Hostname: safeStringGrab("HOSTNAME", "0.0.0.0"), + Port: safeIntGrab("PORT", 8080), + AuthenticationEnabled: safeBoolGrab("AUTH_ENABLED", true), // watchdog configuration // watchdog is the integrity checker/steward diff --git a/internal/server/bootstrap/vargrab.go b/internal/server/bootstrap/vargrab.go new file mode 100644 index 0000000..6123666 --- /dev/null +++ b/internal/server/bootstrap/vargrab.go @@ -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 +} diff --git a/internal/server/database/database.go b/internal/server/database/database.go index 874a511..bbf6ee2 100644 --- a/internal/server/database/database.go +++ b/internal/server/database/database.go @@ -2,6 +2,7 @@ package database import ( "orbits-server/internal/server/bootstrap" + "orbits-server/internal/shared/utility" "path/filepath" "time" @@ -35,7 +36,7 @@ func KickoffDatabase(workDir string) (*gorm.DB, error) { if err := db.FirstOrCreate(&Command{}, Command{ ID: 0, State: "idle", - MediaType: Unspecified, + MediaType: utility.Unspecified, }).Error; err != nil { return nil, err } diff --git a/internal/server/database/define.go b/internal/server/database/define.go index 067680a..6936528 100644 --- a/internal/server/database/define.go +++ b/internal/server/database/define.go @@ -1,18 +1,10 @@ package database import ( + "orbits-server/internal/shared/utility" "time" ) -type MediaType string - -const ( - Video MediaType = "video" - Presentation MediaType = "presentation" - Internet MediaType = "internet" - Unspecified MediaType = "unspecified" -) - type Timestamps struct { CreatedAt time.Time `gorm:"not null;"` UpdatedAt time.Time `gorm:"not null;"` @@ -26,10 +18,10 @@ type Command struct { // video // presentation // 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 // 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 // can be none when there is no media specified (init stage) Location string @@ -64,7 +56,7 @@ type File struct { // video // presentation // 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 MetaName string FileName string `gorm:"not null;"` diff --git a/internal/server/database/functions.go b/internal/server/database/functions.go index 400a5ff..c17e185 100644 --- a/internal/server/database/functions.go +++ b/internal/server/database/functions.go @@ -7,36 +7,14 @@ import ( "orbits-server/internal/shared/utility" "path/filepath" - "github.com/google/uuid" "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 // this function should only be called after manually checking the filetype func BuildFileRecord(r io.Reader, origName string, contentDirectory string) (File, error) { ext := filepath.Ext(origName) - category, ok := CategorizeMediaType(ext) + category, ok := utility.CategorizeMediaType(ext) if !ok { return File{}, fmt.Errorf("unsupported filetype") } @@ -47,7 +25,7 @@ func BuildFileRecord(r io.Reader, origName string, contentDirectory string) (Fil return File{}, err } - safeName := GenerateSafeName(category, ext) + safeName := utility.GenerateSafeName(category, ext) destPath := filepath.Join(contentDirectory, safeName) fData := File{ diff --git a/internal/shared/utility/generator.go b/internal/shared/utility/generator.go new file mode 100644 index 0000000..6c7b97a --- /dev/null +++ b/internal/shared/utility/generator.go @@ -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 +} diff --git a/internal/shared/utility/utility.go b/internal/shared/utility/utility.go index 8cbe95f..df19ea4 100644 --- a/internal/shared/utility/utility.go +++ b/internal/shared/utility/utility.go @@ -6,6 +6,15 @@ import ( "strings" ) +type MediaType string + +const ( + Video MediaType = "video" + Presentation MediaType = "presentation" + Internet MediaType = "internet" + Unspecified MediaType = "unspecified" +) + func RemoveFile(p string) error { err := os.Remove(p) if err != nil { @@ -15,6 +24,23 @@ func RemoveFile(p string) error { 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 { switch strings.ToLower(s) { case "debug":