Files
anthropic-proxy/internal/embedded/perses.go
T
Alexander 0df28e9dd8 refactor: modularize codebase — deduplicate, extract, clean up
- Unify duplicate uTLS transports into shared internal/transport package
- Extract shared version constant into internal/version
- Move LoadDefaultCredentials from config to auth (remove config→auth import)
- Deduplicate handler.go: extract telemetry/error helpers (324→268 lines)
- Break up main.go::run() into initCredential/initEmbedded
- Eliminate logging.Config duplication (use config.LoggingConfig directly)
- Extract logWriter to embedded/log.go, SSE fixtures to consts in sniff.go
- Use uTLS client for usage polling (consistent TLS fingerprint)
- Handle sjson.SetBytes errors in sanitize.go instead of silently swallowing
- Document reverse-engineered magic values in billing.go
- Unexport Credential.CooldownUntil (internal state)
- Replace hardcoded auth bypass paths with map in server.go
2026-04-15 11:01:29 +02:00

134 lines
2.9 KiB
Go

package embedded
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/fujin/anthropic-proxy/internal/config"
"github.com/rs/zerolog/log"
)
type Perses struct {
cfg config.EmbeddedConfig
proxyPort int
cmd *exec.Cmd
tmpDir string
}
func NewPerses(cfg config.EmbeddedConfig, proxyPort int) *Perses {
return &Perses{cfg: cfg, proxyPort: proxyPort}
}
func (p *Perses) Start() error {
bin, err := ensureBinary("perses", p.cfg.PersesBinary, p.cfg.BinDir)
if err != nil {
return fmt.Errorf("perses: %w", err)
}
p.tmpDir, err = os.MkdirTemp("", "perses-*")
if err != nil {
return fmt.Errorf("create temp dir: %w", err)
}
if err := p.writeServerConfig(); err != nil {
return fmt.Errorf("write server config: %w", err)
}
if err := p.writeDatasourceProvision(); err != nil {
return fmt.Errorf("write datasource provision: %w", err)
}
if err := p.writeDashboardProvision(); err != nil {
return fmt.Errorf("write dashboard provision: %w", err)
}
p.cmd = exec.Command(bin,
"--config", filepath.Join(p.tmpDir, "config.yaml"),
"-web.listen-address", fmt.Sprintf(":%d", p.cfg.Port),
)
p.cmd.Dir = filepath.Dir(bin)
p.cmd.Stdout = &logWriter{level: "info", component: "perses"}
p.cmd.Stderr = &logWriter{level: "error", component: "perses"}
if err := p.cmd.Start(); err != nil {
return fmt.Errorf("start perses: %w", err)
}
log.Info().
Str("binary", bin).
Int("port", p.cfg.Port).
Str("config", p.tmpDir).
Msg("perses started")
return nil
}
func (p *Perses) Stop() {
if p.cmd != nil && p.cmd.Process != nil {
_ = p.cmd.Process.Kill()
_ = p.cmd.Wait()
}
if p.tmpDir != "" {
_ = os.RemoveAll(p.tmpDir)
}
}
func (p *Perses) Running() bool {
return p.cmd != nil && p.cmd.Process != nil && p.cmd.ProcessState == nil
}
func (p *Perses) writeServerConfig() error {
provisionDir := filepath.Join(p.tmpDir, "provisions")
if err := os.MkdirAll(filepath.Join(provisionDir, "datasources"), 0o755); err != nil {
return err
}
if err := os.MkdirAll(filepath.Join(provisionDir, "dashboards"), 0o755); err != nil {
return err
}
cfg := fmt.Sprintf(`provisioning:
interval: 1m
folders:
- %s
database:
file:
folder: %s/data
extension: json
security:
readonly: false
enable_auth: false
`, provisionDir, p.tmpDir)
return os.WriteFile(filepath.Join(p.tmpDir, "config.yaml"), []byte(cfg), 0o644)
}
func (p *Perses) writeDatasourceProvision() error {
ds := fmt.Sprintf(`kind: Datasource
metadata:
name: victoria-metrics
project: anthropic-proxy
spec:
default: true
plugin:
kind: PrometheusDatasource
spec:
directUrl: http://localhost:%d
`, p.cfg.VMPort)
return os.WriteFile(
filepath.Join(p.tmpDir, "provisions", "datasources", "vm.yaml"),
[]byte(ds), 0o644,
)
}
func (p *Perses) writeDashboardProvision() error {
dashData, err := DashboardJSON()
if err != nil {
return err
}
return os.WriteFile(
filepath.Join(p.tmpDir, "provisions", "dashboards", "proxy.json"),
dashData, 0o644,
)
}