Skip to content

Commit a19b97f

Browse files
authored
Develop (#5)
* fix: artifact sizes and release dates and add josn validator * update versions
1 parent 0784df8 commit a19b97f

7 files changed

Lines changed: 1113 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- **Minor versions** (0.x.0): New features, non-breaking changes
1111
- **Major versions** (x.0.0): Breaking API changes
1212

13-
## [Unreleased]
14-
15-
### Planned
16-
- [ ] Chat API endpoint with message persistence
17-
- [ ] Contributors API with GitHub integration
18-
- [ ] Global search service (cross-service search)
19-
- [ ] Redis caching layer for improved performance
20-
- [ ] Rate limiting middleware
21-
- [ ] Authentication/Authorization system
22-
- [ ] Comprehensive test suite
23-
- [ ] Docker containerization
24-
- [ ] Monitoring and metrics collection
13+
## [0.2.1] - 2026-02-13
14+
15+
### Added
16+
- **JSON Validator service** - New validation engine with 3 modes
17+
- `generic` — JSON syntax validation with formatted output
18+
- `txadmin-embed` — Discord embed JSON validation for txAdmin status embeds
19+
- Validates all Discord embed properties (title, description, fields, image, thumbnail, author)
20+
- Enforces Discord character limits (title: 256, description: 4096, fields: 25, field name: 256, field value: 1024)
21+
- Detects unknown/unsupported embed properties
22+
- Warns when `color` or `footer` are set (overridden by txAdmin at runtime)
23+
- Validates URLs and txAdmin placeholder syntax
24+
- `txadmin-embed-config` — txAdmin embed config validation
25+
- Validates required fields (`onlineString`, `offlineString`, `onlineColor`, `offlineColor`)
26+
- Hex color format validation (#RGB / #RRGGBB)
27+
- Button array validation (max 5 buttons, required label/url)
28+
- Emoji field validation
29+
- User-friendly JSON parse error messages with line/column numbers
30+
- Severity levels: error, warning, info
31+
32+
- **Validator API endpoints** (2 endpoints)
33+
- `POST /api/validator/validate` — Validate JSON with type-specific schema checks
34+
- `GET /api/validator/info` — Get available validation types, txAdmin placeholders, and Discord limits
35+
36+
- **Artifact metadata enrichment** — Real commit dates and file sizes
37+
- Fetches actual commit dates from GitHub Git Data API per SHA
38+
- Fetches real artifact file sizes via HEAD requests to the CDN
39+
- Concurrent enrichment with semaphore (10 parallel requests)
40+
- Results cached for 24 hours (commit dates and file sizes never change)
41+
- Falls back to estimated values on failure
42+
43+
### Fixed
44+
- **GitHub API 401 handling** — Automatic retry without authentication
45+
- When `GITHUB_TOKEN` is invalid/expired, requests now retry unauthenticated
46+
- Prevents complete API failure due to bad credentials
47+
- Public repos (citizenfx/fivem) work fine without authentication (60 req/hr rate limit)
48+
49+
- **Artifact dates showing "less than a minute ago"** — All artifacts previously used `time.Now()`
50+
- Now fetches real commit dates from GitHub for each artifact
51+
- Dates accurately reflect when each version was actually released
52+
53+
- **Artifact sizes all showing 850.0 MB** — Hardcoded estimate for all Windows artifacts
54+
- Now fetches real file sizes via HEAD requests to the artifact CDN
55+
- Each artifact displays its actual download size
56+
57+
### Changed
58+
- **Thread-safe cache** — Added `sync.RWMutex` to cache operations
59+
- `getCache()` and `setCache()` are now safe for concurrent access
60+
- Cache entries now support per-key TTL via `ttl` field on `cacheEntry`
61+
- Required for concurrent artifact enrichment
62+
63+
- Updated version to `0.2.1`, build time to `2026-02-13`
2564

2665
---
2766

internal/handlers/validator.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package handlers
2+
3+
import (
4+
"github.com/CodeMeAPixel/FixFX-Core/internal/services"
5+
"github.com/gofiber/fiber/v2"
6+
)
7+
8+
var validatorService *services.ValidatorService
9+
10+
// InitValidatorHandler initializes the validator handler
11+
func InitValidatorHandler() {
12+
validatorService = services.NewValidatorService()
13+
}
14+
15+
// ValidateJSON handles POST /api/validator/validate
16+
// @Summary Validate JSON
17+
// @Description Validate JSON with optional txAdmin embed/config schema validation
18+
// @Tags Validator
19+
// @Accept json
20+
// @Produce json
21+
// @Param body body ValidateRequest true "JSON to validate"
22+
// @Success 200 {object} map[string]interface{}
23+
// @Failure 400 {object} map[string]interface{}
24+
// @Router /validator/validate [post]
25+
func ValidateJSON(c *fiber.Ctx) error {
26+
var req struct {
27+
JSON string `json:"json"`
28+
Type string `json:"type"`
29+
}
30+
31+
if err := c.BodyParser(&req); err != nil {
32+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
33+
"error": "Invalid request body",
34+
})
35+
}
36+
37+
if req.JSON == "" {
38+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
39+
"error": "JSON input is required",
40+
})
41+
}
42+
43+
// Determine validation type
44+
validationType := services.ValidationGeneric
45+
switch req.Type {
46+
case "txadmin-embed":
47+
validationType = services.ValidationTxEmbed
48+
case "txadmin-embed-config":
49+
validationType = services.ValidationTxEmbedConf
50+
case "generic", "":
51+
validationType = services.ValidationGeneric
52+
default:
53+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
54+
"error": "Invalid validation type. Valid types: generic, txadmin-embed, txadmin-embed-config",
55+
})
56+
}
57+
58+
result := validatorService.Validate(req.JSON, validationType)
59+
60+
return c.JSON(fiber.Map{
61+
"result": result,
62+
})
63+
}
64+
65+
// GetValidatorInfo handles GET /api/validator/info
66+
// @Summary Get validator information
67+
// @Description Get available validation types, txAdmin placeholders, and schema info
68+
// @Tags Validator
69+
// @Produce json
70+
// @Success 200 {object} map[string]interface{}
71+
// @Router /validator/info [get]
72+
func GetValidatorInfo(c *fiber.Ctx) error {
73+
placeholders := []map[string]string{
74+
{"name": "serverCfxId", "description": "The Cfx.re id of your server, tied to sv_licenseKey"},
75+
{"name": "serverJoinUrl", "description": "The direct join URL (e.g., https://cfx.re/join/xxxxxx)"},
76+
{"name": "serverBrowserUrl", "description": "The FiveM Server browser URL"},
77+
{"name": "serverClients", "description": "Number of players online"},
78+
{"name": "serverMaxClients", "description": "The sv_maxclients value"},
79+
{"name": "serverName", "description": "The txAdmin-given server name"},
80+
{"name": "statusColor", "description": "Hex-encoded color from Config JSON"},
81+
{"name": "statusString", "description": "Status text from Config JSON"},
82+
{"name": "uptime", "description": "How long the server has been online"},
83+
{"name": "nextScheduledRestart", "description": "When the next scheduled restart is"},
84+
}
85+
86+
types := []map[string]string{
87+
{"value": "generic", "label": "Generic JSON", "description": "Validates JSON syntax only"},
88+
{"value": "txadmin-embed", "label": "txAdmin Embed JSON", "description": "Validates Discord embed JSON for txAdmin status embed"},
89+
{"value": "txadmin-embed-config", "label": "txAdmin Embed Config", "description": "Validates txAdmin embed configuration (colors, buttons)"},
90+
}
91+
92+
return c.JSON(fiber.Map{
93+
"types": types,
94+
"placeholders": placeholders,
95+
"limits": fiber.Map{
96+
"embed": fiber.Map{
97+
"title": 256,
98+
"description": 4096,
99+
"fields": 25,
100+
"fieldName": 256,
101+
"fieldValue": 1024,
102+
},
103+
"config": fiber.Map{
104+
"buttons": 5,
105+
"buttonLabel": 80,
106+
},
107+
},
108+
})
109+
}

