Add web auth, infinite-scroll, agent admission and review fixes across agent/server/web

This commit is contained in:
Stanislav Rossovskii
2026-05-28 23:45:12 +04:00
parent 37b1a1d6d6
commit 1026e9ebbe
75 changed files with 3021 additions and 916 deletions

View File

@@ -2,6 +2,7 @@ package config
import (
"fmt"
"math"
"os"
"regexp"
"strconv"
@@ -53,17 +54,28 @@ type MetricsConfig struct {
}
type CheckConfig struct {
ID string `toml:"id"`
Name string `toml:"name"`
Command string `toml:"command"`
Interval Duration `toml:"interval"`
Timeout Duration `toml:"timeout"`
NotificationsEnabled *bool `toml:"notifications_enabled"`
DedupeKey string `toml:"dedupe_key"`
ID string `toml:"id"`
Name string `toml:"name"`
Command string `toml:"command"`
Interval Duration `toml:"interval"`
Timeout Duration `toml:"timeout"`
NotificationsEnabled *bool `toml:"notifications_enabled"`
DedupeKey string `toml:"dedupe_key"`
ResourceLimits ResourceLimits `toml:"resource_limits"`
}
type Duration struct{ time.Duration }
// ByteSize stores a parsed TOML byte size.
type ByteSize struct{ Bytes int64 }
// ResourceLimits defines optional per-check OS resource limits.
type ResourceLimits struct {
CPUTime Duration `toml:"cpu_time"`
Memory ByteSize `toml:"memory"`
OpenFiles int `toml:"open_files"`
}
func (d *Duration) UnmarshalText(b []byte) error {
v, err := time.ParseDuration(string(b))
if err != nil {
@@ -73,6 +85,43 @@ func (d *Duration) UnmarshalText(b []byte) error {
return nil
}
func (s *ByteSize) UnmarshalText(b []byte) error {
raw := strings.TrimSpace(string(b))
if raw == "" {
return fmt.Errorf("empty byte size")
}
valueEnd := 0
for valueEnd < len(raw) && ((raw[valueEnd] >= '0' && raw[valueEnd] <= '9') || raw[valueEnd] == '.') {
valueEnd++
}
if valueEnd == 0 {
return fmt.Errorf("invalid byte size %q", raw)
}
value, err := strconv.ParseFloat(raw[:valueEnd], 64)
if err != nil || value <= 0 {
return fmt.Errorf("invalid byte size %q", raw)
}
unit := strings.ToLower(strings.TrimSpace(raw[valueEnd:]))
mul, ok := map[string]float64{
"": 1, "b": 1,
"k": 1000, "kb": 1000,
"m": 1000 * 1000, "mb": 1000 * 1000,
"g": 1000 * 1000 * 1000, "gb": 1000 * 1000 * 1000,
"kib": 1024,
"mib": 1024 * 1024,
"gib": 1024 * 1024 * 1024,
}[unit]
if !ok {
return fmt.Errorf("unknown byte size unit %q", unit)
}
bytes := value * mul
if bytes > math.MaxInt64 {
return fmt.Errorf("byte size %q is too large", raw)
}
s.Bytes = int64(bytes)
return nil
}
func Load(path string) (*Config, error) {
var c Config
md, err := toml.DecodeFile(path, &c)
@@ -174,6 +223,9 @@ func (c *Config) Validate() error {
if len(ch.DedupeKey) > 256 {
return fmt.Errorf("check %q: dedupe_key too long", ch.ID)
}
if err := validateResourceLimits(ch.ID, ch.ResourceLimits); err != nil {
return err
}
}
return nil
}
@@ -201,8 +253,23 @@ func (c CheckConfig) NotificationsOn() bool {
return c.NotificationsEnabled == nil || *c.NotificationsEnabled
}
func (c CheckConfig) Argv() []string {
return []string{"/bin/sh", "-c", c.Command}
func validateResourceLimits(checkID string, limits ResourceLimits) error {
if limits.CPUTime.Duration < 0 {
return fmt.Errorf("check %q: resource_limits.cpu_time must be > 0", checkID)
}
if limits.Memory.Bytes < 0 {
return fmt.Errorf("check %q: resource_limits.memory must be > 0", checkID)
}
if limits.Memory.Bytes > 0 && limits.Memory.Bytes < 1024*1024 {
return fmt.Errorf("check %q: resource_limits.memory must be >= 1MiB", checkID)
}
if limits.OpenFiles < 0 {
return fmt.Errorf("check %q: resource_limits.open_files must be > 0", checkID)
}
if limits.OpenFiles > 0 && limits.OpenFiles < 16 {
return fmt.Errorf("check %q: resource_limits.open_files must be >= 16", checkID)
}
return nil
}
func ValidateID(field, v string) error {