mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-13 16:02:42 +00:00
134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/warmbly/warmbly/internal/app/nodelogs"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/pkg/nodeevidence"
|
|
)
|
|
|
|
func (h *Handler) logNodeExists(c *gin.Context, id uuid.UUID) bool {
|
|
if h.FleetNodeRepo == nil {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
return false
|
|
}
|
|
_, err := h.FleetNodeRepo.Get(c.Request.Context(), id)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
errx.JSON(c, errx.ErrNotFound)
|
|
return false
|
|
}
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (h *Handler) InternalNodeLogs(c *gin.Context) {
|
|
c.Header("Cache-Control", "no-store")
|
|
if h.NodeLogs == nil {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
return
|
|
}
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
secret := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
|
|
if err := h.NodeLogs.Authenticate(c.Request.Context(), id, secret); err != nil {
|
|
if errors.Is(err, nodelogs.ErrRateLimited) {
|
|
c.Header("Retry-After", "60")
|
|
errx.JSON(c, errx.New(errx.TooManyRequests, "node evidence rate exceeded"))
|
|
return
|
|
}
|
|
if errors.Is(err, nodelogs.ErrUnauthorized) {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
} else {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
}
|
|
return
|
|
}
|
|
if !h.logNodeExists(c, id) {
|
|
return
|
|
}
|
|
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 32768)
|
|
var batch nodeevidence.Batch
|
|
if err := c.ShouldBindJSON(&batch); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid evidence batch"))
|
|
return
|
|
}
|
|
if err := h.NodeLogs.Ingest(c.Request.Context(), id, secret, batch); err != nil {
|
|
if errors.Is(err, nodelogs.ErrUnauthorized) {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
} else if errors.Is(err, nodelogs.ErrInvalid) {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid evidence batch"))
|
|
} else {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
}
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *Handler) AdminNodeLogs(c *gin.Context) {
|
|
c.Header("Cache-Control", "no-store")
|
|
if h.NodeLogs == nil {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
return
|
|
}
|
|
id, ok := h.parseID(c)
|
|
if !ok || !h.logNodeExists(c, id) {
|
|
return
|
|
}
|
|
f := nodelogs.Filter{Level: c.Query("level"), Limit: nodelogs.MaxRead}
|
|
if f.Level != "" && f.Level != "info" && f.Level != "warn" && f.Level != "error" {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "level must be info, warn or error"))
|
|
return
|
|
}
|
|
for key, dest := range map[string]*time.Time{"after": &f.After, "before": &f.Before} {
|
|
if value := c.Query(key); value != "" {
|
|
at, err := time.Parse(time.RFC3339Nano, value)
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "after and before must be RFC3339 timestamps"))
|
|
return
|
|
}
|
|
*dest = at
|
|
}
|
|
}
|
|
if !f.After.IsZero() && !f.Before.IsZero() && f.After.After(f.Before) {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "after must not follow before"))
|
|
return
|
|
}
|
|
if value := c.Query("mailbox_id"); value != "" {
|
|
id, err := uuid.Parse(value)
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "mailbox_id must be a uuid"))
|
|
return
|
|
}
|
|
f.MailboxID = id
|
|
}
|
|
if value := c.Query("limit"); value != "" {
|
|
n, err := strconv.Atoi(value)
|
|
if err != nil || n < 1 || n > nodelogs.MaxRead {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "limit must be between 1 and 200"))
|
|
return
|
|
}
|
|
f.Limit = n
|
|
}
|
|
out, err := h.NodeLogs.History(c.Request.Context(), id, f)
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrServiceDown)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, out)
|
|
}
|