internal/routes/types.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,32 @@ type ErrorResponse struct {
107107
Status int `json:"status"`
108108
Path string `json:"path,omitempty"`
109109
}
110+
111+
// ValidateRequest represents the validator input
112+
type ValidateRequest struct {
113+
JSON string `json:"json"`
114+
Type string `json:"type"`
115+
}
116+
117+
// ValidationIssueResponse represents a single validation issue
118+
type ValidationIssueResponse struct {
119+
Path string `json:"path"`
120+
Message string `json:"message"`
121+
Severity string `json:"severity"`
122+
}
123+
124+
// ValidationResponse represents the validation result
125+
type ValidationResponse struct {
126+
Valid bool `json:"valid"`
127+
Type string `json:"type"`
128+
Issues []ValidationIssueResponse `json:"issues"`
129+
Formatted string `json:"formatted,omitempty"`
130+
ParseError string `json:"parseError,omitempty"`
131+
}
132+
133+
// ValidatorInfoResponse represents validator metadata
134+
type ValidatorInfoResponse struct {
135+
Types []map[string]string `json:"types"`
136+
Placeholders []map[string]string `json:"placeholders"`
137+
Limits map[string]any `json:"limits"`
138+
}

internal/routes/validator.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package routes
2+
3+
import (
4+
"github.com/CodeMeAPixel/FixFX-Core/internal/handlers"
5+
"github.com/gofiber/fiber/v2"
6+
)
7+
8+
func RegisterValidatorRoutes(api fiber.Router) {
9+
validator := api.Group("/validator")
10+
11+
// @Summary Validate JSON
12+
// @Description Validate JSON with optional txAdmin embed/config schema validation
13+
// @Tags Validator
14+
// @Accept json
15+
// @Produce json
16+
// @Param body body ValidateRequest true "JSON to validate"
17+
// @Success 200 {object} ValidationResponse
18+
// @Failure 400 {object} ErrorResponse
19+
// @Router /validator/validate [post]
20+
validator.Post("/validate", handlers.ValidateJSON)
21+
22+
// @Summary Get validator info
23+
// @Description Get available validation types, placeholders, and schema info
24+
// @Tags Validator
25+
// @Produce json
26+
// @Success 200 {object} ValidatorInfoResponse
27+
// @Router /validator/info [get]
28+
validator.Get("/info", handlers.GetValidatorInfo)
29+
}

0 commit comments

Comments
 (0)