mirror of
https://github.com/Dvorinka/Dash.git
synced 2026-06-03 15:02:56 +00:00
b17a06fbba
A clean, customizable homelab dashboard inspired by CasaOS. Features: - Empty-first dashboard (no demo data) - 3 themes: Light, Dark, CasaOS glassmorphism - Widgets: Clock (multi-timezone), Pi-hole, Memos, Immich, Image - Drag & drop app organization - Grid + list view for apps - Groups with collapse/expand - Proper widget refresh handling - Visual timezone picker - Square app cards with hover effects Stack: Go + Gin + PostgreSQL + Next.js 15 + React 19 + Tailwind CSS + shadcn/ui
73 lines
1.6 KiB
Go
73 lines
1.6 KiB
Go
package validation
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func Name(raw string) (string, error) {
|
|
return boundedText(raw, 1, 80, "name")
|
|
}
|
|
|
|
func Label(raw string) (string, error) {
|
|
return boundedText(raw, 1, 40, "label")
|
|
}
|
|
|
|
func AbsoluteHTTP(raw, field string) (string, error) {
|
|
value := strings.TrimSpace(raw)
|
|
if value == "" {
|
|
return "", fmt.Errorf("%s is required", field)
|
|
}
|
|
parsed, err := url.Parse(value)
|
|
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
|
return "", fmt.Errorf("%s must be absolute URL", field)
|
|
}
|
|
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
|
return "", fmt.Errorf("%s must use http or https", field)
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
func OptionalAbsoluteHTTP(raw, field string) (*string, error) {
|
|
value := strings.TrimSpace(raw)
|
|
if value == "" {
|
|
return nil, nil
|
|
}
|
|
clean, err := AbsoluteHTTP(value, field)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &clean, nil
|
|
}
|
|
|
|
func URLKind(kind string) error {
|
|
switch kind {
|
|
case "local", "external", "custom":
|
|
return nil
|
|
default:
|
|
return errors.New("kind must be local, external, or custom")
|
|
}
|
|
}
|
|
|
|
func WidgetType(kind string) error {
|
|
switch kind {
|
|
case "clock", "image", "pihole", "memos":
|
|
return nil
|
|
default:
|
|
return errors.New("widget type must be clock, image, pihole, or memos")
|
|
}
|
|
}
|
|
|
|
func boundedText(raw string, minLen int, maxLen int, field string) (string, error) {
|
|
value := strings.TrimSpace(raw)
|
|
if len(value) < minLen {
|
|
return "", fmt.Errorf("%s is required", field)
|
|
}
|
|
if len(value) > maxLen {
|
|
return "", fmt.Errorf("%s must be at most %d chars", field, maxLen)
|
|
}
|
|
return value, nil
|
|
}
|