1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
package proxy
import (
"encoding/json"
"os"
)
// RootConfig is the top-level structure of config.json.
type RootConfig struct {
APIPort int `json:"api_port"`
APIKeys []APIKey `json:"api_keys"`
DefaultDetectors []string `json:"default_detectors"`
DefaultWhitelist []string `json:"default_whitelist"`
CORSOrigins []string `json:"cors_origins"` // allowed origins for API CORS (empty = CORS off)
UIPasswordHash string `json:"ui_password_hash"` // hex(sha256(password)); empty = default admin/admin, change required
Proxies []Config `json:"proxies"`
}
// APIKey holds the metadata and SHA-256 hash of a single API key.
// The original key value is never stored.
type APIKey struct {
ID string `json:"id"`
Name string `json:"name"`
Hash string `json:"hash"` // hex(sha256(original_key))
Hint string `json:"hint"` // first 12 chars + "****"
Created string `json:"created"` // YYYY-MM-DD
}
// Config is one proxy instance.
type Config struct {
Type string `json:"type"`
Port int `json:"port"`
Upstream string `json:"upstream"`
PrivacyGuard GuardConfig `json:"privacy_guard"`
}
// GuardConfig holds per-instance detector configuration.
type GuardConfig struct {
URL string `json:"url"`
APIKey *string `json:"api_key"`
Detectors []string `json:"detectors"`
Whitelist []string `json:"whitelist"`
DryRun bool `json:"dry_run,omitempty"`
}
// LoadConfigs reads config.json. Falls back to DefaultRootConfig if the file is absent.
func LoadConfigs(path string) (RootConfig, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return DefaultRootConfig(), nil
}
data, err := os.ReadFile(path)
if err != nil {
return RootConfig{}, err
}
var root RootConfig
if err := json.Unmarshal(data, &root); err != nil {
// Backward compatibility: legacy top-level []Config format.
var legacy []Config
if err2 := json.Unmarshal(data, &legacy); err2 != nil {
return RootConfig{}, err
}
root = RootConfig{Proxies: legacy}
}
if len(root.Proxies) == 0 {
root.Proxies = []Config{DefaultConfig()}
}
return root, nil
}
// DefaultRootConfig returns a sensible default configuration.
func DefaultRootConfig() RootConfig {
return RootConfig{
Proxies: []Config{DefaultConfig()},
}
}
// DefaultConfig returns a sensible single-proxy config.
func DefaultConfig() Config {
return Config{
Port: 8080,
Upstream: "https://api.anthropic.com",
PrivacyGuard: GuardConfig{
Detectors: []string{},
Whitelist: []string{},
},
}
}
|