Skip to content

Commit 30e84d9

Browse files
converge v2.1 code onto mainline; add Daylight and Arctic UI themes
- main now actually contains what v2.1.0 shipped (CLI auth, hot reload, nine windows) — the tag had been pushed without its branch - three themes: dark scope (default), Daylight (warm paper, deep-green trace), Arctic (cool light, teal trace); one-click cycle in header, stored in browser; all chart colors now flow from CSS variables; login page follows the theme
1 parent 8f240fb commit 30e84d9

7 files changed

Lines changed: 592 additions & 163 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH \
2525
go build -trimpath -ldflags "-s -w -X main.version=${GITHUB_REF_NAME}" \
2626
-o "$dir/pingping" .
27-
cp README.md LICENSE config.example.jsonc "$dir/"
27+
cp LICENSE "$dir/"
2828
tar czf "$dir.tar.gz" "$dir"
2929
done
3030
sha256sum pingping-*.tar.gz > SHA256SUMS

config.go

Lines changed: 44 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package main
22

33
import (
4-
"encoding/json"
5-
"fmt"
4+
"fmt"
65
"net"
76
"os"
87
"path/filepath"
@@ -12,13 +11,38 @@ import (
1211
)
1312

1413
// pingping 2.0: a probe, text files, and a smoke graph. Nothing else.
14+
// Config: v2.1 has no config file. These are the program's constants —
15+
// the only things a user edits are targets/ping.list and tcp.list.
1516
type Config struct {
16-
Listen string `json:"listen"`
17-
DataDir string `json:"data_dir"`
18-
TargetsDir string `json:"targets_dir"`
19-
Targets []TargetCfg `json:"targets"`
20-
Probe ProbeCfg `json:"probe"`
21-
RetentionDays int `json:"retention_days"` // raw JSONL kept this long, then rm
17+
Listen string
18+
DataDir string
19+
TargetsDir string
20+
Targets []TargetCfg
21+
Probe ProbeCfg
22+
RetentionDays int
23+
}
24+
25+
func defaultConfig() *Config {
26+
return &Config{
27+
Listen: "0.0.0.0:8517",
28+
DataDir: "./data",
29+
TargetsDir: "./targets",
30+
Probe: ProbeCfg{IntervalSec: 60, Packets: 20, GapMs: 50, TimeoutMs: 1000},
31+
RetentionDays: 300,
32+
}
33+
}
34+
35+
// FinishConfig loads targets from the lists and validates them.
36+
func FinishConfig(cfg *Config) error {
37+
listTargets, err := loadTargetLists(cfg.TargetsDir)
38+
if err != nil {
39+
return err
40+
}
41+
cfg.Targets = append(cfg.Targets, listTargets...)
42+
if len(cfg.Targets) == 0 {
43+
return fmt.Errorf("no targets: add one line to %s/ping.list or tcp.list", cfg.TargetsDir)
44+
}
45+
return validateTargets(cfg)
2246
}
2347

2448
type TargetCfg struct {
@@ -40,64 +64,40 @@ type ProbeCfg struct {
4064

4165
var dirSan = regexp.MustCompile(`[^a-zA-Z0-9._\p{Han}-]`)
4266

43-
func LoadConfig(path string) (*Config, error) {
44-
raw, err := os.ReadFile(path)
45-
if err != nil {
46-
return nil, err
47-
}
48-
cfg := &Config{
49-
Listen: "0.0.0.0:8517",
50-
DataDir: "./data",
51-
Probe: ProbeCfg{IntervalSec: 60, Packets: 20, GapMs: 50, TimeoutMs: 1000},
52-
RetentionDays: 300,
53-
}
54-
if err := json.Unmarshal(stripJSONC(raw), cfg); err != nil {
55-
return nil, fmt.Errorf("parse %s: %w", path, err)
56-
}
57-
if cfg.TargetsDir == "" {
58-
cfg.TargetsDir = "./targets"
59-
}
60-
listTargets, err := loadTargetLists(cfg.TargetsDir)
61-
if err != nil {
62-
return nil, err
63-
}
64-
cfg.Targets = append(cfg.Targets, listTargets...)
65-
if len(cfg.Targets) == 0 {
66-
return nil, fmt.Errorf("no targets: add one to %s/ping.list or tcp.list", cfg.TargetsDir)
67-
}
67+
func validateTargets(cfg *Config) error {
6868
seen := map[string]bool{}
6969
for i := range cfg.Targets {
7070
t := &cfg.Targets[i]
7171
if t.Name == "" {
72-
return nil, fmt.Errorf("targets[%d] missing name", i)
72+
return fmt.Errorf("target #%d has no name", i+1)
7373
}
74-
switch t.Type {
75-
case "", "icmp":
74+
if t.Type == "" {
7675
t.Type = "icmp"
76+
}
77+
switch t.Type {
78+
case "icmp":
7779
case "tcp":
7880
if t.Port <= 0 {
79-
return nil, fmt.Errorf("tcp target %q needs a port", t.Name)
81+
return fmt.Errorf("tcp target %q needs a port", t.Name)
8082
}
8183
default:
82-
return nil, fmt.Errorf("target %q: unknown type %q (icmp|tcp)", t.Name, t.Type)
84+
return fmt.Errorf("target %q: unknown type %q", t.Name, t.Type)
8385
}
8486
switch t.Pace {
8587
case "", "fast", "normal", "slow":
8688
default:
87-
return nil, fmt.Errorf("target %q: invalid pace %q (fast|slow)", t.Name, t.Pace)
89+
return fmt.Errorf("target %q: invalid pace %q (fast|slow)", t.Name, t.Pace)
8890
}
8991
t.dir = dirSan.ReplaceAllString(t.Name, "_")
9092
if seen[t.dir] {
91-
return nil, fmt.Errorf("target name %q collides with another target", t.Name)
93+
return fmt.Errorf("duplicate target name: %q", t.Name)
9294
}
9395
seen[t.dir] = true
9496
}
95-
if cfg.Probe.Packets < 3 {
96-
cfg.Probe.Packets = 3 // fewer than 3 samples is not a distribution
97-
}
98-
return cfg, nil
97+
return nil
9998
}
10099

100+
101101
// loadTargetLists reads targets/ping.list and tcp.list.
102102
// line format: host[:port] [name...] [pace=fast|slow] [interval=sec]
103103
func loadTargetLists(dir string) ([]TargetCfg, error) {
@@ -165,41 +165,3 @@ func parseListLine(line, typ string) (TargetCfg, error) {
165165
return t, nil
166166
}
167167

168-
func stripJSONC(b []byte) []byte {
169-
var out strings.Builder
170-
out.Grow(len(b))
171-
inStr, esc := false, false
172-
for i := 0; i < len(b); i++ {
173-
c := b[i]
174-
if inStr {
175-
out.WriteByte(c)
176-
if esc {
177-
esc = false
178-
} else if c == '\\' {
179-
esc = true
180-
} else if c == '"' {
181-
inStr = false
182-
}
183-
continue
184-
}
185-
switch {
186-
case c == '"':
187-
inStr = true
188-
out.WriteByte(c)
189-
case c == '/' && i+1 < len(b) && b[i+1] == '/':
190-
for i < len(b) && b[i] != '\n' {
191-
i++
192-
}
193-
out.WriteByte('\n')
194-
case c == '/' && i+1 < len(b) && b[i+1] == '*':
195-
i += 2
196-
for i+1 < len(b) && !(b[i] == '*' && b[i+1] == '/') {
197-
i++
198-
}
199-
i++
200-
default:
201-
out.WriteByte(c)
202-
}
203-
}
204-
return []byte(out.String())
205-
}

0 commit comments

Comments
 (0)