feat: ✨ initial commit
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user