feat: sync current progress (P0 hardening + P1 observability + deploy docs/systemd)

This commit is contained in:
OpenClaw Agent
2026-02-28 23:51:23 +08:00
commit d17296d794
96 changed files with 6358 additions and 0 deletions

87
internal/api/router.go Normal file
View File

@@ -0,0 +1,87 @@
package api
import (
"asset-tracker/internal/auth"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func RegisterRoutes(r *gin.Engine, h *Handler, tm *auth.TokenManager) {
r.Static("/legacy/static", "./web/legacy/static")
r.GET("/legacy", func(c *gin.Context) {
c.File("./web/legacy/index.html")
})
r.GET("/legacy/records", func(c *gin.Context) {
c.File("./web/legacy/records.html")
})
r.GET("/metrics", gin.WrapH(promhttp.Handler()))
r.Static("/_assets", "./web/dist/_assets")
r.GET("/", func(c *gin.Context) {
c.File("./web/dist/index.html")
})
r.GET("/public/records", h.PublicRecords)
// status endpoint moved here for diagnostics
r.GET("/status", func(c *gin.Context) {
c.JSON(200, gin.H{
"name": "asset-tracker",
"status": "running",
"health": "/health",
"api_base": "/api/v1",
"web_app": "/app",
})
})
r.GET("/app", func(c *gin.Context) {
c.File("./web/dist/index.html")
})
r.GET("/app/", func(c *gin.Context) {
c.File("./web/dist/index.html")
})
r.NoRoute(func(c *gin.Context) {
if c.Request.Method == http.MethodGet {
path := c.Request.URL.Path
if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "/public/") || strings.HasPrefix(path, "/health") || strings.HasPrefix(path, "/status") || strings.HasPrefix(path, "/legacy") {
JSONError(c, http.StatusNotFound, "NOT_FOUND", "not found", nil)
return
}
c.File("./web/dist/index.html")
return
}
JSONError(c, http.StatusNotFound, "NOT_FOUND", "not found", nil)
})
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
r.GET("/healthz", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
r.GET("/readyz", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ready"})
})
v1 := r.Group("/api/v1")
{
v1.POST("/auth/login", h.Login)
v1.POST("/auth/refresh", h.Refresh)
secured := v1.Group("")
secured.Use(AuthRequired(tm))
{
secured.POST("/categories", h.CreateCategory)
secured.GET("/categories", h.ListCategories)
secured.POST("/assets", h.CreateAsset)
secured.GET("/assets", h.ListAssets)
secured.PUT("/assets/:id", h.UpdateAsset)
secured.DELETE("/assets/:id", h.DeleteAsset)
secured.GET("/dashboard/summary", h.DashboardSummary)
secured.GET("/reminders", h.ListReminders)
}
}
}