109 lines
2.4 KiB
Go
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
|
|
}
|