diff --git a/CLAUDE.md b/CLAUDE.md index 4b39071..4b3ec8c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,9 +25,9 @@ BlockyServer is an HTTP API for rendering Hytale character models. It wraps the ``` HTTP Request → api.Handlers → service.MergeService → blockymodel-merger pkg ↓ - render.RenderPNG/GIF (for image endpoints) + render.RenderPNG/GIF/MP4 (for image/video endpoints) ↓ - HTTP Response (GLB/PNG/GIF) + HTTP Response (GLB/PNG/GIF/MP4) ``` ### Package Structure @@ -49,6 +49,17 @@ Server requires these directories at runtime (relative to working directory): - `assets/` - Character models (.blockymodel), textures - `data/` - JSON registry files (accessories, colors, gradients) +### Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `BLOCKY_DISABLE_GLB` | `false` | Disable `/render/glb` endpoint (returns 403) | +| `BLOCKY_DISABLE_PNG` | `false` | Disable `/render/png` endpoint (returns 403) | +| `BLOCKY_DISABLE_GIF` | `false` | Disable `/render/gif` endpoint (returns 403) | +| `BLOCKY_DISABLE_MP4` | `false` | Disable `/render/mp4` endpoint (returns 403) | + +Set to `true`, `1`, or `yes` to disable an endpoint. + ### API Endpoints | Endpoint | Method | Description | @@ -56,6 +67,7 @@ Server requires these directories at runtime (relative to working directory): | `/render/glb` | POST | Character JSON → GLB binary | | `/render/png` | POST | Character JSON + options → PNG image | | `/render/gif` | POST | Character JSON + options → Animated GIF | +| `/render/mp4` | POST | Character JSON + options → MP4 video (requires FFmpeg) | | `/docs` | GET | Swagger UI | | `/openapi.json` | GET | OpenAPI spec | | `/health` | GET | Health check | diff --git a/README.md b/README.md index c01780e..3e41209 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ > Not affiliated with Hypixel Studios. > All trademarks and assets are property of their respective owners. -HTTP API for rendering Hytale character models as GLB, PNG, or animated GIF. +HTTP API for rendering Hytale character models as GLB, PNG, animated GIF, or MP4 video. Built on top of [blockymodel-merger](https://github.com/hytale-tools/blockymodel-merger) by [JackGamesFTW](https://github.com/JackGamesFTW), special thanks to him! @@ -14,6 +14,7 @@ Built on top of [blockymodel-merger](https://github.com/hytale-tools/blockymodel - Export as GLB (glTF binary) - Render to PNG with configurable rotation and background - Render to animated rotating GIF +- Render to MP4 video (requires FFmpeg) - Swagger UI documentation ## Requirements @@ -69,6 +70,24 @@ go build -o blockyserver.exe . ./blockyserver.exe -port 3000 ``` +## Configuration + +### Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `BLOCKY_DISABLE_GLB` | `false` | Disable `/render/glb` endpoint | +| `BLOCKY_DISABLE_PNG` | `false` | Disable `/render/png` endpoint | +| `BLOCKY_DISABLE_GIF` | `false` | Disable `/render/gif` endpoint | +| `BLOCKY_DISABLE_MP4` | `false` | Disable `/render/mp4` endpoint | + +Set to `true`, `1`, or `yes` to disable. Disabled endpoints return `403 Forbidden`. + +```bash +# Example: disable GIF and MP4 endpoints +BLOCKY_DISABLE_GIF=true BLOCKY_DISABLE_MP4=true ./blockyserver.exe +``` + ## Docker ### Using Docker Compose (recommended) @@ -99,6 +118,7 @@ docker run -d -p 8080:8080 \ | `/render/glb` | POST | Returns GLB binary | | `/render/png` | POST | Returns PNG image | | `/render/gif` | POST | Returns animated GIF | +| `/render/mp4` | POST | Returns MP4 video | | `/docs` | GET | Swagger UI | | `/openapi.json` | GET | OpenAPI specification | | `/health` | GET | Health check | diff --git a/internal/api/middleware.go b/internal/api/middleware.go new file mode 100644 index 0000000..46ad85c --- /dev/null +++ b/internal/api/middleware.go @@ -0,0 +1,35 @@ +package api + +import ( + "encoding/json" + "net/http" + + "blockyserver/internal/config" +) + +// EndpointGuard creates middleware that returns 403 if endpoint is disabled +func EndpointGuard(enabled bool, endpointName string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !enabled { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusForbidden) + json.NewEncoder(w).Encode(ErrorResponse{ + Error: endpointName + " endpoint is disabled", + }) + return + } + next.ServeHTTP(w, r) + }) + } +} + +// NewEndpointGuards creates guards for all render endpoints based on config +func NewEndpointGuards(cfg *config.EndpointConfig) map[string]func(http.Handler) http.Handler { + return map[string]func(http.Handler) http.Handler{ + "glb": EndpointGuard(cfg.GLBEnabled, "/render/glb"), + "png": EndpointGuard(cfg.PNGEnabled, "/render/png"), + "gif": EndpointGuard(cfg.GIFEnabled, "/render/gif"), + "mp4": EndpointGuard(cfg.MP4Enabled, "/render/mp4"), + } +} diff --git a/internal/api/server.go b/internal/api/server.go index d0efee5..908ffa7 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -4,6 +4,7 @@ import ( "net/http" "time" + "blockyserver/internal/config" "blockyserver/internal/service" "github.com/go-chi/chi/v5" @@ -19,6 +20,10 @@ func NewServer(svc *service.MergeService) http.Handler { r.Use(middleware.Recoverer) r.Use(middleware.Timeout(60 * time.Second)) + // Load endpoint config + cfg := config.LoadEndpointConfig() + guards := NewEndpointGuards(cfg) + // Create handlers h := NewHandlers(svc) @@ -26,10 +31,10 @@ func NewServer(svc *service.MergeService) http.Handler { r.Get("/health", h.HandleHealth) r.Get("/openapi.json", h.HandleOpenAPISpec) r.Get("/docs", h.HandleSwaggerUI) - r.Post("/render/glb", h.HandleGLB) - r.Post("/render/png", h.HandlePNG) - r.Post("/render/gif", h.HandleGIF) - r.Post("/render/mp4", h.HandleMP4) + r.With(guards["glb"]).Post("/render/glb", h.HandleGLB) + r.With(guards["png"]).Post("/render/png", h.HandlePNG) + r.With(guards["gif"]).Post("/render/gif", h.HandleGIF) + r.With(guards["mp4"]).Post("/render/mp4", h.HandleMP4) return r } diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..ed97196 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,31 @@ +package config + +import ( + "os" + "strings" +) + +// EndpointConfig holds enable/disable flags for render endpoints +type EndpointConfig struct { + GLBEnabled bool + PNGEnabled bool + GIFEnabled bool + MP4Enabled bool +} + +// LoadEndpointConfig reads endpoint configuration from environment variables. +// All endpoints are enabled by default. +// Set BLOCKY_DISABLE_GLB=true, BLOCKY_DISABLE_PNG=true, etc. to disable. +func LoadEndpointConfig() *EndpointConfig { + return &EndpointConfig{ + GLBEnabled: !isDisabled("BLOCKY_DISABLE_GLB"), + PNGEnabled: !isDisabled("BLOCKY_DISABLE_PNG"), + GIFEnabled: !isDisabled("BLOCKY_DISABLE_GIF"), + MP4Enabled: !isDisabled("BLOCKY_DISABLE_MP4"), + } +} + +func isDisabled(envVar string) bool { + val := strings.ToLower(os.Getenv(envVar)) + return val == "true" || val == "1" || val == "yes" +}