Files
tx-sync/main.go
T
jamie 38dc37149c
tx-sync build / build (tx-sync-linux-amd64, amd64, linux) (push) Failing after 50s
tx-sync build / build (tx-sync-windows-amd64.exe, amd64, windows) (push) Failing after 6s
feat: initial commit
2026-03-21 01:02:52 +00:00

109 lines
2.4 KiB
Go

package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/jdbnet/tx-sync/internal/config"
"github.com/jdbnet/tx-sync/internal/sync"
)
/*
tx-sync uploads txAdmin playersDB.json to an HTTP(S) endpoint on a schedule.
Expected server contract (for your dashboard ingest implementation):
- Method: POST
- Header: X-API-Key: <secret>
- Header: Content-Type: application/json
- Body: raw bytes of playersDB.json
- URL: full URL from config (no fixed path in this client)
*/
func main() {
log.SetFlags(log.LstdFlags | log.Lmsgprefix)
log.SetPrefix("tx-sync: ")
defaultPath, err := config.DefaultPath()
if err != nil {
log.Fatalf("resolve config path: %v", err)
}
configPath := flag.String("config", defaultPath, "path to config.json")
setup := flag.Bool("setup", false, "run interactive configuration wizard and exit")
flag.Parse()
if *setup {
if err := config.RunWizard(*configPath); err != nil {
log.Fatal(err)
}
os.Exit(0)
}
if _, statErr := os.Stat(*configPath); statErr != nil {
if errors.Is(statErr, os.ErrNotExist) {
if err := config.RunWizard(*configPath); err != nil {
log.Fatal(err)
}
os.Exit(0)
}
log.Fatalf("config file: %v", statErr)
}
cfg, err := config.Load(*configPath)
if err != nil {
log.Fatalf("load config: %v", err)
}
if err := cfg.Validate(); err != nil {
log.Fatalf("invalid config: %v (run with -setup to reconfigure)", err)
}
interval, err := cfg.IntervalDuration()
if err != nil {
log.Fatalf("sync interval: %v", err)
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
client := sync.NewHTTPClient()
if err := pushOnce(ctx, client, cfg); err != nil {
log.Printf("upload: %v", err)
}
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
log.Println("shutting down")
return
case <-ticker.C:
if err := pushOnce(ctx, client, cfg); err != nil {
log.Printf("upload: %v", err)
}
}
}
}
func pushOnce(ctx context.Context, client *http.Client, cfg *config.Config) error {
body, err := sync.ReadPlayersDB(cfg.PlayersDBPath)
if err != nil {
return fmt.Errorf("read %q: %w", cfg.PlayersDBPath, err)
}
if err := sync.Upload(ctx, client, cfg.Endpoint, cfg.APIKey, body); err != nil {
return err
}
log.Printf("upload ok (%d bytes)", len(body))
return nil
}