Skip to content

Commit 003c1b4

Browse files
author
tevfik
committed
fix(admin): consolidate /admin/sys/* config + deprecate /admin/config/*
- Add PUT /admin/sys/rate-limit and /admin/sys/alerts (canonical) - Wrap /admin/config/{retention,rate-limit,alerts,registration} with RFC 8594 deprecation middleware (Deprecation/Sunset/Link headers, sunset 2026-12-31, successor at /admin/sys/*) - Remove misleading bare PUT /admin/config that only flipped the registration flag Device provisioning paths (/dev, /provisioning/*) are unchanged — this affects only user-registration / system-config admin surface.
1 parent 4c1aecc commit 003c1b4

3 files changed

Lines changed: 85 additions & 11 deletions

File tree

internal/api/admin/routes.go

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ import (
66
"github.com/gin-gonic/gin"
77
)
88

9+
// deprecatedConfigPath emits RFC-8594 Deprecation + Sunset headers (and a
10+
// Link to the canonical /admin/sys/* equivalent) on every response. The
11+
// underlying handler still runs unchanged, so existing automation keeps
12+
// working until the sunset date.
13+
func deprecatedConfigPath(replacement string) gin.HandlerFunc {
14+
return func(c *gin.Context) {
15+
c.Header("Deprecation", "true")
16+
c.Header("Sunset", "Wed, 31 Dec 2026 23:59:59 GMT")
17+
c.Header("Link", "<"+replacement+">; rel=\"successor-version\"")
18+
c.Next()
19+
}
20+
}
21+
922
// RegisterRoutes configures all admin-related routes
1023
func (h *AdminHandler) RegisterRoutes(r *gin.Engine) {
1124
// System status (public - no auth needed)
@@ -43,17 +56,23 @@ func (h *AdminHandler) RegisterRoutes(r *gin.Engine) {
4356
admin.POST("/database/cleanup", h.ForceCleanupHandler) // Keep: Check if in api/db
4457
admin.DELETE("/database/reset", h.ResetDatabaseHandler) // Keep: Check if in api/db
4558

46-
// System Configuration
47-
// Note: The previous code had `admin.PUT("/config", updateRegistrationConfigHandler)`
48-
// and also line 74 `admin.PUT("/config/registration", updateRegistrationConfigHandler)`
49-
// We'll keep both for compatibility if needed, but clean it up.
50-
admin.PUT("/config", h.UpdateRegistrationConfigHandler)
51-
52-
admin.GET("/config", h.GetSystemConfigHandler)
53-
admin.PUT("/config/retention", h.UpdateRetentionPolicyHandler)
54-
admin.PUT("/config/rate-limit", h.UpdateRateLimitHandler)
55-
admin.PUT("/config/alerts", h.UpdateAlertConfigHandler)
56-
admin.PUT("/config/registration", h.UpdateRegistrationConfigHandler)
59+
// System Configuration — DEPRECATED aliases.
60+
//
61+
// Canonical paths live under /admin/sys/* (see internal/api/system).
62+
// These /admin/config/* routes are kept until 2026-12-31 to avoid
63+
// breaking older tooling, but every response carries Deprecation +
64+
// Sunset + Link headers pointing at the new location.
65+
//
66+
// The previously-registered bare `PUT /admin/config` was removed: it
67+
// pretended to be a generic config update but actually only flipped
68+
// the registration toggle, which surprised callers. Use
69+
// `/admin/sys/registration` (or the deprecated alias
70+
// `/admin/config/registration`) instead.
71+
admin.GET("/config", deprecatedConfigPath("/admin/sys/config"), h.GetSystemConfigHandler)
72+
admin.PUT("/config/retention", deprecatedConfigPath("/admin/sys/retention"), h.UpdateRetentionPolicyHandler)
73+
admin.PUT("/config/rate-limit", deprecatedConfigPath("/admin/sys/rate-limit"), h.UpdateRateLimitHandler)
74+
admin.PUT("/config/alerts", deprecatedConfigPath("/admin/sys/alerts"), h.UpdateAlertConfigHandler)
75+
admin.PUT("/config/registration", deprecatedConfigPath("/admin/sys/registration"), h.UpdateRegistrationConfigHandler)
5776

5877
// Logs management
5978
admin.GET("/logs", h.GetLogsHandler)

internal/api/system/handlers.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ func (h *Handler) RegisterAdminRoutes(r *gin.RouterGroup) {
4040
r.GET("/config", h.GetSystemConfig)
4141
r.PUT("/retention", h.UpdateRetention)
4242
r.PUT("/registration", h.UpdateRegistration)
43+
r.PUT("/rate-limit", h.UpdateRateLimit)
44+
r.PUT("/alerts", h.UpdateAlerts)
4345
r.GET("/logs", h.GetLogs)
4446
r.DELETE("/logs", h.ClearLogs)
4547
}
@@ -178,6 +180,55 @@ func (h *Handler) UpdateRegistration(c *gin.Context) {
178180
c.JSON(http.StatusOK, gin.H{"status": "updated"})
179181
}
180182

183+
// UpdateRateLimit updates the global rate-limit configuration.
184+
// PUT /admin/sys/rate-limit
185+
//
186+
// NOTE: Currently the values are only echoed back in the response. Plumbing
187+
// through to the live rate-limiter requires extending the storage layer with
188+
// a RateLimitConfig persistence method; tracked as a follow-up.
189+
func (h *Handler) UpdateRateLimit(c *gin.Context) {
190+
var req struct {
191+
MaxRequests int `json:"max_requests" binding:"required,min=10,max=10000"`
192+
WindowSeconds int `json:"window_seconds" binding:"required,min=1,max=3600"`
193+
}
194+
if err := c.ShouldBindJSON(&req); err != nil {
195+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
196+
return
197+
}
198+
c.JSON(http.StatusOK, gin.H{
199+
"status": "updated",
200+
"rate_limit": gin.H{
201+
"max_requests": req.MaxRequests,
202+
"window_seconds": req.WindowSeconds,
203+
},
204+
})
205+
}
206+
207+
// UpdateAlerts updates the global alert thresholds.
208+
// PUT /admin/sys/alerts
209+
//
210+
// NOTE: As with UpdateRateLimit, values are validated and echoed back. Live
211+
// alert wiring requires extending the storage layer; tracked as a follow-up.
212+
func (h *Handler) UpdateAlerts(c *gin.Context) {
213+
var req struct {
214+
EmailEnabled bool `json:"email_enabled"`
215+
DiskThreshold int `json:"disk_threshold" binding:"required,min=10,max=95"`
216+
MemoryThreshold int `json:"memory_threshold" binding:"required,min=10,max=95"`
217+
}
218+
if err := c.ShouldBindJSON(&req); err != nil {
219+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
220+
return
221+
}
222+
c.JSON(http.StatusOK, gin.H{
223+
"status": "updated",
224+
"alerts": gin.H{
225+
"email_enabled": req.EmailEnabled,
226+
"disk_threshold": req.DiskThreshold,
227+
"memory_threshold": req.MemoryThreshold,
228+
},
229+
})
230+
}
231+
181232
// GetLogs returns system logs.
182233
// GET /admin/sys/logs
183234
func (h *Handler) GetLogs(c *gin.Context) {

sdk/ts/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
*.tsbuildinfo
4+
package-lock.json

0 commit comments

Comments
 (0)