feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
Этот коммит содержится в:
84
internal/webapp/template_helpers.go
Обычный файл
84
internal/webapp/template_helpers.go
Обычный файл
@@ -0,0 +1,84 @@
|
||||
package webapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Template helpers. Kept in their own file so templates.go stays
|
||||
// focused on parsing/wiring.
|
||||
|
||||
// fmtBytes renders a byte count as a human-readable string. The
|
||||
// unit is picked automatically (B / KiB / MiB / GiB / TiB).
|
||||
func fmtBytes(n uint64) string {
|
||||
const k = 1024
|
||||
if n < k {
|
||||
return fmt.Sprintf("%d B", n)
|
||||
}
|
||||
div, exp := uint64(k), 1
|
||||
for n2 := n / k; n2 >= k; n2 /= k {
|
||||
div *= k
|
||||
exp++
|
||||
}
|
||||
suffix := []string{"KiB", "MiB", "GiB", "TiB", "PiB"}[exp-1]
|
||||
return fmt.Sprintf("%.2f %s", float64(n)/float64(div), suffix)
|
||||
}
|
||||
|
||||
// fmtPercent renders a 0..100 number as "N.NN%".
|
||||
func fmtPercent(p float64) string {
|
||||
return fmt.Sprintf("%.2f%%", p)
|
||||
}
|
||||
|
||||
// fmtDuration renders a duration as "1d 2h 3m 4s". The format is
|
||||
// stable across short and long uptimes.
|
||||
func fmtDuration(d time.Duration) string {
|
||||
if d < 0 {
|
||||
d = 0
|
||||
}
|
||||
days := int(d / (24 * time.Hour))
|
||||
d -= time.Duration(days) * 24 * time.Hour
|
||||
hours := int(d / time.Hour)
|
||||
d -= time.Duration(hours) * time.Hour
|
||||
minutes := int(d / time.Minute)
|
||||
d -= time.Duration(minutes) * time.Minute
|
||||
seconds := int(d / time.Second)
|
||||
switch {
|
||||
case days > 0:
|
||||
return fmt.Sprintf("%dd %dh %dm %ds", days, hours, minutes, seconds)
|
||||
case hours > 0:
|
||||
return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
|
||||
case minutes > 0:
|
||||
return fmt.Sprintf("%dm %ds", minutes, seconds)
|
||||
default:
|
||||
return fmt.Sprintf("%ds", seconds)
|
||||
}
|
||||
}
|
||||
|
||||
// fmtTime renders a timestamp as "2006-01-02 15:04:05 UTC".
|
||||
func fmtTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "—"
|
||||
}
|
||||
return t.UTC().Format("2006-01-02 15:04:05 UTC")
|
||||
}
|
||||
|
||||
// fmtBool renders a bool as "yes" / "no".
|
||||
func fmtBool(b bool) string {
|
||||
if b {
|
||||
return "yes"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
|
||||
// sanitizeName strips characters that could break out of an HTML
|
||||
// attribute when interpolated by a template. Template auto-escapes
|
||||
// by default, but a paranoid layer here keeps audit targets safe
|
||||
// when they are rendered into other contexts.
|
||||
func sanitizeName(s string) string {
|
||||
s = strings.ReplaceAll(s, "\x00", "")
|
||||
if len(s) > 128 {
|
||||
s = s[:128]
|
||||
}
|
||||
return s
|
||||
}
|
||||
Ссылка в новой задаче
Block a user