From d5577f10d7c6d3f89893d119e89481ce02e386ea Mon Sep 17 00:00:00 2001 From: lbenedar Date: Thu, 2 Apr 2026 18:37:30 +0300 Subject: [PATCH] init --- Makefile | 7 + README.md | 2 + cmd/api/handlers.go | 56 +++ cmd/api/main.go | 132 +++++ cmd/api/router.go | 16 + go.mod | 133 +++++ go.sum | 454 ++++++++++++++++++ internal/foundry/errors.go | 32 ++ internal/foundry/foundry.go | 232 +++++++++ internal/foundry/get_requests.go | 64 +++ internal/foundry/models/data.go | 44 ++ internal/foundry/models/errors.go | 8 + internal/foundry/models/language.go | 13 + internal/foundry/models/module.go | 49 ++ internal/foundry/models/modules/dice-stats.go | 59 +++ internal/foundry/models/modules/pf2e.go | 12 + internal/foundry/models/release.go | 11 + internal/foundry/models/status.go | 11 + internal/foundry/models/system.go | 36 ++ internal/foundry/models/template.go | 132 +++++ internal/foundry/models/users.go | 45 ++ internal/foundry/models/world.go | 88 ++++ internal/foundry/post_requests.go | 32 ++ internal/foundry/websocket.go | 236 +++++++++ internal/foundry/ws_channels.go | 25 + internal/foundry/ws_message.go | 17 + 26 files changed, 1946 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 cmd/api/handlers.go create mode 100644 cmd/api/main.go create mode 100644 cmd/api/router.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/foundry/errors.go create mode 100644 internal/foundry/foundry.go create mode 100644 internal/foundry/get_requests.go create mode 100644 internal/foundry/models/data.go create mode 100644 internal/foundry/models/errors.go create mode 100644 internal/foundry/models/language.go create mode 100644 internal/foundry/models/module.go create mode 100644 internal/foundry/models/modules/dice-stats.go create mode 100644 internal/foundry/models/modules/pf2e.go create mode 100644 internal/foundry/models/release.go create mode 100644 internal/foundry/models/status.go create mode 100644 internal/foundry/models/system.go create mode 100644 internal/foundry/models/template.go create mode 100644 internal/foundry/models/users.go create mode 100644 internal/foundry/models/world.go create mode 100644 internal/foundry/post_requests.go create mode 100644 internal/foundry/websocket.go create mode 100644 internal/foundry/ws_channels.go create mode 100644 internal/foundry/ws_message.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..01ee2af --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +-include .env + +api/run: + go run ./cmd/api -foundry_pass=${FOUNDRY_PASS} -foundry_host=${FOUNDRY_HOST} -port=${LISTEN_PORT} + +help: + go run ./cmd/api -help \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..31b3fe0 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# foundry_helper_service + diff --git a/cmd/api/handlers.go b/cmd/api/handlers.go new file mode 100644 index 0000000..accd193 --- /dev/null +++ b/cmd/api/handlers.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "net/http" + + "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models" +) + +func (app *application) ShowText(w http.ResponseWriter, r *http.Request) { + msg := app.foundryApp.CreateWSMessageByPage("/join") + data, err := app.foundryApp.HandleWebsocketRequest(msg) + if err != nil { + app.slogger.Error("", "error", err) + w.Write([]byte(err.Error())) + return + } + + // setupData, err := models.ParseSetupModel(data) + // if err != nil { + // app.slogger.Error("", "error", err) + // w.Write([]byte(err.Error())) + // return + // } + + // fmt.Printf("JSON msg: %t\n", setupData.IsAdmin) + + w.Write(data) +} + +func (app *application) WhenNextSession(w http.ResponseWriter, r *http.Request) { + msg := app.foundryApp.CreateWSMessageByPage("/setup") + data, err := app.foundryApp.HandleWebsocketRequest(msg) + if err != nil { + app.slogger.Error("", "error", err) + w.Write([]byte(err.Error())) + return + } + + setupData, err := models.ParseSetupModel(data) + if err != nil { + app.slogger.Error("", "error", err) + w.Write([]byte(err.Error())) + return + } + + nextSession, err := setupData.GetWorlds().GetSessionTime("") + if err != nil { + app.slogger.Error("", "error", err) + w.Write([]byte(err.Error())) + return + } + fmt.Printf("JSON msg: %s\n", nextSession) + + w.Write([]byte(nextSession.Local().String())) +} diff --git a/cmd/api/main.go b/cmd/api/main.go new file mode 100644 index 0000000..bab98d3 --- /dev/null +++ b/cmd/api/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "flag" + "fmt" + "log" + "log/slog" + "net/http" + "os" + "time" + + "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry" +) + +type service_mode string + +const ( + API_MODE = "api" + DISCORD_BOT_MODE = "discord" + TG_BOT_MODE = "tg" +) + +type config struct { + mode service_mode + port string + foundryHost string + foundryPass string +} + +type application struct { + cfg config + foundryApp *foundry.Foundry + slogger *slog.Logger + logger *log.Logger +} + +// TODO: change default values +func (app *application) parseFlags() { + var mode string + + flag.StringVar(&mode, "service_mode", "api", "Type of service mode(api|discord|tg)") + flag.StringVar(&app.cfg.port, "port", ":9090", "Service port") + flag.StringVar(&app.cfg.foundryHost, "foundry_host", "127.0.0.1", "Address to connect to Foundry") + flag.StringVar(&app.cfg.foundryPass, "foundry_pass", "", "Password to connect to Foundry") + flag.Parse() + + app.cfg.mode = service_mode(mode) +} + +func (app *application) ConnectToFoundry(foundryHost, foundryPass string) error { + var err error + + if app.foundryApp == nil { + app.foundryApp, err = foundry.NewFoundry(foundryHost) + if err != nil { + return err + } + } + + fmt.Printf("Foundry1: %v\n", app.foundryApp) + + ok, err := app.foundryApp.CheckSessionToken(foundryHost) + if err != nil { + return err + } + + fmt.Printf("Foundry2: %v\n", app.foundryApp) + + if foundryPass != "" && !ok { + err := app.foundryApp.Authenticate(foundryPass) + if err != nil { + return err + } + } + + fmt.Printf("Foundry3: %v\n", app.foundryApp) + return nil +} + +func (app *application) StartListenFoundry() { + for { + err := app.ConnectToFoundry(app.cfg.foundryHost, app.cfg.foundryPass) + if err != nil { + app.logger.Println(err) + return + } + + err = foundry.ConnectToWebSocket(app.foundryApp) + if err != nil { + app.logger.Println(err) + return + } + + err = app.foundryApp.StartListen() + if err != nil && err != foundry.ListenIsDone { + app.logger.Println(err) + return + } + } +} + +func main() { + app := application{foundryApp: nil} + + app.parseFlags() + app.slogger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) + app.logger = slog.NewLogLogger(app.slogger.Handler(), slog.LevelInfo) + + go app.StartListenFoundry() + + server := &http.Server{ + Addr: app.cfg.port, + Handler: app.routes(), + ErrorLog: app.logger, + // TLSConfig: tlsConfig, + IdleTimeout: time.Minute, + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + switch app.cfg.mode { + case API_MODE: + app.slogger.Info("Server is started", "port", app.cfg.port) + server.ListenAndServe() + case DISCORD_BOT_MODE: + app.slogger.Warn("TODO: implement this mode") + case TG_BOT_MODE: + app.slogger.Warn("TODO: implement this mode") + default: + app.slogger.Error("Wrong Mode. Should be one of: 'api', 'discord', 'tg'", "mode", app.cfg.mode) + } +} diff --git a/cmd/api/router.go b/cmd/api/router.go new file mode 100644 index 0000000..f2f1b75 --- /dev/null +++ b/cmd/api/router.go @@ -0,0 +1,16 @@ +package main + +import ( + "net/http" + + "github.com/julienschmidt/httprouter" +) + +func (app *application) routes() http.Handler { + router := httprouter.New() + + router.HandlerFunc(http.MethodGet, "/", app.ShowText) + router.HandlerFunc(http.MethodGet, "/v1/when", app.WhenNextSession) + + return router +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f8950e4 --- /dev/null +++ b/go.mod @@ -0,0 +1,133 @@ +module gitea.local.lab/Lbenedar/foundry_helper_service + +go 1.25.0 + +require ( + github.com/docker/cli v29.2.1+incompatible + github.com/docker/compose/v5 v5.1.1 +) + +require ( + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/buger/goterm v1.0.4 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/compose-spec/compose-go/v2 v2.10.1 // indirect + github.com/containerd/console v1.0.5 // indirect + github.com/containerd/containerd/api v1.10.0 // indirect + github.com/containerd/containerd/v2 v2.2.2 // indirect + github.com/containerd/continuity v0.4.5 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v1.0.0-rc.2 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect + github.com/containerd/typeurl/v2 v2.2.3 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/buildx v0.31.1 // indirect + github.com/docker/docker v28.5.2+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.5 // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsevents v0.2.0 // indirect + github.com/fvbommel/sortorder v1.1.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/gofrs/flock v0.13.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/in-toto/in-toto-golang v0.9.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/klauspost/compress v1.18.3 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/moby/buildkit v0.27.1 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/moby/api v1.54.0 // indirect + github.com/moby/moby/client v0.3.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/atomicwriter v0.1.0 // indirect + github.com/moby/sys/capability v0.4.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/signal v0.7.1 // indirect + github.com/moby/sys/symlink v0.3.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect + github.com/morikuni/aec v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect + github.com/shibumi/go-pathspec v1.3.0 // indirect + github.com/sigstore/sigstore v1.10.0 // indirect + github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect + github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect + github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect + github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f // indirect + github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect + github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect + github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + tags.cncf.io/container-device-interface v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0496036 --- /dev/null +++ b/go.sum @@ -0,0 +1,454 @@ +cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= +cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0= +github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ= +github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30= +github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= +github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/compose-spec/compose-go/v2 v2.10.1 h1:mFbXobojGRFIVi1UknrvaDAZ+PkJfyjqkA1yseh+vAU= +github.com/compose-spec/compose-go/v2 v2.10.1/go.mod h1:Ohac1SzhO/4fXXrzWIztIVB6ckmKBv1Nt5Z5mGVESUg= +github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4= +github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= +github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= +github.com/containerd/containerd/v2 v2.2.2 h1:mjVQdtfryzT7lOqs5EYUFZm8ioPVjOpkSoG1GJPxEMY= +github.com/containerd/containerd/v2 v2.2.2/go.mod h1:5Jhevmv6/2J+Iu/A2xXAdUIdI5Ah/hfyO7okJ4AFIdY= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/nydus-snapshotter v0.15.10 h1:hphjuKOqSHLGznNJiAvmsOWkdu4qFXjf4DzGrWSuIsM= +github.com/containerd/nydus-snapshotter v0.15.10/go.mod h1:EWRd/QJ0b6UKHAqYgiV5gHlqLC2qq5cQiSlXEdVovrA= +github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= +github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= +github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= +github.com/containerd/stargz-snapshotter v0.17.0 h1:djNS4KU8ztFhLdEDZ1bsfzOiYuVHT6TgSU5qwRk+cNc= +github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE= +github.com/containerd/stargz-snapshotter/estargz v0.17.0/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= +github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= +github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= +github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= +github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I= +github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/buildx v0.31.1 h1:zbvbrb9nxBNVV8nnI33f2F+4aAZBA1gY+AmeBFflMqY= +github.com/docker/buildx v0.31.1/go.mod h1:SD+jYLnt3S4SXqohVtV+8z+dihnOgwMJ8t+bLQvsaCk= +github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg= +github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/compose/v5 v5.1.1 h1:Ngkcd7AtHra5TcEEkDWol9qcZ44pMHJiM7C3dzWcQPQ= +github.com/docker/compose/v5 v5.1.1/go.mod h1:9MiC31ueo0VamH4PvTAPfUrd3lQHIWQYgww2yDB8Dx8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= +github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= +github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c= +github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQwKBr1qH/w= +github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= +github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/analysis v0.24.1 h1:Xp+7Yn/KOnVWYG8d+hPksOYnCYImE3TieBa7rBOesYM= +github.com/go-openapi/analysis v0.24.1/go.mod h1:dU+qxX7QGU1rl7IYhBC8bIfmWQdX4Buoea4TGtxXY84= +github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM= +github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= +github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= +github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= +github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= +github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4= +github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY= +github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0= +github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0= +github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k= +github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA= +github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ= +github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8= +github.com/go-openapi/swag v0.25.3 h1:FAa5wJXyDtI7yUztKDfZxDrSx+8WTg31MfCQ9s3PV+s= +github.com/go-openapi/swag v0.25.3/go.mod h1:tX9vI8Mj8Ny+uCEk39I1QADvIPI7lkndX4qCsEqhkS8= +github.com/go-openapi/swag/cmdutils v0.25.3 h1:EIwGxN143JCThNHnqfqs85R8lJcJG06qjJRZp3VvjLI= +github.com/go-openapi/swag/cmdutils v0.25.3/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.3 h1:PcB18wwfba7MN5BVlBIV+VxvUUeC2kEuCEyJ2/t2X7E= +github.com/go-openapi/swag/conv v0.25.3/go.mod h1:n4Ibfwhn8NJnPXNRhBO5Cqb9ez7alBR40JS4rbASUPU= +github.com/go-openapi/swag/fileutils v0.25.3 h1:P52Uhd7GShkeU/a1cBOuqIcHMHBrA54Z2t5fLlE85SQ= +github.com/go-openapi/swag/fileutils v0.25.3/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.3 h1:U20VKDS74HiPaLV7UZkztpyVOw3JNVsit+w+gTXRj0A= +github.com/go-openapi/swag/jsonname v0.25.3/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.3 h1:kV7wer79KXUM4Ea4tBdAVTU842Rg6tWstX3QbM4fGdw= +github.com/go-openapi/swag/jsonutils v0.25.3/go.mod h1:ILcKqe4HC1VEZmJx51cVuZQ6MF8QvdfXsQfiaCs0z9o= +github.com/go-openapi/swag/loading v0.25.3 h1:Nn65Zlzf4854MY6Ft0JdNrtnHh2bdcS/tXckpSnOb2Y= +github.com/go-openapi/swag/loading v0.25.3/go.mod h1:xajJ5P4Ang+cwM5gKFrHBgkEDWfLcsAKepIuzTmOb/c= +github.com/go-openapi/swag/mangling v0.25.3 h1:rGIrEzXaYWuUW1MkFmG3pcH+EIA0/CoUkQnIyB6TUyo= +github.com/go-openapi/swag/mangling v0.25.3/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.3 h1:XWXHZfL/65ABiv8rvGp9dtE0C6QHTYkCrNV77jTl358= +github.com/go-openapi/swag/netutils v0.25.3/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.3 h1:nAmWq1fUTWl/XiaEPwALjp/8BPZJun70iDHRNq/sH6w= +github.com/go-openapi/swag/stringutils v0.25.3/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.3 h1:2w4mEEo7DQt3V4veWMZw0yTPQibiL3ri2fdDV4t2TQc= +github.com/go-openapi/swag/typeutils v0.25.3/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.3 h1:LKTJjCn/W1ZfMec0XDL4Vxh8kyAnv1orH5F2OREDUrg= +github.com/go-openapi/swag/yamlutils v0.25.3/go.mod h1:Y7QN6Wc5DOBXK14/xeo1cQlq0EA0wvLoSv13gDQoCao= +github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw= +github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A= +github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= +github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E= +github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM= +github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= +github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= +github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/moby/buildkit v0.27.1 h1:qlIWpnZzqCkrYiGkctM1gBD/YZPOJTjtUdRBlI0oBOU= +github.com/moby/buildkit v0.27.1/go.mod h1:99qLrCrIAFgEOiFnCi9Y0Wwp6/qA7QvZ3uq/6wF0IsI= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0= +github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= +github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs= +github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= +github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= +github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= +github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU= +github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= +github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= +github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE= +github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= +github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= +github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= +github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY= +github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= +github.com/sigstore/rekor v1.4.3 h1:2+aw4Gbgumv8vYM/QVg6b+hvr4x4Cukur8stJrVPKU0= +github.com/sigstore/rekor v1.4.3/go.mod h1:o0zgY087Q21YwohVvGwV9vK1/tliat5mfnPiVI3i75o= +github.com/sigstore/rekor-tiles/v2 v2.0.1 h1:1Wfz15oSRNGF5Dzb0lWn5W8+lfO50ork4PGIfEKjZeo= +github.com/sigstore/rekor-tiles/v2 v2.0.1/go.mod h1:Pjsbhzj5hc3MKY8FfVTYHBUHQEnP0ozC4huatu4x7OU= +github.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI= +github.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y= +github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 h1:94NLPmq4bxvdmslzcG670IOkrlS98CGpmob8cjpFHuI= +github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7/go.mod h1:4r/PNX0G7uzkLpc3PSdYs5E2k4bWEJNXTK6kwAyw9TM= +github.com/sigstore/timestamp-authority/v2 v2.0.2 h1:WavlEeLh6HKt+osbmsHDg6/FaM/8Pz9iVUMh9pAsl/o= +github.com/sigstore/timestamp-authority/v2 v2.0.2/go.mod h1:D+wbQg8ASQzKnwBhLo7rIJD+9Zev4Ppqd4myPe8k57E= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg= +github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= +github.com/theupdateframework/go-tuf/v2 v2.3.0 h1:gt3X8xT8qu/HT4w+n1jgv+p7koi5ad8XEkLXXZqG9AA= +github.com/theupdateframework/go-tuf/v2 v2.3.0/go.mod h1:xW8yNvgXRncmovMLvBxKwrKpsOwJZu/8x+aB0KtFcdw= +github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA= +github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g= +github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= +github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY= +github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f h1:Z4NEQ86qFl1mHuCu9gwcE+EYCwDKfXAYXZbdIXyxmEA= +github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= +github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c h1:5a2XDQ2LiAUV+/RjckMyq9sXudfrPSuCY4FuPC1NyAw= +github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c/go.mod h1:g85IafeFJZLxlzZCDRu4JLpfS7HKzR+Hw9qRh3bVzDI= +github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= +github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= +github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= +github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= +go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0/go.mod h1:rjbQTDEPQymPE0YnRQp9/NuPwwtL0sesz/fnqRW/v84= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= +go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM= +google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +tags.cncf.io/container-device-interface v1.1.0 h1:RnxNhxF1JOu6CJUVpetTYvrXHdxw9j9jFYgZpI+anSY= +tags.cncf.io/container-device-interface v1.1.0/go.mod h1:76Oj0Yqp9FwTx/pySDc8Bxjpg+VqXfDb50cKAXVJ34Q= diff --git a/internal/foundry/errors.go b/internal/foundry/errors.go new file mode 100644 index 0000000..c0a86a0 --- /dev/null +++ b/internal/foundry/errors.go @@ -0,0 +1,32 @@ +package foundry + +import ( + "errors" + "fmt" +) + +var ( + ErrorSidWrongFormat = errors.New("Session id wrong format") + ErrorSidNotFound = errors.New("Session id didn't find in response header") + ErrorAuthPassWrong = errors.New("Authentication password is wrong") + ErrorNotAuth = errors.New("Admin is not authenticated") + ErrorIsNotReady = errors.New("Connection is not ready for communication") + + ErrorMsgNotHaveNumber = errors.New("Message doesn't have dataCode and id") + ErrorTimeout = errors.New("Answer has not been received after timeout") + ErrorReadChannel = errors.New("Error when reading from msg channel") + + ListenIsDone = errors.New("Listen for websocket data in foundry is stopped") +) + +type FoundryError struct { + Err error + Type FoundryCode + IsFatal bool +} + +func (e *FoundryError) Error() string { + return fmt.Sprintf("Received from %d: %s", e.Type, e.Err.Error()) +} + +func (e *FoundryError) Unwrap() error { return e.Err } diff --git a/internal/foundry/foundry.go b/internal/foundry/foundry.go new file mode 100644 index 0000000..dbf3829 --- /dev/null +++ b/internal/foundry/foundry.go @@ -0,0 +1,232 @@ +package foundry + +import ( + "errors" + "fmt" + "io" + "net/http" + "strconv" + "strings" +) + +const ( + authPath = "/auth" + licensePath = "/license" + joinPath = "/join" + playersPath = "/players" + setupPath = "/setup" + updatePath = "/update" + + socketPath = "/socket.io" +) + +var WsTypeData = map[string]string{ + authPath: "getAuthData", + licensePath: "getAuthData", + joinPath: "getJoinData", + playersPath: "getPlayersData", + setupPath: "getSetupData", + updatePath: "getUpdateData", +} + +type FoundryCode int + +const ( + WriterCode = FoundryCode(0) + ReaderCode = FoundryCode(1) + + RespSessionData = "0" + RespPingCode = "2" + RespSessionId = "40" + RespCreateSessionCode = "42" + RespDataCode = "43" + + ReqPongCode = "3" + ReqCreateSessionCode = "40" + ReqDataCode = "42" +) + +var RequestCodes = []string{ + RespSessionData, + RespPingCode, + RespSessionId, + RespCreateSessionCode, + RespDataCode, +} + +var CodesRespToReq = map[string]string{ + RespSessionData: ReqCreateSessionCode, + RespPingCode: ReqPongCode, + RespCreateSessionCode: ReqDataCode, + //ReqDataCode: RespCreateSessionCode, +} + +type Foundry struct { + config foundry_config + + sessionID string + isAuth bool + currPage string + + ws *webSocketUtil +} + +type foundry_config struct { + host string +} + +func (foundry *Foundry) checkAuthRespAnswer(r io.Reader, respLength int) error { + authBody := make([]byte, respLength) + + _, err := r.Read(authBody) + if err != nil && err != io.EOF { + return err + } + + if !strings.Contains(string(authBody), "Admin authentication successful") { + return ErrorAuthPassWrong + } + foundry.isAuth = true + return nil +} + +func (foundry *Foundry) getSessionTokenFromHeader(resp http.Header) (bool, error) { + setCookieHeader := resp.Get("Set-Cookie") + if setCookieHeader == "" { + return false, nil + } + + for value := range strings.SplitSeq(setCookieHeader, ";") { + if strings.Contains(value, "session") { + sessionCookie := strings.Split(value, "=") + if len(sessionCookie) != 2 { + return false, ErrorSidWrongFormat + } + + foundry.sessionID = sessionCookie[1] + return true, nil + } + } + + return false, nil +} + +func (foundry *Foundry) Authenticate(password string) error { + if foundry.sessionID == "" { + err := foundry.setUpSessionId() + if err != nil { + return err + } + } + + req, err := createAuthRequest(foundry.config.host, foundry.sessionID, password) + if err != nil { + return err + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + contentLen, err := strconv.Atoi(resp.Header.Get("Content-Length")) + if err != nil { + return err + } + + return foundry.checkAuthRespAnswer(resp.Body, contentLen) +} + +func (foundry *Foundry) StartListen() error { + wsChannels := foundry.ws.InitWsChannels() + defer wsChannels.Close() + go foundry.ListenAndServeWS() + + var err error + for { + select { + case err = <-wsChannels.Err(): + var foundryErr *FoundryError + if errors.As(err, &foundryErr) { + if foundryErr.IsFatal { + foundry.CloseWebSocketConn() + return foundryErr + } + } + case <-wsChannels.Done(): + foundry.CloseWebSocketConn() + return ListenIsDone + } + } +} + +func (foundry *Foundry) CloseWebSocketConn() { + foundry.ws.wsConn.Close() +} + +func (foundry *Foundry) ListenAndServeWS() { + go foundry.ws.ListenWebSocket() + foundry.ws.ServeWebSocket() +} + +func (foundry *Foundry) CreateWSMessageByPage(page string) *wsMessage { + msgToSend := &wsMessage{code: CodesRespToReq[RespCreateSessionCode], id: foundry.ws.currWsId, msgJson: fmt.Sprintf("[\"%s\"]", WsTypeData[page])} + foundry.ws.currWsId++ + + fmt.Printf("Msg: %s\n", msgToSend.toString()) + return msgToSend +} + +func (foundry *Foundry) HandleWebsocketRequest(msg *wsMessage) ([]byte, error) { + return foundry.ws.HandleWebsocketRequest(msg) +} + +func NewFoundry(host string) (*Foundry, error) { + foundry := &Foundry{config: foundry_config{host: host}, isAuth: false, currPage: authPath, ws: NewWebSocketUtil()} + + err := foundry.setUpSessionId() + if err != nil { + return nil, err + } + return foundry, nil +} + +// /** +// * Poll the server to see if a World has become active, and automatically refresh if so. +// * @returns {Promise} +// */ +// async #pollActive() { +// const poll = () => { +// const status = foundry.utils.getRoute("/api/status"); +// this.#activePollState.last = Date.now(); +// this.#activePollState.polling = foundry.utils.fetchJsonWithTimeout(status, {}, { +// timeoutMs: 10000 +// }).catch(() => {}); +// this.#pollActive(); +// }; + +// func (foundry *Foundry) SendPageDataRequest(page string) error { +// msgToSend := &wsComm{code: CodesRespToReq[RespCreateSessionCode], id: foundry.ws.currWsId, msgJson: fmt.Sprintf("[\"%s\"]", WsTypeData[page])} +// foundry.ws.currWsId++ +// log.Printf("send: %s\n", msgToSend.toString()) + +// return foundry.ws.wsConn.WriteMessage(websocket.TextMessage, msgToSend.toByteSlice()) +// } + +// func (foundry *Foundry) setUpWebSocketConnection() error { +// err := foundry.wsConn.WriteMessage(websocket.TextMessage, []byte("40")) +// if err != nil { +// return err +// } +// return nil +// } + +// func (foundry *Foundry) sendPageDataRequest(page string) error { +// msgToSend := &wsComm{code: CodesRespToReq[RespCreateSessionCode], id: foundry.ws.currWsId, msgJson: fmt.Sprintf("[\"%s\"]", WsTypeData[page])} +// foundry.ws.currWsId++ +// log.Printf("send: %s\n", msgToSend.toString()) + +// return foundry.ws.wsConn.WriteMessage(websocket.TextMessage, msgToSend.toByteSlice()) +// } diff --git a/internal/foundry/get_requests.go b/internal/foundry/get_requests.go new file mode 100644 index 0000000..ca2a2de --- /dev/null +++ b/internal/foundry/get_requests.go @@ -0,0 +1,64 @@ +package foundry + +import ( + "encoding/json" + "fmt" + "net/http" + + "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models" +) + +func (foundry *Foundry) setUpSessionId() error { + getResp, err := http.Get(fmt.Sprintf("http://%s%s", foundry.config.host, authPath)) + if err != nil { + return err + } + + _, err = foundry.getSessionTokenFromHeader(getResp.Header) + return err +} + +func (foundry *Foundry) CheckSessionToken(host string) (bool, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("http://%s%s", host, authPath), nil) + if err != nil { + return false, err + } + + header := http.Header{} + header.Set("Cookie", fmt.Sprintf("session=%s", foundry.sessionID)) + header.Set("Connection", "keep-alive") + header.Set("Host", host) + header.Set("Origin", fmt.Sprintf("http://%s", host)) + header.Set("Referer", fmt.Sprintf("http://%s%s", host, authPath)) + + req.Header = header + client := &http.Client{} + + resp, err := client.Do(req) + if err != nil { + return false, err + } + + return foundry.getSessionTokenFromHeader(resp.Header) +} + +func (foundry *Foundry) GetStatus() (*models.Status, error) { + getResp, err := http.Get(fmt.Sprintf("http://%s/api/status", foundry.config.host)) + if err != nil { + return nil, err + } + defer getResp.Body.Close() + + statusByte := make([]byte, 64) + _, err = getResp.Body.Read(statusByte) + if err != nil { + return nil, err + } + + var status models.Status + err = json.Unmarshal(statusByte, &status) + if err != nil { + return nil, err + } + return &status, nil +} diff --git a/internal/foundry/models/data.go b/internal/foundry/models/data.go new file mode 100644 index 0000000..0dc7afc --- /dev/null +++ b/internal/foundry/models/data.go @@ -0,0 +1,44 @@ +package models + +import ( + "encoding/json" +) + +type FoundryState struct { + IsAdmin bool `json:"isAdmin,omitempty"` + IsSetup bool `json:"isSetup,omitempty"` + Languages []Language `json:"languages,omitempty"` + Modules []DataTemplate `json:"modules"` + Release Release `json:"release"` + Systems []DataTemplate `json:"systems,omitempty"` + Worlds []DataTemplate `json:"worlds,omitempty"` + World *DataTemplate `json:"world,omitempty"` + Users Users `json:"users,omitempty"` + Options struct { + Language string `json:"language,omitempty"` + } `json:"options,omitempty"` + + //coreUpdate struct{} + //featuredContent struct{} + //files struct{} + //news struct{} + + //packageWarnings struct{} think about it +} + +func (state FoundryState) GetRelease() Release { + return state.Release +} + +func ParseSetupModel(data []byte) (*FoundryState, error) { + var modelSetup []FoundryState + err := json.Unmarshal(data, &modelSetup) + if err != nil { + return nil, err + } + + if len(modelSetup) > 1 { + return nil, ErrorSetupMoreThanOne + } + return &modelSetup[0], nil +} diff --git a/internal/foundry/models/errors.go b/internal/foundry/models/errors.go new file mode 100644 index 0000000..acaab8c --- /dev/null +++ b/internal/foundry/models/errors.go @@ -0,0 +1,8 @@ +package models + +import "errors" + +var ( + ErrorSetupMoreThanOne = errors.New("Passed more than one setupData") + ErrorSetupNotFound = errors.New("Not found setup data from your request") +) diff --git a/internal/foundry/models/language.go b/internal/foundry/models/language.go new file mode 100644 index 0000000..8c23875 --- /dev/null +++ b/internal/foundry/models/language.go @@ -0,0 +1,13 @@ +package models + +type Language struct { + Id string `json:"id"` + Label string `json:"label"` + Modules []LangModule `json:"modules"` +} + +type LangModule struct { + Id string `json:"id"` + Label string `json:"label"` + Path string `json:"path"` +} diff --git a/internal/foundry/models/module.go b/internal/foundry/models/module.go new file mode 100644 index 0000000..665adfc --- /dev/null +++ b/internal/foundry/models/module.go @@ -0,0 +1,49 @@ +package models + +type Module struct { + Id string + Title string + Description string + Url string + Version string + Compatibility Compatibility + Languages []DataLanguage + Availability int +} + +type Modules []Module + +func (modules Modules) GetById(id int) *Module { + return &modules[id] +} + +func (state *FoundryState) GetModules() Modules { + modulesCopy := make([]Module, 0, 8) + for i := range state.Modules { + module := &(state.Modules[i]) + moduleCopy := Module{ + Id: module.Id, + Title: module.Title, + Description: module.Description, + Compatibility: Compatibility{ + Minimum: module.Compatibility.Minimum, + Maximum: module.Compatibility.Maximum, + }, + Url: module.Url, + Version: module.CoreVersion, + + Availability: module.Availability, + } + for j := range module.Languages { + lang := DataLanguage{ + Lang: module.Languages[j].Lang, + Name: module.Languages[j].Name, + Path: module.Languages[j].Path, + Flags: module.Languages[j].Flags, + } + moduleCopy.Languages = append(moduleCopy.Languages, lang) + } + modulesCopy = append(modulesCopy, moduleCopy) + } + return modulesCopy +} diff --git a/internal/foundry/models/modules/dice-stats.go b/internal/foundry/models/modules/dice-stats.go new file mode 100644 index 0000000..8350cdd --- /dev/null +++ b/internal/foundry/models/modules/dice-stats.go @@ -0,0 +1,59 @@ +package modules + +type DiceStats struct { + PlayerRollData DiceStatsRollData `json:"player_roll_data"` +} + +type DiceStatsRollData struct { + PlayerDice []DicePlayerDice `json:"PLAYER_DICE"` + Username string `json:"USERNAME"` + Userid string `json:"USERID"` + Gm bool `json:"GM"` + PlayerRollInfo DiceRollInfo `json:"PLAYER_ROLL_INFO"` +} + +type DicePlayerDice struct { + Type string `json:"TYPE"` + Max int `json:"MAX"` + TotalRolls int `json:"TOTAL_ROLLS"` + Rolls []int `json:"ROLLS"` + BlindRolls []int `json:"BLIND_ROLLS"` + StreakSize int `json:"STREAK_SIZE"` + StreakInit int `json:"STREAK_INIT"` + StreakIsBlind bool `json:"STREAK_ISBLIND"` + LongestStreak int `json:"LONGEST_STREAK"` + LongestStreakInit int `json:"LONGEST_STREAK_INIT"` + Mean int `json:"MEAN"` + Median int `json:"MEDIAN"` + Mode int `json:"MODE"` + Means []int `json:"MEANS"` + Medians []int `json:"MEDIANS"` + Modes []int `json:"MODES"` + RollCounters []int `json:"ROLL_COUNTERS"` + AtkRolls []int `json:"ATK_ROLLS"` + DmgRolls []int `json:"DMG_ROLLS"` + SavesRolls []int `json:"SAVES_ROLLS"` + SkillsRolls []int `json:"SKILLS_ROLLS"` + AbilityRolls []int `json:"ABILITY_ROLLS"` + UnknownRolls []int `json:"UNKNOWN_ROLLS"` + PerceptionRolls []int `json:"PERCEPTION_ROLLS"` + InitiativeRolls []int `json:"INITIATIVE_ROLLS"` + AtkRollsBlind []int `json:"ATK_ROLLS_BLIND"` + DmgRollsBlind []int `json:"DMG_ROLLS_BLIND"` + SavesRollsBlind []int `json:"SAVES_ROLLS_BLIND"` + SkillsRollsBlind []int `json:"SKILLS_ROLLS_BLIND"` + AbilityRollsBlind []int `json:"ABILITY_ROLLS_BLIND"` + UnknownRollsBlind []int `json:"UNKNOWN_ROLLS_BLIND"` + PerceptionRollsBlind []int `json:"PERCEPTION_ROLLS_BLIND"` + InitiativeRollsBlind []int `json:"INITIATIVE_ROLLS_BLIND"` +} + +type DiceRollInfo struct { + IsRollInfoTracked bool `json:"IS_ROLL_INFO_TRACKED"` + AtkOutcomeTracker []int `json:"ATK_OUTCOME_TRACKER"` + NumUntargetedAtks int `json:"NUM_UNTARGETED_ATKS"` + TotalAttacks int `json:"TOTAL_ATTACKS"` + SaveOutcomeTracker []int `json:"SAVE_OUTCOME_TRACKER"` + NumUntargetedSaves int `json:"NUM_UNTARGETED_SAVES"` + TotalSaves int `json:"TOTAL_SAVES"` +} diff --git a/internal/foundry/models/modules/pf2e.go b/internal/foundry/models/modules/pf2e.go new file mode 100644 index 0000000..76cdd2f --- /dev/null +++ b/internal/foundry/models/modules/pf2e.go @@ -0,0 +1,12 @@ +package modules + +type Pf2eModule struct { + settings Pf2eSettings +} + +type Pf2eSettings struct { + showEffectPanel bool + showCheckDialogs bool + showDamageDialogs bool + monochromeDarkvision bool +} diff --git a/internal/foundry/models/release.go b/internal/foundry/models/release.go new file mode 100644 index 0000000..416a424 --- /dev/null +++ b/internal/foundry/models/release.go @@ -0,0 +1,11 @@ +package models + +type Release struct { + Generation int `json:"generation"` + Channel string `json:"channel"` + Suffix string `json:"suffix"` + Build int `json:"build"` + Node_version int `json:"node_version"` + Time int64 `json:"time"` + flags struct{} `json:"-"` +} diff --git a/internal/foundry/models/status.go b/internal/foundry/models/status.go new file mode 100644 index 0000000..c3d957f --- /dev/null +++ b/internal/foundry/models/status.go @@ -0,0 +1,11 @@ +package models + +type Status struct { + Active bool `json:"active"` + Version string `json:"version"` + World string `json:"world,omitempty"` + System string `json:"system,omitempty"` + SystemVersion string `json:"systemVersion,omitempty"` + Users int `json:"users,omitempty"` + Uptime int64 `json:"uptime,omitempty"` +} diff --git a/internal/foundry/models/system.go b/internal/foundry/models/system.go new file mode 100644 index 0000000..e4c241c --- /dev/null +++ b/internal/foundry/models/system.go @@ -0,0 +1,36 @@ +package models + +type System struct { + Id string + Title string + Description string + Url string + Compatibility Compatibility + Download string +} + +type Systems []System + +func (systems Systems) GetById(id int) *System { + return &systems[id] +} + +func (state *FoundryState) GetSystems() Systems { + systemsCopy := make([]System, 0, 8) + for i := range state.Systems { + system := &(state.Systems[i]) + systemCopy := System{ + Id: system.Id, + Title: system.Title, + Description: system.Description, + Url: system.Url, + Compatibility: Compatibility{ + Minimum: system.Compatibility.Minimum, + Maximum: system.Compatibility.Maximum, + }, + Download: system.Download, + } + systemsCopy = append(systemsCopy, systemCopy) + } + return systemsCopy +} diff --git a/internal/foundry/models/template.go b/internal/foundry/models/template.go new file mode 100644 index 0000000..d33db31 --- /dev/null +++ b/internal/foundry/models/template.go @@ -0,0 +1,132 @@ +package models + +import "time" + +type DataTemplate struct { + Id string `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Authors []DataAuthor `json:"authors,omitempty"` + Url string `json:"url,omitempty"` + Flags Flags `json:"flags,omitempty"` + License string `json:"license,omitempty"` + Readme string `json:"readme,omitempty"` + Bugs string `json:"bugs,omitempty"` + Changelog string `json:"changelog,omitempty"` + Media []DataMedia `json:"media,omitempty"` + Version string `json:"version,omitempty"` + Compatibility Compatibility `json:"compatibility,omitempty"` + Scripts []string `json:"scripts"` + Esmodules []string `json:"esmodules"` + Styles []struct { + Src string `json:"src,omitempty"` + } `json:"styles,omitempty"` + Languages []DataLanguage `json:"languages,omitempty"` + Packs []DataPack `json:"packs,omitempty"` + PackFolder []DataPackFolder `json:"packFolder,omitempty"` + Relationships Relationship `json:"relationships,omitempty"` + Socket bool `json:"socket,omitempty"` + Manifest string `json:"manifest,omitempty"` + Download string `json:"download,omitempty"` + Protected bool `json:"protected,omitempty"` + Exclusive bool `json:"exclusive,omitempty"` + PersistentStorage bool `json:"persistentStorage,omitempty"` + Availability int `json:"availability,omitempty"` + Locked bool `json:"locked,omitempty"` + Owned bool `json:"owned,omitempty"` + HasStorage bool `json:"hasStorage,omitempty"` + + //module + CoreTranslation bool `json:"coreTranslation,omitempty"` + Library bool `json:"library,omitempty"` + + //system + Background string `json:"background,omitempty"` + Grid DataGrid `json:"grid,omitempty"` + PrimaryTokenAttribute string `json:"primaryTokenAttribute,omitempty"` + + //world + System string `json:"system,omitempty"` + JoinTheme string `json:"joinTheme,omitempty"` + CoreVersion string `json:"coreVersion,omitempty"` + SystemVersion string `json:"systemVersion,omitempty"` + LastPlayed string `json:"lastPlayed,omitempty"` + PlayTime int64 `json:"playTime,omitempty"` + NextSession time.Time `json:"nextSession,omitempty"` +} + +type DataAuthor struct { + Name string `json:"name,omitempty"` + Url string `json:"url,omitempty"` + Discord string `json:"discord,omitempty"` + flags struct{} `json:"-"` +} + +type DataMedia struct { + Type string `json:"type,omitempty"` + Url string `json:"url,omitempty"` + Loop bool `json:"loop,omitempty"` + flags struct{} `json:"-"` +} + +type DataLanguage struct { + Lang string `json:"lang,omitempty"` + Name string `json:"name,omitempty"` + Path string `json:"path,omitempty"` + Flags struct{} `json:"-"` +} + +type DataPack struct { + Name string `json:"name,omitempty"` + Label string `json:"label,omitempty"` + Banner string `json:"banner,omitempty"` + Path string `json:"path,omitempty"` + Type string `json:"type,omitempty"` + System string `json:"system,omitempty"` + Ownership map[string]string `json:"ownership,omitempty"` + flags struct{} `json:"-"` +} + +type DataPackFolder struct { + Name string `json:"name,omitempty"` + Sorting string `json:"sorting,omitempty"` + Color string `json:"color,omitempty"` + Packs []string `json:"packs,omitempty"` + Folders []DataPackFolder `json:"folders,omitempty"` +} + +type DataGrid struct { + Type int `json:"type,omitempty"` + Distance int `json:"distance,omitempty"` + Units string `json:"units,omitempty"` + Diagonals int `json:"diagonals,omitempty"` +} + +type Compatibility struct { + Minimum string `json:"minimum,omitempty"` + Verified string `json:"verified,omitempty"` + Maximum string `json:"maximum,omitempty"` +} + +type Flags struct { + HotReload FlagsHotReload `json:"hotReload"` + Styles []string `json:"styles"` +} + +type FlagsHotReload struct { + Enabled bool `json:"enabled"` + Extensions []string `json:"extensions"` + Paths []string `json:"paths"` +} + +type Relationship struct { + systems []struct{} `json:"-"` + requires []struct{} `json:"-"` + Recommends []struct { + Id string `json:"id"` + Type string `json:"type"` + compatibility struct{} `json:"-"` + } `json:"recommends"` + conflicts []struct{} `json:"-"` + flags struct{} `json:"-"` +} diff --git a/internal/foundry/models/users.go b/internal/foundry/models/users.go new file mode 100644 index 0000000..54ee34d --- /dev/null +++ b/internal/foundry/models/users.go @@ -0,0 +1,45 @@ +package models + +import "gitea.local.lab/Lbenedar/foundry_helper_service/internal/foundry/models/modules" + +type User struct { + Name string `json:"name,omitempty"` + Role int `json:"role,omitempty"` + Id string `json:"_id,omitempty"` + avatar struct{} `json:"-"` + Character string `json:"character,omitempty"` + Color string `json:"color,omitempty"` + Pronouns string `json:"pronouns,omitempty"` + Hotbar map[string]string `json:"hotbar,omitempty"` + permissions struct{} `json:"-"` + Flags UserFlags `json:"flags,omitempty"` + Stats UserStats `json:"_stats,omitempty"` +} + +type UserFlags struct { + World map[string]bool `json:"world,omitempty"` + Pf2e modules.Pf2eModule `json:"pf2e,omitempty"` + DiceStats modules.DiceStats `json:"diceStats,omitempty"` +} + +type UserStats struct { + compendiumSource struct{} `json:"-"` + duplicateSource struct{} `json:"-"` + exportSource struct{} `json:"-"` + CoreVersion string `json:"coreVersion,omitempty"` + SystemId string `json:"systemId,omitempty"` + SystemVersion string `json:"systemVersion,omitempty"` + CreatedTime int64 `json:"createdTime,omitempty"` + ModifiedTime int64 `json:"modifiedTime,omitempty"` + LastModifiedBy string `json:"lastModifiedBy,omitempty"` +} + +type Users []User + +func (users Users) GetById(id int) *User { + return &users[id] +} + +func (state *FoundryState) GetUsers() *Users { + return &state.Users +} diff --git a/internal/foundry/models/world.go b/internal/foundry/models/world.go new file mode 100644 index 0000000..649a594 --- /dev/null +++ b/internal/foundry/models/world.go @@ -0,0 +1,88 @@ +package models + +import ( + "time" +) + +type World struct { + Id string + Title string + Description string + Compatibility Compatibility + System string + CoreVersion string + SystemVersion string + LastPlayed string + PlayTime int64 + NextSession time.Time +} + +type Worlds []World + +func (worlds Worlds) GetById(id int) *World { + return &worlds[id] +} + +func (state *FoundryState) GetWorld() World { + return World{ + Id: state.World.Id, + Title: state.World.Title, + Description: state.World.Description, + Compatibility: Compatibility{ + Minimum: state.World.Compatibility.Minimum, + Maximum: state.World.Compatibility.Maximum, + }, + System: state.World.System, + CoreVersion: state.World.CoreVersion, + SystemVersion: state.World.SystemVersion, + LastPlayed: state.World.LastPlayed, + PlayTime: state.World.PlayTime, + NextSession: state.World.NextSession, + } +} + +func (state *FoundryState) GetWorlds() Worlds { + worldsCopy := make([]World, 0, 8) + for i := range state.Worlds { + world := &(state.Worlds[i]) + worldCopy := World{ + Id: world.Id, + Title: world.Title, + Description: world.Description, + Compatibility: Compatibility{ + Minimum: world.Compatibility.Minimum, + Maximum: world.Compatibility.Maximum, + }, + System: world.System, + CoreVersion: world.CoreVersion, + SystemVersion: world.SystemVersion, + LastPlayed: world.LastPlayed, + PlayTime: world.PlayTime, + NextSession: world.NextSession, + } + worldsCopy = append(worldsCopy, worldCopy) + } + return worldsCopy +} + +func (worlds Worlds) GetSessionTime(worldName string) (*time.Time, error) { + if worldName == "" && len(worlds) != 1 { + return nil, ErrorSetupNotFound + } + + if worldName == "" { + return &worlds.GetById(0).NextSession, nil + } else { + for i := range worlds { + if worlds[i].Id == worldName { + return &worlds.GetById(0).NextSession, nil + } + } + } + + return nil, ErrorSetupNotFound +} + +func (world World) GetSessionTime() *time.Time { + return &world.NextSession +} diff --git a/internal/foundry/post_requests.go b/internal/foundry/post_requests.go new file mode 100644 index 0000000..b864a2c --- /dev/null +++ b/internal/foundry/post_requests.go @@ -0,0 +1,32 @@ +package foundry + +import ( + "bytes" + "fmt" + "net/http" + "net/url" +) + +func createAuthRequest(host, sessionID, password string) (*http.Request, error) { + authData := url.Values{} + authData.Add("adminPassword", password) + authData.Add("action", "adminAuth") + + req, err := http.NewRequest("POST", fmt.Sprintf("http://%s%s", host, authPath), bytes.NewBuffer([]byte(authData.Encode()))) + if err != nil { + return nil, err + } + + header := http.Header{} + header.Set("Cookie", fmt.Sprintf("session=%s", sessionID)) + header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") + header.Set("Connection", "keep-alive") + header.Set("Content-Length", fmt.Sprintf("%d", len(authData.Encode()))) + header.Set("Content-Type", "application/x-www-form-urlencoded") + header.Set("Host", host) + header.Set("Origin", fmt.Sprintf("http://%s", host)) + header.Set("Referer", fmt.Sprintf("http://%s%s", host, authPath)) + + req.Header = header + return req, nil +} diff --git a/internal/foundry/websocket.go b/internal/foundry/websocket.go new file mode 100644 index 0000000..3f4f591 --- /dev/null +++ b/internal/foundry/websocket.go @@ -0,0 +1,236 @@ +package foundry + +import ( + "context" + "fmt" + "log" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + "time" + + "github.com/gorilla/websocket" +) + +type webSocketUtil struct { + wsConn *websocket.Conn + currWsId int + isReadReady bool + + msgMap map[int](chan []byte) + + mutex sync.Mutex + channels Channels +} + +func parseCode(msg *string) string { + for j := range RequestCodes { + if !strings.HasPrefix(*msg, RequestCodes[j]) { + continue + } + return RequestCodes[j] + } + return "" +} + +func parseId(msg *string, start int) (int, int) { + msgLen := len(*msg) + j := start + for ; j < msgLen; j++ { + if (*msg)[j] < '0' || (*msg)[j] > '9' { + break + } + } + if j >= msgLen || j <= 0 { + return 0, 0 + } + + msgId, err := strconv.Atoi((*msg)[start:j]) + if err != nil { + return 0, 0 + } + return msgId, j +} + +func parseWsRespMessage(msg string) (*wsMessage, error) { + data := &wsMessage{} + + data.code = parseCode(&msg) + if data.code != RespDataCode { + return data, nil + } + + i := len(data.code) + if i == 0 { + return nil, ErrorMsgNotHaveNumber + } + + data.id, i = parseId(&msg, i) + if i == 0 { + return nil, ErrorMsgNotHaveNumber + } + + data.msgJson = msg[i:] + return data, nil +} + +func ConnectToWebSocket(foundry *Foundry) error { + if !foundry.isAuth { + return ErrorNotAuth + } + + query := url.Values{} + query.Add("session", foundry.sessionID) + query.Add("EIO", "4") + query.Add("transport", "websocket") + + u := url.URL{Scheme: "ws", Host: foundry.config.host, Path: fmt.Sprintf("%s/", socketPath), RawQuery: query.Encode()} + + wsHeader := http.Header{} + wsHeader.Set("Cookie", fmt.Sprintf("session=%s", foundry.sessionID)) + + wsConn, _, err := websocket.DefaultDialer.Dial(u.String(), wsHeader) + if err != nil { + return err + } + + foundry.ws.wsConn = wsConn + foundry.ws.currWsId = 0 + return nil +} + +// TODO: Lookup timeout +func (ws *webSocketUtil) ReceiveMessage(id int) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + for { + select { + case msg, ok := <-ws.msgMap[id]: + if !ok { + time.Sleep(5 * time.Microsecond) + continue + } + ws.closeMsgChannel(id, 0) + return msg, nil + case <-ctx.Done(): + err := ctx.Err() + if err != nil { + return nil, err + } + return nil, ErrorTimeout + default: + time.Sleep(5 * time.Microsecond) + } + } +} + +func (ws *webSocketUtil) closeMsgChannel(id int, timeout time.Duration) bool { + time.Sleep(timeout) + + ws.mutex.Lock() + defer ws.mutex.Unlock() + + ok := true + if _, ok = ws.msgMap[id]; !ok { + return false + } + select { + case _, ok = <-ws.msgMap[id]: + if ok { + close(ws.msgMap[id]) + delete(ws.msgMap, id) + } + default: + close(ws.msgMap[id]) + delete(ws.msgMap, id) + } + fmt.Printf("Channel has been closed(id=%d, timeout=%d)\n", id, timeout) + return ok +} + +func (ws *webSocketUtil) HandleWebsocketRequest(msg *wsMessage) ([]byte, error) { + if !ws.IsReadReady() { + return nil, ErrorIsNotReady + } + + err := ws.wsConn.WriteMessage(websocket.TextMessage, msg.toByteSlice()) + if err != nil { + return nil, err + } + + data, err := ws.ReceiveMessage(msg.id) + if err != nil { + return nil, err + } + return data, nil +} + +func (ws *webSocketUtil) sendOnlyCodeRequest(code string) error { + msgToSend := []byte(CodesRespToReq[code]) + log.Printf("send: %s\n", msgToSend) + + return ws.wsConn.WriteMessage(websocket.TextMessage, msgToSend) +} + +func (ws *webSocketUtil) ListenWebSocket() { + for { + _, message, err := ws.wsConn.ReadMessage() + if err != nil { + ws.channels.Err() <- &FoundryError{Type: ReaderCode, Err: err, IsFatal: true} + return + } + data, err := parseWsRespMessage(string(message)) + if err != nil { + ws.channels.Err() <- &FoundryError{Type: ReaderCode, Err: err} + continue + } + ws.channels.Msg() <- data + } +} + +func (ws *webSocketUtil) ServeWebSocket() { + for { + select { + case message := <-ws.channels.Msg(): + log.Println("recv code:", message.code) + + switch message.code { + case RespPingCode, RespSessionData: + err := ws.sendOnlyCodeRequest(message.code) + if err != nil { + ws.isReadReady = false + ws.channels.err <- &FoundryError{Type: WriterCode, Err: err} + continue + } + case RespCreateSessionCode: + ws.isReadReady = true + case RespDataCode: + ws.msgMap[message.id] = make(chan []byte, 1) + ws.msgMap[message.id] <- []byte(message.msgJson) + go ws.closeMsgChannel(message.id, 5*time.Second) + default: + } + case <-ws.channels.Done(): + return + } + } +} + +func (ws *webSocketUtil) InitWsChannels() *Channels { + ws.channels.done = make(chan struct{}) + ws.channels.err = make(chan error, 10) + ws.channels.msg = make(chan *wsMessage, 10) + + return &ws.channels +} + +func NewWebSocketUtil() *webSocketUtil { + return &webSocketUtil{currWsId: 0, isReadReady: false, msgMap: make(map[int]chan []byte), channels: Channels{}} +} + +func (ws *webSocketUtil) IsReadReady() bool { + return ws.isReadReady +} diff --git a/internal/foundry/ws_channels.go b/internal/foundry/ws_channels.go new file mode 100644 index 0000000..d2adbe4 --- /dev/null +++ b/internal/foundry/ws_channels.go @@ -0,0 +1,25 @@ +package foundry + +type Channels struct { + done chan struct{} + msg chan *wsMessage + err chan error +} + +func (channels Channels) Err() chan error { + return channels.err +} + +func (channels Channels) Msg() chan *wsMessage { + return channels.msg +} + +func (channels Channels) Done() chan struct{} { + return channels.done +} + +func (channels *Channels) Close() { + close(channels.done) + close(channels.err) + close(channels.msg) +} diff --git a/internal/foundry/ws_message.go b/internal/foundry/ws_message.go new file mode 100644 index 0000000..40321ab --- /dev/null +++ b/internal/foundry/ws_message.go @@ -0,0 +1,17 @@ +package foundry + +import "fmt" + +type wsMessage struct { + code string + id int + msgJson string +} + +func (w wsMessage) toString() string { + return fmt.Sprintf("%s%d%s", w.code, w.id, w.msgJson) +} + +func (w wsMessage) toByteSlice() []byte { + return fmt.Appendf([]byte{}, "%s%d%s", w.code, w.id, w.msgJson) +}