diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..e7e5c07 --- /dev/null +++ b/.air.toml @@ -0,0 +1,22 @@ +# Working directory +root = "." +tmp_dir = ".tmp" + +[build] +# Just plain old shell command. You could use `make` as well. +cmd = "make build" +# Binary file yields from `cmd`. +full_bin = "build/sentinel hub start -c ./config.yaml" +# This log file places in your tmp_dir. +log = "air_errors.log" +# Watch these filename extensions. +include_ext = ["go"] +# Ignore these filename extensions or directories. +exclude_dir = [".tmp", "sql", "frontend", "data", "schema", "scripts"] +# It's not necessary to trigger build each time file changes if it's too frequent. +delay = 1000 # ms +send_interrupt = true +kill_delay = 500000000 + +[log] +time = true diff --git a/.gitignore b/.gitignore index 6b8e310..c829d8c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,9 @@ go.work.sum # .vscode/ data +data-agent build config.yaml +config-agent.yaml dist +.tmp diff --git a/Dockerfile b/Dockerfile index 0b6fc8c..4eddaa0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ COPY frontend/ ./ RUN pnpm run build # Backend build stage -FROM golang:1.25.0-alpine AS backend-builder +FROM golang:1.25.1-alpine AS backend-builder # Define build arguments for version, commit, and date. ARG VERSION="unknown" @@ -54,4 +54,4 @@ WORKDIR /root/ COPY --from=backend-builder /app/bin/sentinel . # Run the binary -CMD ["./sentinel", "start"] +ENTRYPOINT ["./sentinel"] diff --git a/Makefile b/Makefile index a6c5b50..b94b3a9 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,11 @@ # Variables BINARY_NAME=sentinel -MAIN_PATH=./cmd/sentinel +SENTINEL_PATH=./cmd/sentinel BUILD_DIR=./build VERSION?=dev LDFLAGS=-ldflags="-w -s -X main.version=${VERSION}" +MIGRATIONS_DIR = ./sql/migrations/ # Default target help: ## Show this help message @@ -17,36 +18,44 @@ help: ## Show this help message @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST) # Development -dev: ## Run in development mode with auto-reload - go run $(MAIN_PATH) start +hub: ## Run in development mode with auto-reload + go run $(SENTINEL_PATH) hub start -c ./config.yaml + +agent: ## Run in development mode with auto-reload + go run $(SENTINEL_PATH) agent start -c ./config-agent.yaml + +migrateup: + go run $(SENTINEL_PATH) migrations up -db-path ./data/hub/sqlite/db.sqlite + +migratedown: + go run $(SENTINEL_PATH) migrations down -db-path ./data/hub/sqlite/db.sqlite + +air: + air -c .air.toml run: build ## Build and run the application ./$(BUILD_DIR)/$(BINARY_NAME) -runtcpserver: - go run ./cmd/tcpserver - -rungrpcserver: - go run ./cmd/grpcserver +runtestservers: + go run ./cmd/testserver -http -grpc -tcp front: cd frontend && pnpm dev # Build targets -build: deps ## Build the application - @mkdir -p $(BUILD_DIR) - go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) $(MAIN_PATH) +build: + go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) $(SENTINEL_PATH) build-linux: deps ## Build for Linux @mkdir -p $(BUILD_DIR) - GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux $(MAIN_PATH) + GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux $(SENTINEL_PATH) build-all: deps ## Build for all platforms @mkdir -p $(BUILD_DIR) - GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 $(MAIN_PATH) - GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-amd64 $(MAIN_PATH) - GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe $(MAIN_PATH) + GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 $(SENTINEL_PATH) + GOOS=darwin GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-amd64 $(SENTINEL_PATH) + GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe $(SENTINEL_PATH) # Dependencies deps: ## Download dependencies @@ -115,9 +124,11 @@ clean: ## Clean build artifacts rm -f coverage.out coverage.html docker-compose down --volumes --remove-orphans || true -# Database -init-db: ## Initialize database directory - mkdir -p data +# db-create-migration: +# migrate create -ext sql -format unix -dir "$(MIGRATIONS_DIR)" $(filter-out $@,$(MAKECMDGOALS)) + +db-create-migration: + go run ./cmd/sentinel migrations create -p ./sql/migrations -name $(filter-out $@,$(MAKECMDGOALS)) # Configuration init-config: ## Copy example configuration @@ -133,3 +144,26 @@ genswagger: rm -rf ./docs/* swag fmt -d ./internal/web swag init -o docs/docsv1 --dir ./internal/web -g handlers.go --parseDependency + +genenvs: + go run ./cmd/sentinel config genenvs + +gensql: + pgxgen crud + pgxgen sqlc generate + +genproto: ## Generate protobuf code + buf lint + rm -rf ./internal/hub/hubserver/api/* + rm -rf frontend/src/api/gen/* + buf generate + rm -rf frontend/src/api/gen/sentinel/hub + +grpcui-hub: + grpcui --plaintext localhost:8080 + +grpcui-server: + grpcui --plaintext localhost:8080 + +%: + @: diff --git a/README.md b/README.md index 4b2c887..d553b98 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,6 @@ monitoring: default_timeout: 10s default_retries: 5 -database: - path: "./data/db.sqlite" - notifications: enabled: true urls: @@ -218,7 +215,7 @@ The gRPC monitor supports three types of checks: ## Notification Setup -Sentinel uses [Shoutrrr](https://github.com/containrrr/shoutrrr) for notifications, which supports multiple providers +Sentinel uses [Shoutrrr](https://github.com/nicholas-fedor/shoutrrr) for notifications, which supports multiple providers You can configure multiple notification providers simultaneously. If one provider fails, notifications will still be sent to the others: diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..8f73e9d --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,19 @@ +version: v2 +managed: + enabled: true + override: + - file_option: go_package_prefix + value: github.com/sxwebdev/sentinel/internal/hub/hubserver/api +plugins: + - remote: buf.build/protocolbuffers/go:v1.36.9 + out: internal/hub/hubserver/api + opt: paths=source_relative + - remote: buf.build/connectrpc/go:v1.18.1 + out: internal/hub/hubserver/api + opt: paths=source_relative + - remote: buf.build/bufbuild/es:v2.9.0 + out: frontend/src/api/gen + opt: target=ts + - remote: buf.build/connectrpc/query-es:v2.2.0 + out: frontend/src/api/gen + opt: target=ts diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..ff6c1e1 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,20 @@ +version: v2 +modules: + - path: schema +lint: + use: + - COMMENT_SERVICE + - STANDARD + except: + - FIELD_NOT_REQUIRED + - PACKAGE_NO_IMPORT_CYCLE + disallow_comment_ignores: true + rpc_allow_google_protobuf_empty_requests: true + rpc_allow_google_protobuf_empty_responses: true + +breaking: + use: + - FILE + except: + - EXTENSION_NO_DELETE + - FIELD_SAME_DEFAULT diff --git a/cmd/grpcserver/main.go b/cmd/grpcserver/main.go deleted file mode 100644 index d022b9b..0000000 --- a/cmd/grpcserver/main.go +++ /dev/null @@ -1,96 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "net" - "os" - "os/signal" - "syscall" - - "google.golang.org/grpc" - "google.golang.org/grpc/health" - "google.golang.org/grpc/health/grpc_health_v1" - "google.golang.org/grpc/reflection" -) - -var port = flag.Int("port", 50051, "The server port") - -func main() { - flag.Parse() - - if err := run(); err != nil { - log.Fatalf("Failed to run server: %v", err) - } -} - -func run() error { - addr := fmt.Sprintf("localhost:%d", *port) - log.Printf("Starting gRPC server on %s\n", addr) - - lis, err := net.Listen("tcp", addr) - if err != nil { - return fmt.Errorf("failed to listen: %w", err) - } - - // Create gRPC server - grpcServer := grpc.NewServer() - - // Create and register health server - healthServer := health.NewServer() - grpc_health_v1.RegisterHealthServer(grpcServer, healthServer) - - // Register gRPC reflection service for debugging - reflection.Register(grpcServer) - - // Set initial serving status - healthServer.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING) - healthServer.SetServingStatus("health", grpc_health_v1.HealthCheckResponse_SERVING) - healthServer.SetServingStatus("test-service", grpc_health_v1.HealthCheckResponse_SERVING) - - // Start server in a goroutine - serverErr := make(chan error, 1) - go func() { - if err := grpcServer.Serve(lis); err != nil { - serverErr <- fmt.Errorf("failed to serve: %w", err) - } - }() - - // Wait for interrupt signal or server error - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - - select { - case <-sigChan: - // Graceful shutdown - case err := <-serverErr: - return err - } - - // Graceful shutdown - grpcServer.GracefulStop() - - return nil -} - -// simulateServiceStatusChanges simulates service status changes for testing -// func simulateServiceStatusChanges(healthServer *health.Server) { -// ticker := time.NewTicker(30 * time.Second) -// defer ticker.Stop() - -// status := grpc_health_v1.HealthCheckResponse_SERVING - -// for range ticker.C { -// // Toggle service status for testing -// if status == grpc_health_v1.HealthCheckResponse_SERVING { -// status = grpc_health_v1.HealthCheckResponse_NOT_SERVING -// log.Println("Setting test-service status to NOT_SERVING") -// } else { -// status = grpc_health_v1.HealthCheckResponse_SERVING -// log.Println("Setting test-service status to SERVING") -// } - -// healthServer.SetServingStatus("test-service", status) -// } -// } diff --git a/cmd/sentinel/agent.go b/cmd/sentinel/agent.go new file mode 100644 index 0000000..03cb010 --- /dev/null +++ b/cmd/sentinel/agent.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/sxwebdev/sentinel/internal/agent" + "github.com/sxwebdev/sentinel/internal/config" + "github.com/sxwebdev/sentinel/internal/models" + "github.com/tkcrm/mx/launcher" + "github.com/tkcrm/mx/logger" + "github.com/tkcrm/mx/service" + "github.com/tkcrm/mx/service/pingpong" + "github.com/urfave/cli/v3" +) + +func agentCMD() *cli.Command { + return &cli.Command{ + Name: "agent", + Usage: "agent commands", + Commands: []*cli.Command{ + { + Name: "start", + Usage: "start the agent", + Flags: []cli.Flag{cfgPathsFlag()}, + Action: func(ctx context.Context, cl *cli.Command) error { + conf := new(config.ConfigAgent) + if err := config.Load(conf, envAgentPrefix, cl.StringSlice("config")); err != nil { + return fmt.Errorf("failed to load config: %w", err) + } + + loggerOpts := append(defaultLoggerOpts(), logger.WithConfig(conf.Log)) + + l := logger.NewExtended(loggerOpts...) + defer func() { + _ = l.Sync() + }() + + // init launcher + ln := launcher.New( + launcher.WithVersion(version), + launcher.WithName(appName), + launcher.WithLogger(l), + launcher.WithContext(ctx), + launcher.WithRunnerServicesSequence(launcher.RunnerServicesSequenceFifo), + launcher.WithOpsConfig(conf.Ops), + launcher.WithAppStartStopLog(true), + ) + + // check if exists data dir, if not create it + if _, err := os.Stat(conf.AgentDataDir()); os.IsNotExist(err) { + l.Infof("creating data directory in %s", conf.AgentDataDir()) + if err := os.MkdirAll(conf.AgentDataDir(), 0o700); err != nil { + return fmt.Errorf("failed to create data dir: %w", err) + } + } + + // get system info + systemInfo := models.GetSystemInfo(version, commitHash, buildDate) + + // init receiver + // rc := receiver.New() + + // init agent service + ag, err := agent.New(ctx, l, conf, *systemInfo) + if err != nil { + return fmt.Errorf("failed to init agent: %w", err) + } + + // register services + ln.ServicesRunner().Register( + service.New(service.WithService(pingpong.New(l))), + // service.New(service.WithService(rc)), + service.New(service.WithService(ag)), + ) + + return ln.Run() + }, + }, + }, + } +} diff --git a/cmd/sentinel/config.go b/cmd/sentinel/config.go index 5212652..b628e32 100644 --- a/cmd/sentinel/config.go +++ b/cmd/sentinel/config.go @@ -8,15 +8,15 @@ import ( "github.com/goccy/go-yaml" "github.com/sxwebdev/sentinel/internal/config" + "github.com/sxwebdev/xconfig" "github.com/urfave/cli/v3" ) -func cfgPathsFlag() *cli.StringFlag { - return &cli.StringFlag{ +func cfgPathsFlag() *cli.StringSliceFlag { + return &cli.StringSliceFlag{ Name: "config", Aliases: []string{"c"}, - Value: "config.yaml", - Usage: "allows you to use your own paths to configuration files. by default it uses config.yaml", + Usage: "allows you to use your own paths to configuration files", } } @@ -29,23 +29,40 @@ func configCMD() *cli.Command { Name: "genenvs", Usage: "generate config yaml template", Action: func(_ context.Context, _ *cli.Command) error { - conf := new(config.Config) - - conf, err := config.Load("") - if err != nil { - return fmt.Errorf("failed to load config: %w", err) + data := []struct { + fileName string + envPrefix string + conf any + }{ + { + fileName: "config.template.yaml", + envPrefix: envHubPrefix, + conf: new(config.ConfigHub), + }, + { + fileName: "config-agent.template.yaml", + envPrefix: envAgentPrefix, + conf: new(config.ConfigAgent), + }, } - buf := bytes.NewBuffer(nil) - enc := yaml.NewEncoder(buf, yaml.Indent(2)) - defer enc.Close() + for _, d := range data { + _, err := xconfig.Load(d.conf, xconfig.WithEnvPrefix(d.envPrefix)) + if err != nil { + return fmt.Errorf("failed to generate markdown: %w", err) + } - if err := enc.Encode(conf); err != nil { - return fmt.Errorf("failed to encode yaml: %w", err) - } + buf := bytes.NewBuffer(nil) + enc := yaml.NewEncoder(buf, yaml.Indent(2)) + defer enc.Close() + + if err := enc.Encode(d.conf); err != nil { + return fmt.Errorf("failed to encode yaml: %w", err) + } - if err := os.WriteFile("config.template.yaml", buf.Bytes(), 0o600); err != nil { - return fmt.Errorf("failed to write file: %w", err) + if err := os.WriteFile(d.fileName, buf.Bytes(), 0o600); err != nil { + return fmt.Errorf("failed to write file: %w", err) + } } return nil diff --git a/cmd/sentinel/hub.go b/cmd/sentinel/hub.go new file mode 100644 index 0000000..c08337d --- /dev/null +++ b/cmd/sentinel/hub.go @@ -0,0 +1,224 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/sxwebdev/sentinel/internal/alertresolver" + "github.com/sxwebdev/sentinel/internal/config" + "github.com/sxwebdev/sentinel/internal/datamigrations" + "github.com/sxwebdev/sentinel/internal/dispatcher" + "github.com/sxwebdev/sentinel/internal/models" + "github.com/sxwebdev/sentinel/internal/receiver" + "github.com/sxwebdev/sentinel/internal/scheduler" + "github.com/sxwebdev/sentinel/internal/servers" + "github.com/sxwebdev/sentinel/internal/services/baseservices" + "github.com/sxwebdev/sentinel/internal/store" + "github.com/sxwebdev/sentinel/internal/store/badgerdb" + updater "github.com/sxwebdev/sentinel/internal/updated" + "github.com/sxwebdev/sentinel/internal/utils" + "github.com/sxwebdev/sentinel/pkg/locker" + "github.com/sxwebdev/sentinel/pkg/migrator" + "github.com/sxwebdev/sentinel/pkg/sqlite" + "github.com/sxwebdev/sentinel/sql" + "github.com/sxwebdev/tokenmanager" + "github.com/tkcrm/mx/launcher" + "github.com/tkcrm/mx/logger" + "github.com/tkcrm/mx/service" + "github.com/tkcrm/mx/service/pingpong" + "github.com/urfave/cli/v3" +) + +func hubStartCMD() *cli.Command { + return &cli.Command{ + Name: "hub", + Usage: "hub commands", + Commands: []*cli.Command{ + { + Name: "start", + Usage: "start the server", + Flags: []cli.Flag{cfgPathsFlag()}, + Action: func(ctx context.Context, cl *cli.Command) error { + conf := new(config.ConfigHub) + if err := config.Load(conf, envHubPrefix, cl.StringSlice("config")); err != nil { + return fmt.Errorf("failed to load config: %w", err) + } + + loggerOpts := append(defaultLoggerOpts(), logger.WithConfig(conf.Log)) + + l := logger.NewExtended(loggerOpts...) + defer func() { + _ = l.Sync() + }() + + // init launcher + ln := launcher.New( + launcher.WithVersion(version), + launcher.WithName(appName), + launcher.WithLogger(l), + launcher.WithContext(ctx), + launcher.WithRunnerServicesSequence(launcher.RunnerServicesSequenceLifo), + launcher.WithOpsConfig(conf.Ops), + launcher.WithAppStartStopLog(true), + ) + + // check if exists data dir, if not create it + if _, err := os.Stat(conf.HubDataDir()); os.IsNotExist(err) { + l.Infof("creating data directory in %s", conf.HubDataDir()) + if err := os.MkdirAll(conf.HubDataDir(), 0o700); err != nil { + return fmt.Errorf("failed to create data dir: %w", err) + } + } + + var authConfig config.AuthConfig + + // get file datadir/hub/secrets.json + // if not exists create it with generated values + secretsFilePath := filepath.Join(conf.HubDataDir(), "secrets.json") + if _, err := os.Stat(secretsFilePath); os.IsNotExist(err) { + l.Infof("creating secrets file in %s", secretsFilePath) + if err := os.MkdirAll(filepath.Dir(secretsFilePath), 0o700); err != nil { + return fmt.Errorf("failed to create secrets dir: %w", err) + } + + accessToken, err := utils.GenerateRandomString(48, "") + if err != nil { + return fmt.Errorf("failed to generate access token secret key: %w", err) + } + + refreshToken, err := utils.GenerateRandomString(48, "") + if err != nil { + return fmt.Errorf("failed to generate refresh token secret key: %w", err) + } + + authConfig = config.AuthConfig{ + AccessTokenSecretKey: accessToken, + RefreshTokenSecretKey: refreshToken, + } + + data, err := json.MarshalIndent(authConfig, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal secrets: %w", err) + } + + if err := os.WriteFile(secretsFilePath, data, 0o600); err != nil { + return fmt.Errorf("failed to create secrets file: %w", err) + } + } else { + data, err := os.ReadFile(secretsFilePath) + if err != nil { + return fmt.Errorf("failed to read secrets file: %w", err) + } + + if err := json.Unmarshal(data, &authConfig); err != nil { + return fmt.Errorf("failed to unmarshal secrets file: %w", err) + } + + if authConfig.AccessTokenSecretKey == "" || authConfig.RefreshTokenSecretKey == "" { + return fmt.Errorf("invalid secrets file: missing keys") + } + } + + // set default timezone + var err error + time.Local, err = time.LoadLocation(conf.Timezone) + if err != nil { + return fmt.Errorf("failed to set timezone: %w", err) + } + + sqliteDbPath := filepath.Join(conf.HubDataDir(), "sqlite", sqliteDBFile) + + // init sqlite + sqliteDB, err := sqlite.New(ctx, sqliteDbPath) + if err != nil { + return fmt.Errorf("failed to initialize sqlite: %w", err) + } + + // init badger + var kvStore tokenmanager.ITokenStore + + var badgerDB *badgerdb.DB + if conf.KvDbEngine == "badgerdb" { + badgerDbPath := filepath.Join(conf.HubDataDir(), "badger") + badgerDB, err = badgerdb.New(l, badgerDbPath) + if err != nil { + return fmt.Errorf("failed to initialize badgerdb: %w", err) + } + kvStore = badgerDB + } else { + kvStore = tokenmanager.NewMemoryTokenStore() + } + + l.Infof("using kv store: %s", conf.KvDbEngine) + + // Print SQLite version if using SQLite storage + sqliteVersion, err := sqliteDB.GetSQLiteVersion(ctx) + if err != nil { + return fmt.Errorf("failed to get SQLite version: %w", err) + } + l.Infof("SQLite version: %s", sqliteVersion) + + // check and run all migrations + m := migrator.New(l, sql.MigrationsFS, sql.MigrationsPath, datamigrations.Migrations) + if err := m.MigrateUpAll(ctx, sqliteDbPath); err != nil { + return fmt.Errorf("failed to run migrations: %w", err) + } + + st, err := store.New(sqliteDB.DB, kvStore) + if err != nil { + return fmt.Errorf("failed to initialize store: %w", err) + } + + systemInfo := models.GetSystemInfo(version, commitHash, buildDate) + systemInfo.SqliteVersion = sqliteVersion + + // Init receiver + rc := receiver.New() + + // Initialize dispatcher + dispatcher := dispatcher.New() + + baseServices := baseservices.New(l, st, authConfig, rc, dispatcher, systemInfo) + + // init alert resolver + ar := alertresolver.New(l, baseServices) + + // Initialize scheduler + sched := scheduler.New(l, rc, baseServices, ar) + + availableUpdateData := locker.New(models.AvailableUpdate{}) + + // Initialize upgrader if configured + updater, err := updater.New(l, conf.Updater, version, availableUpdateData) + if err != nil { + return fmt.Errorf("failed to initialize upgrader: %w", err) + } + + srv := servers.New(ctx, l, conf.Server.Addr, baseServices, ar, systemInfo, availableUpdateData) + + // register services + ln.ServicesRunner().Register( + service.New(service.WithService(pingpong.New(l))), + service.New(service.WithService(sqliteDB)), + service.New(service.WithService(updater)), + service.New(service.WithService(rc)), + service.New(service.WithService(dispatcher)), + service.New(service.WithService(sched)), + service.New(service.WithService(srv)), + service.New(service.WithService(baseServices.Notifications().Sender())), + ) + + if badgerDB != nil { + ln.ServicesRunner().Register(service.New(service.WithService(badgerDB))) + } + + return ln.Run() + }, + }, + }, + } +} diff --git a/cmd/sentinel/main.go b/cmd/sentinel/main.go index 69ea018..452e765 100644 --- a/cmd/sentinel/main.go +++ b/cmd/sentinel/main.go @@ -14,10 +14,13 @@ import ( ) var ( - appName = "sentinel" - version = "local" - commitHash = "unknown" - buildDate = "unknown" + appName = "sentinel" + version = "local" + commitHash = "unknown" + buildDate = "unknown" + envHubPrefix = "SENTINEL_" + envAgentPrefix = "SENTINEL_AGENT_" + sqliteDBFile = "db.sqlite" ) func getBuildVersion() string { @@ -43,15 +46,29 @@ func main() { l := logger.NewExtended(defaultLoggerOpts()...) + // check if os args constains agent command + if len(os.Args) > 1 && os.Args[1] == "agent" { + appName = "sentinel-agent" + } + app := &cli.Command{ Name: appName, Usage: "A CLI application for " + appName, Version: getBuildVersion(), Suggest: true, Commands: []*cli.Command{ - startCMD(), + hubStartCMD(), + agentCMD(), configCMD(), + migrationsCMD(), versionCMD(), + { + Name: "migratedb", + Commands: []*cli.Command{ + exportCmd(), + importCmd(), + }, + }, }, } diff --git a/cmd/sentinel/migrate_data.go b/cmd/sentinel/migrate_data.go new file mode 100644 index 0000000..7de0b97 --- /dev/null +++ b/cmd/sentinel/migrate_data.go @@ -0,0 +1,359 @@ +package main + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/sxwebdev/sentinel/pkg/sqlite" + "github.com/urfave/cli/v3" + _ "modernc.org/sqlite" +) + +// -------- Old schema structs (export format) -------- + +type ServiceOld struct { + ID string `json:"id"` + Name string `json:"name"` + Protocol string `json:"protocol"` + Interval string `json:"interval"` // time.Duration string (e.g., "1s", "250ms") + Timeout string `json:"timeout"` + Retries int64 `json:"retries"` + Tags json.RawMessage `json:"tags"` + Config json.RawMessage `json:"config"` + IsEnabled bool `json:"is_enabled"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` +} + +type StateOld struct { + ID string `json:"id"` + ServiceID string `json:"service_id"` + Status string `json:"status"` + LastCheck *time.Time `json:"last_check"` + NextCheck *time.Time `json:"next_check"` + LastError *string `json:"last_error"` + ConsecutiveFails int64 `json:"consecutive_fails"` + ConsecutiveSucc int64 `json:"consecutive_success"` + TotalChecks int64 `json:"total_checks"` + ResponseTimeNS *int64 `json:"response_time_ns"` + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` +} + +type IncidentOld struct { + ID string `json:"id"` + ServiceID string `json:"service_id"` + StartTime time.Time `json:"start_time"` + EndTime *time.Time `json:"end_time"` + Error string `json:"error"` + DurationNS *int64 `json:"duration_ns"` + Resolved bool `json:"resolved"` + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` +} + +func parseDurationMS(s string) (int64, error) { + if strings.TrimSpace(s) == "" { + return 0, nil + } + d, err := time.ParseDuration(s) + if err != nil { + return 0, err + } + return d.Milliseconds(), nil +} + +func readAll[T any](ctx context.Context, db *sql.DB, query string, scan func(*sql.Rows) (T, error)) ([]T, error) { + rows, err := db.QueryContext(ctx, query) + if err != nil { + return nil, err + } + defer rows.Close() + var out []T + for rows.Next() { + v, err := scan(rows) + if err != nil { + return nil, err + } + out = append(out, v) + } + return out, rows.Err() +} + +func exportCmd() *cli.Command { + return &cli.Command{ + Name: "export", + Usage: "Export old schema tables to JSON files (services/service_states/incidents)", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "db", Required: true, Usage: "Path to SQLite DB file"}, + &cli.StringFlag{Name: "out", Required: true, Usage: "Output directory for JSON files"}, + }, + Action: func(ctx context.Context, c *cli.Command) error { + dbpath := c.String("db") + outDir := c.String("out") + if err := os.MkdirAll(outDir, 0o755); err != nil { + return err + } + db, err := sql.Open("sqlite", fmt.Sprintf("file:%s?_busy_timeout=5000&_pragma=foreign_keys(ON)", dbpath)) + if err != nil { + return err + } + defer db.Close() + + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + // services + services, err := readAll(ctx, db, `SELECT id,name,protocol,interval,timeout,retries,tags,config,is_enabled,created_at,updated_at FROM services`, func(r *sql.Rows) (ServiceOld, error) { + var s ServiceOld + var tags, cfg []byte + if err := r.Scan(&s.ID, &s.Name, &s.Protocol, &s.Interval, &s.Timeout, &s.Retries, &tags, &cfg, &s.IsEnabled, &s.CreatedAt, &s.UpdatedAt); err != nil { + return s, err + } + s.Tags = append([]byte(nil), tags...) + s.Config = append([]byte(nil), cfg...) + return s, nil + }) + if err != nil { + return err + } + + // service_states + states, err := readAll(ctx, db, `SELECT id,service_id,status,last_check,next_check,last_error,consecutive_fails,consecutive_success,total_checks,response_time_ns,created_at,updated_at FROM service_states`, func(r *sql.Rows) (StateOld, error) { + var s StateOld + if err := r.Scan(&s.ID, &s.ServiceID, &s.Status, &s.LastCheck, &s.NextCheck, &s.LastError, &s.ConsecutiveFails, &s.ConsecutiveSucc, &s.TotalChecks, &s.ResponseTimeNS, &s.CreatedAt, &s.UpdatedAt); err != nil { + return s, err + } + return s, nil + }) + if err != nil { + return err + } + + // incidents + incidents, err := readAll(ctx, db, `SELECT id,service_id,start_time,end_time,error,duration_ns,resolved,created_at,updated_at FROM incidents`, func(r *sql.Rows) (IncidentOld, error) { + var s IncidentOld + if err := r.Scan(&s.ID, &s.ServiceID, &s.StartTime, &s.EndTime, &s.Error, &s.DurationNS, &s.Resolved, &s.CreatedAt, &s.UpdatedAt); err != nil { + return s, err + } + return s, nil + }) + if err != nil { + return err + } + + // write files + write := func(name string, v any) error { + b, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + return os.WriteFile(filepath.Join(outDir, name), b, 0o644) + } + + if err := write("services.old.json", services); err != nil { + return err + } + if err := write("service_states.old.json", states); err != nil { + return err + } + if err := write("incidents.old.json", incidents); err != nil { + return err + } + + fmt.Printf("Exported %d services, %d states, %d incidents to %s\n", len(services), len(states), len(incidents), outDir) + return nil + }, + } +} + +func importCmd() *cli.Command { + return &cli.Command{ + Name: "import", + Usage: "Import JSON into NEW schema (converts durations to ms)", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "db", Required: true, Usage: "Path to NEW SQLite DB file"}, + &cli.StringFlag{Name: "in", Required: true, Usage: "Input directory with JSON files"}, + }, + Action: func(ctx context.Context, c *cli.Command) error { + dbpath := c.String("db") + inDir := c.String("in") + + dsn := sqlite.GetDSN(dbpath) + db, err := sql.Open("sqlite", dsn) + if err != nil { + return err + } + defer db.Close() + + ctx, cancel := context.WithTimeout(ctx, 60*time.Second) + defer cancel() + + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { + if err != nil { + _ = tx.Rollback() + } + }() + + // Load JSON + var svcsOld []ServiceOld + var stsOld []StateOld + var incOld []IncidentOld + if err = readJSON(filepath.Join(inDir, "services.old.json"), &svcsOld); err != nil { + return err + } + if err = readJSON(filepath.Join(inDir, "service_states.old.json"), &stsOld); err != nil { + return err + } + if err = readJSON(filepath.Join(inDir, "incidents.old.json"), &incOld); err != nil { + return err + } + + // Convert & insert + if err = insertServices(ctx, tx, svcsOld); err != nil { + return fmt.Errorf("services: %w", err) + } + if err = insertStates(ctx, tx, stsOld); err != nil { + return fmt.Errorf("service_states: %w", err) + } + if err = insertIncidents(ctx, tx, incOld); err != nil { + return fmt.Errorf("incidents: %w", err) + } + + if err = tx.Commit(); err != nil { + return err + } + _, _ = db.Exec(`PRAGMA integrity_check;`) + _, _ = db.Exec(`PRAGMA optimize;`) + fmt.Println("Import OK") + return nil + }, + } +} + +func insertServices(ctx context.Context, tx *sql.Tx, in []ServiceOld) error { + stmt, err := tx.PrepareContext(ctx, ` + INSERT INTO services (id,name,protocol,interval,timeout,retries,tags,config,is_enabled,created_at,updated_at) + VALUES (?,?,?,?,?,?,?,?,?,COALESCE(?,CURRENT_TIMESTAMP),COALESCE(?,CURRENT_TIMESTAMP))`) + if err != nil { + return err + } + defer stmt.Close() + for _, s := range in { + intervalMS, err := parseDurationMS(s.Interval) + if err != nil { + return fmt.Errorf("id=%s interval %q: %w", s.ID, s.Interval, err) + } + timeoutMS, err := parseDurationMS(s.Timeout) + if err != nil { + return fmt.Errorf("id=%s timeout %q: %w", s.ID, s.Timeout, err) + } + protocol := s.Protocol + if protocol == "" { + protocol = "unknown" + } + if s.Tags == nil { + s.Tags = json.RawMessage("[]") + } + if s.Config == nil { + s.Config = json.RawMessage("{}") + } + if _, err := stmt.ExecContext(ctx, s.ID, s.Name, protocol, intervalMS, timeoutMS, s.Retries, s.Tags, s.Config, s.IsEnabled, formatDate(s.CreatedAt), formatDate(s.UpdatedAt)); err != nil { + return err + } + } + return nil +} + +func insertStates(ctx context.Context, tx *sql.Tx, in []StateOld) error { + stmt, err := tx.PrepareContext(ctx, ` + INSERT INTO service_states (id,service_id,status,last_check,last_error,consecutive_fails,consecutive_success,total_checks,avg_response_time,created_at,updated_at) + VALUES (?,?,?,?,?,?,?,?,?,COALESCE(?,CURRENT_TIMESTAMP),COALESCE(?,CURRENT_TIMESTAMP))`) + if err != nil { + return err + } + defer stmt.Close() + for _, s := range in { + var avgMS *int64 + if s.ResponseTimeNS != nil { + v := *s.ResponseTimeNS / 1_000_000 + avgMS = &v + } + if _, err := stmt.ExecContext(ctx, s.ID, s.ServiceID, s.Status, formatDate(s.LastCheck), s.LastError, s.ConsecutiveFails, s.ConsecutiveSucc, s.TotalChecks, avgMS, formatDate(s.CreatedAt), formatDate(s.UpdatedAt)); err != nil { + return err + } + } + return nil +} + +func parseTimeFlexible(s string) (time.Time, error) { + formats := []string{time.RFC3339Nano, time.RFC3339, "2006-01-02 15:04:05", "2006-01-02 15:04:05Z07:00"} + for _, f := range formats { + if t, err := time.Parse(f, s); err == nil { + return t, nil + } + } + return time.Time{}, errors.New("unsupported time format: " + s) +} + +func insertIncidents(ctx context.Context, tx *sql.Tx, in []IncidentOld) error { + stmt, err := tx.PrepareContext(ctx, ` + INSERT INTO incidents (id,service_id,error,duration,started_at,resolved_at,created_at,updated_at) + VALUES (?,?,?,?,?,?,COALESCE(?,CURRENT_TIMESTAMP),COALESCE(?,CURRENT_TIMESTAMP))`) + if err != nil { + return err + } + defer stmt.Close() + for _, s := range in { + var dur *int64 + if s.DurationNS != nil { + v := *s.DurationNS / 1_000_000 + dur = &v + } else if s.EndTime != nil && !s.EndTime.IsZero() && !s.StartTime.IsZero() { + if st, err1 := parseTimeFlexible(s.StartTime.String()); err1 == nil { + if et, err2 := parseTimeFlexible(s.EndTime.String()); err2 == nil { + v := et.Sub(st).Milliseconds() + dur = &v + } + } + } + + var resolvedAt *string + if s.Resolved && s.EndTime != nil { + endTime := formatDate(s.EndTime) + resolvedAt = endTime + } + + if _, err := stmt.ExecContext(ctx, s.ID, s.ServiceID, s.Error, dur, formatDate(&s.StartTime), resolvedAt, formatDate(s.CreatedAt), formatDate(s.UpdatedAt)); err != nil { + return err + } + } + return nil +} + +func readJSON(path string, v any) error { + b, err := os.ReadFile(path) + if err != nil { + return err + } + return json.Unmarshal(b, v) +} + +func formatDate(t *time.Time) *string { + if t == nil { + return nil + } + formatted := t.Format("2006-01-02 15:04:05") + return &formatted +} diff --git a/cmd/sentinel/migrations.go b/cmd/sentinel/migrations.go new file mode 100644 index 0000000..359c35f --- /dev/null +++ b/cmd/sentinel/migrations.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/sxwebdev/sentinel/internal/datamigrations" + "github.com/sxwebdev/sentinel/pkg/migrator" + "github.com/sxwebdev/sentinel/sql" + "github.com/tkcrm/mx/logger" + "github.com/urfave/cli/v3" +) + +func migrationsCMD() *cli.Command { + opts := append( + defaultLoggerOpts(), + logger.WithConsoleColored(true), + logger.WithLogFormat(logger.LoggerFormatConsole), + ) + l := logger.NewExtended(opts...) + return migrator.CliCmd(l, sql.MigrationsFS, sql.MigrationsPath, datamigrations.Migrations) +} diff --git a/cmd/sentinel/start.go b/cmd/sentinel/start.go deleted file mode 100644 index 5b3d6a7..0000000 --- a/cmd/sentinel/start.go +++ /dev/null @@ -1,127 +0,0 @@ -package main - -import ( - "context" - "fmt" - "runtime" - "time" - - "github.com/sxwebdev/sentinel/internal/config" - "github.com/sxwebdev/sentinel/internal/monitor" - "github.com/sxwebdev/sentinel/internal/notifier" - "github.com/sxwebdev/sentinel/internal/receiver" - "github.com/sxwebdev/sentinel/internal/scheduler" - "github.com/sxwebdev/sentinel/internal/storage" - "github.com/sxwebdev/sentinel/internal/upgrader" - "github.com/sxwebdev/sentinel/internal/web" - "github.com/tkcrm/mx/launcher" - "github.com/tkcrm/mx/logger" - "github.com/tkcrm/mx/service" - "github.com/tkcrm/mx/service/pingpong" - "github.com/urfave/cli/v3" -) - -func startCMD() *cli.Command { - return &cli.Command{ - Name: "start", - Usage: "start the server", - Flags: []cli.Flag{cfgPathsFlag()}, - Action: func(ctx context.Context, cl *cli.Command) error { - conf, err := config.Load(cl.String("config")) - if err != nil { - return fmt.Errorf("failed to load config: %w", err) - } - - loggerOpts := append(defaultLoggerOpts(), logger.WithConfig(conf.Log)) - - l := logger.NewExtended(loggerOpts...) - defer func() { - _ = l.Sync() - }() - - // init launcher - ln := launcher.New( - launcher.WithVersion(version), - launcher.WithName(appName), - launcher.WithLogger(l), - launcher.WithContext(ctx), - launcher.WithRunnerServicesSequence(launcher.RunnerServicesSequenceFifo), - launcher.WithOpsConfig(conf.Ops), - launcher.WithAppStartStopLog(true), - ) - - // set default timezone - time.Local, err = time.LoadLocation(conf.Timezone) - if err != nil { - return fmt.Errorf("failed to set timezone: %w", err) - } - - // Initialize storage - store, err := storage.NewStorage(storage.StorageTypeSQLite, conf.Database.Path) - if err != nil { - return fmt.Errorf("failed to initialize storage: %w", err) - } - - // Print SQLite version if using SQLite storage - sqliteVersion, err := store.GetSQLiteVersion(ctx) - if err != nil { - return fmt.Errorf("failed to get SQLite version: %w", err) - } - l.Infof("SQLite version: %s", sqliteVersion) - - // Initialize notifier - var notif *notifier.Notifier - if conf.Notifications.Enabled { - notif, err = notifier.New(l, conf.Notifications.URLs) - if err != nil { - return fmt.Errorf("failed to initialize notifier: %w", err) - } - } - - // Init receiver - rc := receiver.New() - - // Initialize upgrader if configured - upgr, err := upgrader.New(l, conf.Upgrader) - if err != nil { - return fmt.Errorf("failed to initialize upgrader: %w", err) - } - - // Create monitor service - monitorService := monitor.NewMonitorService(store, conf, notif, rc) - - // Initialize scheduler - sched := scheduler.New(l, monitorService, rc) - - webServer, err := web.NewServer(l, conf, web.ServerInfo{ - Version: version, - CommitHash: commitHash, - BuildDate: buildDate, - GoVersion: runtime.Version(), - SqliteVersion: sqliteVersion, - OS: runtime.GOOS, - Arch: runtime.GOARCH, - }, monitorService, store, rc, upgr) - if err != nil { - return fmt.Errorf("failed to initialize web server: %w", err) - } - - // register services - ln.ServicesRunner().Register( - service.New(service.WithService(pingpong.New(l))), - service.New(service.WithService(store)), - service.New(service.WithService(rc)), - service.New(service.WithService(sched)), - service.New(service.WithService(webServer)), - ) - - if notif != nil { - ln.ServicesRunner().Register( - service.New(service.WithService(notif)), - ) - } - - return ln.Run() - }, - } -} diff --git a/cmd/tcpserver/main.go b/cmd/tcpserver/main.go deleted file mode 100644 index 4d9d91a..0000000 --- a/cmd/tcpserver/main.go +++ /dev/null @@ -1,101 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "net" - "time" - - "github.com/sxwebdev/sentinel/internal/utils" -) - -func main() { - if err := run(); err != nil { - fmt.Println("Error:", err) - } -} - -func run() error { - listener, err := net.Listen("tcp", "127.0.0.1:12345") - if err != nil { - return fmt.Errorf("failed to start server: %w", err) - } - defer listener.Close() - - fmt.Println("Server is listening on 127.0.0.1:12345") - - for { - conn, err := listener.Accept() - if err != nil { - fmt.Println("Failed to accept connection:", err) - continue - } - fmt.Println("Client connected:", conn.RemoteAddr()) - go handleConnection(conn) - } -} - -func handleConnection(conn net.Conn) { - defer func() { - conn.Close() - fmt.Printf("Connection closed: %s\n\n", conn.RemoteAddr()) - }() - - // Set connection timeout - close if no data received in 5 seconds - conn.SetReadDeadline(time.Now().Add(5 * time.Second)) - - var accumulated []byte - reader := bufio.NewReader(conn) - - for { - buffer := make([]byte, 1024) - n, err := reader.Read(buffer) - if err != nil { - if utils.IsErrTimeout(err) { - break - } - - fmt.Println("failed to read from connection:", err) - return - } - - if n == 0 { - break - } - - // Accumulate received data - accumulated = append(accumulated, buffer[:n]...) - - // If no more data buffered, client likely finished sending - if reader.Buffered() == 0 { - break - } - } - - // Now process the complete message - if len(accumulated) == 0 { - fmt.Println("No data received from client") - return - } - - msg := string(accumulated) - fmt.Println("Complete message received:", msg) - - // Simple ping-pong protocol - exact match - switch msg { - case "ping": - fmt.Println("Sending pong") - _, err := conn.Write([]byte("pong")) - if err != nil { - fmt.Println("Failed to send response:", err) - } - case "noresponse": - fmt.Println("No response expected") - default: - fmt.Printf("Unknown message '%s', sending ok\n", msg) - _, err := conn.Write([]byte("ok")) - if err != nil { - fmt.Println("Failed to send response:", err) - } - } -} diff --git a/cmd/testapi/README.md b/cmd/testapi/README.md deleted file mode 100644 index 6611ad6..0000000 --- a/cmd/testapi/README.md +++ /dev/null @@ -1,248 +0,0 @@ -# Sentinel API Integration Tests - -This package contains comprehensive integration tests for the Sentinel monitoring system API. - -## What is tested - -### Core API functions: - -- ✅ Dashboard and statistics -- ✅ Service CRUD operations (Create, Read, Update, Delete) -- ✅ Service filtering by various parameters -- ✅ Result pagination -- ✅ Incident management -- ✅ Tag operations -- ✅ Service statistics -- ✅ Manual service checks -- ✅ Error handling - -### Monitoring protocols: - -- ✅ HTTP/HTTPS monitoring with support for: - - Multiple endpoints - - Various HTTP methods (GET, POST, PUT, DELETE) - - Headers and request body - - Basic authentication - - JSON Path validation - - Execution conditions (all/any) -- ✅ TCP monitoring with data sending and receiving -- ✅ gRPC monitoring with various check types - -### Filters and query parameters: - -- ✅ Filtering by service name -- ✅ Filtering by tags (single and multiple) -- ✅ Filtering by status (up/down/unknown) -- ✅ Filtering by enabled status (enabled/disabled) -- ✅ Filtering by protocol (http/tcp/grpc) -- ✅ Sorting by name and creation date -- ✅ Pagination with configurable page size -- ✅ Incident filtering by time -- ✅ Incident filtering by resolution status - -### Data model validation: - -- ✅ Configuration structures for all protocols -- ✅ DTO conversions -- ✅ API response models -- ✅ Error models -- ✅ Pagination structures - -### Error handling: - -- ✅ Invalid JSON data -- ✅ Missing required fields -- ✅ Invalid parameter values -- ✅ Operations on non-existent resources -- ✅ Configuration validation errors - -## Test structure - -### Files: - -- `main.go` - Main file with basic tests and test environment setup -- `extended_tests.go` - Extended tests for complex scenarios -- `model_tests.go` - Data model validation tests - -### Test cases: - -#### Basic tests (main.go): - -1. **TestHealthCheck** - main page availability check -2. **TestDashboardStats** - dashboard statistics API test -3. **TestCreateServices** - creating test services of all types -4. **TestGetServices** - getting service list -5. **TestServiceFilters** - testing service filters -6. **TestServiceDetail** - getting detailed service information -7. **TestUpdateService** - service update -8. **TestServiceStats** - getting service statistics -9. **TestServiceCheck** - manual service check trigger -10. **TestIncidents** - incident operations -11. **TestIncidentFilters** - incident filtering -12. **TestTags** - tag operations -13. **TestPagination** - basic pagination testing -14. **TestErrorHandling** - error handling -15. **TestDeleteService** - service deletion - -#### Extended tests (extended_tests.go): - -1. **TestAdvancedServiceFilters** - complex filter combinations -2. **TestServiceCRUDCompleteFlow** - complete service lifecycle -3. **TestAdvancedIncidentManagement** - advanced incident management -4. **TestCompleteProtocolConfigurations** - testing all protocols -5. **TestAdvancedPaginationAndSorting** - advanced pagination and sorting -6. **TestAdvancedErrorScenarios** - complex error scenarios -7. **TestStatsWithDifferentParameters** - statistics with various parameters - -#### Model tests (model_tests.go): - -1. **TestModelsValidation** - monitoring configuration validation -2. **TestServiceDTOFields** - service DTO field verification -3. **TestIncidentFields** - incident field verification -4. **TestResponseModels** - response model verification -5. **TestServiceStatsModel** - service statistics model verification -6. **TestPaginationResponseModel** - paginated response model verification - -## Running tests - -### Prerequisites: - -- Go 1.21+ -- Sentinel project dependencies must be installed - -### Run command: - -```bash -cd cmd/testapi -go run *.go test -``` - -### Expected output: - -``` -Running TestHealthCheck... -PASS: TestHealthCheck -Running TestDashboardStats... -PASS: TestDashboardStats -... -Running TestPaginationResponseModel... -PASS: TestPaginationResponseModel - -All tests passed! -``` - -### In case of errors: - -``` -Running TestCreateServices... -FAIL: TestCreateServices - service 0: HTTP 400: Service name is required -... - -2 test(s) failed -``` - -## Test data - -Tests create the following test services: - -1. **HTTP Test Service 1** (enabled) - - - Protocol: HTTP - - Endpoint: https://httpbin.org/status/200 - - Tags: [http, production, api] - -2. **HTTP Test Service 2** (disabled) - - - Protocol: HTTP - - Endpoint: https://httpbin.org/status/404 - - Tags: [http, staging, web] - -3. **TCP Test Service** (enabled) - - - Protocol: TCP - - Endpoint: google.com:80 - - Tags: [tcp, database, production] - -4. **gRPC Test Service** (enabled) - - - Protocol: gRPC - - Endpoint: grpc.example.com:443 - - Tags: [grpc, api, microservice] - -5. **Disabled Service** (disabled) - - Protocol: HTTP - - Endpoint: https://httpbin.org/status/500 - - Tags: [disabled, test] - -## API endpoint coverage - -### Dashboard - -- `GET /` - main page -- `GET /api/v1/dashboard/stats` - dashboard statistics - -### Services - -- `GET /api/v1/services` - service list with filters -- `POST /api/v1/services` - service creation -- `GET /api/v1/services/{id}` - service details -- `PUT /api/v1/services/{id}` - service update -- `DELETE /api/v1/services/{id}` - service deletion -- `POST /api/v1/services/{id}/check` - manual check -- `POST /api/v1/services/{id}/resolve` - incident resolution -- `GET /api/v1/services/{id}/stats` - service statistics - -### Incidents - -- `GET /api/v1/incidents` - all incidents list -- `GET /api/v1/services/{id}/incidents` - service incidents -- `DELETE /api/v1/services/{id}/incidents/{incidentId}` - incident deletion - -### Tags - -- `GET /api/v1/tags` - tags list -- `GET /api/v1/tags/count` - tags with usage count - -## Testing features - -### Test isolation - -- Each test run uses a temporary SQLite database -- Test server runs on port 8899 -- All resources are cleaned up after test completion - -### External dependencies - -- Tests use httpbin.org for HTTP checks -- TCP tests use google.com:80 -- gRPC tests use mock endpoints - -### Concurrency - -- Tests run sequentially for predictability -- Each test can create and delete its own resources - -## Test configuration - -Tests use the following configuration: - -- **Database**: temporary SQLite -- **Server**: localhost:8899 -- **Monitoring interval**: 30 seconds (default) -- **Timeout**: 5 seconds (default) -- **Retries**: 3 (default) -- **Timezone**: UTC -- **Notifications**: disabled - -## Debugging - -For debugging, you can add additional logging to test code or use Go debugger. Tests output detailed error messages indicating the specific problem location. - -## Extending tests - -To add new tests: - -1. Create a new function in the appropriate file -2. Add it to the test array in the main() function -3. Ensure the test returns an error on failure -4. Add resource cleanup if necessary diff --git a/cmd/testapi/extended_tests.go b/cmd/testapi/extended_tests.go deleted file mode 100644 index dcb3a7e..0000000 --- a/cmd/testapi/extended_tests.go +++ /dev/null @@ -1,982 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "net/http" - "net/url" - "time" - - "github.com/sxwebdev/sentinel/internal/monitors" - "github.com/sxwebdev/sentinel/internal/storage" - "github.com/sxwebdev/sentinel/internal/web" - "github.com/sxwebdev/sentinel/pkg/dbutils" -) - -// Extended tests for comprehensive API coverage - -func testAdvancedServiceFilters(s *TestSuite) error { - // Test complex tag filtering with multiple tags - resp, err := s.makeRequest("GET", "/api/v1/services?tags=production,api", nil) - if err != nil { - return err - } - - var result dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - // Each service should have both tags - for _, service := range result.Items { - hasProduction := false - hasAPI := false - for _, tag := range service.Tags { - if tag == "production" { - hasProduction = true - } - if tag == "api" { - hasAPI = true - } - } - if !hasProduction || !hasAPI { - return fmt.Errorf("service %s doesn't have both required tags", service.Name) - } - } - - // Test status filtering - resp, err = s.makeRequest("GET", "/api/v1/services?status=up", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - for _, service := range result.Items { - if service.Status != storage.StatusUp { - return fmt.Errorf("service %s status is not 'up'", service.Name) - } - } - - // Test ordering by created_at - resp, err = s.makeRequest("GET", "/api/v1/services?order_by=created_at", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - // Services should be ordered (assuming creation order) - if len(result.Items) > 1 { - fmt.Printf("Order test: found %d services\n", len(result.Items)) - } - - return nil -} - -func testServiceCRUDCompleteFlow(s *TestSuite) error { - // Create a new service with complex HTTP config - complexHTTPService := web.CreateUpdateServiceRequest{ - Name: "Complex HTTP Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 15000, // 15s - Timeout: 10000, // 10s - Retries: 2, - Tags: []string{"complex", "test", "http"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 8000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Main API", - URL: "https://httpbin.org/get", - Method: "GET", - ExpectedStatus: 200, - Headers: map[string]string{ - "Authorization": "Bearer test-token", - "User-Agent": "Sentinel-Test", - }, - }, - { - Name: "Health Check", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - { - Name: "POST Endpoint", - URL: "https://httpbin.org/post", - Method: "POST", - ExpectedStatus: 200, - Body: `{"test": "data"}`, - Headers: map[string]string{ - "Content-Type": "application/json", - }, - }, - }, - Condition: "all", // All endpoints must pass - }, - }, - IsEnabled: true, - } - - // Create service - resp, err := s.makeRequest("POST", "/api/v1/services", complexHTTPService) - if err != nil { - return fmt.Errorf("create complex service: %w", err) - } - - var createdService web.ServiceDTO - if err := s.decodeResponse(resp, &createdService); err != nil { - return fmt.Errorf("decode created service: %w", err) - } - - serviceID := createdService.ID - - // Verify creation - if createdService.Name != complexHTTPService.Name { - return fmt.Errorf("name mismatch in created service") - } - if createdService.Protocol != complexHTTPService.Protocol { - return fmt.Errorf("protocol mismatch in created service") - } - if createdService.Config.HTTP == nil { - return fmt.Errorf("HTTP config is nil in created service") - } - if len(createdService.Config.HTTP.Endpoints) != 3 { - return fmt.Errorf("expected 3 endpoints, got %d", len(createdService.Config.HTTP.Endpoints)) - } - - // Test service detail retrieval - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID, nil) - if err != nil { - return fmt.Errorf("get service detail: %w", err) - } - - var serviceDetail web.ServiceDTO - if err := s.decodeResponse(resp, &serviceDetail); err != nil { - return fmt.Errorf("decode service detail: %w", err) - } - - if serviceDetail.ID != serviceID { - return fmt.Errorf("service detail ID mismatch") - } - - // Update service - modify configuration - updatedConfig := complexHTTPService - updatedConfig.Name = "Updated Complex HTTP Service" - updatedConfig.Interval = 45000 // 45s - updatedConfig.Tags = append(updatedConfig.Tags, "updated") - - // Remove one endpoint - updatedConfig.Config.HTTP.Endpoints = updatedConfig.Config.HTTP.Endpoints[:2] - updatedConfig.Config.HTTP.Condition = "any" // Any endpoint can pass - - resp, err = s.makeRequest("PUT", "/api/v1/services/"+serviceID, updatedConfig) - if err != nil { - return fmt.Errorf("update service: %w", err) - } - - var updatedService web.ServiceDTO - if err := s.decodeResponse(resp, &updatedService); err != nil { - return fmt.Errorf("decode updated service: %w", err) - } - - // Verify updates - if updatedService.Name != updatedConfig.Name { - return fmt.Errorf("updated name mismatch") - } - if updatedService.Interval != updatedConfig.Interval { - return fmt.Errorf("updated interval mismatch") - } - if len(updatedService.Config.HTTP.Endpoints) != 2 { - return fmt.Errorf("expected 2 endpoints after update, got %d", len(updatedService.Config.HTTP.Endpoints)) - } - if updatedService.Config.HTTP.Condition != "any" { - return fmt.Errorf("condition not updated") - } - - // Trigger manual check - resp, err = s.makeRequest("POST", "/api/v1/services/"+serviceID+"/check", nil) - if err != nil { - return fmt.Errorf("trigger check: %w", err) - } - - var checkResult web.SuccessResponse - if err := s.decodeResponse(resp, &checkResult); err != nil { - return fmt.Errorf("decode check result: %w", err) - } - - if checkResult.Message == "" { - return fmt.Errorf("empty check result message") - } - - // Wait a bit and check service stats - time.Sleep(100 * time.Millisecond) - - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/stats?days=1", nil) - if err != nil { - return fmt.Errorf("get service stats: %w", err) - } - - var stats web.ServiceStats - if err := s.decodeResponse(resp, &stats); err != nil { - return fmt.Errorf("decode service stats: %w", err) - } - - if stats.ServiceID != serviceID { - return fmt.Errorf("stats service ID mismatch") - } - - // Get service incidents - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents", nil) - if err != nil { - return fmt.Errorf("get service incidents: %w", err) - } - - var incidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &incidents); err != nil { - return fmt.Errorf("decode service incidents: %w", err) - } - - // All incidents should belong to this service - for _, incident := range incidents.Items { - if incident.ServiceID != serviceID { - return fmt.Errorf("incident %s doesn't belong to service %s", incident.ID, serviceID) - } - } - - // Delete the service - resp, err = s.makeRequest("DELETE", "/api/v1/services/"+serviceID, nil) - if err != nil { - return fmt.Errorf("delete service: %w", err) - } - - if resp.StatusCode != http.StatusNoContent { - return fmt.Errorf("delete service: expected 204, got %d", resp.StatusCode) - } - - // Verify deletion - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID, nil) - if err != nil { - return fmt.Errorf("verify deletion: %w", err) - } - - if resp.StatusCode == http.StatusOK { - return fmt.Errorf("service still exists after deletion") - } - - return nil -} - -func testAdvancedIncidentManagement(s *TestSuite) error { - // Create a dedicated service for incident testing - testService := web.CreateUpdateServiceRequest{ - Name: "Incident Management Test Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 30000, - Timeout: 5000, - Retries: 3, - Tags: []string{"incident-test"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test Endpoint", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: true, - } - - // Create the test service - resp, err := s.makeRequest("POST", "/api/v1/services", testService) - if err != nil { - return fmt.Errorf("create incident test service: %w", err) - } - - var createdService web.ServiceDTO - if err := s.decodeResponse(resp, &createdService); err != nil { - return fmt.Errorf("decode incident test service: %w", err) - } - - serviceID := createdService.ID - - // Ensure cleanup - defer func() { - s.makeRequest("DELETE", "/api/v1/services/"+serviceID, nil) - }() - - // Get service incidents with various filters - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents?resolved=false", nil) - if err != nil { - return err - } - - var unresolvedIncidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &unresolvedIncidents); err != nil { - return err - } - - // All incidents should be unresolved - for _, incident := range unresolvedIncidents.Items { - if incident.Resolved { - return fmt.Errorf("found resolved incident when filtering for unresolved") - } - } - - // Get resolved incidents - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents?resolved=true", nil) - if err != nil { - return err - } - - var resolvedIncidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &resolvedIncidents); err != nil { - return err - } - - // All incidents should be resolved - for _, incident := range resolvedIncidents.Items { - if !incident.Resolved { - return fmt.Errorf("found unresolved incident when filtering for resolved") - } - } - - // Test time-based filtering - now := time.Now() - yesterday := now.AddDate(0, 0, -1) - tomorrow := now.AddDate(0, 0, 1) - - // Filter by time range - timeParams := fmt.Sprintf("start_time=%s&end_time=%s", - url.QueryEscape(yesterday.Format(time.RFC3339)), - url.QueryEscape(tomorrow.Format(time.RFC3339))) - - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents?"+timeParams, nil) - if err != nil { - return err - } - - var timeFilteredIncidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &timeFilteredIncidents); err != nil { - return err - } - - // Test pagination for incidents - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents?page=1&page_size=5", nil) - if err != nil { - return err - } - - var paginatedIncidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &paginatedIncidents); err != nil { - return err - } - - if len(paginatedIncidents.Items) > 5 { - return fmt.Errorf("page size not respected for incidents: got %d items", len(paginatedIncidents.Items)) - } - - // Test resolve service incidents - resp, err = s.makeRequest("POST", "/api/v1/services/"+serviceID+"/resolve", nil) - if err != nil { - return err - } - - var resolveResult web.SuccessResponse - if err := s.decodeResponse(resp, &resolveResult); err != nil { - return err - } - - if resolveResult.Message == "" { - return fmt.Errorf("empty resolve result message") - } - - // Test global incident search - resp, err = s.makeRequest("GET", "/api/v1/incidents?search="+serviceID, nil) - if err != nil { - return err - } - - var searchResults dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &searchResults); err != nil { - return err - } - - // All results should be related to the searched service - for _, incident := range searchResults.Items { - if incident.ServiceID != serviceID && incident.ID != serviceID { - // Check if the search term appears in service name or incident data - hasSearchTerm := false - if incident.ServiceID == serviceID || incident.ID == serviceID { - hasSearchTerm = true - } - if !hasSearchTerm { - return fmt.Errorf("search result doesn't match search criteria") - } - } - } - - return nil -} - -func testCompleteProtocolConfigurations(s *TestSuite) error { - // Test TCP service with advanced configuration - tcpService := web.CreateUpdateServiceRequest{ - Name: "Advanced TCP Service", - Protocol: storage.ServiceProtocolTypeTCP, - Interval: 20000, - Timeout: 8000, - Retries: 2, - Tags: []string{"tcp", "advanced", "database"}, - Config: monitors.Config{ - TCP: &monitors.TCPConfig{ - Endpoint: "google.com:80", - SendData: "GET / HTTP/1.1\r\nHost: google.com\r\n\r\n", - ExpectData: "HTTP/1.1", - }, - }, - IsEnabled: true, - } - - resp, err := s.makeRequest("POST", "/api/v1/services", tcpService) - if err != nil { - return fmt.Errorf("create TCP service: %w", err) - } - - var createdTCPService web.ServiceDTO - if err := s.decodeResponse(resp, &createdTCPService); err != nil { - return fmt.Errorf("decode TCP service: %w", err) - } - - if createdTCPService.Config.TCP == nil { - return fmt.Errorf("TCP config is nil") - } - if createdTCPService.Config.TCP.Endpoint != tcpService.Config.TCP.Endpoint { - return fmt.Errorf("TCP endpoint mismatch") - } - if createdTCPService.Config.TCP.SendData != tcpService.Config.TCP.SendData { - return fmt.Errorf("TCP send data mismatch") - } - - tcpServiceID := createdTCPService.ID - - // Test gRPC service with advanced configuration - grpcService := web.CreateUpdateServiceRequest{ - Name: "Advanced gRPC Service", - Protocol: storage.ServiceProtocolTypeGRPC, - Interval: 25000, - Timeout: 10000, - Retries: 3, - Tags: []string{"grpc", "advanced", "microservice"}, - Config: monitors.Config{ - GRPC: &monitors.GRPCConfig{ - Endpoint: "grpc.example.com:443", - CheckType: "health", - ServiceName: "example.HealthService", - TLS: true, - InsecureTLS: false, - }, - }, - IsEnabled: true, - } - - resp, err = s.makeRequest("POST", "/api/v1/services", grpcService) - if err != nil { - return fmt.Errorf("create gRPC service: %w", err) - } - - var createdGRPCService web.ServiceDTO - if err := s.decodeResponse(resp, &createdGRPCService); err != nil { - return fmt.Errorf("decode gRPC service: %w", err) - } - - if createdGRPCService.Config.GRPC == nil { - return fmt.Errorf("gRPC config is nil") - } - if createdGRPCService.Config.GRPC.Endpoint != grpcService.Config.GRPC.Endpoint { - return fmt.Errorf("gRPC endpoint mismatch") - } - if createdGRPCService.Config.GRPC.CheckType != grpcService.Config.GRPC.CheckType { - return fmt.Errorf("gRPC check type mismatch") - } - if createdGRPCService.Config.GRPC.TLS != grpcService.Config.GRPC.TLS { - return fmt.Errorf("gRPC TLS setting mismatch") - } - - grpcServiceID := createdGRPCService.ID - - // Test both services individually - services := []struct { - id string - protocol storage.ServiceProtocolType - }{ - {tcpServiceID, storage.ServiceProtocolTypeTCP}, - {grpcServiceID, storage.ServiceProtocolTypeGRPC}, - } - - for _, svc := range services { - // Test service detail - resp, err = s.makeRequest("GET", "/api/v1/services/"+svc.id, nil) - if err != nil { - return fmt.Errorf("get %s service detail: %w", svc.protocol, err) - } - - var serviceDetail web.ServiceDTO - if err := s.decodeResponse(resp, &serviceDetail); err != nil { - return fmt.Errorf("decode %s service detail: %w", svc.protocol, err) - } - - if serviceDetail.Protocol != svc.protocol { - return fmt.Errorf("%s service protocol mismatch", svc.protocol) - } - - // Test service check - resp, err = s.makeRequest("POST", "/api/v1/services/"+svc.id+"/check", nil) - if err != nil { - return fmt.Errorf("trigger %s service check: %w", svc.protocol, err) - } - - var checkResult web.SuccessResponse - if err := s.decodeResponse(resp, &checkResult); err != nil { - return fmt.Errorf("decode %s check result: %w", svc.protocol, err) - } - - // Test service stats - resp, err = s.makeRequest("GET", "/api/v1/services/"+svc.id+"/stats", nil) - if err != nil { - return fmt.Errorf("get %s service stats: %w", svc.protocol, err) - } - - var stats web.ServiceStats - if err := s.decodeResponse(resp, &stats); err != nil { - return fmt.Errorf("decode %s service stats: %w", svc.protocol, err) - } - - if stats.ServiceID != svc.id { - return fmt.Errorf("%s service stats ID mismatch", svc.protocol) - } - } - - // Clean up - delete both services - for _, svc := range services { - resp, err = s.makeRequest("DELETE", "/api/v1/services/"+svc.id, nil) - if err != nil { - return fmt.Errorf("delete %s service: %w", svc.protocol, err) - } - - if resp.StatusCode != http.StatusNoContent { - return fmt.Errorf("delete %s service: expected 204, got %d", svc.protocol, resp.StatusCode) - } - } - - return nil -} - -func testAdvancedPaginationAndSorting(s *TestSuite) error { - // Create multiple services for better pagination testing - testServices := []web.CreateUpdateServiceRequest{} - for i := 0; i < 10; i++ { - service := web.CreateUpdateServiceRequest{ - Name: fmt.Sprintf("Pagination Test Service %02d", i), - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 30000, - Timeout: 5000, - Retries: 3, - Tags: []string{fmt.Sprintf("page-test-%d", i%3), "pagination"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test Endpoint", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: i%2 == 0, // Alternate enabled/disabled - } - testServices = append(testServices, service) - } - - // Create all test services - createdIDs := []string{} - for i, service := range testServices { - resp, err := s.makeRequest("POST", "/api/v1/services", service) - if err != nil { - return fmt.Errorf("create pagination test service %d: %w", i, err) - } - - var created web.ServiceDTO - if err := s.decodeResponse(resp, &created); err != nil { - return fmt.Errorf("decode pagination test service %d: %w", i, err) - } - - createdIDs = append(createdIDs, created.ID) - } - - // Test various page sizes - pageSizes := []int{3, 5, 7} - for _, pageSize := range pageSizes { - page := 1 - allItems := []web.ServiceDTO{} - seenIDs := make(map[string]bool) - - for { - resp, err := s.makeRequest("GET", - fmt.Sprintf("/api/v1/services?page=%d&page_size=%d&order_by=name", page, pageSize), nil) - if err != nil { - return fmt.Errorf("pagination test page %d size %d: %w", page, pageSize, err) - } - - var result dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result); err != nil { - return fmt.Errorf("decode pagination test page %d size %d: %w", page, pageSize, err) - } - - if len(result.Items) == 0 { - break // No more items - } - - if len(result.Items) > pageSize { - return fmt.Errorf("page size exceeded: requested %d, got %d", pageSize, len(result.Items)) - } - - // Check for duplicates - for _, item := range result.Items { - if seenIDs[item.ID] { - return fmt.Errorf("duplicate item %s found across pages", item.ID) - } - seenIDs[item.ID] = true - allItems = append(allItems, item) - } - - page++ - if page > 20 { // Safety break - break - } - } - - // Check if items are properly sorted by name - for i := 1; i < len(allItems); i++ { - if allItems[i-1].Name > allItems[i].Name { - return fmt.Errorf("items not sorted by name: %s > %s", allItems[i-1].Name, allItems[i].Name) - } - } - } - - // Test ordering by created_at - resp, err := s.makeRequest("GET", "/api/v1/services?order_by=created_at&page_size=20", nil) - if err != nil { - return fmt.Errorf("order by created_at test: %w", err) - } - - var orderedResult dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &orderedResult); err != nil { - return fmt.Errorf("decode order by created_at test: %w", err) - } - - // Clean up - delete all test services - for _, id := range createdIDs { - _, err := s.makeRequest("DELETE", "/api/v1/services/"+id, nil) - if err != nil { - // Log error but continue cleanup - fmt.Printf("Warning: failed to delete test service %s: %v\n", id, err) - } - } - - return nil -} - -func testAdvancedErrorScenarios(s *TestSuite) error { - // Test various error conditions - - // 1. Invalid JSON in request body - invalidJSON := bytes.NewBuffer([]byte(`{"name": "test", "protocol": "http", invalid json`)) - req, _ := http.NewRequest("POST", s.baseURL+"/api/v1/services", invalidJSON) - req.Header.Set("Content-Type", "application/json") - resp, err := s.client.Do(req) - if err != nil { - return fmt.Errorf("invalid JSON test request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("invalid JSON: expected 400, got %d", resp.StatusCode) - } - - // 2. Missing required fields - incompleteService := map[string]interface{}{ - "protocol": "http", - // Missing name - } - - resp, err = s.makeRequest("POST", "/api/v1/services", incompleteService) - if err != nil { - return fmt.Errorf("incomplete service test: %w", err) - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("incomplete service: expected 400, got %d", resp.StatusCode) - } - - // 3. Invalid protocol - invalidProtocolService := web.CreateUpdateServiceRequest{ - Name: "Invalid Protocol Service", - Protocol: "invalid-protocol", - Config: monitors.Config{}, - } - - resp, err = s.makeRequest("POST", "/api/v1/services", invalidProtocolService) - if err != nil { - return fmt.Errorf("invalid protocol test: %w", err) - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("invalid protocol: expected 400, got %d", resp.StatusCode) - } - - // 4. Invalid query parameters - invalidQueries := []string{ - "?status=invalid-status", - "?protocol=invalid-protocol", - "?order_by=invalid-field", - "?page=0", - "?page_size=0", - "?page_size=1000", - } - - for _, query := range invalidQueries { - resp, err = s.makeRequest("GET", "/api/v1/services"+query, nil) - if err != nil { - return fmt.Errorf("invalid query %s test: %w", query, err) - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("invalid query %s: expected 400, got %d", query, resp.StatusCode) - } - } - - // 5. Operations on non-existent service - nonExistentID := "non-existent-service-id" - - // GET non-existent service - resp, err = s.makeRequest("GET", "/api/v1/services/"+nonExistentID, nil) - if err != nil { - return fmt.Errorf("get non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("get non-existent service: expected 500, got %d", resp.StatusCode) - } - - // UPDATE non-existent service - updateReq := web.CreateUpdateServiceRequest{ - Name: "Updated Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 60000, - Timeout: 10000, - Retries: 3, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 30000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "test", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: true, - } - - resp, err = s.makeRequest("PUT", "/api/v1/services/"+nonExistentID, updateReq) - if err != nil { - return fmt.Errorf("update non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("update non-existent service: expected 500, got %d", resp.StatusCode) - } - - // DELETE non-existent service - resp, err = s.makeRequest("DELETE", "/api/v1/services/"+nonExistentID, nil) - if err != nil { - return fmt.Errorf("delete non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("delete non-existent service: expected 500, got %d", resp.StatusCode) - } - - // CHECK non-existent service - resp, err = s.makeRequest("POST", "/api/v1/services/"+nonExistentID+"/check", nil) - if err != nil { - return fmt.Errorf("check non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("check non-existent service: expected 404, got %d", resp.StatusCode) - } - - // RESOLVE non-existent service - resp, err = s.makeRequest("POST", "/api/v1/services/"+nonExistentID+"/resolve", nil) - if err != nil { - return fmt.Errorf("resolve non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("resolve non-existent service: expected 500, got %d", resp.StatusCode) - } - - // STATS for non-existent service - resp, err = s.makeRequest("GET", "/api/v1/services/"+nonExistentID+"/stats", nil) - if err != nil { - return fmt.Errorf("stats non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("stats non-existent service: expected 500, got %d", resp.StatusCode) - } - - // INCIDENTS for non-existent service - resp, err = s.makeRequest("GET", "/api/v1/services/"+nonExistentID+"/incidents", nil) - if err != nil { - return fmt.Errorf("incidents non-existent service test: %w", err) - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("incidents non-existent service: expected 400, got %d", resp.StatusCode) - } - - return nil -} - -func testStatsWithDifferentParameters(s *TestSuite) error { - // Create a dedicated service for stats testing - testService := web.CreateUpdateServiceRequest{ - Name: "Stats Test Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 30000, - Timeout: 5000, - Retries: 3, - Tags: []string{"stats-test"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test Endpoint", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: true, - } - - // Create the test service - resp, err := s.makeRequest("POST", "/api/v1/services", testService) - if err != nil { - return fmt.Errorf("create stats test service: %w", err) - } - - var createdService web.ServiceDTO - if err := s.decodeResponse(resp, &createdService); err != nil { - return fmt.Errorf("decode stats test service: %w", err) - } - - serviceID := createdService.ID - - // Ensure cleanup - defer func() { - s.makeRequest("DELETE", "/api/v1/services/"+serviceID, nil) - }() - - // Test different days parameters - daysTests := []struct { - days string - expected bool - }{ - {"1", true}, - {"7", true}, - {"30", true}, - {"365", true}, - {"0", true}, // Should work with 0 days - {"-1", true}, // Negative should be handled gracefully - {"abc", true}, // Invalid string should default to 30 - {"", true}, // Empty should default to 30 - } - - for _, test := range daysTests { - path := "/api/v1/services/" + serviceID + "/stats" - if test.days != "" { - path += "?days=" + test.days - } - - resp, err := s.makeRequest("GET", path, nil) - if err != nil { - if test.expected { - return fmt.Errorf("stats with days=%s failed: %w", test.days, err) - } - continue // Expected to fail - } - - if !test.expected { - return fmt.Errorf("stats with days=%s should have failed but didn't", test.days) - } - - var stats web.ServiceStats - if err := s.decodeResponse(resp, &stats); err != nil { - return fmt.Errorf("decode stats with days=%s: %w", test.days, err) - } - - if stats.ServiceID != serviceID { - return fmt.Errorf("stats service ID mismatch for days=%s", test.days) - } - - // Basic validation of stats - if stats.UptimePercentage < 0 || stats.UptimePercentage > 100 { - return fmt.Errorf("invalid uptime percentage for days=%s: %f", test.days, stats.UptimePercentage) - } - } - - return nil -} - -// Helper function to add extended tests to the main test suite -func getExtendedTests() []struct { - name string - fn func(*TestSuite) error -} { - return []struct { - name string - fn func(*TestSuite) error - }{ - {"TestAdvancedServiceFilters", testAdvancedServiceFilters}, - {"TestServiceCRUDCompleteFlow", testServiceCRUDCompleteFlow}, - {"TestAdvancedIncidentManagement", testAdvancedIncidentManagement}, - {"TestCompleteProtocolConfigurations", testCompleteProtocolConfigurations}, - {"TestAdvancedPaginationAndSorting", testAdvancedPaginationAndSorting}, - {"TestAdvancedErrorScenarios", testAdvancedErrorScenarios}, - {"TestStatsWithDifferentParameters", testStatsWithDifferentParameters}, - } -} diff --git a/cmd/testapi/main.go b/cmd/testapi/main.go deleted file mode 100644 index 2c08c5c..0000000 --- a/cmd/testapi/main.go +++ /dev/null @@ -1,955 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "net/url" - "os" - "path/filepath" - "reflect" - "time" - - "github.com/sxwebdev/sentinel/internal/config" - "github.com/sxwebdev/sentinel/internal/monitor" - "github.com/sxwebdev/sentinel/internal/monitors" - "github.com/sxwebdev/sentinel/internal/notifier" - "github.com/sxwebdev/sentinel/internal/receiver" - "github.com/sxwebdev/sentinel/internal/storage" - "github.com/sxwebdev/sentinel/internal/upgrader" - "github.com/sxwebdev/sentinel/internal/web" - "github.com/sxwebdev/sentinel/pkg/dbutils" - "github.com/tkcrm/mx/logger" -) - -type TestSuite struct { - server *web.Server - baseURL string - client *http.Client - stor storage.Storage - ctx context.Context - services map[string]*web.ServiceDTO - incidents map[string]*web.Incident - testServices []TestService -} - -type TestService struct { - Name string - Protocol storage.ServiceProtocolType - Tags []string - Config monitors.Config - Enabled bool -} - -var testServices = []TestService{ - { - Name: "HTTP Test Service 1", - Protocol: storage.ServiceProtocolTypeHTTP, - Tags: []string{"http", "production", "api"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Health Check", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - Enabled: true, - }, - { - Name: "HTTP Test Service 2", - Protocol: storage.ServiceProtocolTypeHTTP, - Tags: []string{"http", "staging", "web"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 3000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Home Page", - URL: "https://httpbin.org/status/404", - Method: "GET", - ExpectedStatus: 404, - }, - }, - }, - }, - Enabled: false, - }, - { - Name: "TCP Test Service", - Protocol: storage.ServiceProtocolTypeTCP, - Tags: []string{"tcp", "database", "production"}, - Config: monitors.Config{ - TCP: &monitors.TCPConfig{ - Endpoint: "google.com:80", - }, - }, - Enabled: true, - }, - { - Name: "gRPC Test Service", - Protocol: storage.ServiceProtocolTypeGRPC, - Tags: []string{"grpc", "api", "microservice"}, - Config: monitors.Config{ - GRPC: &monitors.GRPCConfig{ - Endpoint: "grpc.example.com:443", - CheckType: "connectivity", - TLS: true, - }, - }, - Enabled: true, - }, - { - Name: "Disabled Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Tags: []string{"disabled", "test"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test Endpoint", - URL: "https://httpbin.org/status/500", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - Enabled: false, - }, -} - -func main() { - if len(os.Args) < 2 || os.Args[1] != "test" { - fmt.Println("Usage: go run main.go test") - os.Exit(1) - } - - // Run tests - suite, err := setupTestSuite() - if err != nil { - log.Fatalf("Failed to setup test suite: %v", err) - } - defer suite.cleanup() - - // Run all tests - tests := []struct { - name string - fn func(*TestSuite) error - }{ - {"TestHealthCheck", testHealthCheck}, - {"TestDashboardStats", testDashboardStats}, - {"TestCreateServices", testCreateServices}, - {"TestGetServices", testGetServices}, - {"TestServiceFilters", testServiceFilters}, - {"TestServiceDetail", testServiceDetail}, - {"TestUpdateService", testUpdateService}, - {"TestServiceStats", testServiceStats}, - {"TestServiceCheck", testServiceCheck}, - {"TestIncidents", testIncidents}, - {"TestIncidentFilters", testIncidentFilters}, - {"TestTags", testTags}, - {"TestPagination", testPagination}, - {"TestErrorHandling", testErrorHandling}, - {"TestDeleteService", testDeleteService}, - } - - // Add extended tests - extendedTests := getExtendedTests() - tests = append(tests, extendedTests...) - - // Add model validation tests - modelTests := getModelValidationTests() - tests = append(tests, modelTests...) - - failed := 0 - for _, test := range tests { - fmt.Printf("Running %s...\n", test.name) - if err := test.fn(suite); err != nil { - fmt.Printf("FAIL: %s - %v\n", test.name, err) - failed++ - } else { - fmt.Printf("PASS: %s\n", test.name) - } - } - - if failed > 0 { - fmt.Printf("\n%d test(s) failed\n", failed) - os.Exit(1) - } else { - fmt.Println("\nAll tests passed!") - } -} - -func setupTestSuite() (*TestSuite, error) { - ctx := context.Background() - - // Create temporary database file - tmpDir, err := os.MkdirTemp("", "sentinel_test_*") - if err != nil { - return nil, fmt.Errorf("failed to create temp dir: %w", err) - } - - dbPath := filepath.Join(tmpDir, "test.db") - - // Load config - cfg := &config.Config{ - Database: config.DatabaseConfig{ - Path: dbPath, - }, - Monitoring: config.MonitoringConfig{ - Global: config.GlobalConfig{ - DefaultInterval: time.Minute, - DefaultTimeout: 5 * time.Second, - DefaultRetries: 5, - }, - }, - Server: config.ServerConfig{ - Host: "localhost", - Port: 8899, // Use different port for testing - BaseHost: "localhost:8899", - }, - Timezone: "UTC", - } - - l := logger.Default() - - // Initialize storage - stor, err := storage.NewStorage(storage.StorageTypeSQLite, dbPath) - if err != nil { - return nil, fmt.Errorf("failed to initialize storage: %w", err) - } - - // Initialize notifier (disabled for tests) - var notif *notifier.Notifier - - // Initialize receiver - rc := receiver.New() - if err := rc.Start(ctx); err != nil { - return nil, fmt.Errorf("failed to start receiver: %w", err) - } - - // Initialize upgrader if configured - upgr, err := upgrader.New(l, cfg.Upgrader) - if err != nil { - return nil, fmt.Errorf("failed to initialize upgrader: %w", err) - } - - // Create monitor service - monitorService := monitor.NewMonitorService(stor, cfg, notif, rc) - - // Create web server - webServer, err := web.NewServer(l, cfg, web.ServerInfo{}, monitorService, stor, rc, upgr) - if err != nil { - return nil, fmt.Errorf("failed to create web server: %w", err) - } - - // Start server in background - go func() { - if err := webServer.App().Listen(fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)); err != nil { - log.Printf("Server error: %v", err) - } - }() - - // Wait a bit for server to start - time.Sleep(100 * time.Millisecond) - - suite := &TestSuite{ - server: webServer, - baseURL: fmt.Sprintf("http://%s:%d", cfg.Server.Host, cfg.Server.Port), - client: &http.Client{Timeout: 10 * time.Second}, - stor: stor, - ctx: ctx, - services: make(map[string]*web.ServiceDTO), - incidents: make(map[string]*web.Incident), - testServices: testServices, - } - - return suite, nil -} - -func (s *TestSuite) cleanup() { - if s.stor != nil { - s.stor.Stop(context.Background()) - } -} - -func (s *TestSuite) makeRequest(method, path string, body interface{}) (*http.Response, error) { - var reqBody io.Reader - if body != nil { - jsonBody, err := json.Marshal(body) - if err != nil { - return nil, err - } - reqBody = bytes.NewBuffer(jsonBody) - } - - req, err := http.NewRequest(method, s.baseURL+path, reqBody) - if err != nil { - return nil, err - } - - if body != nil { - req.Header.Set("Content-Type", "application/json") - } - - return s.client.Do(req) -} - -func (s *TestSuite) decodeResponse(resp *http.Response, target interface{}) error { - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - if resp.StatusCode >= 400 { - var errResp web.ErrorResponse - if err := json.Unmarshal(body, &errResp); err != nil { - return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) - } - return fmt.Errorf("HTTP %d: %s", resp.StatusCode, errResp.Error) - } - - return json.Unmarshal(body, target) -} - -// Test cases implementation - -func testHealthCheck(s *TestSuite) error { - resp, err := s.makeRequest("GET", "/", nil) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("expected status 200, got %d", resp.StatusCode) - } - - return nil -} - -func testDashboardStats(s *TestSuite) error { - resp, err := s.makeRequest("GET", "/api/v1/dashboard/stats", nil) - if err != nil { - return err - } - - var stats web.DashboardStats - if err := s.decodeResponse(resp, &stats); err != nil { - return err - } - - // Basic validation - if stats.TotalServices < 0 { - return fmt.Errorf("total services should be >= 0, got %d", stats.TotalServices) - } - - return nil -} - -func testCreateServices(s *TestSuite) error { - for i, testSvc := range s.testServices { - createReq := web.CreateUpdateServiceRequest{ - Name: testSvc.Name, - Protocol: testSvc.Protocol, - Interval: 30000, // 30s - Timeout: 5000, // 5s - Retries: 3, - Tags: testSvc.Tags, - Config: testSvc.Config, - IsEnabled: testSvc.Enabled, - } - - resp, err := s.makeRequest("POST", "/api/v1/services", createReq) - if err != nil { - return fmt.Errorf("service %d: %w", i, err) - } - - var service web.ServiceDTO - if err := s.decodeResponse(resp, &service); err != nil { - return fmt.Errorf("service %d: %w", i, err) - } - - // Validate response - if service.ID == "" { - return fmt.Errorf("service %d: missing ID", i) - } - if service.Name != testSvc.Name { - return fmt.Errorf("service %d: name mismatch", i) - } - if service.Protocol != testSvc.Protocol { - return fmt.Errorf("service %d: protocol mismatch", i) - } - if !reflect.DeepEqual(service.Tags, testSvc.Tags) { - return fmt.Errorf("service %d: tags mismatch", i) - } - if service.IsEnabled != testSvc.Enabled { - return fmt.Errorf("service %d: enabled status mismatch", i) - } - - s.services[service.Name] = &service - } - - return nil -} - -func testGetServices(s *TestSuite) error { - resp, err := s.makeRequest("GET", "/api/v1/services", nil) - if err != nil { - return err - } - - var result dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - if int(result.Count) != len(s.testServices) { - return fmt.Errorf("expected %d services, got %d", len(s.testServices), result.Count) - } - - if len(result.Items) != len(s.testServices) { - return fmt.Errorf("expected %d items, got %d", len(s.testServices), len(result.Items)) - } - - return nil -} - -func testServiceFilters(s *TestSuite) error { - // Test filter by name - resp, err := s.makeRequest("GET", "/api/v1/services?name=HTTP Test Service 1", nil) - if err != nil { - return err - } - - var result dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - if result.Count != 1 { - return fmt.Errorf("name filter: expected 1 service, got %d", result.Count) - } - if result.Items[0].Name != "HTTP Test Service 1" { - return fmt.Errorf("name filter: wrong service returned") - } - - // Test filter by protocol - resp, err = s.makeRequest("GET", "/api/v1/services?protocol=http", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - expectedHTTPServices := 0 - for _, svc := range s.testServices { - if svc.Protocol == storage.ServiceProtocolTypeHTTP { - expectedHTTPServices++ - } - } - - if int(result.Count) != expectedHTTPServices { - return fmt.Errorf("protocol filter: expected %d HTTP services, got %d", expectedHTTPServices, result.Count) - } - - // Test filter by tags - resp, err = s.makeRequest("GET", "/api/v1/services?tags=production", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - expectedProdServices := 0 - for _, svc := range s.testServices { - for _, tag := range svc.Tags { - if tag == "production" { - expectedProdServices++ - break - } - } - } - - if int(result.Count) != expectedProdServices { - return fmt.Errorf("tags filter: expected %d production services, got %d", expectedProdServices, result.Count) - } - - // Test filter by enabled status - resp, err = s.makeRequest("GET", "/api/v1/services?is_enabled=true", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - expectedEnabledServices := 0 - for _, svc := range s.testServices { - if svc.Enabled { - expectedEnabledServices++ - } - } - - if int(result.Count) != expectedEnabledServices { - return fmt.Errorf("enabled filter: expected %d enabled services, got %d", expectedEnabledServices, result.Count) - } - - // Test ordering - resp, err = s.makeRequest("GET", "/api/v1/services?order_by=name", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - // Check if results are ordered by name - for i := 1; i < len(result.Items); i++ { - if result.Items[i-1].Name > result.Items[i].Name { - return fmt.Errorf("services are not ordered by name") - } - } - - // Test multiple filters - resp, err = s.makeRequest("GET", "/api/v1/services?protocol=http&tags=production&is_enabled=true", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - // Validate each service matches all filters - for _, item := range result.Items { - if item.Protocol != storage.ServiceProtocolTypeHTTP { - return fmt.Errorf("multiple filters: service %s doesn't match protocol filter", item.Name) - } - if !item.IsEnabled { - return fmt.Errorf("multiple filters: service %s doesn't match enabled filter", item.Name) - } - hasProdTag := false - for _, tag := range item.Tags { - if tag == "production" { - hasProdTag = true - break - } - } - if !hasProdTag { - return fmt.Errorf("multiple filters: service %s doesn't have production tag", item.Name) - } - } - - return nil -} - -func testServiceDetail(s *TestSuite) error { - // Get first service - var serviceID string - for _, svc := range s.services { - serviceID = svc.ID - break - } - - resp, err := s.makeRequest("GET", "/api/v1/services/"+serviceID, nil) - if err != nil { - return err - } - - var service web.ServiceDTO - if err := s.decodeResponse(resp, &service); err != nil { - return err - } - - if service.ID != serviceID { - return fmt.Errorf("service detail: ID mismatch") - } - - // Test non-existent service - resp, err = s.makeRequest("GET", "/api/v1/services/non-existent", nil) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("expected 500 for non-existent service, got %d", resp.StatusCode) - } - - return nil -} - -func testUpdateService(s *TestSuite) error { - // Get first service - var service *web.ServiceDTO - for _, svc := range s.services { - service = svc - break - } - - // Update service - updateReq := web.CreateUpdateServiceRequest{ - Name: service.Name + " Updated", - Protocol: service.Protocol, - Interval: 60000, // 60s - Timeout: 10000, // 10s - Retries: 5, - Tags: append(service.Tags, "updated"), - Config: service.Config, - IsEnabled: !service.IsEnabled, - } - - resp, err := s.makeRequest("PUT", "/api/v1/services/"+service.ID, updateReq) - if err != nil { - return err - } - - var updatedService web.ServiceDTO - if err := s.decodeResponse(resp, &updatedService); err != nil { - return err - } - - // Validate updates - if updatedService.Name != updateReq.Name { - return fmt.Errorf("update: name not updated") - } - if updatedService.Interval != updateReq.Interval { - return fmt.Errorf("update: interval not updated") - } - if updatedService.Timeout != updateReq.Timeout { - return fmt.Errorf("update: timeout not updated") - } - if updatedService.Retries != updateReq.Retries { - return fmt.Errorf("update: retries not updated") - } - if updatedService.IsEnabled != updateReq.IsEnabled { - return fmt.Errorf("update: enabled status not updated") - } - - return nil -} - -func testServiceStats(s *TestSuite) error { - // Get first service - var serviceID string - for _, svc := range s.services { - serviceID = svc.ID - break - } - - // Test service stats - resp, err := s.makeRequest("GET", "/api/v1/services/"+serviceID+"/stats", nil) - if err != nil { - return err - } - - var stats web.ServiceStats - if err := s.decodeResponse(resp, &stats); err != nil { - return err - } - - if stats.ServiceID != serviceID { - return fmt.Errorf("stats: service ID mismatch") - } - - // Test with custom days parameter - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/stats?days=7", nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &stats); err != nil { - return err - } - - return nil -} - -func testServiceCheck(s *TestSuite) error { - // Get first service - var serviceID string - for _, svc := range s.services { - serviceID = svc.ID - break - } - - // Trigger check - resp, err := s.makeRequest("POST", "/api/v1/services/"+serviceID+"/check", nil) - if err != nil { - return err - } - - var result web.SuccessResponse - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - if result.Message == "" { - return fmt.Errorf("check: empty success message") - } - - return nil -} - -func testIncidents(s *TestSuite) error { - // Test get all incidents - resp, err := s.makeRequest("GET", "/api/v1/incidents", nil) - if err != nil { - return err - } - - var incidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &incidents); err != nil { - return err - } - - // Get first service for service-specific incidents - var serviceID string - for _, svc := range s.services { - serviceID = svc.ID - break - } - - // Test service incidents - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents", nil) - if err != nil { - return err - } - - var serviceIncidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &serviceIncidents); err != nil { - return err - } - - // All incidents should belong to the service - for _, incident := range serviceIncidents.Items { - if incident.ServiceID != serviceID { - return fmt.Errorf("incident %s doesn't belong to service %s", incident.ID, serviceID) - } - } - - return nil -} - -func testIncidentFilters(s *TestSuite) error { - // Get first service - var serviceID string - for _, svc := range s.services { - serviceID = svc.ID - break - } - - // Test filter by resolved status - resp, err := s.makeRequest("GET", "/api/v1/services/"+serviceID+"/incidents?resolved=false", nil) - if err != nil { - return err - } - - var incidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &incidents); err != nil { - return err - } - - // All incidents should be unresolved - for _, incident := range incidents.Items { - if incident.Resolved { - return fmt.Errorf("incident filter: found resolved incident when filtering for unresolved") - } - } - - // Test time-based filtering - now := time.Now() - yesterday := now.AddDate(0, 0, -1) - resp, err = s.makeRequest("GET", "/api/v1/incidents?start_time="+url.QueryEscape(yesterday.Format(time.RFC3339)), nil) - if err != nil { - return err - } - - if err := s.decodeResponse(resp, &incidents); err != nil { - return err - } - - return nil -} - -func testTags(s *TestSuite) error { - // Test get all tags - resp, err := s.makeRequest("GET", "/api/v1/tags", nil) - if err != nil { - return err - } - - var tags []string - if err := s.decodeResponse(resp, &tags); err != nil { - return err - } - - // Collect expected tags - expectedTags := make(map[string]bool) - for _, svc := range s.testServices { - for _, tag := range svc.Tags { - expectedTags[tag] = true - } - } - - // Check if all expected tags are present - tagSet := make(map[string]bool) - for _, tag := range tags { - tagSet[tag] = true - } - - for expectedTag := range expectedTags { - if !tagSet[expectedTag] { - return fmt.Errorf("tags: missing expected tag %s", expectedTag) - } - } - - // Test get tags with count - resp, err = s.makeRequest("GET", "/api/v1/tags/count", nil) - if err != nil { - return err - } - - var tagsWithCount map[string]int - if err := s.decodeResponse(resp, &tagsWithCount); err != nil { - return err - } - - // Validate counts - for tag, count := range tagsWithCount { - if count <= 0 { - return fmt.Errorf("tags count: tag %s has invalid count %d", tag, count) - } - } - - return nil -} - -func testPagination(s *TestSuite) error { - // Test services pagination - resp, err := s.makeRequest("GET", "/api/v1/services?page=1&page_size=2", nil) - if err != nil { - return err - } - - var result dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result); err != nil { - return err - } - - if len(result.Items) > 2 { - return fmt.Errorf("pagination: page size not respected, got %d items", len(result.Items)) - } - - // Test second page - resp, err = s.makeRequest("GET", "/api/v1/services?page=2&page_size=2", nil) - if err != nil { - return err - } - - var result2 dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result2); err != nil { - return err - } - - // Make sure pages don't overlap - if len(result.Items) > 0 && len(result2.Items) > 0 { - for _, item1 := range result.Items { - for _, item2 := range result2.Items { - if item1.ID == item2.ID { - return fmt.Errorf("pagination: services overlap between pages") - } - } - } - } - - return nil -} - -func testErrorHandling(s *TestSuite) error { - // Test invalid service creation - invalidService := web.CreateUpdateServiceRequest{ - Name: "", // Empty name should fail - Protocol: storage.ServiceProtocolTypeHTTP, - } - - resp, err := s.makeRequest("POST", "/api/v1/services", invalidService) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("error handling: expected 400 for invalid service, got %d", resp.StatusCode) - } - - // Test invalid query parameters - resp, err = s.makeRequest("GET", "/api/v1/services?status=invalid", nil) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("error handling: expected 400 for invalid status filter, got %d", resp.StatusCode) - } - - // Test invalid pagination - resp, err = s.makeRequest("GET", "/api/v1/services?page=0", nil) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("error handling: expected 400 for invalid page, got %d", resp.StatusCode) - } - - return nil -} - -func testDeleteService(s *TestSuite) error { - // Get a service to delete - var serviceID string - for _, svc := range s.services { - serviceID = svc.ID - break - } - - // Delete service - resp, err := s.makeRequest("DELETE", "/api/v1/services/"+serviceID, nil) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusNoContent { - return fmt.Errorf("delete: expected 204, got %d", resp.StatusCode) - } - - // Verify service is deleted - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID, nil) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusInternalServerError { - return fmt.Errorf("delete: service still exists after deletion") - } - - return nil -} diff --git a/cmd/testapi/model_tests.go b/cmd/testapi/model_tests.go deleted file mode 100644 index b0d8275..0000000 --- a/cmd/testapi/model_tests.go +++ /dev/null @@ -1,602 +0,0 @@ -package main - -import ( - "fmt" - "reflect" - - "github.com/sxwebdev/sentinel/internal/monitors" - "github.com/sxwebdev/sentinel/internal/storage" - "github.com/sxwebdev/sentinel/internal/web" - "github.com/sxwebdev/sentinel/pkg/dbutils" -) - -// Model validation tests - -func testModelsValidation(s *TestSuite) error { - // Test HTTP config validation - httpConfig := monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Valid Endpoint", - URL: "https://example.com/api", - Method: "GET", - ExpectedStatus: 200, - Headers: map[string]string{ - "Authorization": "Bearer token", - "Content-Type": "application/json", - }, - Body: `{"test": true}`, - JSONPath: "$.status", - Username: "user", - Password: "pass", - }, - }, - Condition: "all", - }, - } - - if err := httpConfig.Validate(storage.ServiceProtocolTypeHTTP); err != nil { - return fmt.Errorf("valid HTTP config should not fail validation: %w", err) - } - - // Test invalid HTTP config - missing endpoints - invalidHTTPConfig := monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{}, // Empty endpoints - }, - } - - if err := invalidHTTPConfig.Validate(storage.ServiceProtocolTypeHTTP); err == nil { - return fmt.Errorf("HTTP config with empty endpoints should fail validation") - } - - // Test invalid HTTP config - invalid endpoint - invalidEndpointConfig := monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "", // Empty name - URL: "invalid-url", - Method: "INVALID", - ExpectedStatus: 999, // Invalid status - }, - }, - }, - } - - if err := invalidEndpointConfig.Validate(storage.ServiceProtocolTypeHTTP); err == nil { - return fmt.Errorf("HTTP config with invalid endpoint should fail validation") - } - - // Test TCP config validation - tcpConfig := monitors.Config{ - TCP: &monitors.TCPConfig{ - Endpoint: "example.com:80", - SendData: "test data", - ExpectData: "expected response", - }, - } - - if err := tcpConfig.Validate(storage.ServiceProtocolTypeTCP); err != nil { - return fmt.Errorf("valid TCP config should not fail validation: %w", err) - } - - // Test invalid TCP config - missing endpoint - invalidTCPConfig := monitors.Config{ - TCP: &monitors.TCPConfig{ - Endpoint: "", // Empty endpoint - }, - } - - if err := invalidTCPConfig.Validate(storage.ServiceProtocolTypeTCP); err == nil { - return fmt.Errorf("TCP config with empty endpoint should fail validation") - } - - // Test gRPC config validation - grpcConfig := monitors.Config{ - GRPC: &monitors.GRPCConfig{ - Endpoint: "grpc.example.com:443", - CheckType: "health", - ServiceName: "ExampleService", - TLS: true, - InsecureTLS: false, - }, - } - - if err := grpcConfig.Validate(storage.ServiceProtocolTypeGRPC); err != nil { - return fmt.Errorf("valid gRPC config should not fail validation: %w", err) - } - - // Test invalid gRPC config - missing endpoint - invalidGRPCConfig := monitors.Config{ - GRPC: &monitors.GRPCConfig{ - Endpoint: "", // Empty endpoint - CheckType: "invalid-type", - }, - } - - if err := invalidGRPCConfig.Validate(storage.ServiceProtocolTypeGRPC); err == nil { - return fmt.Errorf("gRPC config with empty endpoint should fail validation") - } - - // Test config with wrong protocol - httpConfigForTCP := monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test", - URL: "https://example.com", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - } - - if err := httpConfigForTCP.Validate(storage.ServiceProtocolTypeTCP); err == nil { - return fmt.Errorf("HTTP config should fail validation for TCP protocol") - } - - return nil -} - -func testServiceDTOFields(s *TestSuite) error { - // Create a test service to validate DTO conversion - testService := web.CreateUpdateServiceRequest{ - Name: "DTO Test Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 30000, - Timeout: 5000, - Retries: 3, - Tags: []string{"dto", "test", "validation"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test Endpoint", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: true, - } - - // Create service - resp, err := s.makeRequest("POST", "/api/v1/services", testService) - if err != nil { - return fmt.Errorf("create DTO test service: %w", err) - } - - var service web.ServiceDTO - if err := s.decodeResponse(resp, &service); err != nil { - return fmt.Errorf("decode DTO test service: %w", err) - } - - // Validate all DTO fields - if service.ID == "" { - return fmt.Errorf("service ID should not be empty") - } - if service.Name != testService.Name { - return fmt.Errorf("service name mismatch") - } - if service.Protocol != testService.Protocol { - return fmt.Errorf("service protocol mismatch") - } - if service.Interval != testService.Interval { - return fmt.Errorf("service interval mismatch") - } - if service.Timeout != testService.Timeout { - return fmt.Errorf("service timeout mismatch") - } - if service.Retries != testService.Retries { - return fmt.Errorf("service retries mismatch") - } - if !reflect.DeepEqual(service.Tags, testService.Tags) { - return fmt.Errorf("service tags mismatch") - } - if service.IsEnabled != testService.IsEnabled { - return fmt.Errorf("service enabled status mismatch") - } - - // Check config conversion - if service.Config.HTTP == nil { - return fmt.Errorf("HTTP config should not be nil") - } - if service.Config.HTTP.Timeout != testService.Config.HTTP.Timeout { - return fmt.Errorf("HTTP config timeout mismatch") - } - if len(service.Config.HTTP.Endpoints) != len(testService.Config.HTTP.Endpoints) { - return fmt.Errorf("HTTP endpoints count mismatch") - } - - // Check endpoint details - endpoint := service.Config.HTTP.Endpoints[0] - originalEndpoint := testService.Config.HTTP.Endpoints[0] - if endpoint.Name != originalEndpoint.Name { - return fmt.Errorf("endpoint name mismatch") - } - if endpoint.URL != originalEndpoint.URL { - return fmt.Errorf("endpoint URL mismatch") - } - if endpoint.Method != originalEndpoint.Method { - return fmt.Errorf("endpoint method mismatch") - } - if endpoint.ExpectedStatus != originalEndpoint.ExpectedStatus { - return fmt.Errorf("endpoint expected status mismatch") - } - - // Check state fields are present (even if default values) - if service.ActiveIncidents < 0 { - return fmt.Errorf("active incidents should be >= 0") - } - if service.TotalIncidents < 0 { - return fmt.Errorf("total incidents should be >= 0") - } - if service.ConsecutiveFails < 0 { - return fmt.Errorf("consecutive fails should be >= 0") - } - if service.ConsecutiveSuccess < 0 { - return fmt.Errorf("consecutive success should be >= 0") - } - if service.TotalChecks < 0 { - return fmt.Errorf("total checks should be >= 0") - } - - // Status should be one of the valid values - validStatuses := []storage.ServiceStatus{ - storage.StatusUnknown, - storage.StatusUp, - storage.StatusDown, - storage.StatusMaintenance, - } - isValidStatus := false - for _, validStatus := range validStatuses { - if service.Status == validStatus { - isValidStatus = true - break - } - } - if !isValidStatus { - return fmt.Errorf("invalid service status: %s", service.Status) - } - - // Clean up - if _, err := s.makeRequest("DELETE", "/api/v1/services/"+service.ID, nil); err != nil { - return fmt.Errorf("cleanup DTO test service: %w", err) - } - - return nil -} - -func testIncidentFields(s *TestSuite) error { - // Get incidents to validate structure - resp, err := s.makeRequest("GET", "/api/v1/incidents?page_size=1", nil) - if err != nil { - return fmt.Errorf("get incidents for validation: %w", err) - } - - var incidents dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &incidents); err != nil { - return fmt.Errorf("decode incidents for validation: %w", err) - } - - if len(incidents.Items) > 0 { - incident := incidents.Items[0] - - // Validate incident fields - if incident.ID == "" { - return fmt.Errorf("incident ID should not be empty") - } - if incident.ServiceID == "" { - return fmt.Errorf("incident service ID should not be empty") - } - if incident.ServiceName == "" { - return fmt.Errorf("incident service name should not be empty") - } - if incident.Status == "" { - return fmt.Errorf("incident status should not be empty") - } - if incident.Message == "" { - return fmt.Errorf("incident message should not be empty") - } - - // StartedAt should be a valid time - if incident.StartedAt.IsZero() { - return fmt.Errorf("incident started at should not be zero") - } - - // If resolved, ResolvedAt should not be nil - if incident.Resolved && incident.ResolvedAt == nil { - return fmt.Errorf("resolved incident should have resolved at time") - } - - // If not resolved, ResolvedAt should be nil - if !incident.Resolved && incident.ResolvedAt != nil { - return fmt.Errorf("unresolved incident should not have resolved at time") - } - - // Duration should be a valid string - if incident.Duration == "" { - return fmt.Errorf("incident duration should not be empty") - } - } - - return nil -} - -func testResponseModels(s *TestSuite) error { - // Test ErrorResponse - resp, err := s.makeRequest("GET", "/api/v1/services/non-existent", nil) - if err != nil { - return fmt.Errorf("test error response: %w", err) - } - - if resp.StatusCode >= 400 { - var errorResp web.ErrorResponse - if err := s.decodeResponse(resp, &errorResp); err == nil { - if errorResp.Error == "" { - return fmt.Errorf("error response should have non-empty error message") - } - } - } - - // Test SuccessResponse by triggering a check - // Create a test service first to ensure we have a valid service ID - createReq := web.CreateUpdateServiceRequest{ - Name: "Test Service for Response Models", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 60000, - Timeout: 10000, - Retries: 3, - Tags: []string{"test", "response-models"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 30000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "test", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: true, - } - - resp, err = s.makeRequest("POST", "/api/v1/services", createReq) - if err != nil { - return fmt.Errorf("create test service: %w", err) - } - - var createResponse web.ServiceDTO - if err := s.decodeResponse(resp, &createResponse); err != nil { - return fmt.Errorf("decode create service response: %w", err) - } - - serviceID := createResponse.ID - - // Now test the service check endpoint - resp, err = s.makeRequest("POST", "/api/v1/services/"+serviceID+"/check", nil) - if err != nil { - return fmt.Errorf("test success response: %w", err) - } - - var successResp web.SuccessResponse - if err := s.decodeResponse(resp, &successResp); err != nil { - return fmt.Errorf("decode success response: %w", err) - } - - if successResp.Message == "" { - return fmt.Errorf("success response should have non-empty message") - } - - // Clean up: delete the test service - _, err = s.makeRequest("DELETE", "/api/v1/services/"+serviceID, nil) - if err != nil { - return fmt.Errorf("cleanup test service: %w", err) - } - - // Test DashboardStats - resp, err = s.makeRequest("GET", "/api/v1/dashboard/stats", nil) - if err != nil { - return fmt.Errorf("test dashboard stats: %w", err) - } - - var stats web.DashboardStats - if err := s.decodeResponse(resp, &stats); err != nil { - return fmt.Errorf("decode dashboard stats: %w", err) - } - - // Validate stats fields - if stats.TotalServices < 0 { - return fmt.Errorf("total services should be >= 0") - } - if stats.ServicesUp < 0 { - return fmt.Errorf("services up should be >= 0") - } - if stats.ServicesDown < 0 { - return fmt.Errorf("services down should be >= 0") - } - if stats.ServicesUnknown < 0 { - return fmt.Errorf("services unknown should be >= 0") - } - if stats.ActiveIncidents < 0 { - return fmt.Errorf("active incidents should be >= 0") - } - if stats.UptimePercentage < 0 || stats.UptimePercentage > 100 { - return fmt.Errorf("uptime percentage should be between 0 and 100") - } - if stats.TotalChecks < 0 { - return fmt.Errorf("total checks should be >= 0") - } - if stats.ChecksPerMinute < 0 { - return fmt.Errorf("checks per minute should be >= 0") - } - - // Protocols map should contain valid protocols - for protocol, count := range stats.Protocols { - validProtocols := []storage.ServiceProtocolType{ - storage.ServiceProtocolTypeHTTP, - storage.ServiceProtocolTypeTCP, - storage.ServiceProtocolTypeGRPC, - } - isValid := false - for _, validProtocol := range validProtocols { - if protocol == validProtocol { - isValid = true - break - } - } - if !isValid { - return fmt.Errorf("invalid protocol in stats: %s", protocol) - } - if count < 0 { - return fmt.Errorf("protocol count should be >= 0") - } - } - - return nil -} - -func testServiceStatsModel(s *TestSuite) error { - // Create a dedicated service for stats model testing - testService := web.CreateUpdateServiceRequest{ - Name: "Stats Model Test Service", - Protocol: storage.ServiceProtocolTypeHTTP, - Interval: 30000, - Timeout: 5000, - Retries: 3, - Tags: []string{"stats-model-test"}, - Config: monitors.Config{ - HTTP: &monitors.HTTPConfig{ - Timeout: 5000, - Endpoints: []monitors.EndpointConfig{ - { - Name: "Test Endpoint", - URL: "https://httpbin.org/status/200", - Method: "GET", - ExpectedStatus: 200, - }, - }, - }, - }, - IsEnabled: true, - } - - // Create the test service - resp, err := s.makeRequest("POST", "/api/v1/services", testService) - if err != nil { - return fmt.Errorf("create stats model test service: %w", err) - } - - var createdService web.ServiceDTO - if err := s.decodeResponse(resp, &createdService); err != nil { - return fmt.Errorf("decode stats model test service: %w", err) - } - - serviceID := createdService.ID - - // Ensure cleanup - defer func() { - s.makeRequest("DELETE", "/api/v1/services/"+serviceID, nil) - }() - - resp, err = s.makeRequest("GET", "/api/v1/services/"+serviceID+"/stats", nil) - if err != nil { - return fmt.Errorf("get service stats: %w", err) - } - - var stats web.ServiceStats - if err := s.decodeResponse(resp, &stats); err != nil { - return fmt.Errorf("decode service stats: %w", err) - } - - // Validate stats fields - if stats.ServiceID != serviceID { - return fmt.Errorf("service stats service ID mismatch") - } - if stats.TotalIncidents < 0 { - return fmt.Errorf("total incidents should be >= 0") - } - if stats.UptimePercentage < 0 || stats.UptimePercentage > 100 { - return fmt.Errorf("uptime percentage should be between 0 and 100") - } - if stats.TotalDowntime < 0 { - return fmt.Errorf("total downtime should be >= 0") - } - if stats.Period <= 0 { - return fmt.Errorf("period should be > 0") - } - if stats.AvgResponseTime < 0 { - return fmt.Errorf("avg response time should be >= 0") - } - - return nil -} - -func testPaginationResponseModel(s *TestSuite) error { - // Test services pagination response - resp, err := s.makeRequest("GET", "/api/v1/services?page=1&page_size=3", nil) - if err != nil { - return fmt.Errorf("get paginated services: %w", err) - } - - var result dbutils.FindResponseWithCount[web.ServiceDTO] - if err := s.decodeResponse(resp, &result); err != nil { - return fmt.Errorf("decode paginated services: %w", err) - } - - // Validate pagination structure - // Count is uint32, so it's always >= 0 - if len(result.Items) > 3 { - return fmt.Errorf("items should not exceed page size") - } - - // Test incidents pagination response - resp, err = s.makeRequest("GET", "/api/v1/incidents?page=1&page_size=5", nil) - if err != nil { - return fmt.Errorf("get paginated incidents: %w", err) - } - - var incidentResult dbutils.FindResponseWithCount[web.Incident] - if err := s.decodeResponse(resp, &incidentResult); err != nil { - return fmt.Errorf("decode paginated incidents: %w", err) - } - - // Validate pagination structure - // Count is uint32, so it's always >= 0 - if len(incidentResult.Items) > 5 { - return fmt.Errorf("incident items should not exceed page size") - } - - return nil -} - -// Helper function to add model validation tests -func getModelValidationTests() []struct { - name string - fn func(*TestSuite) error -} { - return []struct { - name string - fn func(*TestSuite) error - }{ - {"TestModelsValidation", testModelsValidation}, - {"TestServiceDTOFields", testServiceDTOFields}, - {"TestIncidentFields", testIncidentFields}, - {"TestResponseModels", testResponseModels}, - {"TestServiceStatsModel", testServiceStatsModel}, - {"TestPaginationResponseModel", testPaginationResponseModel}, - } -} diff --git a/cmd/testserver/main.go b/cmd/testserver/main.go new file mode 100644 index 0000000..5de25d3 --- /dev/null +++ b/cmd/testserver/main.go @@ -0,0 +1,300 @@ +package main + +import ( + "bufio" + "context" + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/sxwebdev/sentinel/internal/utils" + "google.golang.org/grpc" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/reflection" +) + +var ( + enableTCP = flag.Bool("tcp", false, "Enable TCP server") + enableGRPC = flag.Bool("grpc", false, "Enable gRPC server") + enableHTTP = flag.Bool("http", false, "Enable HTTP server") + tcpPort = flag.Int("tcp-port", 12345, "TCP server port") + grpcPort = flag.Int("grpc-port", 50051, "gRPC server port") + httpPort = flag.Int("http-port", 8085, "HTTP server port") +) + +func main() { + flag.Parse() + + // Check if at least one server is enabled + if !*enableTCP && !*enableGRPC && !*enableHTTP { + log.Fatal("At least one server must be enabled. Use -tcp, -grpc, or -http flags.") + } + + if err := run(); err != nil { + log.Fatalf("Failed to run servers: %v", err) + } +} + +func run() error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + var wg sync.WaitGroup + errChan := make(chan error, 3) // Buffer for all possible servers + + // Start enabled servers + if *enableTCP { + wg.Go(func() { + log.Printf("Starting TCP server on port %d", *tcpPort) + if err := runTCPServer(ctx, *tcpPort); err != nil { + errChan <- fmt.Errorf("TCP server error: %w", err) + } + }) + } + + if *enableGRPC { + wg.Go(func() { + log.Printf("Starting gRPC server on port %d", *grpcPort) + if err := runGRPCServer(ctx, *grpcPort); err != nil { + errChan <- fmt.Errorf("gRPC server error: %w", err) + } + }) + } + + if *enableHTTP { + wg.Go(func() { + log.Printf("Starting HTTP server on port %d", *httpPort) + if err := runHTTPServer(ctx, *httpPort); err != nil { + errChan <- fmt.Errorf("HTTP server error: %w", err) + } + }) + } + + // Wait for interrupt signal or server error + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + select { + case <-sigChan: + log.Println("Received interrupt signal, shutting down...") + cancel() // Signal all servers to stop + case err := <-errChan: + log.Printf("Server error: %v", err) + cancel() + return err + } + + // Wait for all servers to shut down gracefully + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + + select { + case <-done: + log.Println("All servers shut down gracefully") + case <-time.After(10 * time.Second): + log.Println("Timeout waiting for servers to shut down") + } + + return nil +} + +// runTCPServer runs the TCP server with original logic from cmd/tcpserver/main.go +func runTCPServer(ctx context.Context, port int) error { + addr := fmt.Sprintf("localhost:%d", port) + listener, err := net.Listen("tcp", addr) + if err != nil { + return fmt.Errorf("failed to start TCP server: %w", err) + } + defer listener.Close() + + // Channel to signal when to stop accepting new connections + stopChan := make(chan struct{}) + + // Goroutine to handle context cancellation + go func() { + <-ctx.Done() + close(stopChan) + listener.Close() // This will cause Accept() to return an error + }() + + for { + select { + case <-stopChan: + return nil + default: + } + + conn, err := listener.Accept() + if err != nil { + select { + case <-stopChan: + return nil // Expected error due to context cancellation + default: + log.Printf("Failed to accept TCP connection: %v", err) + continue + } + } + + // log.Printf("TCP client connected: %s", conn.RemoteAddr()) + go handleTCPConnection(conn) + } +} + +// handleTCPConnection handles individual TCP connections (original logic from cmd/tcpserver/main.go) +func handleTCPConnection(conn net.Conn) { + defer func() { + conn.Close() + // log.Printf("TCP connection closed: %s\n", conn.RemoteAddr()) + }() + + // Set connection timeout - close if no data received in 5 seconds + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + + var accumulated []byte + reader := bufio.NewReader(conn) + + for { + buffer := make([]byte, 1024) + n, err := reader.Read(buffer) + if err != nil { + if utils.IsErrTimeout(err) { + break + } + log.Printf("Failed to read from TCP connection: %v", err) + return + } + + if n == 0 { + break + } + + // Accumulate received data + accumulated = append(accumulated, buffer[:n]...) + + // If no more data buffered, client likely finished sending + if reader.Buffered() == 0 { + break + } + } + + // Now process the complete message + if len(accumulated) == 0 { + log.Println("No data received from TCP client") + return + } + + msg := string(accumulated) + // log.Printf("TCP complete message received: %s", msg) + + // Simple ping-pong protocol - exact match + switch msg { + case "ping": + // log.Println("TCP: Sending pong") + _, err := conn.Write([]byte("pong")) + if err != nil { + log.Printf("Failed to send TCP response: %v", err) + } + case "noresponse": + log.Println("TCP: No response expected") + default: + log.Printf("TCP: Unknown message '%s', sending ok", msg) + _, err := conn.Write([]byte("ok")) + if err != nil { + log.Printf("Failed to send TCP response: %v", err) + } + } +} + +// runGRPCServer runs the gRPC server with original logic from cmd/grpcserver/main.go +func runGRPCServer(ctx context.Context, port int) error { + addr := fmt.Sprintf("localhost:%d", port) + + lis, err := net.Listen("tcp", addr) + if err != nil { + return fmt.Errorf("failed to listen: %w", err) + } + + // Create gRPC server + grpcServer := grpc.NewServer() + + // Create and register health server + healthServer := health.NewServer() + grpc_health_v1.RegisterHealthServer(grpcServer, healthServer) + + // Register gRPC reflection service for debugging + reflection.Register(grpcServer) + + // Set initial serving status + healthServer.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING) + healthServer.SetServingStatus("health", grpc_health_v1.HealthCheckResponse_SERVING) + healthServer.SetServingStatus("test-service", grpc_health_v1.HealthCheckResponse_SERVING) + + // Start server in a goroutine + serverErr := make(chan error, 1) + go func() { + if err := grpcServer.Serve(lis); err != nil { + serverErr <- fmt.Errorf("failed to serve: %w", err) + } + }() + + // Wait for context cancellation or server error + select { + case <-ctx.Done(): + // Graceful shutdown + log.Println("Shutting down gRPC server...") + grpcServer.GracefulStop() + return nil + case err := <-serverErr: + return err + } +} + +// runHTTPServer runs a simple HTTP server using standard library +func runHTTPServer(ctx context.Context, port int) error { + mux := http.NewServeMux() + + // Simple handler that returns {"ok":true} + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"ok":true}`)) + }) + + server := &http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: mux, + } + + // Start server in a goroutine + serverErr := make(chan error, 1) + go func() { + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + serverErr <- fmt.Errorf("failed to start HTTP server: %w", err) + } + }() + + // Wait for context cancellation or server error + select { + case <-ctx.Done(): + log.Println("Shutting down HTTP server...") + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := server.Shutdown(shutdownCtx); err != nil { + return fmt.Errorf("failed to shutdown HTTP server: %w", err) + } + return nil + case err := <-serverErr: + return err + } +} diff --git a/config-agent.template.yaml b/config-agent.template.yaml new file mode 100644 index 0000000..436b3d1 --- /dev/null +++ b/config-agent.template.yaml @@ -0,0 +1,33 @@ +log: + format: json + level: info + console_colored: false + trace: fatal + with_caller: false + with_stack_trace: false +ops: + enabled: false + network: tcp + tracing_enabled: false + metrics: + enabled: false + path: /metrics + port: "10000" + basic_auth: + enabled: false + username: "" + password: "" + healthy: + enabled: false + path: /healthy + port: "10000" + profiler: + enabled: false + path: /debug/pprof + port: "10000" + write_timeout: 60 +hub_server: + name: connectrpc-client + addr: "" +data_dir: ./data +token: "" diff --git a/config.template.yaml b/config.template.yaml index 8f88de6..c726991 100644 --- a/config.template.yaml +++ b/config.template.yaml @@ -1,49 +1,39 @@ log: - format: "" - level: "" + format: json + level: info console_colored: false - trace: "" + trace: fatal with_caller: false with_stack_trace: false ops: enabled: false - network: "" + network: tcp tracing_enabled: false metrics: enabled: false - path: "" - port: "" + path: /metrics + port: "10000" basic_auth: enabled: false username: "" password: "" healthy: enabled: false - path: "" - port: "" + path: /healthy + port: "10000" profiler: enabled: false - path: "" - port: "" - write_timeout: 0 + path: /debug/pprof + port: "10000" + write_timeout: 60 +data_dir: ./data server: - port: 8080 - host: 0.0.0.0 - base_host: localhost:8080 - frontend: - base_url: http://localhost:8080/api/v1 - socket_url: ws://localhost:8080/ws + addr: :8080 auth: - enabled: false - users: [] -monitoring: - global: - default_interval: 60s - default_timeout: 10s - default_retries: 5 -database: - path: ./data/db.sqlite -notifications: - enabled: false - urls: [] + access_token_secret_key: "" + refresh_token_secret_key: "" timezone: UTC +updater: + is_enabled: false + command: "" +kv_db_engine: inmemory diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 7bfb380..5c3728d 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -1,5 +1,6 @@ services: sentinel: + container_name: sentinel build: context: . dockerfile: Dockerfile @@ -7,6 +8,7 @@ services: VERSION: local-dev COMMIT_HASH: local BUILD_DATE: local + command: ["hub", "start", "--config", "./config.yaml"] ports: - "8080:8080" volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 3349c6c..493c0d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,8 @@ services: sentinel: + container_name: sentinel image: sxwebdev/sentinel:latest + command: ["hub", "start", "--config", "./config.yaml"] ports: - "8080:8080" volumes: diff --git a/docs/docsv1/docs.go b/docs/docsv1/docs.go deleted file mode 100644 index 9fa6fa5..0000000 --- a/docs/docsv1/docs.go +++ /dev/null @@ -1,1412 +0,0 @@ -// Package docsv1 Code generated by swaggo/swag. DO NOT EDIT -package docsv1 - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "termsOfService": "http://swagger.io/terms/", - "contact": { - "name": "API Support", - "url": "http://www.swagger.io/support", - "email": "support@swagger.io" - }, - "license": { - "name": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - }, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": { - "/dashboard/stats": { - "get": { - "description": "Returns statistics for the dashboard", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "dashboard" - ], - "summary": "Get dashboard statistics", - "responses": { - "200": { - "description": "Dashboard statistics", - "schema": { - "$ref": "#/definitions/web.DashboardStats" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/incidents": { - "get": { - "description": "Returns a list of recent incidents across all services", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Get recent incidents", - "parameters": [ - { - "type": "string", - "description": "Filter by service ID or incident ID", - "name": "search", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by resolved status", - "name": "resolved", - "in": "query" - }, - { - "type": "integer", - "description": "Page number (default 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Number of items per page (default 100)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of incidents", - "schema": { - "$ref": "#/definitions/dbutils.FindResponseWithCount-storage_Incident" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/incidents/stats": { - "get": { - "description": "Returns the stats of incidents by date range", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Get incidents stats by date range", - "parameters": [ - { - "type": "string", - "description": "Start time (RFC3339 format)", - "name": "start_time", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "End time (RFC3339 format)", - "name": "end_time", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "Incidents stats by date range", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/web.getIncidentsStatsItem" - } - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/server/health": { - "get": { - "description": "Checks the health of the server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "server" - ], - "summary": "Health check", - "responses": { - "200": { - "description": "Health check successful", - "schema": { - "$ref": "#/definitions/web.healthCheckResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/server/info": { - "get": { - "description": "Returns basic information about the server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "server" - ], - "summary": "Get server info", - "responses": { - "200": { - "description": "Server information", - "schema": { - "$ref": "#/definitions/web.ServerInfoResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/server/upgrade": { - "get": { - "description": "Triggers a manual upgrade of the server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "server" - ], - "summary": "Manual upgrade", - "responses": { - "200": { - "description": "Upgrade initiated successfully", - "schema": { - "$ref": "#/definitions/web.SuccessResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services": { - "get": { - "description": "Returns a list of all services with their current states", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Get all services", - "parameters": [ - { - "type": "string", - "description": "Filter by service name", - "name": "name", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "csv", - "description": "Filter by service tags", - "name": "tags", - "in": "query" - }, - { - "type": "string", - "description": "Filter by service status", - "name": "status", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by enabled status", - "name": "is_enabled", - "in": "query" - }, - { - "type": "string", - "description": "Filter by protocol", - "name": "protocol", - "in": "query" - }, - { - "type": "string", - "description": "Order by field", - "name": "order_by", - "in": "query" - }, - { - "type": "integer", - "description": "Page number (for pagination)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Number of items per page (default 20)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of services with states", - "schema": { - "$ref": "#/definitions/dbutils.FindResponseWithCount-web_ServiceDTO" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - }, - "post": { - "description": "Creates a new service for monitoring", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Create new service", - "parameters": [ - { - "description": "Service configuration", - "name": "service", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/web.CreateUpdateServiceRequest" - } - } - ], - "responses": { - "201": { - "description": "Service created", - "schema": { - "$ref": "#/definitions/web.ServiceDTO" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}": { - "get": { - "description": "Returns detailed information about a specific service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Get service details", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Service details with state", - "schema": { - "$ref": "#/definitions/web.ServiceDTO" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Service not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - }, - "put": { - "description": "Updates an existing service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Update service", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "New service configuration", - "name": "service", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/web.CreateUpdateServiceRequest" - } - } - ], - "responses": { - "200": { - "description": "Service updated", - "schema": { - "$ref": "#/definitions/web.ServiceDTO" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Service not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Deletes a service from the monitoring system", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Delete service", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "Service deleted" - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/check": { - "post": { - "description": "Triggers a manual check of service status", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Trigger service check", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Check triggered successfully", - "schema": { - "$ref": "#/definitions/web.SuccessResponse" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Service not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/incidents": { - "get": { - "description": "Returns a list of incidents for a specific service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Get service incidents", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Filter by incident ID", - "name": "incident_id", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by resolved status", - "name": "resolved", - "in": "query" - }, - { - "type": "integer", - "description": "Page number (for pagination)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Number of items per page (default 20)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of incidents", - "schema": { - "$ref": "#/definitions/dbutils.FindResponseWithCount-storage_Incident" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/incidents/{incidentId}": { - "delete": { - "description": "Deletes a specific incident for a service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Delete incident", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Incident ID", - "name": "incidentId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "Incident deleted" - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Incident not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/resolve": { - "post": { - "description": "Forcefully resolves all active incidents for a service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Resolve service incidents", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Incidents resolved successfully", - "schema": { - "$ref": "#/definitions/web.SuccessResponse" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/stats": { - "get": { - "description": "Returns service statistics for the specified period", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "statistics" - ], - "summary": "Get service statistics", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Number of days (default 30)", - "name": "days", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Service statistics", - "schema": { - "$ref": "#/definitions/storage.ServiceStats" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/tags": { - "get": { - "description": "Retrieves all unique tags used across services", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "tags" - ], - "summary": "Get all tags", - "responses": { - "200": { - "description": "List of unique tags", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/tags/count": { - "get": { - "description": "Retrieves all unique tags along with their usage count across services", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "tags" - ], - "summary": "Get all tags with usage count", - "responses": { - "200": { - "description": "Map of tags with their usage count", - "schema": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - } - }, - "definitions": { - "dbutils.FindResponseWithCount-storage_Incident": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/storage.Incident" - } - } - } - }, - "dbutils.FindResponseWithCount-web_ServiceDTO": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/web.ServiceDTO" - } - } - } - }, - "monitors.Config": { - "type": "object", - "properties": { - "grpc": { - "$ref": "#/definitions/monitors.GRPCConfig" - }, - "http": { - "$ref": "#/definitions/monitors.HTTPConfig" - }, - "tcp": { - "$ref": "#/definitions/monitors.TCPConfig" - } - } - }, - "monitors.EndpointConfig": { - "type": "object", - "required": [ - "expected_status", - "method", - "name", - "url" - ], - "properties": { - "body": { - "type": "string" - }, - "expected_status": { - "type": "integer", - "maximum": 599, - "minimum": 100 - }, - "headers": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "json_path": { - "description": "Path to extract value from JSON response", - "type": "string" - }, - "method": { - "type": "string", - "enum": [ - "GET", - "POST", - "PUT", - "DELETE", - "HEAD", - "OPTIONS" - ] - }, - "name": { - "type": "string" - }, - "password": { - "description": "Basic Auth password", - "type": "string" - }, - "url": { - "type": "string" - }, - "username": { - "description": "Basic Auth username", - "type": "string" - } - } - }, - "monitors.GRPCConfig": { - "type": "object", - "required": [ - "check_type", - "endpoint" - ], - "properties": { - "check_type": { - "type": "string", - "enum": [ - "health", - "reflection", - "connectivity" - ] - }, - "endpoint": { - "type": "string" - }, - "insecure_tls": { - "type": "boolean" - }, - "service_name": { - "type": "string" - }, - "tls": { - "type": "boolean" - } - } - }, - "monitors.HTTPConfig": { - "type": "object", - "required": [ - "endpoints" - ], - "properties": { - "condition": { - "type": "string" - }, - "endpoints": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/monitors.EndpointConfig" - } - }, - "timeout": { - "type": "integer", - "example": 30000 - } - } - }, - "monitors.TCPConfig": { - "type": "object", - "required": [ - "endpoint" - ], - "properties": { - "endpoint": { - "type": "string" - }, - "expect_data": { - "type": "string" - }, - "send_data": { - "type": "string" - } - } - }, - "storage.Incident": { - "type": "object", - "properties": { - "duration": { - "type": "integer" - }, - "end_time": { - "type": "string" - }, - "error": { - "type": "string" - }, - "id": { - "type": "string" - }, - "resolved": { - "type": "boolean" - }, - "service_id": { - "type": "string" - }, - "start_time": { - "type": "string" - } - } - }, - "storage.ServiceProtocolType": { - "type": "string", - "enum": [ - "http", - "tcp", - "grpc" - ], - "x-enum-varnames": [ - "ServiceProtocolTypeHTTP", - "ServiceProtocolTypeTCP", - "ServiceProtocolTypeGRPC" - ] - }, - "storage.ServiceStats": { - "type": "object", - "properties": { - "avg_response_time": { - "type": "integer" - }, - "period": { - "type": "integer" - }, - "service_id": { - "type": "string" - }, - "total_downtime": { - "type": "integer" - }, - "total_incidents": { - "type": "integer" - }, - "uptime_percentage": { - "type": "number" - } - } - }, - "storage.ServiceStatus": { - "type": "string", - "enum": [ - "unknown", - "up", - "down", - "maintenance" - ], - "x-enum-varnames": [ - "StatusUnknown", - "StatusUp", - "StatusDown", - "StatusMaintenance" - ] - }, - "web.AvailableUpdate": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "is_available_manual": { - "type": "boolean" - }, - "tag_name": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "web.CreateUpdateServiceRequest": { - "type": "object", - "properties": { - "config": { - "$ref": "#/definitions/monitors.Config" - }, - "interval": { - "type": "integer", - "example": 60000 - }, - "is_enabled": { - "type": "boolean", - "example": true - }, - "name": { - "type": "string", - "example": "Web Server" - }, - "protocol": { - "allOf": [ - { - "$ref": "#/definitions/storage.ServiceProtocolType" - } - ], - "example": "http" - }, - "retries": { - "type": "integer", - "example": 5 - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "web", - "production" - ] - }, - "timeout": { - "type": "integer", - "example": 10000 - } - } - }, - "web.DashboardStats": { - "description": "Dashboard statistics", - "type": "object", - "properties": { - "active_incidents": { - "type": "integer", - "example": 2 - }, - "avg_response_time": { - "type": "integer", - "example": 150 - }, - "checks_per_minute": { - "type": "integer", - "example": 60 - }, - "last_check_time": { - "type": "string" - }, - "protocols": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "services_down": { - "type": "integer", - "example": 1 - }, - "services_unknown": { - "type": "integer", - "example": 1 - }, - "services_up": { - "type": "integer", - "example": 8 - }, - "total_checks": { - "type": "integer", - "example": 1000 - }, - "total_services": { - "type": "integer", - "example": 10 - }, - "uptime_percentage": { - "type": "number", - "example": 95.5 - } - } - }, - "web.ErrorResponse": { - "description": "Error response", - "type": "object", - "properties": { - "error": { - "type": "string", - "example": "Error description" - } - } - }, - "web.ServerInfoResponse": { - "type": "object", - "properties": { - "arch": { - "type": "string", - "example": "amd64" - }, - "available_update": { - "$ref": "#/definitions/web.AvailableUpdate" - }, - "build_date": { - "type": "string", - "example": "2023-10-01T12:00:00Z" - }, - "commit_hash": { - "type": "string", - "example": "abc123def456" - }, - "go_version": { - "type": "string", - "example": "go1.24.4" - }, - "os": { - "type": "string", - "example": "linux" - }, - "sqlite_version": { - "type": "string", - "example": "3.50.1" - }, - "version": { - "type": "string", - "example": "1.0.0" - } - } - }, - "web.ServiceDTO": { - "type": "object", - "properties": { - "active_incidents": { - "type": "integer", - "example": 2 - }, - "config": { - "$ref": "#/definitions/monitors.Config" - }, - "consecutive_fails": { - "type": "integer", - "example": 1 - }, - "consecutive_success": { - "type": "integer", - "example": 5 - }, - "id": { - "type": "string", - "example": "service-1" - }, - "interval": { - "type": "integer", - "example": 60000 - }, - "is_enabled": { - "type": "boolean", - "example": true - }, - "last_check": { - "type": "string", - "example": "2023-10-01T12:00:00Z" - }, - "last_error": { - "type": "string", - "example": "Connection timeout" - }, - "name": { - "type": "string", - "example": "Web Server" - }, - "next_check": { - "type": "string", - "example": "2023-10-01T12:05:00Z" - }, - "protocol": { - "allOf": [ - { - "$ref": "#/definitions/storage.ServiceProtocolType" - } - ], - "example": "http" - }, - "response_time": { - "type": "integer", - "example": 150000000 - }, - "retries": { - "type": "integer", - "example": 5 - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/storage.ServiceStatus" - } - ], - "example": "up / down / unknown" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "web", - "production" - ] - }, - "timeout": { - "type": "integer", - "example": 10000 - }, - "total_checks": { - "type": "integer", - "example": 100 - }, - "total_incidents": { - "type": "integer", - "example": 10 - } - } - }, - "web.SuccessResponse": { - "description": "Successful response", - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "Operation completed successfully" - } - } - }, - "web.getIncidentsStatsItem": { - "type": "object", - "properties": { - "avg_duration": { - "type": "integer" - }, - "avg_duration_human": { - "type": "string" - }, - "count": { - "type": "integer" - }, - "date": { - "type": "string" - }, - "total_duration": { - "type": "integer" - }, - "total_duration_human": { - "type": "string" - } - } - }, - "web.healthCheckResponse": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "healthy" - } - } - } - }, - "securityDefinitions": { - "BasicAuth": { - "type": "basic" - } - } -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "1.0", - Host: "", - BasePath: "/api/v1", - Schemes: []string{}, - Title: "Sentinel Monitoring API", - Description: "API for service monitoring and incident management", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/docs/docsv1/swagger.json b/docs/docsv1/swagger.json deleted file mode 100644 index 382fb79..0000000 --- a/docs/docsv1/swagger.json +++ /dev/null @@ -1,1387 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "API for service monitoring and incident management", - "title": "Sentinel Monitoring API", - "termsOfService": "http://swagger.io/terms/", - "contact": { - "name": "API Support", - "url": "http://www.swagger.io/support", - "email": "support@swagger.io" - }, - "license": { - "name": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0.html" - }, - "version": "1.0" - }, - "basePath": "/api/v1", - "paths": { - "/dashboard/stats": { - "get": { - "description": "Returns statistics for the dashboard", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "dashboard" - ], - "summary": "Get dashboard statistics", - "responses": { - "200": { - "description": "Dashboard statistics", - "schema": { - "$ref": "#/definitions/web.DashboardStats" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/incidents": { - "get": { - "description": "Returns a list of recent incidents across all services", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Get recent incidents", - "parameters": [ - { - "type": "string", - "description": "Filter by service ID or incident ID", - "name": "search", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by resolved status", - "name": "resolved", - "in": "query" - }, - { - "type": "integer", - "description": "Page number (default 1)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Number of items per page (default 100)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of incidents", - "schema": { - "$ref": "#/definitions/dbutils.FindResponseWithCount-storage_Incident" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/incidents/stats": { - "get": { - "description": "Returns the stats of incidents by date range", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Get incidents stats by date range", - "parameters": [ - { - "type": "string", - "description": "Start time (RFC3339 format)", - "name": "start_time", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "End time (RFC3339 format)", - "name": "end_time", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "Incidents stats by date range", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/web.getIncidentsStatsItem" - } - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/server/health": { - "get": { - "description": "Checks the health of the server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "server" - ], - "summary": "Health check", - "responses": { - "200": { - "description": "Health check successful", - "schema": { - "$ref": "#/definitions/web.healthCheckResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/server/info": { - "get": { - "description": "Returns basic information about the server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "server" - ], - "summary": "Get server info", - "responses": { - "200": { - "description": "Server information", - "schema": { - "$ref": "#/definitions/web.ServerInfoResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/server/upgrade": { - "get": { - "description": "Triggers a manual upgrade of the server", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "server" - ], - "summary": "Manual upgrade", - "responses": { - "200": { - "description": "Upgrade initiated successfully", - "schema": { - "$ref": "#/definitions/web.SuccessResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services": { - "get": { - "description": "Returns a list of all services with their current states", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Get all services", - "parameters": [ - { - "type": "string", - "description": "Filter by service name", - "name": "name", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "csv", - "description": "Filter by service tags", - "name": "tags", - "in": "query" - }, - { - "type": "string", - "description": "Filter by service status", - "name": "status", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by enabled status", - "name": "is_enabled", - "in": "query" - }, - { - "type": "string", - "description": "Filter by protocol", - "name": "protocol", - "in": "query" - }, - { - "type": "string", - "description": "Order by field", - "name": "order_by", - "in": "query" - }, - { - "type": "integer", - "description": "Page number (for pagination)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Number of items per page (default 20)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of services with states", - "schema": { - "$ref": "#/definitions/dbutils.FindResponseWithCount-web_ServiceDTO" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - }, - "post": { - "description": "Creates a new service for monitoring", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Create new service", - "parameters": [ - { - "description": "Service configuration", - "name": "service", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/web.CreateUpdateServiceRequest" - } - } - ], - "responses": { - "201": { - "description": "Service created", - "schema": { - "$ref": "#/definitions/web.ServiceDTO" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}": { - "get": { - "description": "Returns detailed information about a specific service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Get service details", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Service details with state", - "schema": { - "$ref": "#/definitions/web.ServiceDTO" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Service not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - }, - "put": { - "description": "Updates an existing service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Update service", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "New service configuration", - "name": "service", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/web.CreateUpdateServiceRequest" - } - } - ], - "responses": { - "200": { - "description": "Service updated", - "schema": { - "$ref": "#/definitions/web.ServiceDTO" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Service not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Deletes a service from the monitoring system", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Delete service", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "Service deleted" - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/check": { - "post": { - "description": "Triggers a manual check of service status", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "services" - ], - "summary": "Trigger service check", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Check triggered successfully", - "schema": { - "$ref": "#/definitions/web.SuccessResponse" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Service not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/incidents": { - "get": { - "description": "Returns a list of incidents for a specific service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Get service incidents", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Filter by incident ID", - "name": "incident_id", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by resolved status", - "name": "resolved", - "in": "query" - }, - { - "type": "integer", - "description": "Page number (for pagination)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Number of items per page (default 20)", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "List of incidents", - "schema": { - "$ref": "#/definitions/dbutils.FindResponseWithCount-storage_Incident" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/incidents/{incidentId}": { - "delete": { - "description": "Deletes a specific incident for a service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Delete incident", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Incident ID", - "name": "incidentId", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "Incident deleted" - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "404": { - "description": "Incident not found", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/resolve": { - "post": { - "description": "Forcefully resolves all active incidents for a service", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "incidents" - ], - "summary": "Resolve service incidents", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Incidents resolved successfully", - "schema": { - "$ref": "#/definitions/web.SuccessResponse" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/services/{id}/stats": { - "get": { - "description": "Returns service statistics for the specified period", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "statistics" - ], - "summary": "Get service statistics", - "parameters": [ - { - "type": "string", - "description": "Service ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Number of days (default 30)", - "name": "days", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Service statistics", - "schema": { - "$ref": "#/definitions/storage.ServiceStats" - } - }, - "400": { - "description": "Bad request", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/tags": { - "get": { - "description": "Retrieves all unique tags used across services", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "tags" - ], - "summary": "Get all tags", - "responses": { - "200": { - "description": "List of unique tags", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - }, - "/tags/count": { - "get": { - "description": "Retrieves all unique tags along with their usage count across services", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "tags" - ], - "summary": "Get all tags with usage count", - "responses": { - "200": { - "description": "Map of tags with their usage count", - "schema": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/web.ErrorResponse" - } - } - } - } - } - }, - "definitions": { - "dbutils.FindResponseWithCount-storage_Incident": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/storage.Incident" - } - } - } - }, - "dbutils.FindResponseWithCount-web_ServiceDTO": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/web.ServiceDTO" - } - } - } - }, - "monitors.Config": { - "type": "object", - "properties": { - "grpc": { - "$ref": "#/definitions/monitors.GRPCConfig" - }, - "http": { - "$ref": "#/definitions/monitors.HTTPConfig" - }, - "tcp": { - "$ref": "#/definitions/monitors.TCPConfig" - } - } - }, - "monitors.EndpointConfig": { - "type": "object", - "required": [ - "expected_status", - "method", - "name", - "url" - ], - "properties": { - "body": { - "type": "string" - }, - "expected_status": { - "type": "integer", - "maximum": 599, - "minimum": 100 - }, - "headers": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "json_path": { - "description": "Path to extract value from JSON response", - "type": "string" - }, - "method": { - "type": "string", - "enum": [ - "GET", - "POST", - "PUT", - "DELETE", - "HEAD", - "OPTIONS" - ] - }, - "name": { - "type": "string" - }, - "password": { - "description": "Basic Auth password", - "type": "string" - }, - "url": { - "type": "string" - }, - "username": { - "description": "Basic Auth username", - "type": "string" - } - } - }, - "monitors.GRPCConfig": { - "type": "object", - "required": [ - "check_type", - "endpoint" - ], - "properties": { - "check_type": { - "type": "string", - "enum": [ - "health", - "reflection", - "connectivity" - ] - }, - "endpoint": { - "type": "string" - }, - "insecure_tls": { - "type": "boolean" - }, - "service_name": { - "type": "string" - }, - "tls": { - "type": "boolean" - } - } - }, - "monitors.HTTPConfig": { - "type": "object", - "required": [ - "endpoints" - ], - "properties": { - "condition": { - "type": "string" - }, - "endpoints": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/monitors.EndpointConfig" - } - }, - "timeout": { - "type": "integer", - "example": 30000 - } - } - }, - "monitors.TCPConfig": { - "type": "object", - "required": [ - "endpoint" - ], - "properties": { - "endpoint": { - "type": "string" - }, - "expect_data": { - "type": "string" - }, - "send_data": { - "type": "string" - } - } - }, - "storage.Incident": { - "type": "object", - "properties": { - "duration": { - "type": "integer" - }, - "end_time": { - "type": "string" - }, - "error": { - "type": "string" - }, - "id": { - "type": "string" - }, - "resolved": { - "type": "boolean" - }, - "service_id": { - "type": "string" - }, - "start_time": { - "type": "string" - } - } - }, - "storage.ServiceProtocolType": { - "type": "string", - "enum": [ - "http", - "tcp", - "grpc" - ], - "x-enum-varnames": [ - "ServiceProtocolTypeHTTP", - "ServiceProtocolTypeTCP", - "ServiceProtocolTypeGRPC" - ] - }, - "storage.ServiceStats": { - "type": "object", - "properties": { - "avg_response_time": { - "type": "integer" - }, - "period": { - "type": "integer" - }, - "service_id": { - "type": "string" - }, - "total_downtime": { - "type": "integer" - }, - "total_incidents": { - "type": "integer" - }, - "uptime_percentage": { - "type": "number" - } - } - }, - "storage.ServiceStatus": { - "type": "string", - "enum": [ - "unknown", - "up", - "down", - "maintenance" - ], - "x-enum-varnames": [ - "StatusUnknown", - "StatusUp", - "StatusDown", - "StatusMaintenance" - ] - }, - "web.AvailableUpdate": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "is_available_manual": { - "type": "boolean" - }, - "tag_name": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, - "web.CreateUpdateServiceRequest": { - "type": "object", - "properties": { - "config": { - "$ref": "#/definitions/monitors.Config" - }, - "interval": { - "type": "integer", - "example": 60000 - }, - "is_enabled": { - "type": "boolean", - "example": true - }, - "name": { - "type": "string", - "example": "Web Server" - }, - "protocol": { - "allOf": [ - { - "$ref": "#/definitions/storage.ServiceProtocolType" - } - ], - "example": "http" - }, - "retries": { - "type": "integer", - "example": 5 - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "web", - "production" - ] - }, - "timeout": { - "type": "integer", - "example": 10000 - } - } - }, - "web.DashboardStats": { - "description": "Dashboard statistics", - "type": "object", - "properties": { - "active_incidents": { - "type": "integer", - "example": 2 - }, - "avg_response_time": { - "type": "integer", - "example": 150 - }, - "checks_per_minute": { - "type": "integer", - "example": 60 - }, - "last_check_time": { - "type": "string" - }, - "protocols": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "services_down": { - "type": "integer", - "example": 1 - }, - "services_unknown": { - "type": "integer", - "example": 1 - }, - "services_up": { - "type": "integer", - "example": 8 - }, - "total_checks": { - "type": "integer", - "example": 1000 - }, - "total_services": { - "type": "integer", - "example": 10 - }, - "uptime_percentage": { - "type": "number", - "example": 95.5 - } - } - }, - "web.ErrorResponse": { - "description": "Error response", - "type": "object", - "properties": { - "error": { - "type": "string", - "example": "Error description" - } - } - }, - "web.ServerInfoResponse": { - "type": "object", - "properties": { - "arch": { - "type": "string", - "example": "amd64" - }, - "available_update": { - "$ref": "#/definitions/web.AvailableUpdate" - }, - "build_date": { - "type": "string", - "example": "2023-10-01T12:00:00Z" - }, - "commit_hash": { - "type": "string", - "example": "abc123def456" - }, - "go_version": { - "type": "string", - "example": "go1.24.4" - }, - "os": { - "type": "string", - "example": "linux" - }, - "sqlite_version": { - "type": "string", - "example": "3.50.1" - }, - "version": { - "type": "string", - "example": "1.0.0" - } - } - }, - "web.ServiceDTO": { - "type": "object", - "properties": { - "active_incidents": { - "type": "integer", - "example": 2 - }, - "config": { - "$ref": "#/definitions/monitors.Config" - }, - "consecutive_fails": { - "type": "integer", - "example": 1 - }, - "consecutive_success": { - "type": "integer", - "example": 5 - }, - "id": { - "type": "string", - "example": "service-1" - }, - "interval": { - "type": "integer", - "example": 60000 - }, - "is_enabled": { - "type": "boolean", - "example": true - }, - "last_check": { - "type": "string", - "example": "2023-10-01T12:00:00Z" - }, - "last_error": { - "type": "string", - "example": "Connection timeout" - }, - "name": { - "type": "string", - "example": "Web Server" - }, - "next_check": { - "type": "string", - "example": "2023-10-01T12:05:00Z" - }, - "protocol": { - "allOf": [ - { - "$ref": "#/definitions/storage.ServiceProtocolType" - } - ], - "example": "http" - }, - "response_time": { - "type": "integer", - "example": 150000000 - }, - "retries": { - "type": "integer", - "example": 5 - }, - "status": { - "allOf": [ - { - "$ref": "#/definitions/storage.ServiceStatus" - } - ], - "example": "up / down / unknown" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "example": [ - "web", - "production" - ] - }, - "timeout": { - "type": "integer", - "example": 10000 - }, - "total_checks": { - "type": "integer", - "example": 100 - }, - "total_incidents": { - "type": "integer", - "example": 10 - } - } - }, - "web.SuccessResponse": { - "description": "Successful response", - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "Operation completed successfully" - } - } - }, - "web.getIncidentsStatsItem": { - "type": "object", - "properties": { - "avg_duration": { - "type": "integer" - }, - "avg_duration_human": { - "type": "string" - }, - "count": { - "type": "integer" - }, - "date": { - "type": "string" - }, - "total_duration": { - "type": "integer" - }, - "total_duration_human": { - "type": "string" - } - } - }, - "web.healthCheckResponse": { - "type": "object", - "properties": { - "status": { - "type": "string", - "example": "healthy" - } - } - } - }, - "securityDefinitions": { - "BasicAuth": { - "type": "basic" - } - } -} \ No newline at end of file diff --git a/docs/docsv1/swagger.yaml b/docs/docsv1/swagger.yaml deleted file mode 100644 index 2ad658e..0000000 --- a/docs/docsv1/swagger.yaml +++ /dev/null @@ -1,934 +0,0 @@ -basePath: /api/v1 -definitions: - dbutils.FindResponseWithCount-storage_Incident: - properties: - count: - type: integer - items: - items: - $ref: '#/definitions/storage.Incident' - type: array - type: object - dbutils.FindResponseWithCount-web_ServiceDTO: - properties: - count: - type: integer - items: - items: - $ref: '#/definitions/web.ServiceDTO' - type: array - type: object - monitors.Config: - properties: - grpc: - $ref: '#/definitions/monitors.GRPCConfig' - http: - $ref: '#/definitions/monitors.HTTPConfig' - tcp: - $ref: '#/definitions/monitors.TCPConfig' - type: object - monitors.EndpointConfig: - properties: - body: - type: string - expected_status: - maximum: 599 - minimum: 100 - type: integer - headers: - additionalProperties: - type: string - type: object - json_path: - description: Path to extract value from JSON response - type: string - method: - enum: - - GET - - POST - - PUT - - DELETE - - HEAD - - OPTIONS - type: string - name: - type: string - password: - description: Basic Auth password - type: string - url: - type: string - username: - description: Basic Auth username - type: string - required: - - expected_status - - method - - name - - url - type: object - monitors.GRPCConfig: - properties: - check_type: - enum: - - health - - reflection - - connectivity - type: string - endpoint: - type: string - insecure_tls: - type: boolean - service_name: - type: string - tls: - type: boolean - required: - - check_type - - endpoint - type: object - monitors.HTTPConfig: - properties: - condition: - type: string - endpoints: - items: - $ref: '#/definitions/monitors.EndpointConfig' - minItems: 1 - type: array - timeout: - example: 30000 - type: integer - required: - - endpoints - type: object - monitors.TCPConfig: - properties: - endpoint: - type: string - expect_data: - type: string - send_data: - type: string - required: - - endpoint - type: object - storage.Incident: - properties: - duration: - type: integer - end_time: - type: string - error: - type: string - id: - type: string - resolved: - type: boolean - service_id: - type: string - start_time: - type: string - type: object - storage.ServiceProtocolType: - enum: - - http - - tcp - - grpc - type: string - x-enum-varnames: - - ServiceProtocolTypeHTTP - - ServiceProtocolTypeTCP - - ServiceProtocolTypeGRPC - storage.ServiceStats: - properties: - avg_response_time: - type: integer - period: - type: integer - service_id: - type: string - total_downtime: - type: integer - total_incidents: - type: integer - uptime_percentage: - type: number - type: object - storage.ServiceStatus: - enum: - - unknown - - up - - down - - maintenance - type: string - x-enum-varnames: - - StatusUnknown - - StatusUp - - StatusDown - - StatusMaintenance - web.AvailableUpdate: - properties: - description: - type: string - is_available_manual: - type: boolean - tag_name: - type: string - url: - type: string - type: object - web.CreateUpdateServiceRequest: - properties: - config: - $ref: '#/definitions/monitors.Config' - interval: - example: 60000 - type: integer - is_enabled: - example: true - type: boolean - name: - example: Web Server - type: string - protocol: - allOf: - - $ref: '#/definitions/storage.ServiceProtocolType' - example: http - retries: - example: 5 - type: integer - tags: - example: - - web - - production - items: - type: string - type: array - timeout: - example: 10000 - type: integer - type: object - web.DashboardStats: - description: Dashboard statistics - properties: - active_incidents: - example: 2 - type: integer - avg_response_time: - example: 150 - type: integer - checks_per_minute: - example: 60 - type: integer - last_check_time: - type: string - protocols: - additionalProperties: - type: integer - type: object - services_down: - example: 1 - type: integer - services_unknown: - example: 1 - type: integer - services_up: - example: 8 - type: integer - total_checks: - example: 1000 - type: integer - total_services: - example: 10 - type: integer - uptime_percentage: - example: 95.5 - type: number - type: object - web.ErrorResponse: - description: Error response - properties: - error: - example: Error description - type: string - type: object - web.ServerInfoResponse: - properties: - arch: - example: amd64 - type: string - available_update: - $ref: '#/definitions/web.AvailableUpdate' - build_date: - example: "2023-10-01T12:00:00Z" - type: string - commit_hash: - example: abc123def456 - type: string - go_version: - example: go1.24.4 - type: string - os: - example: linux - type: string - sqlite_version: - example: 3.50.1 - type: string - version: - example: 1.0.0 - type: string - type: object - web.ServiceDTO: - properties: - active_incidents: - example: 2 - type: integer - config: - $ref: '#/definitions/monitors.Config' - consecutive_fails: - example: 1 - type: integer - consecutive_success: - example: 5 - type: integer - id: - example: service-1 - type: string - interval: - example: 60000 - type: integer - is_enabled: - example: true - type: boolean - last_check: - example: "2023-10-01T12:00:00Z" - type: string - last_error: - example: Connection timeout - type: string - name: - example: Web Server - type: string - next_check: - example: "2023-10-01T12:05:00Z" - type: string - protocol: - allOf: - - $ref: '#/definitions/storage.ServiceProtocolType' - example: http - response_time: - example: 150000000 - type: integer - retries: - example: 5 - type: integer - status: - allOf: - - $ref: '#/definitions/storage.ServiceStatus' - example: up / down / unknown - tags: - example: - - web - - production - items: - type: string - type: array - timeout: - example: 10000 - type: integer - total_checks: - example: 100 - type: integer - total_incidents: - example: 10 - type: integer - type: object - web.SuccessResponse: - description: Successful response - properties: - message: - example: Operation completed successfully - type: string - type: object - web.getIncidentsStatsItem: - properties: - avg_duration: - type: integer - avg_duration_human: - type: string - count: - type: integer - date: - type: string - total_duration: - type: integer - total_duration_human: - type: string - type: object - web.healthCheckResponse: - properties: - status: - example: healthy - type: string - type: object -info: - contact: - email: support@swagger.io - name: API Support - url: http://www.swagger.io/support - description: API for service monitoring and incident management - license: - name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html - termsOfService: http://swagger.io/terms/ - title: Sentinel Monitoring API - version: "1.0" -paths: - /dashboard/stats: - get: - consumes: - - application/json - description: Returns statistics for the dashboard - produces: - - application/json - responses: - "200": - description: Dashboard statistics - schema: - $ref: '#/definitions/web.DashboardStats' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get dashboard statistics - tags: - - dashboard - /incidents: - get: - consumes: - - application/json - description: Returns a list of recent incidents across all services - parameters: - - description: Filter by service ID or incident ID - in: query - name: search - type: string - - description: Filter by resolved status - in: query - name: resolved - type: boolean - - description: Page number (default 1) - in: query - name: page - type: integer - - description: Number of items per page (default 100) - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: List of incidents - schema: - $ref: '#/definitions/dbutils.FindResponseWithCount-storage_Incident' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get recent incidents - tags: - - incidents - /incidents/stats: - get: - consumes: - - application/json - description: Returns the stats of incidents by date range - parameters: - - description: Start time (RFC3339 format) - in: query - name: start_time - required: true - type: string - - description: End time (RFC3339 format) - in: query - name: end_time - required: true - type: string - produces: - - application/json - responses: - "200": - description: Incidents stats by date range - schema: - items: - $ref: '#/definitions/web.getIncidentsStatsItem' - type: array - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get incidents stats by date range - tags: - - incidents - /server/health: - get: - consumes: - - application/json - description: Checks the health of the server - produces: - - application/json - responses: - "200": - description: Health check successful - schema: - $ref: '#/definitions/web.healthCheckResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Health check - tags: - - server - /server/info: - get: - consumes: - - application/json - description: Returns basic information about the server - produces: - - application/json - responses: - "200": - description: Server information - schema: - $ref: '#/definitions/web.ServerInfoResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get server info - tags: - - server - /server/upgrade: - get: - consumes: - - application/json - description: Triggers a manual upgrade of the server - produces: - - application/json - responses: - "200": - description: Upgrade initiated successfully - schema: - $ref: '#/definitions/web.SuccessResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Manual upgrade - tags: - - server - /services: - get: - consumes: - - application/json - description: Returns a list of all services with their current states - parameters: - - description: Filter by service name - in: query - name: name - type: string - - collectionFormat: csv - description: Filter by service tags - in: query - items: - type: string - name: tags - type: array - - description: Filter by service status - in: query - name: status - type: string - - description: Filter by enabled status - in: query - name: is_enabled - type: boolean - - description: Filter by protocol - in: query - name: protocol - type: string - - description: Order by field - in: query - name: order_by - type: string - - description: Page number (for pagination) - in: query - name: page - type: integer - - description: Number of items per page (default 20) - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: List of services with states - schema: - $ref: '#/definitions/dbutils.FindResponseWithCount-web_ServiceDTO' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get all services - tags: - - services - post: - consumes: - - application/json - description: Creates a new service for monitoring - parameters: - - description: Service configuration - in: body - name: service - required: true - schema: - $ref: '#/definitions/web.CreateUpdateServiceRequest' - produces: - - application/json - responses: - "201": - description: Service created - schema: - $ref: '#/definitions/web.ServiceDTO' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Create new service - tags: - - services - /services/{id}: - delete: - consumes: - - application/json - description: Deletes a service from the monitoring system - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: Service deleted - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Delete service - tags: - - services - get: - consumes: - - application/json - description: Returns detailed information about a specific service - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Service details with state - schema: - $ref: '#/definitions/web.ServiceDTO' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "404": - description: Service not found - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get service details - tags: - - services - put: - consumes: - - application/json - description: Updates an existing service - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - - description: New service configuration - in: body - name: service - required: true - schema: - $ref: '#/definitions/web.CreateUpdateServiceRequest' - produces: - - application/json - responses: - "200": - description: Service updated - schema: - $ref: '#/definitions/web.ServiceDTO' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "404": - description: Service not found - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Update service - tags: - - services - /services/{id}/check: - post: - consumes: - - application/json - description: Triggers a manual check of service status - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Check triggered successfully - schema: - $ref: '#/definitions/web.SuccessResponse' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "404": - description: Service not found - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Trigger service check - tags: - - services - /services/{id}/incidents: - get: - consumes: - - application/json - description: Returns a list of incidents for a specific service - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - - description: Filter by incident ID - in: query - name: incident_id - type: string - - description: Filter by resolved status - in: query - name: resolved - type: boolean - - description: Page number (for pagination) - in: query - name: page - type: integer - - description: Number of items per page (default 20) - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: List of incidents - schema: - $ref: '#/definitions/dbutils.FindResponseWithCount-storage_Incident' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get service incidents - tags: - - incidents - /services/{id}/incidents/{incidentId}: - delete: - consumes: - - application/json - description: Deletes a specific incident for a service - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - - description: Incident ID - in: path - name: incidentId - required: true - type: string - produces: - - application/json - responses: - "204": - description: Incident deleted - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "404": - description: Incident not found - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Delete incident - tags: - - incidents - /services/{id}/resolve: - post: - consumes: - - application/json - description: Forcefully resolves all active incidents for a service - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Incidents resolved successfully - schema: - $ref: '#/definitions/web.SuccessResponse' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Resolve service incidents - tags: - - incidents - /services/{id}/stats: - get: - consumes: - - application/json - description: Returns service statistics for the specified period - parameters: - - description: Service ID - in: path - name: id - required: true - type: string - - description: Number of days (default 30) - in: query - name: days - type: integer - produces: - - application/json - responses: - "200": - description: Service statistics - schema: - $ref: '#/definitions/storage.ServiceStats' - "400": - description: Bad request - schema: - $ref: '#/definitions/web.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get service statistics - tags: - - statistics - /tags: - get: - consumes: - - application/json - description: Retrieves all unique tags used across services - produces: - - application/json - responses: - "200": - description: List of unique tags - schema: - items: - type: string - type: array - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get all tags - tags: - - tags - /tags/count: - get: - consumes: - - application/json - description: Retrieves all unique tags along with their usage count across services - produces: - - application/json - responses: - "200": - description: Map of tags with their usage count - schema: - additionalProperties: - type: integer - type: object - "500": - description: Internal server error - schema: - $ref: '#/definitions/web.ErrorResponse' - summary: Get all tags with usage count - tags: - - tags -securityDefinitions: - BasicAuth: - type: basic -swagger: "2.0" diff --git a/frontend/index.html b/frontend/index.html index d17235f..6b3127d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,14 +2,14 @@ - + Sentinel - -
+ +
diff --git a/frontend/orval.config.ts b/frontend/orval.config.ts deleted file mode 100644 index 2db046e..0000000 --- a/frontend/orval.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineConfig } from "orval"; - -export default defineConfig({ - api: { - input: "../docs/docsv1/swagger.json", // путь к Swagger JSON - output: { - mode: "tags-split", // или 'split' / 'single' - target: "./src/shared/api/generated.ts", // куда будет сгенерировано - schemas: "./src/shared/types/model", // типы - client: "axios", - override: { - mutator: { - path: "./src/shared/api/baseApi.ts", - name: "customFetcher", - }, - }, - }, - hooks: { - afterAllFilesWrite: "pnpm format", - }, - }, -}); diff --git a/frontend/package.json b/frontend/package.json index 92cffb4..edfea1d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,26 +4,31 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --host", + "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", "format": "prettier --config .prettierrc --write .", - "format-check": "prettier --config .prettierrc --check .", - "gen-api": "rm src/shared/types/model/* && npx orval" + "format-check": "prettier --config .prettierrc --check ." }, "dependencies": { + "@bufbuild/protobuf": "^2.9.0", + "@connectrpc/connect": "^2.1.0", + "@connectrpc/connect-query": "^2.2.0", + "@connectrpc/connect-web": "^2.1.0", + "@tanstack/react-form": "^1.23.7", + "@tanstack/react-query": "^5.90.2", "@tanstack/react-router": "^1.131.36", "@tanstack/react-router-devtools": "^1.131.36", "@tanstack/react-table": "^8.21.3", - "axios": "^1.11.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "emblor": "^1.4.8", "formik": "^2.4.6", "lucide-react": "^0.543.0", - "radix-ui": "^1.4.3", + "prism-react-renderer": "^2.4.1", + "radix-ui": "latest", "react": "^19.1.1", "react-dom": "^19.1.1", "react-markdown": "^10.1.0", @@ -33,13 +38,14 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.13", + "tw-animate-css": "^1.3.8", "yup": "^1.7.0", - "zustand": "^5.0.8", - "tw-animate-css": "^1.3.8" + "zod": "^4.1.11", + "zustand": "^5.0.8" }, "devDependencies": { - "@tailwindcss/vite": "^4.1.13", "@eslint/js": "^9.35.0", + "@tailwindcss/vite": "^4.1.13", "@tanstack/router-plugin": "^1.131.36", "@types/node": "^24.3.1", "@types/react": "^19.1.12", @@ -49,7 +55,6 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", - "orval": "^7.11.2", "prettier": "3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", "typescript": "~5.9.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index bc68f50..24cbeca 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,6 +8,24 @@ importers: .: dependencies: + '@bufbuild/protobuf': + specifier: ^2.9.0 + version: 2.9.0 + '@connectrpc/connect': + specifier: ^2.1.0 + version: 2.1.0(@bufbuild/protobuf@2.9.0) + '@connectrpc/connect-query': + specifier: ^2.2.0 + version: 2.2.0(@bufbuild/protobuf@2.9.0)(@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@connectrpc/connect-web': + specifier: ^2.1.0 + version: 2.1.0(@bufbuild/protobuf@2.9.0)(@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0)) + '@tanstack/react-form': + specifier: ^1.23.7 + version: 1.23.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-query': + specifier: ^5.90.2 + version: 5.90.2(react@19.1.1) '@tanstack/react-router': specifier: ^1.131.36 version: 1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -17,9 +35,6 @@ importers: '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - axios: - specifier: ^1.11.0 - version: 1.11.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -38,8 +53,11 @@ importers: lucide-react: specifier: ^0.543.0 version: 0.543.0(react@19.1.1) + prism-react-renderer: + specifier: ^2.4.1 + version: 2.4.1(react@19.1.1) radix-ui: - specifier: ^1.4.3 + specifier: latest version: 1.4.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: specifier: ^19.1.1 @@ -74,6 +92,9 @@ importers: yup: specifier: ^1.7.0 version: 1.7.0 + zod: + specifier: ^4.1.11 + version: 4.1.11 zustand: specifier: ^5.0.8 version: 5.0.8(@types/react@19.1.12)(immer@10.1.1)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) @@ -111,9 +132,6 @@ importers: globals: specifier: ^16.3.0 version: 16.3.0 - orval: - specifier: ^7.11.2 - version: 7.11.2(openapi-types@12.1.3) prettier: specifier: 3.6.2 version: 3.6.2 @@ -136,25 +154,6 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@apidevtools/json-schema-ref-parser@11.7.2': - resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==} - engines: {node: '>= 16'} - - '@apidevtools/openapi-schemas@2.1.0': - resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} - engines: {node: '>=10'} - - '@apidevtools/swagger-methods@3.0.2': - resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - - '@apidevtools/swagger-parser@10.1.1': - resolution: {integrity: sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==} - peerDependencies: - openapi-types: '>=7' - - '@asyncapi/specs@6.8.1': - resolution: {integrity: sha512-czHoAk3PeXTLR+X8IUaD+IpT+g+zUvkcgMDJVothBsan+oHN3jfcFcFUNdOPAAFoUCQN1hXF1dWuphWy05THlA==} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -288,6 +287,36 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@bufbuild/protobuf@2.9.0': + resolution: {integrity: sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==} + + '@connectrpc/connect-query-core@2.2.0': + resolution: {integrity: sha512-t/CuxW/vP84y2iyS+PnbAnBwgOTYMzHXTSoBUKC1vIn706aNiZP40Y6mGJybglyH63RhAPcOdUgzG7DjzaAHCw==} + peerDependencies: + '@bufbuild/protobuf': 2.x + '@connectrpc/connect': ^2.0.1 + '@tanstack/query-core': '>=5.62.0' + + '@connectrpc/connect-query@2.2.0': + resolution: {integrity: sha512-oQ+coXOwBmfl4/t6EOrTfzW0zdoGDe3kvUYqZHrbzORkRFd693Cz8PxuDBjRCjmBPhDRCQMxFhR1jn2+SbgEKQ==} + peerDependencies: + '@bufbuild/protobuf': 2.x + '@connectrpc/connect': ^2.0.1 + '@tanstack/react-query': '>=5.62.0' + react: ^18 || ^19 + react-dom: ^18 || ^19 + + '@connectrpc/connect-web@2.1.0': + resolution: {integrity: sha512-4IBFeMeXS1RVtmmFE/MwH+vWq/5vDRKys70va+DAaWDh83Rdy0iUQOJbITUDzvonlY5as3vwfs5yy9Yp2miHSw==} + peerDependencies: + '@bufbuild/protobuf': ^2.7.0 + '@connectrpc/connect': 2.1.0 + + '@connectrpc/connect@2.1.0': + resolution: {integrity: sha512-xhiwnYlJNHzmFsRw+iSPIwXR/xweTvTw8x5HiwWp10sbVtd4OpOXbRgE7V58xs1EC17fzusF1f5uOAy24OkBuA==} + peerDependencies: + '@bufbuild/protobuf': ^2.7.0 + '@esbuild/aix-ppc64@0.25.9': resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} @@ -488,9 +517,6 @@ packages: resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@exodus/schemasafe@1.3.0': - resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} - '@floating-ui/core@1.7.2': resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} @@ -506,9 +532,6 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@gerrit0/mini-shiki@3.8.1': - resolution: {integrity: sha512-HVZW+8pxoOExr5ZMPK15U79jQAZTO/S6i5byQyyZGjtNj+qaYd82cizTncwFzTQgiLo8uUBym6vh+/1tfJklTw==} - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -529,14 +552,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@ibm-cloud/openapi-ruleset-utilities@1.9.0': - resolution: {integrity: sha512-AoFbSarOqFBYH+1TZ9Ahkm2IWYSi5v0pBk88fpV+5b3qGJukypX8PwvCWADjuyIccKg48/F73a6hTTkBzDQ2UA==} - engines: {node: '>=16.0.0'} - - '@ibm-cloud/openapi-ruleset@1.31.1': - resolution: {integrity: sha512-3WK2FREmDA2aadCjD71PE7tx5evyvmhg80ts1kXp2IzXIA0ZJ7guGM66tj40kxaqwpMSGchwEnnfYswntav76g==} - engines: {node: '>=16.0.0'} - '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -560,27 +575,6 @@ packages: '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - '@jsdevtools/ono@7.1.3': - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - - '@jsep-plugin/assignment@1.3.0': - resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} - engines: {node: '>= 10.16.0'} - peerDependencies: - jsep: ^0.4.0||^1.0.0 - - '@jsep-plugin/regex@1.0.4': - resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} - engines: {node: '>= 10.16.0'} - peerDependencies: - jsep: ^0.4.0||^1.0.0 - - '@jsep-plugin/ternary@1.1.4': - resolution: {integrity: sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==} - engines: {node: '>= 10.16.0'} - peerDependencies: - jsep: ^0.4.0||^1.0.0 - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -593,36 +587,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@orval/angular@7.11.2': - resolution: {integrity: sha512-v7I3MXlc1DTFHZlCo10uqBmss/4puXi1EbYdlYGfeZ2sYQiwtRFEYAMnSIxHzMtdtI4jd7iDEH0fZRA7W6yloA==} - - '@orval/axios@7.11.2': - resolution: {integrity: sha512-X5TJTFofCeJrQcHWoH0wz/032DBhPOQuZUUOPYO3DItOnq9/nfHJYKnUfg13wtYw0LVjCxyTZpeGLUBZnY804A==} - - '@orval/core@7.11.2': - resolution: {integrity: sha512-5k2j4ro53yZ3J+tGMu3LpLgVb2OBtxNDgyrJik8qkrFyuORBLx/a+AJRFoPYwZmtnMZzzRXoH4J/fbpW5LXIyg==} - - '@orval/fetch@7.11.2': - resolution: {integrity: sha512-FuupASqk4Dn8ZET7u5Ra5djKy22KfRfec60zRR/o5+L5iQkWKEe/A5DBT1PwjTMnp9789PEGlFPQjZNwMG98Tg==} - - '@orval/hono@7.11.2': - resolution: {integrity: sha512-SddhKMYMB/dJH3YQx3xi0Zd+4tfhrEkqJdqQaYLXgENJiw0aGbdaZTdY6mb/e6qP38TTK6ME2PkYOqwkl2DQ7g==} - - '@orval/mcp@7.11.2': - resolution: {integrity: sha512-9kGKko8wLuCbeETp8Pd8lXLtBpLzEJfR2kl2m19AI3nAoHXE/Tnn3KgjMIg0qvCcsRXGXdYJB7wfxy2URdAxVA==} - - '@orval/mock@7.11.2': - resolution: {integrity: sha512-+uRq6BT6NU2z0UQtgeD6FMuLAxQ5bjJ5PZK3AsbDYFRSmAWUWoeaQcoWyF38F4t7ez779beGs3AlUg+z0Ec4rQ==} - - '@orval/query@7.11.2': - resolution: {integrity: sha512-C/it+wNfcDtuvpB6h/78YwWU+Rjk7eU1Av8jAoGnvxMRli4nnzhSZ83HMILGhYQbE9WcfNZxQJ6OaBoTWqACPg==} - - '@orval/swr@7.11.2': - resolution: {integrity: sha512-95GkKLVy67xJvsiVvK4nTOsCpebWM54FvQdKQaqlJ0FGCNUbqDjVRwBKbjP6dLc/B3wTmBAWlFSLbdVmjGCTYg==} - - '@orval/zod@7.11.2': - resolution: {integrity: sha512-4MzTg5Wms8/LlM3CbYu80dvCbP88bVlQjnYsBdFXuEv0K2GYkBCAhVOrmXCVrPXE89neV6ABkvWQeuKZQpkdxQ==} - '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -833,19 +797,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dialog@1.1.14': - resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-dialog@1.1.15': resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: @@ -1739,94 +1690,6 @@ packages: cpu: [x64] os: [win32] - '@shikijs/engine-oniguruma@3.8.1': - resolution: {integrity: sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==} - - '@shikijs/langs@3.8.1': - resolution: {integrity: sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ==} - - '@shikijs/themes@3.8.1': - resolution: {integrity: sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ==} - - '@shikijs/types@3.8.1': - resolution: {integrity: sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg==} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@stoplight/better-ajv-errors@1.0.3': - resolution: {integrity: sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==} - engines: {node: ^12.20 || >= 14.13} - peerDependencies: - ajv: '>=8' - - '@stoplight/json-ref-readers@1.2.2': - resolution: {integrity: sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==} - engines: {node: '>=8.3.0'} - - '@stoplight/json-ref-resolver@3.1.6': - resolution: {integrity: sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==} - engines: {node: '>=8.3.0'} - - '@stoplight/json@3.21.7': - resolution: {integrity: sha512-xcJXgKFqv/uCEgtGlPxy3tPA+4I+ZI4vAuMJ885+ThkTHFVkC+0Fm58lA9NlsyjnkpxFh4YiQWpH+KefHdbA0A==} - engines: {node: '>=8.3.0'} - - '@stoplight/ordered-object-literal@1.0.5': - resolution: {integrity: sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==} - engines: {node: '>=8'} - - '@stoplight/path@1.3.2': - resolution: {integrity: sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==} - engines: {node: '>=8'} - - '@stoplight/spectral-core@1.20.0': - resolution: {integrity: sha512-5hBP81nCC1zn1hJXL/uxPNRKNcB+/pEIHgCjPRpl/w/qy9yC9ver04tw1W0l/PMiv0UeB5dYgozXVQ4j5a6QQQ==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/spectral-formats@1.8.2': - resolution: {integrity: sha512-c06HB+rOKfe7tuxg0IdKDEA5XnjL2vrn/m/OVIIxtINtBzphZrOgtRn7epQ5bQF5SWp84Ue7UJWaGgDwVngMFw==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/spectral-functions@1.10.1': - resolution: {integrity: sha512-obu8ZfoHxELOapfGsCJixKZXZcffjg+lSoNuttpmUFuDzVLT3VmH8QkPXfOGOL5Pz80BR35ClNAToDkdnYIURg==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/spectral-parsers@1.0.5': - resolution: {integrity: sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/spectral-ref-resolver@1.0.5': - resolution: {integrity: sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/spectral-rulesets@1.22.0': - resolution: {integrity: sha512-l2EY2jiKKLsvnPfGy+pXC0LeGsbJzcQP5G/AojHgf+cwN//VYxW1Wvv4WKFx/CLmLxc42mJYF2juwWofjWYNIQ==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/spectral-runtime@1.1.4': - resolution: {integrity: sha512-YHbhX3dqW0do6DhiPSgSGQzr6yQLlWybhKwWx0cqxjMwxej3TqLv3BXMfIUYFKKUqIwH4Q2mV8rrMM8qD2N0rQ==} - engines: {node: ^16.20 || ^18.18 || >= 20.17} - - '@stoplight/types@13.20.0': - resolution: {integrity: sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==} - engines: {node: ^12.20 || >=14.13} - - '@stoplight/types@13.6.0': - resolution: {integrity: sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==} - engines: {node: ^12.20 || >=14.13} - - '@stoplight/types@14.1.1': - resolution: {integrity: sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==} - engines: {node: ^12.20 || >=14.13} - - '@stoplight/yaml-ast-parser@0.0.50': - resolution: {integrity: sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==} - - '@stoplight/yaml@4.3.0': - resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} - engines: {node: '>=10.8'} - '@swc/core-darwin-arm64@1.13.3': resolution: {integrity: sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==} engines: {node: '>=10'} @@ -1992,10 +1855,34 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@tanstack/devtools-event-client@0.3.3': + resolution: {integrity: sha512-RfV+OPV/M3CGryYqTue684u10jUt55PEqeBOnOtCe6tAmHI9Iqyc8nHeDhWPEV9715gShuauFVaMc9RiUVNdwg==} + engines: {node: '>=18'} + + '@tanstack/form-core@1.24.3': + resolution: {integrity: sha512-e+HzSD49NWr4aIqJWtPPzmi+/phBJAP3nSPN8dvxwmJWqAxuB/cH138EcmCFf3+oA7j3BXvwvTY0I+8UweGPjQ==} + '@tanstack/history@1.131.2': resolution: {integrity: sha512-cs1WKawpXIe+vSTeiZUuSBy8JFjEuDgdMKZFRLKwQysKo8y2q6Q1HvS74Yw+m5IhOW1nTZooa6rlgdfXcgFAaw==} engines: {node: '>=12'} + '@tanstack/query-core@5.90.2': + resolution: {integrity: sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ==} + + '@tanstack/react-form@1.23.7': + resolution: {integrity: sha512-p/j9Gi2+s135sOjj48RjM+6xZQr1FVpliQlETLYBEGmmmxWHgYYs2b62mTDSnuv7AqtuZhpQ+t0CRFVfbQLsFA==} + peerDependencies: + '@tanstack/react-start': ^1.130.10 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@tanstack/react-start': + optional: true + + '@tanstack/react-query@5.90.2': + resolution: {integrity: sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw==} + peerDependencies: + react: ^18 || ^19 + '@tanstack/react-router-devtools@1.131.36': resolution: {integrity: sha512-2huBmW+mqPoJs6ZHfjuunEkVRfgWZh67IUjgdSyqdaYGLa3qsG3zcG4bpTIq6HwJuzcK00JRM3AQ4NLPdttaJQ==} engines: {node: '>=12'} @@ -2017,6 +1904,12 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/react-store@0.7.7': + resolution: {integrity: sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/react-table@8.21.3': resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} engines: {node: '>=12'} @@ -2072,6 +1965,9 @@ packages: '@tanstack/store@0.7.2': resolution: {integrity: sha512-RP80Z30BYiPX2Pyo0Nyw4s1SJFH2jyM6f9i3HfX4pA+gm5jsnYryscdq2aIQLnL4TaGuQMO+zXmN9nh1Qck+Pg==} + '@tanstack/store@0.7.7': + resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==} + '@tanstack/table-core@8.21.3': resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} @@ -2110,9 +2006,6 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/es-aggregate-error@1.0.6': - resolution: {integrity: sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==} - '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -2137,6 +2030,9 @@ packages: '@types/node@24.3.1': resolution: {integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==} + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} + '@types/react-dom@19.1.9': resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} peerDependencies: @@ -2151,9 +2047,6 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/urijs@1.19.25': - resolution: {integrity: sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==} - '@typescript-eslint/eslint-plugin@8.43.0': resolution: {integrity: sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2222,10 +2115,6 @@ packages: peerDependencies: vite: ^4 || ^5 || ^6 || ^7 - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2236,41 +2125,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv-draft-04@1.0.0: - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-errors@3.0.0: - resolution: {integrity: sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==} - peerDependencies: - ajv: ^8.0.1 - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -2290,44 +2147,14 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - array-move@3.0.1: resolution: {integrity: sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==} engines: {node: '>=10'} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - ast-types@0.16.1: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} - astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} - hasBin: true - - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - axios@1.11.0: - resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} - babel-dead-code-elimination@1.0.10: resolution: {integrity: sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==} @@ -2356,25 +2183,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2405,10 +2213,6 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -2416,10 +2220,6 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -2443,16 +2243,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - compare-versions@6.1.1: - resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2513,18 +2306,6 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -2537,6 +2318,9 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decode-formdata@0.9.0: + resolution: {integrity: sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==} + decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} @@ -2547,22 +2331,6 @@ packages: resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} engines: {node: '>=0.10.0'} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - dependency-graph@0.11.0: - resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} - engines: {node: '>= 0.6.0'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -2574,6 +2342,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devalue@5.4.1: + resolution: {integrity: sha512-YtoaOfsqjbZQKGIMRYDWKjUmSB4VJ/RElB+bXZawQAQYAo4xu08GKTMVlsZDTF6R2MbAgjcAQRPI5eIyRAT2OQ==} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -2581,17 +2352,9 @@ packages: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - electron-to-chromium@1.5.203: resolution: {integrity: sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==} @@ -2601,52 +2364,10 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} - engines: {node: '>= 0.4'} - - es-aggregate-error@1.0.14: - resolution: {integrity: sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} - esbuild@0.25.9: resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} @@ -2725,17 +2446,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -2756,15 +2469,6 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-memoize@2.5.2: - resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} - fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -2796,75 +2500,24 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} - engines: {node: '>= 6'} - formik@2.4.6: resolution: {integrity: sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==} peerDependencies: react: '>=16.8.0' - fs-extra@11.3.0: - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} - engines: {node: '>=14.14'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -2884,56 +2537,21 @@ packages: resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} engines: {node: '>=18'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - goober@2.1.16: resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} peerDependencies: csstype: ^3.0.10 - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} @@ -2946,13 +2564,6 @@ packages: html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - http2-client@1.3.5: - resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2964,9 +2575,6 @@ packages: immer@10.1.1: resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} - immer@9.0.21: - resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2978,10 +2586,6 @@ packages: inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - internmap@2.0.3: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} @@ -2992,38 +2596,10 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} - is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -3031,18 +2607,6 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -3050,18 +2614,6 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -3070,49 +2622,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isbot@5.1.29: resolution: {integrity: sha512-DelDWWoa3mBoyWTq3wjp+GIWx/yZdN7zLUE7NFhKjAiJ+uJVRkbLlwykdduCE4sPUUy8mlTYTmdhBUYu91F+sw==} engines: {node: '>=18'} @@ -3131,10 +2640,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsep@1.4.0: - resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} - engines: {node: '>= 10.16.0'} - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3146,9 +2651,6 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -3157,31 +2659,9 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@2.2.1: - resolution: {integrity: sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==} - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - - jsonpath-plus@10.3.0: - resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==} - engines: {node: '>=18.0.0'} - hasBin: true - - jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - - jsonschema@1.5.0: - resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3250,9 +2730,6 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3260,37 +2737,12 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - lodash.isempty@4.4.0: - resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.omitby@4.6.0: - resolution: {integrity: sha512-5OrRcIVR75M288p4nbI2WLAf3ndw2GD9fyNv3Bc15+WCxJDdZ4lYndSxGd7hnG6PVjiJTeJE2dHEGhIuKGicIQ==} - - lodash.topath@4.5.2: - resolution: {integrity: sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==} - - lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - - lodash.uniqby@4.7.0: - resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} - - lodash.uniqwith@4.5.0: - resolution: {integrity: sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - loglevel-plugin-prefix@0.8.4: - resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} - - loglevel@1.9.2: - resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} - engines: {node: '>= 0.6.0'} - longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -3306,23 +2758,12 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lunr@2.3.9: - resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} - markdown-it@14.1.0: - resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} - hasBin: true - markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} @@ -3368,12 +2809,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3466,25 +2901,9 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@6.2.0: - resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} - engines: {node: '>=10'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3513,94 +2932,21 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - nimma@0.2.3: - resolution: {integrity: sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==} - engines: {node: ^12.20 || >=14.13} - - node-fetch-h2@2.3.0: - resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==} - engines: {node: 4.x || >=6.0.0} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-readfiles@0.2.0: - resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} - - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - oas-kit-common@1.0.8: - resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==} - - oas-linter@3.2.2: - resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==} - - oas-resolver@2.5.6: - resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==} - hasBin: true - - oas-schema-walker@1.1.5: - resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==} - - oas-validator@5.0.8: - resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - - openapi3-ts@4.2.2: - resolution: {integrity: sha512-+9g4actZKeb3czfi9gVQ4Br2Ju3KwhCAQJBNaKgye5KggqcBLIhFHH+nIkcm0BUX00TrAJl6dH4JWgM4G4JWrw==} - - openapi3-ts@4.4.0: - resolution: {integrity: sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - orval@7.11.2: - resolution: {integrity: sha512-Cjc/dgnQwAOkvymzvPpFqFc2nQwZ29E+ZFWUI8yKejleHaoFKIdwvkM/b1njtLEjePDcF0hyqXXCTz2wWaXLig==} - hasBin: true - - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -3624,10 +2970,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3639,14 +2981,6 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pony-cause@1.1.1: - resolution: {integrity: sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==} - engines: {node: '>=12.0.0'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -3721,6 +3055,11 @@ packages: engines: {node: '>=14'} hasBin: true + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} + peerDependencies: + react: '>=16.0.0' + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -3730,13 +3069,6 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3857,10 +3189,6 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - recast@0.23.11: resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} engines: {node: '>= 4'} @@ -3875,17 +3203,6 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - - reftools@1.1.9: - resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} - - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} - remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -3898,14 +3215,6 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3925,21 +3234,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - - safe-stable-stringify@1.1.1: - resolution: {integrity: sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==} - scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} @@ -3962,18 +3256,6 @@ packages: resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} engines: {node: '>=10'} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3982,51 +3264,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - should-equal@2.0.0: - resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} - - should-format@3.0.3: - resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} - - should-type-adaptors@1.1.0: - resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} - - should-type@1.4.0: - resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} - - should-util@1.0.1: - resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} - - should@13.2.3: - resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - simple-eval@1.0.1: - resolution: {integrity: sha512-LH7FpTAkeD+y5xQC4fzS+tFtaNlvt3Ib1zKzvhjv/Y+cioV4zIuw4IZr2yhRLu67CWL7FR9/6KXKnjRoZTvGGQ==} - engines: {node: '>=12'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - solid-js@1.9.9: resolution: {integrity: sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==} @@ -4051,41 +3288,9 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - - string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -4100,10 +3305,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - swagger2openapi@7.0.8: - resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} - hasBin: true - tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} @@ -4138,9 +3339,6 @@ packages: toposort@2.0.2: resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -4153,19 +3351,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - tsconfck@2.1.2: - resolution: {integrity: sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==} - engines: {node: ^14.13.1 || ^16 || >=18} - hasBin: true - peerDependencies: - typescript: ^4.3.5 || ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - tslib@2.0.1: resolution: {integrity: sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==} @@ -4188,35 +3373,6 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} - - typedoc-plugin-markdown@4.8.1: - resolution: {integrity: sha512-ug7fc4j0SiJxSwBGLncpSo8tLvrT9VONvPUQqQDTKPxCoFQBADLli832RGPtj6sfSVJebNSrHZQRUdEryYH/7g==} - engines: {node: '>= 18'} - peerDependencies: - typedoc: 0.28.x - - typedoc@0.28.7: - resolution: {integrity: sha512-lpz0Oxl6aidFkmS90VQDQjk/Qf2iw0IUvFqirdONBdj7jPSN9mGXhy66BcGNDxx5ZMyKKiBVAREvPEzT6Uxipw==} - engines: {node: '>= 18', pnpm: '>= 10'} - hasBin: true - peerDependencies: - typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x - typescript-eslint@8.43.0: resolution: {integrity: sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4229,13 +3385,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} @@ -4257,10 +3406,6 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - unplugin@2.3.6: resolution: {integrity: sha512-+/MdXl8bLTXI2lJF22gUBeCFqZruEpL/oM9f8wxCuKh9+Mw9qeul3gTqgbKpMeOFlusCzc0s7x2Kax2xKW+FQg==} engines: {node: '>=18.12.0'} @@ -4274,9 +3419,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - urijs@1.19.11: - resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} - use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -4302,14 +3444,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - utility-types@3.11.0: - resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} - engines: {node: '>= 4'} - - validator@13.15.15: - resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} - engines: {node: '>= 0.10'} - vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -4359,31 +3493,9 @@ packages: yaml: optional: true - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4393,14 +3505,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -4408,23 +3512,11 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - yaml@2.8.0: resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} hasBin: true - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -4435,6 +3527,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.11: + resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} + zustand@5.0.8: resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} engines: {node: '>=12.20.0'} @@ -4463,31 +3558,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 - '@apidevtools/json-schema-ref-parser@11.7.2': - dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - js-yaml: 4.1.0 - - '@apidevtools/openapi-schemas@2.1.0': {} - - '@apidevtools/swagger-methods@3.0.2': {} - - '@apidevtools/swagger-parser@10.1.1(openapi-types@12.1.3)': - dependencies: - '@apidevtools/json-schema-ref-parser': 11.7.2 - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - '@jsdevtools/ono': 7.1.3 - ajv: 8.17.1 - ajv-draft-04: 1.0.0(ajv@8.17.1) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 - - '@asyncapi/specs@6.8.1': - dependencies: - '@types/json-schema': 7.0.15 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -4676,6 +3746,34 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@bufbuild/protobuf@2.9.0': {} + + '@connectrpc/connect-query-core@2.2.0(@bufbuild/protobuf@2.9.0)(@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0))(@tanstack/query-core@5.90.2)': + dependencies: + '@bufbuild/protobuf': 2.9.0 + '@connectrpc/connect': 2.1.0(@bufbuild/protobuf@2.9.0) + '@tanstack/query-core': 5.90.2 + + '@connectrpc/connect-query@2.2.0(@bufbuild/protobuf@2.9.0)(@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@bufbuild/protobuf': 2.9.0 + '@connectrpc/connect': 2.1.0(@bufbuild/protobuf@2.9.0) + '@connectrpc/connect-query-core': 2.2.0(@bufbuild/protobuf@2.9.0)(@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0))(@tanstack/query-core@5.90.2) + '@tanstack/react-query': 5.90.2(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + transitivePeerDependencies: + - '@tanstack/query-core' + + '@connectrpc/connect-web@2.1.0(@bufbuild/protobuf@2.9.0)(@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0))': + dependencies: + '@bufbuild/protobuf': 2.9.0 + '@connectrpc/connect': 2.1.0(@bufbuild/protobuf@2.9.0) + + '@connectrpc/connect@2.1.0(@bufbuild/protobuf@2.9.0)': + dependencies: + '@bufbuild/protobuf': 2.9.0 + '@esbuild/aix-ppc64@0.25.9': optional: true @@ -4803,8 +3901,6 @@ snapshots: '@eslint/core': 0.15.2 levn: 0.4.1 - '@exodus/schemasafe@1.3.0': {} - '@floating-ui/core@1.7.2': dependencies: '@floating-ui/utils': 0.2.10 @@ -4822,14 +3918,6 @@ snapshots: '@floating-ui/utils@0.2.10': {} - '@gerrit0/mini-shiki@3.8.1': - dependencies: - '@shikijs/engine-oniguruma': 3.8.1 - '@shikijs/langs': 3.8.1 - '@shikijs/themes': 3.8.1 - '@shikijs/types': 3.8.1 - '@shikijs/vscode-textmate': 10.0.2 - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -4843,24 +3931,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@ibm-cloud/openapi-ruleset-utilities@1.9.0': {} - - '@ibm-cloud/openapi-ruleset@1.31.1': - dependencies: - '@ibm-cloud/openapi-ruleset-utilities': 1.9.0 - '@stoplight/spectral-formats': 1.8.2 - '@stoplight/spectral-functions': 1.10.1 - '@stoplight/spectral-rulesets': 1.22.0 - chalk: 4.1.2 - jsonschema: 1.5.0 - lodash: 4.17.21 - loglevel: 1.9.2 - loglevel-plugin-prefix: 0.8.4 - minimatch: 6.2.0 - validator: 13.15.15 - transitivePeerDependencies: - - encoding - '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.2 @@ -4886,20 +3956,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.4 - '@jsdevtools/ono@7.1.3': {} - - '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': - dependencies: - jsep: 1.4.0 - - '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': - dependencies: - jsep: 1.4.0 - - '@jsep-plugin/ternary@1.1.4(jsep@1.4.0)': - dependencies: - jsep: 1.4.0 - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4912,112 +3968,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@orval/angular@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/axios@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/core@7.11.2(openapi-types@12.1.3)': - dependencies: - '@apidevtools/swagger-parser': 10.1.1(openapi-types@12.1.3) - '@ibm-cloud/openapi-ruleset': 1.31.1 - acorn: 8.15.0 - ajv: 8.17.1 - chalk: 4.1.2 - compare-versions: 6.1.1 - debug: 4.4.1 - esbuild: 0.25.9 - esutils: 2.0.3 - fs-extra: 11.3.0 - globby: 11.1.0 - lodash.isempty: 4.4.0 - lodash.uniq: 4.5.0 - lodash.uniqby: 4.7.0 - lodash.uniqwith: 4.5.0 - micromatch: 4.0.8 - openapi3-ts: 4.4.0 - swagger2openapi: 7.0.8 - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/fetch@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/hono@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - '@orval/zod': 7.11.2(openapi-types@12.1.3) - lodash.uniq: 4.5.0 - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/mcp@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - '@orval/fetch': 7.11.2(openapi-types@12.1.3) - '@orval/zod': 7.11.2(openapi-types@12.1.3) - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/mock@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - openapi3-ts: 4.4.0 - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/query@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - '@orval/fetch': 7.11.2(openapi-types@12.1.3) - lodash.omitby: 4.6.0 - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/swr@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - '@orval/fetch': 7.11.2(openapi-types@12.1.3) - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - '@orval/zod@7.11.2(openapi-types@12.1.3)': - dependencies: - '@orval/core': 7.11.2(openapi-types@12.1.3) - lodash.uniq: 4.5.0 - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.0.0': @@ -5242,28 +4192,6 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.1) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.12)(react@19.1.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.12)(react@19.1.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.1) - aria-hidden: 1.2.6 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - react-remove-scroll: 2.7.1(@types/react@19.1.12)(react@19.1.1) - optionalDependencies: - '@types/react': 19.1.12 - '@types/react-dom': 19.1.9(@types/react@19.1.12) - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -6189,212 +5117,29 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.44.2': optional: true - '@shikijs/engine-oniguruma@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 - '@shikijs/vscode-textmate': 10.0.2 + '@swc/core-darwin-arm64@1.13.3': + optional: true - '@shikijs/langs@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 + '@swc/core-darwin-x64@1.13.3': + optional: true - '@shikijs/themes@3.8.1': - dependencies: - '@shikijs/types': 3.8.1 + '@swc/core-linux-arm-gnueabihf@1.13.3': + optional: true - '@shikijs/types@3.8.1': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@swc/core-linux-arm64-gnu@1.13.3': + optional: true - '@shikijs/vscode-textmate@10.0.2': {} + '@swc/core-linux-arm64-musl@1.13.3': + optional: true - '@stoplight/better-ajv-errors@1.0.3(ajv@8.17.1)': - dependencies: - ajv: 8.17.1 - jsonpointer: 5.0.1 - leven: 3.1.0 + '@swc/core-linux-x64-gnu@1.13.3': + optional: true - '@stoplight/json-ref-readers@1.2.2': - dependencies: - node-fetch: 2.7.0 - tslib: 1.14.1 - transitivePeerDependencies: - - encoding + '@swc/core-linux-x64-musl@1.13.3': + optional: true - '@stoplight/json-ref-resolver@3.1.6': - dependencies: - '@stoplight/json': 3.21.7 - '@stoplight/path': 1.3.2 - '@stoplight/types': 13.20.0 - '@types/urijs': 1.19.25 - dependency-graph: 0.11.0 - fast-memoize: 2.5.2 - immer: 9.0.21 - lodash: 4.17.21 - tslib: 2.8.1 - urijs: 1.19.11 - - '@stoplight/json@3.21.7': - dependencies: - '@stoplight/ordered-object-literal': 1.0.5 - '@stoplight/path': 1.3.2 - '@stoplight/types': 13.20.0 - jsonc-parser: 2.2.1 - lodash: 4.17.21 - safe-stable-stringify: 1.1.1 - - '@stoplight/ordered-object-literal@1.0.5': {} - - '@stoplight/path@1.3.2': {} - - '@stoplight/spectral-core@1.20.0': - dependencies: - '@stoplight/better-ajv-errors': 1.0.3(ajv@8.17.1) - '@stoplight/json': 3.21.7 - '@stoplight/path': 1.3.2 - '@stoplight/spectral-parsers': 1.0.5 - '@stoplight/spectral-ref-resolver': 1.0.5 - '@stoplight/spectral-runtime': 1.1.4 - '@stoplight/types': 13.6.0 - '@types/es-aggregate-error': 1.0.6 - '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-errors: 3.0.0(ajv@8.17.1) - ajv-formats: 2.1.1(ajv@8.17.1) - es-aggregate-error: 1.0.14 - jsonpath-plus: 10.3.0 - lodash: 4.17.21 - lodash.topath: 4.5.2 - minimatch: 3.1.2 - nimma: 0.2.3 - pony-cause: 1.1.1 - simple-eval: 1.0.1 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@stoplight/spectral-formats@1.8.2': - dependencies: - '@stoplight/json': 3.21.7 - '@stoplight/spectral-core': 1.20.0 - '@types/json-schema': 7.0.15 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@stoplight/spectral-functions@1.10.1': - dependencies: - '@stoplight/better-ajv-errors': 1.0.3(ajv@8.17.1) - '@stoplight/json': 3.21.7 - '@stoplight/spectral-core': 1.20.0 - '@stoplight/spectral-formats': 1.8.2 - '@stoplight/spectral-runtime': 1.1.4 - ajv: 8.17.1 - ajv-draft-04: 1.0.0(ajv@8.17.1) - ajv-errors: 3.0.0(ajv@8.17.1) - ajv-formats: 2.1.1(ajv@8.17.1) - lodash: 4.17.21 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@stoplight/spectral-parsers@1.0.5': - dependencies: - '@stoplight/json': 3.21.7 - '@stoplight/types': 14.1.1 - '@stoplight/yaml': 4.3.0 - tslib: 2.8.1 - - '@stoplight/spectral-ref-resolver@1.0.5': - dependencies: - '@stoplight/json-ref-readers': 1.2.2 - '@stoplight/json-ref-resolver': 3.1.6 - '@stoplight/spectral-runtime': 1.1.4 - dependency-graph: 0.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@stoplight/spectral-rulesets@1.22.0': - dependencies: - '@asyncapi/specs': 6.8.1 - '@stoplight/better-ajv-errors': 1.0.3(ajv@8.17.1) - '@stoplight/json': 3.21.7 - '@stoplight/spectral-core': 1.20.0 - '@stoplight/spectral-formats': 1.8.2 - '@stoplight/spectral-functions': 1.10.1 - '@stoplight/spectral-runtime': 1.1.4 - '@stoplight/types': 13.20.0 - '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - json-schema-traverse: 1.0.0 - leven: 3.1.0 - lodash: 4.17.21 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@stoplight/spectral-runtime@1.1.4': - dependencies: - '@stoplight/json': 3.21.7 - '@stoplight/path': 1.3.2 - '@stoplight/types': 13.20.0 - abort-controller: 3.0.0 - lodash: 4.17.21 - node-fetch: 2.7.0 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@stoplight/types@13.20.0': - dependencies: - '@types/json-schema': 7.0.15 - utility-types: 3.11.0 - - '@stoplight/types@13.6.0': - dependencies: - '@types/json-schema': 7.0.15 - utility-types: 3.11.0 - - '@stoplight/types@14.1.1': - dependencies: - '@types/json-schema': 7.0.15 - utility-types: 3.11.0 - - '@stoplight/yaml-ast-parser@0.0.50': {} - - '@stoplight/yaml@4.3.0': - dependencies: - '@stoplight/ordered-object-literal': 1.0.5 - '@stoplight/types': 14.1.1 - '@stoplight/yaml-ast-parser': 0.0.50 - tslib: 2.8.1 - - '@swc/core-darwin-arm64@1.13.3': - optional: true - - '@swc/core-darwin-x64@1.13.3': - optional: true - - '@swc/core-linux-arm-gnueabihf@1.13.3': - optional: true - - '@swc/core-linux-arm64-gnu@1.13.3': - optional: true - - '@swc/core-linux-arm64-musl@1.13.3': - optional: true - - '@swc/core-linux-x64-gnu@1.13.3': - optional: true - - '@swc/core-linux-x64-musl@1.13.3': - optional: true - - '@swc/core-win32-arm64-msvc@1.13.3': - optional: true + '@swc/core-win32-arm64-msvc@1.13.3': + optional: true '@swc/core-win32-ia32-msvc@1.13.3': optional: true @@ -6495,8 +5240,32 @@ snapshots: tailwindcss: 4.1.13 vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@2.8.0) + '@tanstack/devtools-event-client@0.3.3': {} + + '@tanstack/form-core@1.24.3': + dependencies: + '@tanstack/devtools-event-client': 0.3.3 + '@tanstack/store': 0.7.7 + '@tanstack/history@1.131.2': {} + '@tanstack/query-core@5.90.2': {} + + '@tanstack/react-form@1.23.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@tanstack/form-core': 1.24.3 + '@tanstack/react-store': 0.7.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + decode-formdata: 0.9.0 + devalue: 5.4.1 + react: 19.1.1 + transitivePeerDependencies: + - react-dom + + '@tanstack/react-query@5.90.2(react@19.1.1)': + dependencies: + '@tanstack/query-core': 5.90.2 + react: 19.1.1 + '@tanstack/react-router-devtools@1.131.36(@tanstack/react-router@1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.36)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.9)(tiny-invariant@1.3.3)': dependencies: '@tanstack/react-router': 1.131.36(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -6527,6 +5296,13 @@ snapshots: react-dom: 19.1.1(react@19.1.1) use-sync-external-store: 1.5.0(react@19.1.1) + '@tanstack/react-store@0.7.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@tanstack/store': 0.7.7 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + use-sync-external-store: 1.5.0(react@19.1.1) + '@tanstack/react-table@8.21.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@tanstack/table-core': 8.21.3 @@ -6601,6 +5377,8 @@ snapshots: '@tanstack/store@0.7.2': {} + '@tanstack/store@0.7.7': {} + '@tanstack/table-core@8.21.3': {} '@tanstack/virtual-file-routes@1.131.2': {} @@ -6633,10 +5411,6 @@ snapshots: dependencies: '@types/ms': 2.1.0 - '@types/es-aggregate-error@1.0.6': - dependencies: - '@types/node': 24.3.1 - '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -6664,6 +5438,8 @@ snapshots: dependencies: undici-types: 7.10.0 + '@types/prismjs@1.26.5': {} + '@types/react-dom@19.1.9(@types/react@19.1.12)': dependencies: '@types/react': 19.1.12 @@ -6676,8 +5452,6 @@ snapshots: '@types/unist@3.0.3': {} - '@types/urijs@1.19.25': {} - '@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -6781,28 +5555,12 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} - ajv-draft-04@1.0.0(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - - ajv-errors@3.0.0(ajv@8.17.1): - dependencies: - ajv: 8.17.1 - - ajv-formats@2.1.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -6810,17 +5568,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-colors@4.1.3: {} - - ansi-regex@5.0.1: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -6838,47 +5585,12 @@ snapshots: dependencies: tslib: 2.8.1 - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - array-move@3.0.1: {} - array-union@2.1.0: {} - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - ast-types@0.16.1: dependencies: tslib: 2.8.1 - astring@1.9.0: {} - - async-function@1.0.0: {} - - asynckit@0.4.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - axios@1.11.0: - dependencies: - follow-redirects: 1.15.9 - form-data: 4.0.4 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - babel-dead-code-elimination@1.0.10: dependencies: '@babel/core': 7.28.3 @@ -6914,27 +5626,6 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.2) - cac@6.7.14: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - call-me-maybe@1.0.2: {} - callsites@3.1.0: {} caniuse-lite@1.0.30001735: {} @@ -6966,22 +5657,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - chownr@3.0.0: {} class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - clsx@2.1.1: {} cmdk@0.2.1(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): @@ -6995,7 +5676,7 @@ snapshots: cmdk@1.1.1(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1) - '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-id': 1.1.1(@types/react@19.1.12)(react@19.1.1) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react: 19.1.1 @@ -7010,14 +5691,8 @@ snapshots: color-name@1.1.4: {} - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - comma-separated-tokens@2.0.3: {} - compare-versions@6.1.1: {} - concat-map@0.0.1: {} convert-source-map@2.0.0: {} @@ -7070,30 +5745,14 @@ snapshots: d3-timer@3.0.1: {} - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - debug@4.4.1: dependencies: ms: 2.1.3 decimal.js-light@2.5.1: {} + decode-formdata@0.9.0: {} + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -7102,49 +5761,25 @@ snapshots: deepmerge@2.2.1: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - delayed-stream@1.0.0: {} - - dependency-graph@0.11.0: {} - dequal@2.0.3: {} detect-libc@2.0.4: {} detect-node-es@1.1.0: {} + devalue@5.4.1: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 diff@8.0.2: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.27.6 csstype: 3.1.3 - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - electron-to-chromium@1.5.203: {} emblor@1.4.8(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): @@ -7163,111 +5798,11 @@ snapshots: - '@types/react' - '@types/react-dom' - emoji-regex@8.0.0: {} - enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 tapable: 2.2.2 - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - entities@4.5.0: {} - - es-abstract@1.24.0: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-negative-zero: 2.0.3 - is-regex: 1.2.1 - is-set: 2.0.3 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - - es-aggregate-error@1.0.14: - dependencies: - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - globalthis: 1.0.4 - has-property-descriptors: 1.0.2 - set-function-name: 2.0.2 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - - es6-promise@3.3.1: {} - esbuild@0.25.9: optionalDependencies: '@esbuild/aix-ppc64': 0.25.9 @@ -7384,22 +5919,8 @@ snapshots: esutils@2.0.3: {} - event-target-shim@5.0.1: {} - eventemitter3@4.0.7: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - extend@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -7418,12 +5939,6 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-memoize@2.5.2: {} - - fast-safe-stringify@2.1.1: {} - - fast-uri@3.0.6: {} - fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -7452,20 +5967,6 @@ snapshots: flatted@3.3.3: {} - follow-redirects@1.15.9: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - form-data@4.0.4: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - formik@2.4.6(react@19.1.1): dependencies: '@types/hoist-non-react-statics': 3.3.6 @@ -7478,60 +5979,13 @@ snapshots: tiny-warning: 1.0.3 tslib: 2.8.1 - fs-extra@11.3.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@6.0.1: {} - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -7548,52 +6002,16 @@ snapshots: globals@16.3.0: {} - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - goober@2.1.16(csstype@3.1.3): dependencies: csstype: 3.1.3 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} graphemer@1.4.0: {} - has-bigints@1.1.0: {} - has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.8 @@ -7624,10 +6042,6 @@ snapshots: html-url-attributes@3.0.1: {} - http2-client@1.3.5: {} - - human-signals@2.1.0: {} - ignore@5.3.2: {} ignore@7.0.5: {} @@ -7635,8 +6049,6 @@ snapshots: immer@10.1.1: optional: true - immer@9.0.21: {} - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -7646,12 +6058,6 @@ snapshots: inline-style-parser@0.2.4: {} - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - internmap@2.0.3: {} is-alphabetical@2.0.1: {} @@ -7661,125 +6067,24 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-decimal@2.0.1: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-fullwidth-code-point@3.0.0: {} - - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-hexadecimal@2.0.1: {} - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-number@7.0.0: {} is-plain-obj@4.1.0: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - - is-stream@2.0.1: {} - - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - isarray@2.0.5: {} - isbot@5.1.29: {} isexe@2.0.0: {} @@ -7792,44 +6097,20 @@ snapshots: dependencies: argparse: 2.0.1 - jsep@1.4.0: {} - jsesc@3.1.0: {} json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} - jsonc-parser@2.2.1: {} - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jsonpath-plus@10.3.0: - dependencies: - '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) - '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) - jsep: 1.4.0 - - jsonpointer@5.0.1: {} - - jsonschema@1.5.0: {} - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - leven@3.1.0: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -7880,36 +6161,16 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash-es@4.17.21: {} - lodash.isempty@4.4.0: {} - lodash.merge@4.6.2: {} - lodash.omitby@4.6.0: {} - - lodash.topath@4.5.2: {} - - lodash.uniq@4.5.0: {} - - lodash.uniqby@4.7.0: {} - - lodash.uniqwith@4.5.0: {} - lodash@4.17.21: {} - loglevel-plugin-prefix@0.8.4: {} - - loglevel@1.9.2: {} - longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -7924,25 +6185,12 @@ snapshots: dependencies: react: 19.1.1 - lunr@2.3.9: {} - magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - markdown-it@14.1.0: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - markdown-table@3.0.4: {} - math-intrinsics@1.1.0: {} - mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -8096,10 +6344,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdurl@2.0.0: {} - - merge-stream@2.0.0: {} - merge2@1.4.1: {} micromark-core-commonmark@2.0.3: @@ -8298,22 +6542,10 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mimic-fn@2.1.0: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 - minimatch@6.2.0: - dependencies: - brace-expansion: 2.0.2 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 @@ -8332,96 +6564,12 @@ snapshots: natural-compare@1.4.0: {} - nimma@0.2.3: - dependencies: - '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) - '@jsep-plugin/ternary': 1.1.4(jsep@1.4.0) - astring: 1.9.0 - jsep: 1.4.0 - optionalDependencies: - jsonpath-plus: 10.3.0 - lodash.topath: 4.5.2 - - node-fetch-h2@2.3.0: - dependencies: - http2-client: 1.3.5 - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - - node-readfiles@0.2.0: - dependencies: - es6-promise: 3.3.1 - node-releases@2.0.19: {} normalize-path@3.0.0: {} - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - oas-kit-common@1.0.8: - dependencies: - fast-safe-stringify: 2.1.1 - - oas-linter@3.2.2: - dependencies: - '@exodus/schemasafe': 1.3.0 - should: 13.2.3 - yaml: 1.10.2 - - oas-resolver@2.5.6: - dependencies: - node-fetch-h2: 2.3.0 - oas-kit-common: 1.0.8 - reftools: 1.1.9 - yaml: 1.10.2 - yargs: 17.7.2 - - oas-schema-walker@1.1.5: {} - - oas-validator@5.0.8: - dependencies: - call-me-maybe: 1.0.2 - oas-kit-common: 1.0.8 - oas-linter: 3.2.2 - oas-resolver: 2.5.6 - oas-schema-walker: 1.1.5 - reftools: 1.1.9 - should: 13.2.3 - yaml: 1.10.2 - object-assign@4.1.1: {} - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - openapi-types@12.1.3: {} - - openapi3-ts@4.2.2: - dependencies: - yaml: 2.8.0 - - openapi3-ts@4.4.0: - dependencies: - yaml: 2.8.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -8431,45 +6579,6 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - orval@7.11.2(openapi-types@12.1.3): - dependencies: - '@apidevtools/swagger-parser': 10.1.1(openapi-types@12.1.3) - '@orval/angular': 7.11.2(openapi-types@12.1.3) - '@orval/axios': 7.11.2(openapi-types@12.1.3) - '@orval/core': 7.11.2(openapi-types@12.1.3) - '@orval/fetch': 7.11.2(openapi-types@12.1.3) - '@orval/hono': 7.11.2(openapi-types@12.1.3) - '@orval/mcp': 7.11.2(openapi-types@12.1.3) - '@orval/mock': 7.11.2(openapi-types@12.1.3) - '@orval/query': 7.11.2(openapi-types@12.1.3) - '@orval/swr': 7.11.2(openapi-types@12.1.3) - '@orval/zod': 7.11.2(openapi-types@12.1.3) - ajv: 8.17.1 - cac: 6.7.14 - chalk: 4.1.2 - chokidar: 4.0.3 - enquirer: 2.4.1 - execa: 5.1.1 - find-up: 5.0.0 - fs-extra: 11.3.0 - lodash.uniq: 4.5.0 - openapi3-ts: 4.2.2 - string-argv: 0.3.2 - tsconfck: 2.1.2(typescript@5.9.2) - typedoc: 0.28.7(typescript@5.9.2) - typedoc-plugin-markdown: 4.8.1(typedoc@0.28.7(typescript@5.9.2)) - typescript: 5.9.2 - transitivePeerDependencies: - - encoding - - openapi-types - - supports-color - - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -8496,18 +6605,12 @@ snapshots: path-key@3.1.1: {} - path-type@4.0.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.3: {} - pony-cause@1.1.1: {} - - possible-typed-array-names@1.1.0: {} - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -8522,6 +6625,12 @@ snapshots: prettier@3.6.2: {} + prism-react-renderer@2.4.1(react@19.1.1): + dependencies: + '@types/prismjs': 1.26.5 + clsx: 2.1.1 + react: 19.1.1 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -8532,10 +6641,6 @@ snapshots: property-information@7.1.0: {} - proxy-from-env@1.1.0: {} - - punycode.js@2.3.1: {} - punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -8713,8 +6818,6 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.1.2: {} - recast@0.23.11: dependencies: ast-types: 0.16.1 @@ -8740,28 +6843,6 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.9.2 - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - - reftools@1.1.9: {} - - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 - remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -8796,10 +6877,6 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -8836,27 +6913,6 @@ snapshots: dependencies: queue-microtask: 1.2.3 - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - - safe-stable-stringify@1.1.1: {} - scheduler@0.26.0: {} semver@6.3.1: {} @@ -8869,96 +6925,12 @@ snapshots: seroval@1.3.2: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - should-equal@2.0.0: - dependencies: - should-type: 1.4.0 - - should-format@3.0.3: - dependencies: - should-type: 1.4.0 - should-type-adaptors: 1.1.0 - - should-type-adaptors@1.1.0: - dependencies: - should-type: 1.4.0 - should-util: 1.0.1 - - should-type@1.4.0: {} - - should-util@1.0.1: {} - - should@13.2.3: - dependencies: - should-equal: 2.0.0 - should-format: 3.0.3 - should-type: 1.4.0 - should-type-adaptors: 1.1.0 - should-util: 1.0.1 - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - signal-exit@3.0.7: {} - - simple-eval@1.0.1: - dependencies: - jsep: 1.4.0 - - slash@3.0.0: {} - solid-js@1.9.9: dependencies: csstype: 3.1.3 @@ -8978,53 +6950,11 @@ snapshots: space-separated-tokens@2.0.2: {} - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - - string-argv@0.3.2: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} style-to-js@1.1.17: @@ -9039,22 +6969,6 @@ snapshots: dependencies: has-flag: 4.0.0 - swagger2openapi@7.0.8: - dependencies: - call-me-maybe: 1.0.2 - node-fetch: 2.7.0 - node-fetch-h2: 2.3.0 - node-readfiles: 0.2.0 - oas-kit-common: 1.0.8 - oas-resolver: 2.5.6 - oas-schema-walker: 1.1.5 - oas-validator: 5.0.8 - reftools: 1.1.9 - yaml: 1.10.2 - yargs: 17.7.2 - transitivePeerDependencies: - - encoding - tailwind-merge@3.3.1: {} tailwindcss@4.1.13: {} @@ -9087,8 +7001,6 @@ snapshots: toposort@2.0.2: {} - tr46@0.0.3: {} - trim-lines@3.0.1: {} trough@2.2.0: {} @@ -9097,12 +7009,6 @@ snapshots: dependencies: typescript: 5.9.2 - tsconfck@2.1.2(typescript@5.9.2): - optionalDependencies: - typescript: 5.9.2 - - tslib@1.14.1: {} - tslib@2.0.1: {} tslib@2.8.1: {} @@ -9122,52 +7028,6 @@ snapshots: type-fest@2.19.0: {} - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 - - typedoc-plugin-markdown@4.8.1(typedoc@0.28.7(typescript@5.9.2)): - dependencies: - typedoc: 0.28.7(typescript@5.9.2) - - typedoc@0.28.7(typescript@5.9.2): - dependencies: - '@gerrit0/mini-shiki': 3.8.1 - lunr: 2.3.9 - markdown-it: 14.1.0 - minimatch: 9.0.5 - typescript: 5.9.2 - yaml: 2.8.0 - typescript-eslint@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2): dependencies: '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) @@ -9181,15 +7041,6 @@ snapshots: typescript@5.9.2: {} - uc.micro@2.1.0: {} - - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - undici-types@7.10.0: {} unified@11.0.5: @@ -9225,8 +7076,6 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - universalify@2.0.1: {} - unplugin@2.3.6: dependencies: '@jridgewell/remapping': 2.3.5 @@ -9244,8 +7093,6 @@ snapshots: dependencies: punycode: 2.3.1 - urijs@1.19.11: {} - use-callback-ref@1.3.3(@types/react@19.1.12)(react@19.1.1): dependencies: react: 19.1.1 @@ -9265,10 +7112,6 @@ snapshots: dependencies: react: 19.1.1 - utility-types@3.11.0: {} - - validator@13.15.15: {} - vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -9312,89 +7155,20 @@ snapshots: tsx: 4.20.4 yaml: 2.8.0 - webidl-conversions@3.0.1: {} - webpack-virtual-modules@0.6.2: {} - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.19 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 word-wrap@1.2.5: {} - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - y18n@5.0.8: {} - yallist@3.1.1: {} yallist@5.0.0: {} - yaml@1.10.2: {} - - yaml@2.8.0: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 + yaml@2.8.0: + optional: true yocto-queue@0.1.0: {} @@ -9407,6 +7181,8 @@ snapshots: zod@3.25.76: {} + zod@4.1.11: {} + zustand@5.0.8(@types/react@19.1.12)(immer@10.1.1)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): optionalDependencies: '@types/react': 19.1.12 diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg new file mode 100644 index 0000000..5d8f168 --- /dev/null +++ b/frontend/public/logo.svg @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts new file mode 100644 index 0000000..bdf0d3e --- /dev/null +++ b/frontend/src/api/api.ts @@ -0,0 +1,88 @@ +import { + Code, + ConnectError, + createClient, + type Interceptor, +} from "@connectrpc/connect"; +import { createConnectTransport } from "@connectrpc/connect-web"; +import { QueryClient } from "@tanstack/react-query"; + +import { NotificationService } from "./gen/sentinel/notifications/v1/notifications_pb"; +import { AgentsService } from "./gen/sentinel/agents/v1/agents_pb"; +import { AuthService } from "./gen/sentinel/auth/v1/auth_pb"; +import { ProjectsService } from "./gen/sentinel/projects/v1/projects_pb"; +import { SystemService } from "./gen/sentinel/system/v1/service_pb"; +import { useAuthStore } from "@/app/stores/auth"; +import { useProjectStore } from "@/app/stores/projects"; + +export const VITE_SERVER_API_BASE_URL = + import.meta.env.VITE_SERVER_API_BASE_URL || window.location.origin + "/api"; + +const skipAuthMethods = ["RefreshToken", "Authorization"]; + +const authInterceptor: Interceptor = (next) => async (req) => { + const { session, refreshToken } = useAuthStore.getState(); + const { selectedProjectId } = useProjectStore.getState(); + + if (selectedProjectId) { + req.header.set("X-Project-ID", selectedProjectId); + } + + if (session?.accessToken) { + req.header.set("Authorization", `Bearer ${session.accessToken}`); + } + + try { + return await next(req); + } catch (error) { + if ( + error instanceof ConnectError && + error.code === Code.Unauthenticated && + !skipAuthMethods.includes(req.method.name) + ) { + try { + await refreshToken(); + if (!session?.accessToken) { + throw new Error("No access token"); + } + + req.header.set("Authorization", `Bearer ${session?.accessToken}`); + return await next(req); + } catch (refreshError) { + console.error("Failed to refresh token:", refreshError); + throw refreshError; + } + } + throw error; + } + + // try { + // return await next(req); + // } catch (error) { + // if (error instanceof ConnectError && error.code === Code.Unauthenticated) { + // clear(); + // window.location.href = "/login"; + // } + // throw error; + // } +}; + +export const connectTransport = createConnectTransport({ + baseUrl: VITE_SERVER_API_BASE_URL, + interceptors: [authInterceptor], +}); + +export const connectQueryClient = new QueryClient(); + +export const systemClient = createClient(SystemService, connectTransport); + +export const authClient = createClient(AuthService, connectTransport); + +export const projectsClient = createClient(ProjectsService, connectTransport); + +export const agentsClient = createClient(AgentsService, connectTransport); + +export const notificationsClient = createClient( + NotificationService, + connectTransport, +); diff --git a/frontend/src/api/gen/sentinel/agents/v1/agents-AgentsService_connectquery.ts b/frontend/src/api/gen/sentinel/agents/v1/agents-AgentsService_connectquery.ts new file mode 100644 index 0000000..b0c763d --- /dev/null +++ b/frontend/src/api/gen/sentinel/agents/v1/agents-AgentsService_connectquery.ts @@ -0,0 +1,30 @@ +// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts" +// @generated from file sentinel/agents/v1/agents.proto (package sentinel.agents.v1, syntax proto3) +/* eslint-disable */ + +import { AgentsService } from "./agents_pb"; + +/** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsList + */ +export const agentsList = AgentsService.method.agentsList; + +/** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsGet + */ +export const agentsGet = AgentsService.method.agentsGet; + +/** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsCreate + */ +export const agentsCreate = AgentsService.method.agentsCreate; + +/** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsUpdate + */ +export const agentsUpdate = AgentsService.method.agentsUpdate; + +/** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsDelete + */ +export const agentsDelete = AgentsService.method.agentsDelete; diff --git a/frontend/src/api/gen/sentinel/agents/v1/agents_pb.ts b/frontend/src/api/gen/sentinel/agents/v1/agents_pb.ts new file mode 100644 index 0000000..490d840 --- /dev/null +++ b/frontend/src/api/gen/sentinel/agents/v1/agents_pb.ts @@ -0,0 +1,537 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/agents/v1/agents.proto (package sentinel.agents.v1, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { SystemInfo } from "../../system/v1/service_pb"; +import { file_sentinel_system_v1_service } from "../../system/v1/service_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/agents/v1/agents.proto. + */ +export const file_sentinel_agents_v1_agents: GenFile = /*@__PURE__*/ + fileDesc("Ch9zZW50aW5lbC9hZ2VudHMvdjEvYWdlbnRzLnByb3RvEhJzZW50aW5lbC5hZ2VudHMudjEiDQoLQWdlbnRDb25maWcikAQKBUFnZW50EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSEgoKdG9rZW5faGludBgEIAEoCRIYCgtmaW5nZXJwcmludBgFIAEoCUgAiAEBEisKBGtpbmQYBiABKA4yHS5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRLaW5kEi8KBnN0YXR1cxgHIAEoDjIfLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudFN0YXR1cxISCgppc19lbmFibGVkGAggASgIEhUKCGxvY2F0aW9uGAkgASgJSAGIAQESDAoEdGFncxgKIAMoCRIvCgZjb25maWcYCyABKAsyHy5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRDb25maWcSMwoLc3lzdGVtX2luZm8YDCABKAsyHi5zZW50aW5lbC5zeXN0ZW0udjEuU3lzdGVtSW5mbxIwCgxsYXN0X3NlZW5fYXQYDSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCmNyZWF0ZWRfYXQYDiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYDyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQg4KDF9maW5nZXJwcmludEILCglfbG9jYXRpb24iEwoRQWdlbnRzTGlzdFJlcXVlc3QiTQoSQWdlbnRzTGlzdFJlc3BvbnNlEigKBWl0ZW1zGAEgAygLMhkuc2VudGluZWwuYWdlbnRzLnYxLkFnZW50Eg0KBWNvdW50GAIgASgNIh4KEEFnZW50c0dldFJlcXVlc3QSCgoCaWQYASABKAkiPAoRQWdlbnRzR2V0UmVzcG9uc2USJwoEaXRlbRgBIAEoCzIZLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudCKLAQoTQWdlbnRzQ3JlYXRlUmVxdWVzdBIMCgRuYW1lGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEhIKCmlzX2VuYWJsZWQYAyABKAgSDAoEdGFncxgEIAMoCRIvCgZjb25maWcYBSABKAsyHy5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRDb25maWci7QIKFEFnZW50c0NyZWF0ZVJlc3BvbnNlEgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSDQoFdG9rZW4YBCABKAkSEgoKdG9rZW5faGludBgFIAEoCRIvCgZzdGF0dXMYBiABKA4yHy5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRTdGF0dXMSKwoEa2luZBgHIAEoDjIdLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudEtpbmQSEgoKaXNfZW5hYmxlZBgIIAEoCBIVCghsb2NhdGlvbhgJIAEoCUgAiAEBEgwKBHRhZ3MYCiADKAkSLwoGY29uZmlnGAsgASgLMh8uc2VudGluZWwuYWdlbnRzLnYxLkFnZW50Q29uZmlnEi4KCmNyZWF0ZWRfYXQYDCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgsKCV9sb2NhdGlvbiK7AQoTQWdlbnRzVXBkYXRlUmVxdWVzdBIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEhIKCmlzX2VuYWJsZWQYBCABKAgSFQoIbG9jYXRpb24YBSABKAlIAIgBARIMCgR0YWdzGAYgAygJEi8KBmNvbmZpZxgHIAEoCzIfLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudENvbmZpZ0ILCglfbG9jYXRpb24iPwoUQWdlbnRzVXBkYXRlUmVzcG9uc2USJwoEaXRlbRgBIAEoCzIZLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudCIhChNBZ2VudHNEZWxldGVSZXF1ZXN0EgoKAmlkGAEgASgJIhYKFEFnZW50c0RlbGV0ZVJlc3BvbnNlIhgKFkFnZW50c1N1YnNjcmliZVJlcXVlc3QiGQoXQWdlbnRzU3Vic2NyaWJlUmVzcG9uc2UqXwoLQWdlbnRTdGF0dXMSHAoYQUdFTlRfU1RBVFVTX1VOU1BFQ0lGSUVEEAASFwoTQUdFTlRfU1RBVFVTX0FDVElWRRABEhkKFUFHRU5UX1NUQVRVU19JTkFDVElWRRACKlQKCUFnZW50S2luZBIaChZBR0VOVF9LSU5EX1VOU1BFQ0lGSUVEEAASEgoOQUdFTlRfS0lORF9IVUIQARIXChNBR0VOVF9LSU5EX0VYVEVSTkFMEAIy3QQKDUFnZW50c1NlcnZpY2USWwoKQWdlbnRzTGlzdBIlLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudHNMaXN0UmVxdWVzdBomLnNlbnRpbmVsLmFnZW50cy52MS5BZ2VudHNMaXN0UmVzcG9uc2USWAoJQWdlbnRzR2V0EiQuc2VudGluZWwuYWdlbnRzLnYxLkFnZW50c0dldFJlcXVlc3QaJS5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRzR2V0UmVzcG9uc2USYQoMQWdlbnRzQ3JlYXRlEicuc2VudGluZWwuYWdlbnRzLnYxLkFnZW50c0NyZWF0ZVJlcXVlc3QaKC5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRzQ3JlYXRlUmVzcG9uc2USYQoMQWdlbnRzVXBkYXRlEicuc2VudGluZWwuYWdlbnRzLnYxLkFnZW50c1VwZGF0ZVJlcXVlc3QaKC5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRzVXBkYXRlUmVzcG9uc2USYQoMQWdlbnRzRGVsZXRlEicuc2VudGluZWwuYWdlbnRzLnYxLkFnZW50c0RlbGV0ZVJlcXVlc3QaKC5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRzRGVsZXRlUmVzcG9uc2USbAoPQWdlbnRzU3Vic2NyaWJlEiouc2VudGluZWwuYWdlbnRzLnYxLkFnZW50c1N1YnNjcmliZVJlcXVlc3QaKy5zZW50aW5lbC5hZ2VudHMudjEuQWdlbnRzU3Vic2NyaWJlUmVzcG9uc2UwAULkAQoWY29tLnNlbnRpbmVsLmFnZW50cy52MUILQWdlbnRzUHJvdG9QAVpTZ2l0aHViLmNvbS9zeHdlYmRldi9zZW50aW5lbC9pbnRlcm5hbC9odWIvaHVic2VydmVyL2FwaS9zZW50aW5lbC9hZ2VudHMvdjE7YWdlbnRzdjGiAgNTQViqAhJTZW50aW5lbC5BZ2VudHMuVjHKAhJTZW50aW5lbFxBZ2VudHNcVjHiAh5TZW50aW5lbFxBZ2VudHNcVjFcR1BCTWV0YWRhdGHqAhRTZW50aW5lbDo6QWdlbnRzOjpWMWIGcHJvdG8z", [file_google_protobuf_timestamp, file_sentinel_system_v1_service]); + +/** + * @generated from message sentinel.agents.v1.AgentConfig + */ +export type AgentConfig = Message<"sentinel.agents.v1.AgentConfig"> & { +}; + +/** + * Describes the message sentinel.agents.v1.AgentConfig. + * Use `create(AgentConfigSchema)` to create a new message. + */ +export const AgentConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 0); + +/** + * @generated from message sentinel.agents.v1.Agent + */ +export type Agent = Message<"sentinel.agents.v1.Agent"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: string description = 3; + */ + description: string; + + /** + * @generated from field: string token_hint = 4; + */ + tokenHint: string; + + /** + * @generated from field: optional string fingerprint = 5; + */ + fingerprint?: string; + + /** + * @generated from field: sentinel.agents.v1.AgentKind kind = 6; + */ + kind: AgentKind; + + /** + * @generated from field: sentinel.agents.v1.AgentStatus status = 7; + */ + status: AgentStatus; + + /** + * @generated from field: bool is_enabled = 8; + */ + isEnabled: boolean; + + /** + * @generated from field: optional string location = 9; + */ + location?: string; + + /** + * @generated from field: repeated string tags = 10; + */ + tags: string[]; + + /** + * @generated from field: sentinel.agents.v1.AgentConfig config = 11; + */ + config?: AgentConfig; + + /** + * @generated from field: sentinel.system.v1.SystemInfo system_info = 12; + */ + systemInfo?: SystemInfo; + + /** + * @generated from field: google.protobuf.Timestamp last_seen_at = 13; + */ + lastSeenAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 14; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 15; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.agents.v1.Agent. + * Use `create(AgentSchema)` to create a new message. + */ +export const AgentSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 1); + +/** + * AgentsList agents lists all agents. + * + * @generated from message sentinel.agents.v1.AgentsListRequest + */ +export type AgentsListRequest = Message<"sentinel.agents.v1.AgentsListRequest"> & { +}; + +/** + * Describes the message sentinel.agents.v1.AgentsListRequest. + * Use `create(AgentsListRequestSchema)` to create a new message. + */ +export const AgentsListRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 2); + +/** + * @generated from message sentinel.agents.v1.AgentsListResponse + */ +export type AgentsListResponse = Message<"sentinel.agents.v1.AgentsListResponse"> & { + /** + * @generated from field: repeated sentinel.agents.v1.Agent items = 1; + */ + items: Agent[]; + + /** + * @generated from field: uint32 count = 2; + */ + count: number; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsListResponse. + * Use `create(AgentsListResponseSchema)` to create a new message. + */ +export const AgentsListResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 3); + +/** + * AgentsGet agent gets an agent by ID. + * + * @generated from message sentinel.agents.v1.AgentsGetRequest + */ +export type AgentsGetRequest = Message<"sentinel.agents.v1.AgentsGetRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsGetRequest. + * Use `create(AgentsGetRequestSchema)` to create a new message. + */ +export const AgentsGetRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 4); + +/** + * @generated from message sentinel.agents.v1.AgentsGetResponse + */ +export type AgentsGetResponse = Message<"sentinel.agents.v1.AgentsGetResponse"> & { + /** + * @generated from field: sentinel.agents.v1.Agent item = 1; + */ + item?: Agent; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsGetResponse. + * Use `create(AgentsGetResponseSchema)` to create a new message. + */ +export const AgentsGetResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 5); + +/** + * AgentsCreate agent creates a new agent. + * + * @generated from message sentinel.agents.v1.AgentsCreateRequest + */ +export type AgentsCreateRequest = Message<"sentinel.agents.v1.AgentsCreateRequest"> & { + /** + * @generated from field: string name = 1; + */ + name: string; + + /** + * @generated from field: string description = 2; + */ + description: string; + + /** + * @generated from field: bool is_enabled = 3; + */ + isEnabled: boolean; + + /** + * @generated from field: repeated string tags = 4; + */ + tags: string[]; + + /** + * @generated from field: sentinel.agents.v1.AgentConfig config = 5; + */ + config?: AgentConfig; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsCreateRequest. + * Use `create(AgentsCreateRequestSchema)` to create a new message. + */ +export const AgentsCreateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 6); + +/** + * @generated from message sentinel.agents.v1.AgentsCreateResponse + */ +export type AgentsCreateResponse = Message<"sentinel.agents.v1.AgentsCreateResponse"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: string description = 3; + */ + description: string; + + /** + * @generated from field: string token = 4; + */ + token: string; + + /** + * @generated from field: string token_hint = 5; + */ + tokenHint: string; + + /** + * @generated from field: sentinel.agents.v1.AgentStatus status = 6; + */ + status: AgentStatus; + + /** + * @generated from field: sentinel.agents.v1.AgentKind kind = 7; + */ + kind: AgentKind; + + /** + * @generated from field: bool is_enabled = 8; + */ + isEnabled: boolean; + + /** + * @generated from field: optional string location = 9; + */ + location?: string; + + /** + * @generated from field: repeated string tags = 10; + */ + tags: string[]; + + /** + * @generated from field: sentinel.agents.v1.AgentConfig config = 11; + */ + config?: AgentConfig; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 12; + */ + createdAt?: Timestamp; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsCreateResponse. + * Use `create(AgentsCreateResponseSchema)` to create a new message. + */ +export const AgentsCreateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 7); + +/** + * AgentsUpdate agent updates an existing agent. + * + * @generated from message sentinel.agents.v1.AgentsUpdateRequest + */ +export type AgentsUpdateRequest = Message<"sentinel.agents.v1.AgentsUpdateRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: string description = 3; + */ + description: string; + + /** + * @generated from field: bool is_enabled = 4; + */ + isEnabled: boolean; + + /** + * @generated from field: optional string location = 5; + */ + location?: string; + + /** + * @generated from field: repeated string tags = 6; + */ + tags: string[]; + + /** + * @generated from field: sentinel.agents.v1.AgentConfig config = 7; + */ + config?: AgentConfig; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsUpdateRequest. + * Use `create(AgentsUpdateRequestSchema)` to create a new message. + */ +export const AgentsUpdateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 8); + +/** + * @generated from message sentinel.agents.v1.AgentsUpdateResponse + */ +export type AgentsUpdateResponse = Message<"sentinel.agents.v1.AgentsUpdateResponse"> & { + /** + * @generated from field: sentinel.agents.v1.Agent item = 1; + */ + item?: Agent; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsUpdateResponse. + * Use `create(AgentsUpdateResponseSchema)` to create a new message. + */ +export const AgentsUpdateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 9); + +/** + * Delete agent deletes an agent by ID. + * + * @generated from message sentinel.agents.v1.AgentsDeleteRequest + */ +export type AgentsDeleteRequest = Message<"sentinel.agents.v1.AgentsDeleteRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.agents.v1.AgentsDeleteRequest. + * Use `create(AgentsDeleteRequestSchema)` to create a new message. + */ +export const AgentsDeleteRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 10); + +/** + * @generated from message sentinel.agents.v1.AgentsDeleteResponse + */ +export type AgentsDeleteResponse = Message<"sentinel.agents.v1.AgentsDeleteResponse"> & { +}; + +/** + * Describes the message sentinel.agents.v1.AgentsDeleteResponse. + * Use `create(AgentsDeleteResponseSchema)` to create a new message. + */ +export const AgentsDeleteResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 11); + +/** + * AgentsSubscribe subscribes to agent events. + * + * @generated from message sentinel.agents.v1.AgentsSubscribeRequest + */ +export type AgentsSubscribeRequest = Message<"sentinel.agents.v1.AgentsSubscribeRequest"> & { +}; + +/** + * Describes the message sentinel.agents.v1.AgentsSubscribeRequest. + * Use `create(AgentsSubscribeRequestSchema)` to create a new message. + */ +export const AgentsSubscribeRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 12); + +/** + * @generated from message sentinel.agents.v1.AgentsSubscribeResponse + */ +export type AgentsSubscribeResponse = Message<"sentinel.agents.v1.AgentsSubscribeResponse"> & { +}; + +/** + * Describes the message sentinel.agents.v1.AgentsSubscribeResponse. + * Use `create(AgentsSubscribeResponseSchema)` to create a new message. + */ +export const AgentsSubscribeResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_agents_v1_agents, 13); + +/** + * @generated from enum sentinel.agents.v1.AgentStatus + */ +export enum AgentStatus { + /** + * @generated from enum value: AGENT_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: AGENT_STATUS_ACTIVE = 1; + */ + ACTIVE = 1, + + /** + * @generated from enum value: AGENT_STATUS_INACTIVE = 2; + */ + INACTIVE = 2, +} + +/** + * Describes the enum sentinel.agents.v1.AgentStatus. + */ +export const AgentStatusSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_agents_v1_agents, 0); + +/** + * @generated from enum sentinel.agents.v1.AgentKind + */ +export enum AgentKind { + /** + * @generated from enum value: AGENT_KIND_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: AGENT_KIND_HUB = 1; + */ + HUB = 1, + + /** + * @generated from enum value: AGENT_KIND_EXTERNAL = 2; + */ + EXTERNAL = 2, +} + +/** + * Describes the enum sentinel.agents.v1.AgentKind. + */ +export const AgentKindSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_agents_v1_agents, 1); + +/** + * AgentsService is the service for managing agents. + * + * @generated from service sentinel.agents.v1.AgentsService + */ +export const AgentsService: GenService<{ + /** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsList + */ + agentsList: { + methodKind: "unary"; + input: typeof AgentsListRequestSchema; + output: typeof AgentsListResponseSchema; + }, + /** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsGet + */ + agentsGet: { + methodKind: "unary"; + input: typeof AgentsGetRequestSchema; + output: typeof AgentsGetResponseSchema; + }, + /** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsCreate + */ + agentsCreate: { + methodKind: "unary"; + input: typeof AgentsCreateRequestSchema; + output: typeof AgentsCreateResponseSchema; + }, + /** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsUpdate + */ + agentsUpdate: { + methodKind: "unary"; + input: typeof AgentsUpdateRequestSchema; + output: typeof AgentsUpdateResponseSchema; + }, + /** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsDelete + */ + agentsDelete: { + methodKind: "unary"; + input: typeof AgentsDeleteRequestSchema; + output: typeof AgentsDeleteResponseSchema; + }, + /** + * @generated from rpc sentinel.agents.v1.AgentsService.AgentsSubscribe + */ + agentsSubscribe: { + methodKind: "server_streaming"; + input: typeof AgentsSubscribeRequestSchema; + output: typeof AgentsSubscribeResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_sentinel_agents_v1_agents, 0); + diff --git a/frontend/src/api/gen/sentinel/auth/v1/auth-AuthService_connectquery.ts b/frontend/src/api/gen/sentinel/auth/v1/auth-AuthService_connectquery.ts new file mode 100644 index 0000000..c28cb3d --- /dev/null +++ b/frontend/src/api/gen/sentinel/auth/v1/auth-AuthService_connectquery.ts @@ -0,0 +1,44 @@ +// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts" +// @generated from file sentinel/auth/v1/auth.proto (package sentinel.auth.v1, syntax proto3) +/* eslint-disable */ + +import { AuthService } from "./auth_pb"; + +/** + * Auth + * + * @generated from rpc sentinel.auth.v1.AuthService.Authorization + */ +export const authorization = AuthService.method.authorization; + +/** + * @generated from rpc sentinel.auth.v1.AuthService.Authenticate + */ +export const authenticate = AuthService.method.authenticate; + +/** + * @generated from rpc sentinel.auth.v1.AuthService.RefreshToken + */ +export const refreshToken = AuthService.method.refreshToken; + +/** + * @generated from rpc sentinel.auth.v1.AuthService.Logout + */ +export const logout = AuthService.method.logout; + +/** + * Sessions + * + * @generated from rpc sentinel.auth.v1.AuthService.ActiveSessions + */ +export const activeSessions = AuthService.method.activeSessions; + +/** + * @generated from rpc sentinel.auth.v1.AuthService.DeleteSession + */ +export const deleteSession = AuthService.method.deleteSession; + +/** + * @generated from rpc sentinel.auth.v1.AuthService.TerminateAllSessions + */ +export const terminateAllSessions = AuthService.method.terminateAllSessions; diff --git a/frontend/src/api/gen/sentinel/auth/v1/auth_pb.ts b/frontend/src/api/gen/sentinel/auth/v1/auth_pb.ts new file mode 100644 index 0000000..b29cbf1 --- /dev/null +++ b/frontend/src/api/gen/sentinel/auth/v1/auth_pb.ts @@ -0,0 +1,489 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/auth/v1/auth.proto (package sentinel.auth.v1, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { User } from "../../users/v1/users_pb"; +import { file_sentinel_users_v1_users } from "../../users/v1/users_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/auth/v1/auth.proto. + */ +export const file_sentinel_auth_v1_auth: GenFile = /*@__PURE__*/ + fileDesc("ChtzZW50aW5lbC9hdXRoL3YxL2F1dGgucHJvdG8SEHNlbnRpbmVsLmF1dGgudjEipQEKCkRldmljZUluZm8SEQoJZGV2aWNlX2lkGAEgASgJEjEKC2RldmljZV90eXBlGAIgASgOMhwuc2VudGluZWwuYXV0aC52MS5EZXZpY2VUeXBlEhMKC2RldmljZV9uYW1lGAMgASgJEhMKC2ZpbmdlcnByaW50GAQgASgJEhIKCm9zX3ZlcnNpb24YBSABKAkSEwoLYXBwX3ZlcnNpb24YBiABKAkiiQEKB1Nlc3Npb24SMQoLZGV2aWNlX2luZm8YASABKAsyHC5zZW50aW5lbC5hdXRoLnYxLkRldmljZUluZm8SDwoHY291bnRyeRgCIAEoCRIKCgJpcBgDIAEoCRIuCgpjcmVhdGVkX2F0GAQgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCJqChRBdXRob3JpemF0aW9uUmVxdWVzdBINCgVlbWFpbBgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIxCgtkZXZpY2VfaW5mbxgEIAEoCzIcLnNlbnRpbmVsLmF1dGgudjEuRGV2aWNlSW5mbyL6AQoLQXV0aFBheWxvYWQSFAoMYWNjZXNzX3Rva2VuGAEgASgJEhUKDXJlZnJlc2hfdG9rZW4YAiABKAkSOwoXYWNjZXNzX3Rva2VuX2V4cGlyZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEjwKGHJlZnJlc2hfdG9rZW5fZXhwaXJlZF9hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASEQoJZGV2aWNlX2lkGAUgASgJEhwKD29yZ2FuaXphdGlvbl9pZBgGIAEoCUgAiAEBQhIKEF9vcmdhbml6YXRpb25faWQicwoVQXV0aG9yaXphdGlvblJlc3BvbnNlEjMKDGF1dGhfcGF5bG9hZBgBIAEoCzIdLnNlbnRpbmVsLmF1dGgudjEuQXV0aFBheWxvYWQSJQoEdXNlchgCIAEoCzIXLnNlbnRpbmVsLnVzZXJzLnYxLlVzZXIiKwoTQXV0aGVudGljYXRlUmVxdWVzdBIUCgxhY2Nlc3NfdG9rZW4YASABKAkiPQoUQXV0aGVudGljYXRlUmVzcG9uc2USJQoEdXNlchgCIAEoCzIXLnNlbnRpbmVsLnVzZXJzLnYxLlVzZXIiLAoTUmVmcmVzaFRva2VuUmVxdWVzdBIVCg1yZWZyZXNoX3Rva2VuGAEgASgJIr4BChRSZWZyZXNoVG9rZW5SZXNwb25zZRIUCgxhY2Nlc3NfdG9rZW4YASABKAkSFQoNcmVmcmVzaF90b2tlbhgCIAEoCRI7ChdhY2Nlc3NfdG9rZW5fZXhwaXJlZF9hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASPAoYcmVmcmVzaF90b2tlbl9leHBpcmVkX2F0GAQgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCIXChVBY3RpdmVTZXNzaW9uc1JlcXVlc3QiRQoWQWN0aXZlU2Vzc2lvbnNSZXNwb25zZRIrCghzZXNzaW9ucxgBIAMoCzIZLnNlbnRpbmVsLmF1dGgudjEuU2Vzc2lvbiIpChREZWxldGVTZXNzaW9uUmVxdWVzdBIRCglkZXZpY2VfaWQYASABKAkiFwoVRGVsZXRlU2Vzc2lvblJlc3BvbnNlIkMKG1Rlcm1pbmF0ZUFsbFNlc3Npb25zUmVxdWVzdBIWCglkZXZpY2VfaWQYASABKAlIAIgBAUIMCgpfZGV2aWNlX2lkIh4KHFRlcm1pbmF0ZUFsbFNlc3Npb25zUmVzcG9uc2UiDwoNTG9nb3V0UmVxdWVzdCIQCg5Mb2dvdXRSZXNwb25zZSo+CgpEZXZpY2VUeXBlEhsKF0RFVklDRV9UWVBFX1VOU1BFQ0lGSUVEEAASEwoPREVWSUNFX1RZUEVfV0VCEAEyxgUKC0F1dGhTZXJ2aWNlEmIKDUF1dGhvcml6YXRpb24SJi5zZW50aW5lbC5hdXRoLnYxLkF1dGhvcml6YXRpb25SZXF1ZXN0Gicuc2VudGluZWwuYXV0aC52MS5BdXRob3JpemF0aW9uUmVzcG9uc2UiABJfCgxBdXRoZW50aWNhdGUSJS5zZW50aW5lbC5hdXRoLnYxLkF1dGhlbnRpY2F0ZVJlcXVlc3QaJi5zZW50aW5lbC5hdXRoLnYxLkF1dGhlbnRpY2F0ZVJlc3BvbnNlIgASXwoMUmVmcmVzaFRva2VuEiUuc2VudGluZWwuYXV0aC52MS5SZWZyZXNoVG9rZW5SZXF1ZXN0GiYuc2VudGluZWwuYXV0aC52MS5SZWZyZXNoVG9rZW5SZXNwb25zZSIAEk0KBkxvZ291dBIfLnNlbnRpbmVsLmF1dGgudjEuTG9nb3V0UmVxdWVzdBogLnNlbnRpbmVsLmF1dGgudjEuTG9nb3V0UmVzcG9uc2UiABJlCg5BY3RpdmVTZXNzaW9ucxInLnNlbnRpbmVsLmF1dGgudjEuQWN0aXZlU2Vzc2lvbnNSZXF1ZXN0Giguc2VudGluZWwuYXV0aC52MS5BY3RpdmVTZXNzaW9uc1Jlc3BvbnNlIgASYgoNRGVsZXRlU2Vzc2lvbhImLnNlbnRpbmVsLmF1dGgudjEuRGVsZXRlU2Vzc2lvblJlcXVlc3QaJy5zZW50aW5lbC5hdXRoLnYxLkRlbGV0ZVNlc3Npb25SZXNwb25zZSIAEncKFFRlcm1pbmF0ZUFsbFNlc3Npb25zEi0uc2VudGluZWwuYXV0aC52MS5UZXJtaW5hdGVBbGxTZXNzaW9uc1JlcXVlc3QaLi5zZW50aW5lbC5hdXRoLnYxLlRlcm1pbmF0ZUFsbFNlc3Npb25zUmVzcG9uc2UiAELUAQoUY29tLnNlbnRpbmVsLmF1dGgudjFCCUF1dGhQcm90b1ABWk9naXRodWIuY29tL3N4d2ViZGV2L3NlbnRpbmVsL2ludGVybmFsL2h1Yi9odWJzZXJ2ZXIvYXBpL3NlbnRpbmVsL2F1dGgvdjE7YXV0aHYxogIDU0FYqgIQU2VudGluZWwuQXV0aC5WMcoCEFNlbnRpbmVsXEF1dGhcVjHiAhxTZW50aW5lbFxBdXRoXFYxXEdQQk1ldGFkYXRh6gISU2VudGluZWw6OkF1dGg6OlYxYgZwcm90bzM", [file_google_protobuf_timestamp, file_sentinel_users_v1_users]); + +/** + * @generated from message sentinel.auth.v1.DeviceInfo + */ +export type DeviceInfo = Message<"sentinel.auth.v1.DeviceInfo"> & { + /** + * @generated from field: string device_id = 1; + */ + deviceId: string; + + /** + * @generated from field: sentinel.auth.v1.DeviceType device_type = 2; + */ + deviceType: DeviceType; + + /** + * @generated from field: string device_name = 3; + */ + deviceName: string; + + /** + * @generated from field: string fingerprint = 4; + */ + fingerprint: string; + + /** + * @generated from field: string os_version = 5; + */ + osVersion: string; + + /** + * @generated from field: string app_version = 6; + */ + appVersion: string; +}; + +/** + * Describes the message sentinel.auth.v1.DeviceInfo. + * Use `create(DeviceInfoSchema)` to create a new message. + */ +export const DeviceInfoSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 0); + +/** + * @generated from message sentinel.auth.v1.Session + */ +export type Session = Message<"sentinel.auth.v1.Session"> & { + /** + * @generated from field: sentinel.auth.v1.DeviceInfo device_info = 1; + */ + deviceInfo?: DeviceInfo; + + /** + * @generated from field: string country = 2; + */ + country: string; + + /** + * @generated from field: string ip = 3; + */ + ip: string; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 4; + */ + createdAt?: Timestamp; +}; + +/** + * Describes the message sentinel.auth.v1.Session. + * Use `create(SessionSchema)` to create a new message. + */ +export const SessionSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 1); + +/** + * Authorization + * + * @generated from message sentinel.auth.v1.AuthorizationRequest + */ +export type AuthorizationRequest = Message<"sentinel.auth.v1.AuthorizationRequest"> & { + /** + * @generated from field: string email = 1; + */ + email: string; + + /** + * @generated from field: string password = 2; + */ + password: string; + + /** + * @generated from field: sentinel.auth.v1.DeviceInfo device_info = 4; + */ + deviceInfo?: DeviceInfo; +}; + +/** + * Describes the message sentinel.auth.v1.AuthorizationRequest. + * Use `create(AuthorizationRequestSchema)` to create a new message. + */ +export const AuthorizationRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 2); + +/** + * @generated from message sentinel.auth.v1.AuthPayload + */ +export type AuthPayload = Message<"sentinel.auth.v1.AuthPayload"> & { + /** + * @generated from field: string access_token = 1; + */ + accessToken: string; + + /** + * @generated from field: string refresh_token = 2; + */ + refreshToken: string; + + /** + * @generated from field: google.protobuf.Timestamp access_token_expired_at = 3; + */ + accessTokenExpiredAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp refresh_token_expired_at = 4; + */ + refreshTokenExpiredAt?: Timestamp; + + /** + * @generated from field: string device_id = 5; + */ + deviceId: string; + + /** + * @generated from field: optional string organization_id = 6; + */ + organizationId?: string; +}; + +/** + * Describes the message sentinel.auth.v1.AuthPayload. + * Use `create(AuthPayloadSchema)` to create a new message. + */ +export const AuthPayloadSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 3); + +/** + * @generated from message sentinel.auth.v1.AuthorizationResponse + */ +export type AuthorizationResponse = Message<"sentinel.auth.v1.AuthorizationResponse"> & { + /** + * @generated from field: sentinel.auth.v1.AuthPayload auth_payload = 1; + */ + authPayload?: AuthPayload; + + /** + * @generated from field: sentinel.users.v1.User user = 2; + */ + user?: User; +}; + +/** + * Describes the message sentinel.auth.v1.AuthorizationResponse. + * Use `create(AuthorizationResponseSchema)` to create a new message. + */ +export const AuthorizationResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 4); + +/** + * Authenticate + * + * @generated from message sentinel.auth.v1.AuthenticateRequest + */ +export type AuthenticateRequest = Message<"sentinel.auth.v1.AuthenticateRequest"> & { + /** + * @generated from field: string access_token = 1; + */ + accessToken: string; +}; + +/** + * Describes the message sentinel.auth.v1.AuthenticateRequest. + * Use `create(AuthenticateRequestSchema)` to create a new message. + */ +export const AuthenticateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 5); + +/** + * @generated from message sentinel.auth.v1.AuthenticateResponse + */ +export type AuthenticateResponse = Message<"sentinel.auth.v1.AuthenticateResponse"> & { + /** + * @generated from field: sentinel.users.v1.User user = 2; + */ + user?: User; +}; + +/** + * Describes the message sentinel.auth.v1.AuthenticateResponse. + * Use `create(AuthenticateResponseSchema)` to create a new message. + */ +export const AuthenticateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 6); + +/** + * Refresh token + * + * @generated from message sentinel.auth.v1.RefreshTokenRequest + */ +export type RefreshTokenRequest = Message<"sentinel.auth.v1.RefreshTokenRequest"> & { + /** + * @generated from field: string refresh_token = 1; + */ + refreshToken: string; +}; + +/** + * Describes the message sentinel.auth.v1.RefreshTokenRequest. + * Use `create(RefreshTokenRequestSchema)` to create a new message. + */ +export const RefreshTokenRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 7); + +/** + * @generated from message sentinel.auth.v1.RefreshTokenResponse + */ +export type RefreshTokenResponse = Message<"sentinel.auth.v1.RefreshTokenResponse"> & { + /** + * @generated from field: string access_token = 1; + */ + accessToken: string; + + /** + * @generated from field: string refresh_token = 2; + */ + refreshToken: string; + + /** + * @generated from field: google.protobuf.Timestamp access_token_expired_at = 3; + */ + accessTokenExpiredAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp refresh_token_expired_at = 4; + */ + refreshTokenExpiredAt?: Timestamp; +}; + +/** + * Describes the message sentinel.auth.v1.RefreshTokenResponse. + * Use `create(RefreshTokenResponseSchema)` to create a new message. + */ +export const RefreshTokenResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 8); + +/** + * Active sessions + * + * @generated from message sentinel.auth.v1.ActiveSessionsRequest + */ +export type ActiveSessionsRequest = Message<"sentinel.auth.v1.ActiveSessionsRequest"> & { +}; + +/** + * Describes the message sentinel.auth.v1.ActiveSessionsRequest. + * Use `create(ActiveSessionsRequestSchema)` to create a new message. + */ +export const ActiveSessionsRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 9); + +/** + * @generated from message sentinel.auth.v1.ActiveSessionsResponse + */ +export type ActiveSessionsResponse = Message<"sentinel.auth.v1.ActiveSessionsResponse"> & { + /** + * @generated from field: repeated sentinel.auth.v1.Session sessions = 1; + */ + sessions: Session[]; +}; + +/** + * Describes the message sentinel.auth.v1.ActiveSessionsResponse. + * Use `create(ActiveSessionsResponseSchema)` to create a new message. + */ +export const ActiveSessionsResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 10); + +/** + * Delete session + * + * @generated from message sentinel.auth.v1.DeleteSessionRequest + */ +export type DeleteSessionRequest = Message<"sentinel.auth.v1.DeleteSessionRequest"> & { + /** + * @generated from field: string device_id = 1; + */ + deviceId: string; +}; + +/** + * Describes the message sentinel.auth.v1.DeleteSessionRequest. + * Use `create(DeleteSessionRequestSchema)` to create a new message. + */ +export const DeleteSessionRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 11); + +/** + * @generated from message sentinel.auth.v1.DeleteSessionResponse + */ +export type DeleteSessionResponse = Message<"sentinel.auth.v1.DeleteSessionResponse"> & { +}; + +/** + * Describes the message sentinel.auth.v1.DeleteSessionResponse. + * Use `create(DeleteSessionResponseSchema)` to create a new message. + */ +export const DeleteSessionResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 12); + +/** + * Terminate all sessions + * + * @generated from message sentinel.auth.v1.TerminateAllSessionsRequest + */ +export type TerminateAllSessionsRequest = Message<"sentinel.auth.v1.TerminateAllSessionsRequest"> & { + /** + * Current device id. This session will not be terminated. + * If not set, all sessions will be terminated. + * + * @generated from field: optional string device_id = 1; + */ + deviceId?: string; +}; + +/** + * Describes the message sentinel.auth.v1.TerminateAllSessionsRequest. + * Use `create(TerminateAllSessionsRequestSchema)` to create a new message. + */ +export const TerminateAllSessionsRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 13); + +/** + * @generated from message sentinel.auth.v1.TerminateAllSessionsResponse + */ +export type TerminateAllSessionsResponse = Message<"sentinel.auth.v1.TerminateAllSessionsResponse"> & { +}; + +/** + * Describes the message sentinel.auth.v1.TerminateAllSessionsResponse. + * Use `create(TerminateAllSessionsResponseSchema)` to create a new message. + */ +export const TerminateAllSessionsResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 14); + +/** + * Logout + * + * @generated from message sentinel.auth.v1.LogoutRequest + */ +export type LogoutRequest = Message<"sentinel.auth.v1.LogoutRequest"> & { +}; + +/** + * Describes the message sentinel.auth.v1.LogoutRequest. + * Use `create(LogoutRequestSchema)` to create a new message. + */ +export const LogoutRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 15); + +/** + * @generated from message sentinel.auth.v1.LogoutResponse + */ +export type LogoutResponse = Message<"sentinel.auth.v1.LogoutResponse"> & { +}; + +/** + * Describes the message sentinel.auth.v1.LogoutResponse. + * Use `create(LogoutResponseSchema)` to create a new message. + */ +export const LogoutResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_auth_v1_auth, 16); + +/** + * @generated from enum sentinel.auth.v1.DeviceType + */ +export enum DeviceType { + /** + * @generated from enum value: DEVICE_TYPE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: DEVICE_TYPE_WEB = 1; + */ + WEB = 1, +} + +/** + * Describes the enum sentinel.auth.v1.DeviceType. + */ +export const DeviceTypeSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_auth_v1_auth, 0); + +/** + * AuthService is the service for authentication and authorization. + * + * @generated from service sentinel.auth.v1.AuthService + */ +export const AuthService: GenService<{ + /** + * Auth + * + * @generated from rpc sentinel.auth.v1.AuthService.Authorization + */ + authorization: { + methodKind: "unary"; + input: typeof AuthorizationRequestSchema; + output: typeof AuthorizationResponseSchema; + }, + /** + * @generated from rpc sentinel.auth.v1.AuthService.Authenticate + */ + authenticate: { + methodKind: "unary"; + input: typeof AuthenticateRequestSchema; + output: typeof AuthenticateResponseSchema; + }, + /** + * @generated from rpc sentinel.auth.v1.AuthService.RefreshToken + */ + refreshToken: { + methodKind: "unary"; + input: typeof RefreshTokenRequestSchema; + output: typeof RefreshTokenResponseSchema; + }, + /** + * @generated from rpc sentinel.auth.v1.AuthService.Logout + */ + logout: { + methodKind: "unary"; + input: typeof LogoutRequestSchema; + output: typeof LogoutResponseSchema; + }, + /** + * Sessions + * + * @generated from rpc sentinel.auth.v1.AuthService.ActiveSessions + */ + activeSessions: { + methodKind: "unary"; + input: typeof ActiveSessionsRequestSchema; + output: typeof ActiveSessionsResponseSchema; + }, + /** + * @generated from rpc sentinel.auth.v1.AuthService.DeleteSession + */ + deleteSession: { + methodKind: "unary"; + input: typeof DeleteSessionRequestSchema; + output: typeof DeleteSessionResponseSchema; + }, + /** + * @generated from rpc sentinel.auth.v1.AuthService.TerminateAllSessions + */ + terminateAllSessions: { + methodKind: "unary"; + input: typeof TerminateAllSessionsRequestSchema; + output: typeof TerminateAllSessionsResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_sentinel_auth_v1_auth, 0); + diff --git a/frontend/src/api/gen/sentinel/common/v1/common_pb.ts b/frontend/src/api/gen/sentinel/common/v1/common_pb.ts new file mode 100644 index 0000000..5d346f1 --- /dev/null +++ b/frontend/src/api/gen/sentinel/common/v1/common_pb.ts @@ -0,0 +1,46 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/common/v1/common.proto (package sentinel.common.v1, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/common/v1/common.proto. + */ +export const file_sentinel_common_v1_common: GenFile = /*@__PURE__*/ + fileDesc("Ch9zZW50aW5lbC9jb21tb24vdjEvY29tbW9uLnByb3RvEhJzZW50aW5lbC5jb21tb24udjEijwEKEUZpbmRSZXF1ZXN0Q29tbW9uEhEKBHBhZ2UYASABKA1IAIgBARIWCglwYWdlX3NpemUYAiABKA1IAYgBARIVCghvcmRlcl9ieRgDIAEoCUgCiAEBEhQKDGlzX2FzY19vcmRlchgEIAEoCEIHCgVfcGFnZUIMCgpfcGFnZV9zaXplQgsKCV9vcmRlcl9ieULkAQoWY29tLnNlbnRpbmVsLmNvbW1vbi52MUILQ29tbW9uUHJvdG9QAVpTZ2l0aHViLmNvbS9zeHdlYmRldi9zZW50aW5lbC9pbnRlcm5hbC9odWIvaHVic2VydmVyL2FwaS9zZW50aW5lbC9jb21tb24vdjE7Y29tbW9udjGiAgNTQ1iqAhJTZW50aW5lbC5Db21tb24uVjHKAhJTZW50aW5lbFxDb21tb25cVjHiAh5TZW50aW5lbFxDb21tb25cVjFcR1BCTWV0YWRhdGHqAhRTZW50aW5lbDo6Q29tbW9uOjpWMWIGcHJvdG8z"); + +/** + * @generated from message sentinel.common.v1.FindRequestCommon + */ +export type FindRequestCommon = Message<"sentinel.common.v1.FindRequestCommon"> & { + /** + * @generated from field: optional uint32 page = 1; + */ + page?: number; + + /** + * @generated from field: optional uint32 page_size = 2; + */ + pageSize?: number; + + /** + * @generated from field: optional string order_by = 3; + */ + orderBy?: string; + + /** + * @generated from field: bool is_asc_order = 4; + */ + isAscOrder: boolean; +}; + +/** + * Describes the message sentinel.common.v1.FindRequestCommon. + * Use `create(FindRequestCommonSchema)` to create a new message. + */ +export const FindRequestCommonSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_common_v1_common, 0); + diff --git a/frontend/src/api/gen/sentinel/notifications/v1/notifications-NotificationService_connectquery.ts b/frontend/src/api/gen/sentinel/notifications/v1/notifications-NotificationService_connectquery.ts new file mode 100644 index 0000000..4287229 --- /dev/null +++ b/frontend/src/api/gen/sentinel/notifications/v1/notifications-NotificationService_connectquery.ts @@ -0,0 +1,40 @@ +// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts" +// @generated from file sentinel/notifications/v1/notifications.proto (package sentinel.notifications.v1, syntax proto3) +/* eslint-disable */ + +import { NotificationService } from "./notifications_pb"; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProvidersList + */ +export const providersList = NotificationService.method.providersList; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderCreate + */ +export const providerCreate = NotificationService.method.providerCreate; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderUpdate + */ +export const providerUpdate = NotificationService.method.providerUpdate; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderDelete + */ +export const providerDelete = NotificationService.method.providerDelete; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderTest + */ +export const providerTest = NotificationService.method.providerTest; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.HistoryList + */ +export const historyList = NotificationService.method.historyList; + +/** + * @generated from rpc sentinel.notifications.v1.NotificationService.HistoryDeleteAll + */ +export const historyDeleteAll = NotificationService.method.historyDeleteAll; diff --git a/frontend/src/api/gen/sentinel/notifications/v1/notifications_pb.ts b/frontend/src/api/gen/sentinel/notifications/v1/notifications_pb.ts new file mode 100644 index 0000000..800fade --- /dev/null +++ b/frontend/src/api/gen/sentinel/notifications/v1/notifications_pb.ts @@ -0,0 +1,552 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/notifications/v1/notifications.proto (package sentinel.notifications.v1, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { FindRequestCommon } from "../../common/v1/common_pb"; +import { file_sentinel_common_v1_common } from "../../common/v1/common_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/notifications/v1/notifications.proto. + */ +export const file_sentinel_notifications_v1_notifications: GenFile = /*@__PURE__*/ + fileDesc("Ci1zZW50aW5lbC9ub3RpZmljYXRpb25zL3YxL25vdGlmaWNhdGlvbnMucHJvdG8SGXNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEiHQoOU2hvdXRycnJDb25maWcSCwoDdXJsGAEgASgJIlkKDlByb3ZpZGVyQ29uZmlnEj0KCHNob3V0cnJyGAEgASgLMikuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5TaG91dHJyckNvbmZpZ0gAQggKBmNvbmZpZyL8AQoIUHJvdmlkZXISCgoCaWQYASABKAkSNQoEdHlwZRgCIAEoDjInLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuUHJvdmlkZXJUeXBlEjkKBmNvbmZpZxgDIAEoCzIpLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuUHJvdmlkZXJDb25maWcSEgoKaXNfZW5hYmxlZBgEIAEoCBIuCgpjcmVhdGVkX2F0GAUgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCLgAgoLSGlzdG9yeUl0ZW0SCgoCaWQYASABKAkSDwoHbWVzc2FnZRgCIAEoCRIOCgZzdGF0dXMYAyABKAkSFQoIcmVzcG9uc2UYBCABKAlIAIgBARIQCghhdHRlbXB0cxgFIAEoAxIaCg1lcnJvcl9tZXNzYWdlGAYgASgJSAGIAQESMwoPbGFzdF9hdHRlbXB0X2F0GAcgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIrCgdzZW50X2F0GAggASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgpjcmVhdGVkX2F0GAkgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAogASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEILCglfcmVzcG9uc2VCEAoOX2Vycm9yX21lc3NhZ2UiFgoUUHJvdmlkZXJzTGlzdFJlcXVlc3QiSwoVUHJvdmlkZXJzTGlzdFJlc3BvbnNlEjIKBWl0ZW1zGAEgAygLMiMuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5Qcm92aWRlciKdAQoVUHJvdmlkZXJDcmVhdGVSZXF1ZXN0EjUKBHR5cGUYASABKA4yJy5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyVHlwZRI5CgZjb25maWcYAiABKAsyKS5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyQ29uZmlnEhIKCmlzX2VuYWJsZWQYAyABKAgiSwoWUHJvdmlkZXJDcmVhdGVSZXNwb25zZRIxCgRpdGVtGAEgASgLMiMuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5Qcm92aWRlciKpAQoVUHJvdmlkZXJVcGRhdGVSZXF1ZXN0EgoKAmlkGAEgASgJEjUKBHR5cGUYAiABKA4yJy5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyVHlwZRI5CgZjb25maWcYAyABKAsyKS5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyQ29uZmlnEhIKCmlzX2VuYWJsZWQYBCABKAgiSwoWUHJvdmlkZXJVcGRhdGVSZXNwb25zZRIxCgRpdGVtGAEgASgLMiMuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5Qcm92aWRlciIjChVQcm92aWRlckRlbGV0ZVJlcXVlc3QSCgoCaWQYASABKAkiGAoWUHJvdmlkZXJEZWxldGVSZXNwb25zZSIhChNQcm92aWRlclRlc3RSZXF1ZXN0EgoKAmlkGAEgASgJIhYKFFByb3ZpZGVyVGVzdFJlc3BvbnNlIlsKEkhpc3RvcnlMaXN0UmVxdWVzdBI1CgZjb21tb24YASABKAsyJS5zZW50aW5lbC5jb21tb24udjEuRmluZFJlcXVlc3RDb21tb24SDgoGc3RhdHVzGAIgASgJIlsKE0hpc3RvcnlMaXN0UmVzcG9uc2USNQoFaXRlbXMYASADKAsyJi5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLkhpc3RvcnlJdGVtEg0KBWNvdW50GAIgASgNIhkKF0hpc3RvcnlEZWxldGVBbGxSZXF1ZXN0IhoKGEhpc3RvcnlEZWxldGVBbGxSZXNwb25zZSIZChdIaXN0b3J5U3Vic2NyaWJlUmVxdWVzdCIaChhIaXN0b3J5U3Vic2NyaWJlUmVzcG9uc2UqSQoMUHJvdmlkZXJUeXBlEh0KGVBST1ZJREVSX1RZUEVfVU5TUEVDSUZJRUQQABIaChZQUk9WSURFUl9UWVBFX1NIT1VUUlJSEAEyyQcKE05vdGlmaWNhdGlvblNlcnZpY2UScgoNUHJvdmlkZXJzTGlzdBIvLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuUHJvdmlkZXJzTGlzdFJlcXVlc3QaMC5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyc0xpc3RSZXNwb25zZRJ1Cg5Qcm92aWRlckNyZWF0ZRIwLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuUHJvdmlkZXJDcmVhdGVSZXF1ZXN0GjEuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5Qcm92aWRlckNyZWF0ZVJlc3BvbnNlEnUKDlByb3ZpZGVyVXBkYXRlEjAuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5Qcm92aWRlclVwZGF0ZVJlcXVlc3QaMS5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyVXBkYXRlUmVzcG9uc2USdQoOUHJvdmlkZXJEZWxldGUSMC5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyRGVsZXRlUmVxdWVzdBoxLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuUHJvdmlkZXJEZWxldGVSZXNwb25zZRJvCgxQcm92aWRlclRlc3QSLi5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyVGVzdFJlcXVlc3QaLy5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLlByb3ZpZGVyVGVzdFJlc3BvbnNlEmwKC0hpc3RvcnlMaXN0Ei0uc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5IaXN0b3J5TGlzdFJlcXVlc3QaLi5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLkhpc3RvcnlMaXN0UmVzcG9uc2USewoQSGlzdG9yeURlbGV0ZUFsbBIyLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuSGlzdG9yeURlbGV0ZUFsbFJlcXVlc3QaMy5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxLkhpc3RvcnlEZWxldGVBbGxSZXNwb25zZRJ9ChBIaXN0b3J5U3Vic2NyaWJlEjIuc2VudGluZWwubm90aWZpY2F0aW9ucy52MS5IaXN0b3J5U3Vic2NyaWJlUmVxdWVzdBozLnNlbnRpbmVsLm5vdGlmaWNhdGlvbnMudjEuSGlzdG9yeVN1YnNjcmliZVJlc3BvbnNlMAFCnAIKHWNvbS5zZW50aW5lbC5ub3RpZmljYXRpb25zLnYxQhJOb3RpZmljYXRpb25zUHJvdG9QAVphZ2l0aHViLmNvbS9zeHdlYmRldi9zZW50aW5lbC9pbnRlcm5hbC9odWIvaHVic2VydmVyL2FwaS9zZW50aW5lbC9ub3RpZmljYXRpb25zL3YxO25vdGlmaWNhdGlvbnN2MaICA1NOWKoCGVNlbnRpbmVsLk5vdGlmaWNhdGlvbnMuVjHKAhlTZW50aW5lbFxOb3RpZmljYXRpb25zXFYx4gIlU2VudGluZWxcTm90aWZpY2F0aW9uc1xWMVxHUEJNZXRhZGF0YeoCG1NlbnRpbmVsOjpOb3RpZmljYXRpb25zOjpWMWIGcHJvdG8z", [file_google_protobuf_timestamp, file_sentinel_common_v1_common]); + +/** + * @generated from message sentinel.notifications.v1.ShoutrrrConfig + */ +export type ShoutrrrConfig = Message<"sentinel.notifications.v1.ShoutrrrConfig"> & { + /** + * @generated from field: string url = 1; + */ + url: string; +}; + +/** + * Describes the message sentinel.notifications.v1.ShoutrrrConfig. + * Use `create(ShoutrrrConfigSchema)` to create a new message. + */ +export const ShoutrrrConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 0); + +/** + * @generated from message sentinel.notifications.v1.ProviderConfig + */ +export type ProviderConfig = Message<"sentinel.notifications.v1.ProviderConfig"> & { + /** + * @generated from oneof sentinel.notifications.v1.ProviderConfig.config + */ + config: { + /** + * @generated from field: sentinel.notifications.v1.ShoutrrrConfig shoutrrr = 1; + */ + value: ShoutrrrConfig; + case: "shoutrrr"; + } | { case: undefined; value?: undefined }; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderConfig. + * Use `create(ProviderConfigSchema)` to create a new message. + */ +export const ProviderConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 1); + +/** + * @generated from message sentinel.notifications.v1.Provider + */ +export type Provider = Message<"sentinel.notifications.v1.Provider"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: sentinel.notifications.v1.ProviderType type = 2; + */ + type: ProviderType; + + /** + * @generated from field: sentinel.notifications.v1.ProviderConfig config = 3; + */ + config?: ProviderConfig; + + /** + * @generated from field: bool is_enabled = 4; + */ + isEnabled: boolean; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 5; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 6; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.notifications.v1.Provider. + * Use `create(ProviderSchema)` to create a new message. + */ +export const ProviderSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 2); + +/** + * @generated from message sentinel.notifications.v1.HistoryItem + */ +export type HistoryItem = Message<"sentinel.notifications.v1.HistoryItem"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string message = 2; + */ + message: string; + + /** + * @generated from field: string status = 3; + */ + status: string; + + /** + * @generated from field: optional string response = 4; + */ + response?: string; + + /** + * @generated from field: int64 attempts = 5; + */ + attempts: bigint; + + /** + * @generated from field: optional string error_message = 6; + */ + errorMessage?: string; + + /** + * @generated from field: google.protobuf.Timestamp last_attempt_at = 7; + */ + lastAttemptAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp sent_at = 8; + */ + sentAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 9; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 10; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.notifications.v1.HistoryItem. + * Use `create(HistoryItemSchema)` to create a new message. + */ +export const HistoryItemSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 3); + +/** + * ProvidersList lists all notification providers. + * + * @generated from message sentinel.notifications.v1.ProvidersListRequest + */ +export type ProvidersListRequest = Message<"sentinel.notifications.v1.ProvidersListRequest"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.ProvidersListRequest. + * Use `create(ProvidersListRequestSchema)` to create a new message. + */ +export const ProvidersListRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 4); + +/** + * @generated from message sentinel.notifications.v1.ProvidersListResponse + */ +export type ProvidersListResponse = Message<"sentinel.notifications.v1.ProvidersListResponse"> & { + /** + * @generated from field: repeated sentinel.notifications.v1.Provider items = 1; + */ + items: Provider[]; +}; + +/** + * Describes the message sentinel.notifications.v1.ProvidersListResponse. + * Use `create(ProvidersListResponseSchema)` to create a new message. + */ +export const ProvidersListResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 5); + +/** + * ProviderCreate creates a new notification provider. + * + * @generated from message sentinel.notifications.v1.ProviderCreateRequest + */ +export type ProviderCreateRequest = Message<"sentinel.notifications.v1.ProviderCreateRequest"> & { + /** + * @generated from field: sentinel.notifications.v1.ProviderType type = 1; + */ + type: ProviderType; + + /** + * @generated from field: sentinel.notifications.v1.ProviderConfig config = 2; + */ + config?: ProviderConfig; + + /** + * @generated from field: bool is_enabled = 3; + */ + isEnabled: boolean; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderCreateRequest. + * Use `create(ProviderCreateRequestSchema)` to create a new message. + */ +export const ProviderCreateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 6); + +/** + * @generated from message sentinel.notifications.v1.ProviderCreateResponse + */ +export type ProviderCreateResponse = Message<"sentinel.notifications.v1.ProviderCreateResponse"> & { + /** + * @generated from field: sentinel.notifications.v1.Provider item = 1; + */ + item?: Provider; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderCreateResponse. + * Use `create(ProviderCreateResponseSchema)` to create a new message. + */ +export const ProviderCreateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 7); + +/** + * ProviderUpdate updates an existing notification provider. + * + * @generated from message sentinel.notifications.v1.ProviderUpdateRequest + */ +export type ProviderUpdateRequest = Message<"sentinel.notifications.v1.ProviderUpdateRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: sentinel.notifications.v1.ProviderType type = 2; + */ + type: ProviderType; + + /** + * @generated from field: sentinel.notifications.v1.ProviderConfig config = 3; + */ + config?: ProviderConfig; + + /** + * @generated from field: bool is_enabled = 4; + */ + isEnabled: boolean; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderUpdateRequest. + * Use `create(ProviderUpdateRequestSchema)` to create a new message. + */ +export const ProviderUpdateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 8); + +/** + * @generated from message sentinel.notifications.v1.ProviderUpdateResponse + */ +export type ProviderUpdateResponse = Message<"sentinel.notifications.v1.ProviderUpdateResponse"> & { + /** + * @generated from field: sentinel.notifications.v1.Provider item = 1; + */ + item?: Provider; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderUpdateResponse. + * Use `create(ProviderUpdateResponseSchema)` to create a new message. + */ +export const ProviderUpdateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 9); + +/** + * ProviderDelete deletes a notification provider. + * + * @generated from message sentinel.notifications.v1.ProviderDeleteRequest + */ +export type ProviderDeleteRequest = Message<"sentinel.notifications.v1.ProviderDeleteRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderDeleteRequest. + * Use `create(ProviderDeleteRequestSchema)` to create a new message. + */ +export const ProviderDeleteRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 10); + +/** + * @generated from message sentinel.notifications.v1.ProviderDeleteResponse + */ +export type ProviderDeleteResponse = Message<"sentinel.notifications.v1.ProviderDeleteResponse"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderDeleteResponse. + * Use `create(ProviderDeleteResponseSchema)` to create a new message. + */ +export const ProviderDeleteResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 11); + +/** + * ProviderTest sends a test notification using the specified provider. + * + * @generated from message sentinel.notifications.v1.ProviderTestRequest + */ +export type ProviderTestRequest = Message<"sentinel.notifications.v1.ProviderTestRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderTestRequest. + * Use `create(ProviderTestRequestSchema)` to create a new message. + */ +export const ProviderTestRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 12); + +/** + * @generated from message sentinel.notifications.v1.ProviderTestResponse + */ +export type ProviderTestResponse = Message<"sentinel.notifications.v1.ProviderTestResponse"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.ProviderTestResponse. + * Use `create(ProviderTestResponseSchema)` to create a new message. + */ +export const ProviderTestResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 13); + +/** + * History retrieves the notification history with optional filters. + * + * @generated from message sentinel.notifications.v1.HistoryListRequest + */ +export type HistoryListRequest = Message<"sentinel.notifications.v1.HistoryListRequest"> & { + /** + * @generated from field: sentinel.common.v1.FindRequestCommon common = 1; + */ + common?: FindRequestCommon; + + /** + * @generated from field: string status = 2; + */ + status: string; +}; + +/** + * Describes the message sentinel.notifications.v1.HistoryListRequest. + * Use `create(HistoryListRequestSchema)` to create a new message. + */ +export const HistoryListRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 14); + +/** + * @generated from message sentinel.notifications.v1.HistoryListResponse + */ +export type HistoryListResponse = Message<"sentinel.notifications.v1.HistoryListResponse"> & { + /** + * @generated from field: repeated sentinel.notifications.v1.HistoryItem items = 1; + */ + items: HistoryItem[]; + + /** + * @generated from field: uint32 count = 2; + */ + count: number; +}; + +/** + * Describes the message sentinel.notifications.v1.HistoryListResponse. + * Use `create(HistoryListResponseSchema)` to create a new message. + */ +export const HistoryListResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 15); + +/** + * HistoryDeleteAll deletes all notification history items. + * + * @generated from message sentinel.notifications.v1.HistoryDeleteAllRequest + */ +export type HistoryDeleteAllRequest = Message<"sentinel.notifications.v1.HistoryDeleteAllRequest"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.HistoryDeleteAllRequest. + * Use `create(HistoryDeleteAllRequestSchema)` to create a new message. + */ +export const HistoryDeleteAllRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 16); + +/** + * @generated from message sentinel.notifications.v1.HistoryDeleteAllResponse + */ +export type HistoryDeleteAllResponse = Message<"sentinel.notifications.v1.HistoryDeleteAllResponse"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.HistoryDeleteAllResponse. + * Use `create(HistoryDeleteAllResponseSchema)` to create a new message. + */ +export const HistoryDeleteAllResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 17); + +/** + * HistorySubscribe subscribes to real-time notification history updates. + * + * @generated from message sentinel.notifications.v1.HistorySubscribeRequest + */ +export type HistorySubscribeRequest = Message<"sentinel.notifications.v1.HistorySubscribeRequest"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.HistorySubscribeRequest. + * Use `create(HistorySubscribeRequestSchema)` to create a new message. + */ +export const HistorySubscribeRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 18); + +/** + * @generated from message sentinel.notifications.v1.HistorySubscribeResponse + */ +export type HistorySubscribeResponse = Message<"sentinel.notifications.v1.HistorySubscribeResponse"> & { +}; + +/** + * Describes the message sentinel.notifications.v1.HistorySubscribeResponse. + * Use `create(HistorySubscribeResponseSchema)` to create a new message. + */ +export const HistorySubscribeResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_notifications_v1_notifications, 19); + +/** + * @generated from enum sentinel.notifications.v1.ProviderType + */ +export enum ProviderType { + /** + * @generated from enum value: PROVIDER_TYPE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: PROVIDER_TYPE_SHOUTRRR = 1; + */ + SHOUTRRR = 1, +} + +/** + * Describes the enum sentinel.notifications.v1.ProviderType. + */ +export const ProviderTypeSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_notifications_v1_notifications, 0); + +/** + * NotificationService is the service for managing notification providers and + * history. + * + * @generated from service sentinel.notifications.v1.NotificationService + */ +export const NotificationService: GenService<{ + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProvidersList + */ + providersList: { + methodKind: "unary"; + input: typeof ProvidersListRequestSchema; + output: typeof ProvidersListResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderCreate + */ + providerCreate: { + methodKind: "unary"; + input: typeof ProviderCreateRequestSchema; + output: typeof ProviderCreateResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderUpdate + */ + providerUpdate: { + methodKind: "unary"; + input: typeof ProviderUpdateRequestSchema; + output: typeof ProviderUpdateResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderDelete + */ + providerDelete: { + methodKind: "unary"; + input: typeof ProviderDeleteRequestSchema; + output: typeof ProviderDeleteResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.ProviderTest + */ + providerTest: { + methodKind: "unary"; + input: typeof ProviderTestRequestSchema; + output: typeof ProviderTestResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.HistoryList + */ + historyList: { + methodKind: "unary"; + input: typeof HistoryListRequestSchema; + output: typeof HistoryListResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.HistoryDeleteAll + */ + historyDeleteAll: { + methodKind: "unary"; + input: typeof HistoryDeleteAllRequestSchema; + output: typeof HistoryDeleteAllResponseSchema; + }, + /** + * @generated from rpc sentinel.notifications.v1.NotificationService.HistorySubscribe + */ + historySubscribe: { + methodKind: "server_streaming"; + input: typeof HistorySubscribeRequestSchema; + output: typeof HistorySubscribeResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_sentinel_notifications_v1_notifications, 0); + diff --git a/frontend/src/api/gen/sentinel/projects/v1/projects-ProjectsService_connectquery.ts b/frontend/src/api/gen/sentinel/projects/v1/projects-ProjectsService_connectquery.ts new file mode 100644 index 0000000..8de874c --- /dev/null +++ b/frontend/src/api/gen/sentinel/projects/v1/projects-ProjectsService_connectquery.ts @@ -0,0 +1,30 @@ +// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts" +// @generated from file sentinel/projects/v1/projects.proto (package sentinel.projects.v1, syntax proto3) +/* eslint-disable */ + +import { ProjectsService } from "./projects_pb"; + +/** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectsList + */ +export const projectsList = ProjectsService.method.projectsList; + +/** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectCreate + */ +export const projectCreate = ProjectsService.method.projectCreate; + +/** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectGet + */ +export const projectGet = ProjectsService.method.projectGet; + +/** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectUpdate + */ +export const projectUpdate = ProjectsService.method.projectUpdate; + +/** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectDelete + */ +export const projectDelete = ProjectsService.method.projectDelete; diff --git a/frontend/src/api/gen/sentinel/projects/v1/projects_pb.ts b/frontend/src/api/gen/sentinel/projects/v1/projects_pb.ts new file mode 100644 index 0000000..04c6dc1 --- /dev/null +++ b/frontend/src/api/gen/sentinel/projects/v1/projects_pb.ts @@ -0,0 +1,352 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/projects/v1/projects.proto (package sentinel.projects.v1, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/projects/v1/projects.proto. + */ +export const file_sentinel_projects_v1_projects: GenFile = /*@__PURE__*/ + fileDesc("CiNzZW50aW5lbC9wcm9qZWN0cy92MS9wcm9qZWN0cy5wcm90bxIUc2VudGluZWwucHJvamVjdHMudjEiTAoWUHJvamVjdE1vbml0b3JEZWZhdWx0cxIQCghpbnRlcnZhbBgBIAEoAxIPCgd0aW1lb3V0GAIgASgDEg8KB3JldHJpZXMYAyABKAMiWQoPUHJvamVjdFNldHRpbmdzEkYKEG1vbml0b3JfZGVmYXVsdHMYASABKAsyLC5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0TW9uaXRvckRlZmF1bHRzItEBCgdQcm9qZWN0EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSNwoIc2V0dGluZ3MYBiABKAsyJS5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0U2V0dGluZ3MSLgoKY3JlYXRlZF9hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgFIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiFQoTUHJvamVjdHNMaXN0UmVxdWVzdCJEChRQcm9qZWN0c0xpc3RSZXNwb25zZRIsCgVpdGVtcxgBIAMoCzIdLnNlbnRpbmVsLnByb2plY3RzLnYxLlByb2plY3QicgoUUHJvamVjdENyZWF0ZVJlcXVlc3QSDAoEbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCRI3CghzZXR0aW5ncxgDIAEoCzIlLnNlbnRpbmVsLnByb2plY3RzLnYxLlByb2plY3RTZXR0aW5ncyJEChVQcm9qZWN0Q3JlYXRlUmVzcG9uc2USKwoEaXRlbRgBIAEoCzIdLnNlbnRpbmVsLnByb2plY3RzLnYxLlByb2plY3QiHwoRUHJvamVjdEdldFJlcXVlc3QSCgoCaWQYASABKAkiQQoSUHJvamVjdEdldFJlc3BvbnNlEisKBGl0ZW0YASABKAsyHS5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0In4KFFByb2plY3RVcGRhdGVSZXF1ZXN0EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSNwoIc2V0dGluZ3MYBCABKAsyJS5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0U2V0dGluZ3MiRAoVUHJvamVjdFVwZGF0ZVJlc3BvbnNlEisKBGl0ZW0YASABKAsyHS5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0IiIKFFByb2plY3REZWxldGVSZXF1ZXN0EgoKAmlkGAEgASgJIhcKFVByb2plY3REZWxldGVSZXNwb25zZTKXBAoPUHJvamVjdHNTZXJ2aWNlEmUKDFByb2plY3RzTGlzdBIpLnNlbnRpbmVsLnByb2plY3RzLnYxLlByb2plY3RzTGlzdFJlcXVlc3QaKi5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0c0xpc3RSZXNwb25zZRJoCg1Qcm9qZWN0Q3JlYXRlEiouc2VudGluZWwucHJvamVjdHMudjEuUHJvamVjdENyZWF0ZVJlcXVlc3QaKy5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0Q3JlYXRlUmVzcG9uc2USXwoKUHJvamVjdEdldBInLnNlbnRpbmVsLnByb2plY3RzLnYxLlByb2plY3RHZXRSZXF1ZXN0Giguc2VudGluZWwucHJvamVjdHMudjEuUHJvamVjdEdldFJlc3BvbnNlEmgKDVByb2plY3RVcGRhdGUSKi5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0VXBkYXRlUmVxdWVzdBorLnNlbnRpbmVsLnByb2plY3RzLnYxLlByb2plY3RVcGRhdGVSZXNwb25zZRJoCg1Qcm9qZWN0RGVsZXRlEiouc2VudGluZWwucHJvamVjdHMudjEuUHJvamVjdERlbGV0ZVJlcXVlc3QaKy5zZW50aW5lbC5wcm9qZWN0cy52MS5Qcm9qZWN0RGVsZXRlUmVzcG9uc2VC9AEKGGNvbS5zZW50aW5lbC5wcm9qZWN0cy52MUINUHJvamVjdHNQcm90b1ABWldnaXRodWIuY29tL3N4d2ViZGV2L3NlbnRpbmVsL2ludGVybmFsL2h1Yi9odWJzZXJ2ZXIvYXBpL3NlbnRpbmVsL3Byb2plY3RzL3YxO3Byb2plY3RzdjGiAgNTUFiqAhRTZW50aW5lbC5Qcm9qZWN0cy5WMcoCFFNlbnRpbmVsXFByb2plY3RzXFYx4gIgU2VudGluZWxcUHJvamVjdHNcVjFcR1BCTWV0YWRhdGHqAhZTZW50aW5lbDo6UHJvamVjdHM6OlYxYgZwcm90bzM", [file_google_protobuf_timestamp]); + +/** + * @generated from message sentinel.projects.v1.ProjectMonitorDefaults + */ +export type ProjectMonitorDefaults = Message<"sentinel.projects.v1.ProjectMonitorDefaults"> & { + /** + * in milliseconds + * + * @generated from field: int64 interval = 1; + */ + interval: bigint; + + /** + * in milliseconds + * + * @generated from field: int64 timeout = 2; + */ + timeout: bigint; + + /** + * @generated from field: int64 retries = 3; + */ + retries: bigint; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectMonitorDefaults. + * Use `create(ProjectMonitorDefaultsSchema)` to create a new message. + */ +export const ProjectMonitorDefaultsSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 0); + +/** + * @generated from message sentinel.projects.v1.ProjectSettings + */ +export type ProjectSettings = Message<"sentinel.projects.v1.ProjectSettings"> & { + /** + * @generated from field: sentinel.projects.v1.ProjectMonitorDefaults monitor_defaults = 1; + */ + monitorDefaults?: ProjectMonitorDefaults; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectSettings. + * Use `create(ProjectSettingsSchema)` to create a new message. + */ +export const ProjectSettingsSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 1); + +/** + * @generated from message sentinel.projects.v1.Project + */ +export type Project = Message<"sentinel.projects.v1.Project"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: string description = 3; + */ + description: string; + + /** + * @generated from field: sentinel.projects.v1.ProjectSettings settings = 6; + */ + settings?: ProjectSettings; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 4; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 5; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.projects.v1.Project. + * Use `create(ProjectSchema)` to create a new message. + */ +export const ProjectSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 2); + +/** + * ProjectsListRequest is the request message for listing projects. + * + * @generated from message sentinel.projects.v1.ProjectsListRequest + */ +export type ProjectsListRequest = Message<"sentinel.projects.v1.ProjectsListRequest"> & { +}; + +/** + * Describes the message sentinel.projects.v1.ProjectsListRequest. + * Use `create(ProjectsListRequestSchema)` to create a new message. + */ +export const ProjectsListRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 3); + +/** + * @generated from message sentinel.projects.v1.ProjectsListResponse + */ +export type ProjectsListResponse = Message<"sentinel.projects.v1.ProjectsListResponse"> & { + /** + * @generated from field: repeated sentinel.projects.v1.Project items = 1; + */ + items: Project[]; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectsListResponse. + * Use `create(ProjectsListResponseSchema)` to create a new message. + */ +export const ProjectsListResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 4); + +/** + * ProjectCreateRequest is the request message for creating a project. + * + * @generated from message sentinel.projects.v1.ProjectCreateRequest + */ +export type ProjectCreateRequest = Message<"sentinel.projects.v1.ProjectCreateRequest"> & { + /** + * @generated from field: string name = 1; + */ + name: string; + + /** + * @generated from field: string description = 2; + */ + description: string; + + /** + * @generated from field: sentinel.projects.v1.ProjectSettings settings = 3; + */ + settings?: ProjectSettings; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectCreateRequest. + * Use `create(ProjectCreateRequestSchema)` to create a new message. + */ +export const ProjectCreateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 5); + +/** + * @generated from message sentinel.projects.v1.ProjectCreateResponse + */ +export type ProjectCreateResponse = Message<"sentinel.projects.v1.ProjectCreateResponse"> & { + /** + * @generated from field: sentinel.projects.v1.Project item = 1; + */ + item?: Project; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectCreateResponse. + * Use `create(ProjectCreateResponseSchema)` to create a new message. + */ +export const ProjectCreateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 6); + +/** + * ProjectGetRequest is the request message for getting a project. + * + * @generated from message sentinel.projects.v1.ProjectGetRequest + */ +export type ProjectGetRequest = Message<"sentinel.projects.v1.ProjectGetRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectGetRequest. + * Use `create(ProjectGetRequestSchema)` to create a new message. + */ +export const ProjectGetRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 7); + +/** + * @generated from message sentinel.projects.v1.ProjectGetResponse + */ +export type ProjectGetResponse = Message<"sentinel.projects.v1.ProjectGetResponse"> & { + /** + * @generated from field: sentinel.projects.v1.Project item = 1; + */ + item?: Project; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectGetResponse. + * Use `create(ProjectGetResponseSchema)` to create a new message. + */ +export const ProjectGetResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 8); + +/** + * ProjectUpdateRequest is the request message for updating a project. + * + * @generated from message sentinel.projects.v1.ProjectUpdateRequest + */ +export type ProjectUpdateRequest = Message<"sentinel.projects.v1.ProjectUpdateRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: string description = 3; + */ + description: string; + + /** + * @generated from field: sentinel.projects.v1.ProjectSettings settings = 4; + */ + settings?: ProjectSettings; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectUpdateRequest. + * Use `create(ProjectUpdateRequestSchema)` to create a new message. + */ +export const ProjectUpdateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 9); + +/** + * @generated from message sentinel.projects.v1.ProjectUpdateResponse + */ +export type ProjectUpdateResponse = Message<"sentinel.projects.v1.ProjectUpdateResponse"> & { + /** + * @generated from field: sentinel.projects.v1.Project item = 1; + */ + item?: Project; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectUpdateResponse. + * Use `create(ProjectUpdateResponseSchema)` to create a new message. + */ +export const ProjectUpdateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 10); + +/** + * ProjectDeleteRequest is the request message for deleting a project. + * + * @generated from message sentinel.projects.v1.ProjectDeleteRequest + */ +export type ProjectDeleteRequest = Message<"sentinel.projects.v1.ProjectDeleteRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.projects.v1.ProjectDeleteRequest. + * Use `create(ProjectDeleteRequestSchema)` to create a new message. + */ +export const ProjectDeleteRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 11); + +/** + * @generated from message sentinel.projects.v1.ProjectDeleteResponse + */ +export type ProjectDeleteResponse = Message<"sentinel.projects.v1.ProjectDeleteResponse"> & { +}; + +/** + * Describes the message sentinel.projects.v1.ProjectDeleteResponse. + * Use `create(ProjectDeleteResponseSchema)` to create a new message. + */ +export const ProjectDeleteResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_projects_v1_projects, 12); + +/** + * ProjectsService is the service for managing projects. + * + * @generated from service sentinel.projects.v1.ProjectsService + */ +export const ProjectsService: GenService<{ + /** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectsList + */ + projectsList: { + methodKind: "unary"; + input: typeof ProjectsListRequestSchema; + output: typeof ProjectsListResponseSchema; + }, + /** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectCreate + */ + projectCreate: { + methodKind: "unary"; + input: typeof ProjectCreateRequestSchema; + output: typeof ProjectCreateResponseSchema; + }, + /** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectGet + */ + projectGet: { + methodKind: "unary"; + input: typeof ProjectGetRequestSchema; + output: typeof ProjectGetResponseSchema; + }, + /** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectUpdate + */ + projectUpdate: { + methodKind: "unary"; + input: typeof ProjectUpdateRequestSchema; + output: typeof ProjectUpdateResponseSchema; + }, + /** + * @generated from rpc sentinel.projects.v1.ProjectsService.ProjectDelete + */ + projectDelete: { + methodKind: "unary"; + input: typeof ProjectDeleteRequestSchema; + output: typeof ProjectDeleteResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_sentinel_projects_v1_projects, 0); + diff --git a/frontend/src/api/gen/sentinel/resources/v1/service-ResourcesService_connectquery.ts b/frontend/src/api/gen/sentinel/resources/v1/service-ResourcesService_connectquery.ts new file mode 100644 index 0000000..f626856 --- /dev/null +++ b/frontend/src/api/gen/sentinel/resources/v1/service-ResourcesService_connectquery.ts @@ -0,0 +1,30 @@ +// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts" +// @generated from file sentinel/resources/v1/service.proto (package sentinel.resources.v1, syntax proto3) +/* eslint-disable */ + +import { ResourcesService } from "./service_pb"; + +/** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourcesList + */ +export const resourcesList = ResourcesService.method.resourcesList; + +/** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceGet + */ +export const resourceGet = ResourcesService.method.resourceGet; + +/** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceCreate + */ +export const resourceCreate = ResourcesService.method.resourceCreate; + +/** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceUpdate + */ +export const resourceUpdate = ResourcesService.method.resourceUpdate; + +/** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceDelete + */ +export const resourceDelete = ResourcesService.method.resourceDelete; diff --git a/frontend/src/api/gen/sentinel/resources/v1/service_pb.ts b/frontend/src/api/gen/sentinel/resources/v1/service_pb.ts new file mode 100644 index 0000000..434a6e2 --- /dev/null +++ b/frontend/src/api/gen/sentinel/resources/v1/service_pb.ts @@ -0,0 +1,399 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/resources/v1/service.proto (package sentinel.resources.v1, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { FindRequestCommon } from "../../common/v1/common_pb"; +import { file_sentinel_common_v1_common } from "../../common/v1/common_pb"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/resources/v1/service.proto. + */ +export const file_sentinel_resources_v1_service: GenFile = /*@__PURE__*/ + fileDesc("CiNzZW50aW5lbC9yZXNvdXJjZXMvdjEvc2VydmljZS5wcm90bxIVc2VudGluZWwucmVzb3VyY2VzLnYxIgkKB1BheWxvYWQinwIKCFJlc291cmNlEgoKAmlkGAEgASgJEhIKCnByb2plY3RfaWQYAiABKAkSDAoEbmFtZRgDIAEoCRITCgtkZXNjcmlwdGlvbhgEIAEoCRIxCgRraW5kGAUgASgOMiMuc2VudGluZWwucmVzb3VyY2VzLnYxLlJlc291cmNlS2luZBIMCgR0YWdzGAYgAygJEi8KB3BheWxvYWQYByABKAsyHi5zZW50aW5lbC5yZXNvdXJjZXMudjEuUGF5bG9hZBIuCgpjcmVhdGVkX2F0GAggASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAkgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCJNChRSZXNvdXJjZXNMaXN0UmVxdWVzdBI1CgZjb21tb24YASABKAsyJS5zZW50aW5lbC5jb21tb24udjEuRmluZFJlcXVlc3RDb21tb24iVgoVUmVzb3VyY2VzTGlzdFJlc3BvbnNlEi4KBWl0ZW1zGAEgAygLMh8uc2VudGluZWwucmVzb3VyY2VzLnYxLlJlc291cmNlEg0KBWNvdW50GAIgASgNIiAKElJlc291cmNlR2V0UmVxdWVzdBIKCgJpZBgBIAEoCSJEChNSZXNvdXJjZUdldFJlc3BvbnNlEi0KBGl0ZW0YASABKAsyHy5zZW50aW5lbC5yZXNvdXJjZXMudjEuUmVzb3VyY2Ui0wEKFVJlc291cmNlQ3JlYXRlUmVxdWVzdBISCgpwcm9qZWN0X2lkGAEgASgJEhEKCWFnZW50X2lkcxgCIAMoCRIMCgRuYW1lGAMgASgJEhMKC2Rlc2NyaXB0aW9uGAQgASgJEjEKBGtpbmQYBSABKA4yIy5zZW50aW5lbC5yZXNvdXJjZXMudjEuUmVzb3VyY2VLaW5kEgwKBHRhZ3MYBiADKAkSLwoHcGF5bG9hZBgHIAEoCzIeLnNlbnRpbmVsLnJlc291cmNlcy52MS5QYXlsb2FkIkcKFlJlc291cmNlQ3JlYXRlUmVzcG9uc2USLQoEaXRlbRgBIAEoCzIfLnNlbnRpbmVsLnJlc291cmNlcy52MS5SZXNvdXJjZSKYAQoVUmVzb3VyY2VVcGRhdGVSZXF1ZXN0EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSDAoEdGFncxgEIAMoCRIvCgdwYXlsb2FkGAUgASgLMh4uc2VudGluZWwucmVzb3VyY2VzLnYxLlBheWxvYWQSEQoJYWdlbnRfaWRzGAYgAygJIkcKFlJlc291cmNlVXBkYXRlUmVzcG9uc2USLQoEaXRlbRgBIAEoCzIfLnNlbnRpbmVsLnJlc291cmNlcy52MS5SZXNvdXJjZSIjChVSZXNvdXJjZURlbGV0ZVJlcXVlc3QSCgoCaWQYASABKAkiGAoWUmVzb3VyY2VEZWxldGVSZXNwb25zZSpiCgxSZXNvdXJjZUtpbmQSHQoZUkVTT1VSQ0VfS0lORF9VTlNQRUNJRklFRBAAEhkKFVJFU09VUkNFX0tJTkRfU0VSVklDRRABEhgKFFJFU09VUkNFX0tJTkRfU0VSVkVSEAIysQQKEFJlc291cmNlc1NlcnZpY2USagoNUmVzb3VyY2VzTGlzdBIrLnNlbnRpbmVsLnJlc291cmNlcy52MS5SZXNvdXJjZXNMaXN0UmVxdWVzdBosLnNlbnRpbmVsLnJlc291cmNlcy52MS5SZXNvdXJjZXNMaXN0UmVzcG9uc2USZAoLUmVzb3VyY2VHZXQSKS5zZW50aW5lbC5yZXNvdXJjZXMudjEuUmVzb3VyY2VHZXRSZXF1ZXN0Giouc2VudGluZWwucmVzb3VyY2VzLnYxLlJlc291cmNlR2V0UmVzcG9uc2USbQoOUmVzb3VyY2VDcmVhdGUSLC5zZW50aW5lbC5yZXNvdXJjZXMudjEuUmVzb3VyY2VDcmVhdGVSZXF1ZXN0Gi0uc2VudGluZWwucmVzb3VyY2VzLnYxLlJlc291cmNlQ3JlYXRlUmVzcG9uc2USbQoOUmVzb3VyY2VVcGRhdGUSLC5zZW50aW5lbC5yZXNvdXJjZXMudjEuUmVzb3VyY2VVcGRhdGVSZXF1ZXN0Gi0uc2VudGluZWwucmVzb3VyY2VzLnYxLlJlc291cmNlVXBkYXRlUmVzcG9uc2USbQoOUmVzb3VyY2VEZWxldGUSLC5zZW50aW5lbC5yZXNvdXJjZXMudjEuUmVzb3VyY2VEZWxldGVSZXF1ZXN0Gi0uc2VudGluZWwucmVzb3VyY2VzLnYxLlJlc291cmNlRGVsZXRlUmVzcG9uc2VC+gEKGWNvbS5zZW50aW5lbC5yZXNvdXJjZXMudjFCDFNlcnZpY2VQcm90b1ABWllnaXRodWIuY29tL3N4d2ViZGV2L3NlbnRpbmVsL2ludGVybmFsL2h1Yi9odWJzZXJ2ZXIvYXBpL3NlbnRpbmVsL3Jlc291cmNlcy92MTtyZXNvdXJjZXN2MaICA1NSWKoCFVNlbnRpbmVsLlJlc291cmNlcy5WMcoCFVNlbnRpbmVsXFJlc291cmNlc1xWMeICIVNlbnRpbmVsXFJlc291cmNlc1xWMVxHUEJNZXRhZGF0YeoCF1NlbnRpbmVsOjpSZXNvdXJjZXM6OlYxYgZwcm90bzM", [file_google_protobuf_timestamp, file_sentinel_common_v1_common]); + +/** + * @generated from message sentinel.resources.v1.Payload + */ +export type Payload = Message<"sentinel.resources.v1.Payload"> & { +}; + +/** + * Describes the message sentinel.resources.v1.Payload. + * Use `create(PayloadSchema)` to create a new message. + */ +export const PayloadSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 0); + +/** + * @generated from message sentinel.resources.v1.Resource + */ +export type Resource = Message<"sentinel.resources.v1.Resource"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string project_id = 2; + */ + projectId: string; + + /** + * @generated from field: string name = 3; + */ + name: string; + + /** + * @generated from field: string description = 4; + */ + description: string; + + /** + * @generated from field: sentinel.resources.v1.ResourceKind kind = 5; + */ + kind: ResourceKind; + + /** + * @generated from field: repeated string tags = 6; + */ + tags: string[]; + + /** + * @generated from field: sentinel.resources.v1.Payload payload = 7; + */ + payload?: Payload; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 8; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 9; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.resources.v1.Resource. + * Use `create(ResourceSchema)` to create a new message. + */ +export const ResourceSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 1); + +/** + * ResourcesList requests a list of resources. + * + * @generated from message sentinel.resources.v1.ResourcesListRequest + */ +export type ResourcesListRequest = Message<"sentinel.resources.v1.ResourcesListRequest"> & { + /** + * @generated from field: sentinel.common.v1.FindRequestCommon common = 1; + */ + common?: FindRequestCommon; +}; + +/** + * Describes the message sentinel.resources.v1.ResourcesListRequest. + * Use `create(ResourcesListRequestSchema)` to create a new message. + */ +export const ResourcesListRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 2); + +/** + * @generated from message sentinel.resources.v1.ResourcesListResponse + */ +export type ResourcesListResponse = Message<"sentinel.resources.v1.ResourcesListResponse"> & { + /** + * @generated from field: repeated sentinel.resources.v1.Resource items = 1; + */ + items: Resource[]; + + /** + * @generated from field: uint32 count = 2; + */ + count: number; +}; + +/** + * Describes the message sentinel.resources.v1.ResourcesListResponse. + * Use `create(ResourcesListResponseSchema)` to create a new message. + */ +export const ResourcesListResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 3); + +/** + * ResourceGet requests a resource by ID. + * + * @generated from message sentinel.resources.v1.ResourceGetRequest + */ +export type ResourceGetRequest = Message<"sentinel.resources.v1.ResourceGetRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceGetRequest. + * Use `create(ResourceGetRequestSchema)` to create a new message. + */ +export const ResourceGetRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 4); + +/** + * @generated from message sentinel.resources.v1.ResourceGetResponse + */ +export type ResourceGetResponse = Message<"sentinel.resources.v1.ResourceGetResponse"> & { + /** + * @generated from field: sentinel.resources.v1.Resource item = 1; + */ + item?: Resource; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceGetResponse. + * Use `create(ResourceGetResponseSchema)` to create a new message. + */ +export const ResourceGetResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 5); + +/** + * ResourceCreate creates a new resource. + * + * @generated from message sentinel.resources.v1.ResourceCreateRequest + */ +export type ResourceCreateRequest = Message<"sentinel.resources.v1.ResourceCreateRequest"> & { + /** + * @generated from field: string project_id = 1; + */ + projectId: string; + + /** + * @generated from field: repeated string agent_ids = 2; + */ + agentIds: string[]; + + /** + * @generated from field: string name = 3; + */ + name: string; + + /** + * @generated from field: string description = 4; + */ + description: string; + + /** + * @generated from field: sentinel.resources.v1.ResourceKind kind = 5; + */ + kind: ResourceKind; + + /** + * @generated from field: repeated string tags = 6; + */ + tags: string[]; + + /** + * @generated from field: sentinel.resources.v1.Payload payload = 7; + */ + payload?: Payload; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceCreateRequest. + * Use `create(ResourceCreateRequestSchema)` to create a new message. + */ +export const ResourceCreateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 6); + +/** + * @generated from message sentinel.resources.v1.ResourceCreateResponse + */ +export type ResourceCreateResponse = Message<"sentinel.resources.v1.ResourceCreateResponse"> & { + /** + * @generated from field: sentinel.resources.v1.Resource item = 1; + */ + item?: Resource; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceCreateResponse. + * Use `create(ResourceCreateResponseSchema)` to create a new message. + */ +export const ResourceCreateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 7); + +/** + * ResourceUpdate updates an existing resource. + * + * @generated from message sentinel.resources.v1.ResourceUpdateRequest + */ +export type ResourceUpdateRequest = Message<"sentinel.resources.v1.ResourceUpdateRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: string description = 3; + */ + description: string; + + /** + * @generated from field: repeated string tags = 4; + */ + tags: string[]; + + /** + * @generated from field: sentinel.resources.v1.Payload payload = 5; + */ + payload?: Payload; + + /** + * @generated from field: repeated string agent_ids = 6; + */ + agentIds: string[]; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceUpdateRequest. + * Use `create(ResourceUpdateRequestSchema)` to create a new message. + */ +export const ResourceUpdateRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 8); + +/** + * @generated from message sentinel.resources.v1.ResourceUpdateResponse + */ +export type ResourceUpdateResponse = Message<"sentinel.resources.v1.ResourceUpdateResponse"> & { + /** + * @generated from field: sentinel.resources.v1.Resource item = 1; + */ + item?: Resource; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceUpdateResponse. + * Use `create(ResourceUpdateResponseSchema)` to create a new message. + */ +export const ResourceUpdateResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 9); + +/** + * ResourceDelete deletes a resource by ID. + * + * @generated from message sentinel.resources.v1.ResourceDeleteRequest + */ +export type ResourceDeleteRequest = Message<"sentinel.resources.v1.ResourceDeleteRequest"> & { + /** + * @generated from field: string id = 1; + */ + id: string; +}; + +/** + * Describes the message sentinel.resources.v1.ResourceDeleteRequest. + * Use `create(ResourceDeleteRequestSchema)` to create a new message. + */ +export const ResourceDeleteRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 10); + +/** + * @generated from message sentinel.resources.v1.ResourceDeleteResponse + */ +export type ResourceDeleteResponse = Message<"sentinel.resources.v1.ResourceDeleteResponse"> & { +}; + +/** + * Describes the message sentinel.resources.v1.ResourceDeleteResponse. + * Use `create(ResourceDeleteResponseSchema)` to create a new message. + */ +export const ResourceDeleteResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_resources_v1_service, 11); + +/** + * @generated from enum sentinel.resources.v1.ResourceKind + */ +export enum ResourceKind { + /** + * @generated from enum value: RESOURCE_KIND_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: RESOURCE_KIND_SERVICE = 1; + */ + SERVICE = 1, + + /** + * @generated from enum value: RESOURCE_KIND_SERVER = 2; + */ + SERVER = 2, +} + +/** + * Describes the enum sentinel.resources.v1.ResourceKind. + */ +export const ResourceKindSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_resources_v1_service, 0); + +/** + * ResourcesService is the service for managing resources. + * + * @generated from service sentinel.resources.v1.ResourcesService + */ +export const ResourcesService: GenService<{ + /** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourcesList + */ + resourcesList: { + methodKind: "unary"; + input: typeof ResourcesListRequestSchema; + output: typeof ResourcesListResponseSchema; + }, + /** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceGet + */ + resourceGet: { + methodKind: "unary"; + input: typeof ResourceGetRequestSchema; + output: typeof ResourceGetResponseSchema; + }, + /** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceCreate + */ + resourceCreate: { + methodKind: "unary"; + input: typeof ResourceCreateRequestSchema; + output: typeof ResourceCreateResponseSchema; + }, + /** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceUpdate + */ + resourceUpdate: { + methodKind: "unary"; + input: typeof ResourceUpdateRequestSchema; + output: typeof ResourceUpdateResponseSchema; + }, + /** + * @generated from rpc sentinel.resources.v1.ResourcesService.ResourceDelete + */ + resourceDelete: { + methodKind: "unary"; + input: typeof ResourceDeleteRequestSchema; + output: typeof ResourceDeleteResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_sentinel_resources_v1_service, 0); + diff --git a/frontend/src/api/gen/sentinel/service/v1/service_pb.ts b/frontend/src/api/gen/sentinel/service/v1/service_pb.ts new file mode 100644 index 0000000..7690176 --- /dev/null +++ b/frontend/src/api/gen/sentinel/service/v1/service_pb.ts @@ -0,0 +1,320 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/service/v1/service.proto (package sentinel.service.v1, syntax proto3) +/* eslint-disable */ + +import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/service/v1/service.proto. + */ +export const file_sentinel_service_v1_service: GenFile = /*@__PURE__*/ + fileDesc("CiFzZW50aW5lbC9zZXJ2aWNlL3YxL3NlcnZpY2UucHJvdG8SE3NlbnRpbmVsLnNlcnZpY2UudjEi/AIKCkhUVFBDb25maWcSQQoJZW5kcG9pbnRzGAEgAygLMi4uc2VudGluZWwuc2VydmljZS52MS5IVFRQQ29uZmlnLkVuZHBvaW50Q29uZmlnEhEKCWNvbmRpdGlvbhgCIAEoCRqXAgoORW5kcG9pbnRDb25maWcSDAoEbmFtZRgBIAEoCRILCgN1cmwYAiABKAkSDgoGbWV0aG9kGAMgASgJEkwKB2hlYWRlcnMYBCADKAsyOy5zZW50aW5lbC5zZXJ2aWNlLnYxLkhUVFBDb25maWcuRW5kcG9pbnRDb25maWcuSGVhZGVyc0VudHJ5EgwKBGJvZHkYBSABKAkSFwoPZXhwZWN0ZWRfc3RhdHVzGAYgASgFEhEKCWpzb25fcGF0aBgHIAEoCRIQCgh1c2VybmFtZRgIIAEoCRIQCghwYXNzd29yZBgJIAEoCRouCgxIZWFkZXJzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJFCglUQ1BDb25maWcSEAoIZW5kcG9pbnQYASABKAkSEQoJc2VuZF9kYXRhGAIgASgJEhMKC2V4cGVjdF9kYXRhGAMgASgJImsKCkdSUENDb25maWcSEAoIZW5kcG9pbnQYASABKAkSEgoKY2hlY2tfdHlwZRgCIAEoCRIUCgxzZXJ2aWNlX25hbWUYAyABKAkSCwoDdGxzGAQgASgIEhQKDGluc2VjdXJlX3RscxgFIAEoCCLtAQoNU2VydmljZUNvbmZpZxI5CgtodHRwX2NvbmZpZxgBIAEoCzIfLnNlbnRpbmVsLnNlcnZpY2UudjEuSFRUUENvbmZpZ0gAiAEBEjcKCnRjcF9jb25maWcYAiABKAsyHi5zZW50aW5lbC5zZXJ2aWNlLnYxLlRDUENvbmZpZ0gBiAEBEjkKC2dycGNfY29uZmlnGAMgASgLMh8uc2VudGluZWwuc2VydmljZS52MS5HUlBDQ29uZmlnSAKIAQFCDgoMX2h0dHBfY29uZmlnQg0KC190Y3BfY29uZmlnQg4KDF9ncnBjX2NvbmZpZyLnAgoHU2VydmljZRIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEjYKCHByb3RvY29sGAMgASgOMiQuc2VudGluZWwuc2VydmljZS52MS5TZXJ2aWNlUHJvdG9jb2wSEAoIaW50ZXJ2YWwYBCABKAMSDwoHdGltZW91dBgFIAEoAxIPCgdyZXRyaWVzGAYgASgDEgwKBHRhZ3MYByADKAkSMgoGY29uZmlnGAggASgLMiIuc2VudGluZWwuc2VydmljZS52MS5TZXJ2aWNlQ29uZmlnEiAKGGlzX25vdGlmaWNhdGlvbnNfZW5hYmxlZBgJIAEoCBISCgppc19lbmFibGVkGAogASgIEi4KCmNyZWF0ZWRfYXQYCyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYDCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wKoMBCg9TZXJ2aWNlUHJvdG9jb2wSIAocU0VSVklDRV9QUk9UT0NPTF9VTlNQRUNJRklFRBAAEhkKFVNFUlZJQ0VfUFJPVE9DT0xfSFRUUBABEhgKFFNFUlZJQ0VfUFJPVE9DT0xfVENQEAISGQoVU0VSVklDRV9QUk9UT0NPTF9HUlBDEAMqewoNU2VydmljZVN0YXR1cxIeChpTRVJWSUNFX1NUQVRVU19VTlNQRUNJRklFRBAAEhoKFlNFUlZJQ0VfU1RBVFVTX1VOS05PV04QARIVChFTRVJWSUNFX1NUQVRVU19VUBACEhcKE1NFUlZJQ0VfU1RBVFVTX0RPV04QA0LsAQoXY29tLnNlbnRpbmVsLnNlcnZpY2UudjFCDFNlcnZpY2VQcm90b1ABWlVnaXRodWIuY29tL3N4d2ViZGV2L3NlbnRpbmVsL2ludGVybmFsL2h1Yi9odWJzZXJ2ZXIvYXBpL3NlbnRpbmVsL3NlcnZpY2UvdjE7c2VydmljZXYxogIDU1NYqgITU2VudGluZWwuU2VydmljZS5WMcoCE1NlbnRpbmVsXFNlcnZpY2VcVjHiAh9TZW50aW5lbFxTZXJ2aWNlXFYxXEdQQk1ldGFkYXRh6gIVU2VudGluZWw6OlNlcnZpY2U6OlYxYgZwcm90bzM", [file_google_protobuf_timestamp]); + +/** + * @generated from message sentinel.service.v1.HTTPConfig + */ +export type HTTPConfig = Message<"sentinel.service.v1.HTTPConfig"> & { + /** + * @generated from field: repeated sentinel.service.v1.HTTPConfig.EndpointConfig endpoints = 1; + */ + endpoints: HTTPConfig_EndpointConfig[]; + + /** + * @generated from field: string condition = 2; + */ + condition: string; +}; + +/** + * Describes the message sentinel.service.v1.HTTPConfig. + * Use `create(HTTPConfigSchema)` to create a new message. + */ +export const HTTPConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_service_v1_service, 0); + +/** + * @generated from message sentinel.service.v1.HTTPConfig.EndpointConfig + */ +export type HTTPConfig_EndpointConfig = Message<"sentinel.service.v1.HTTPConfig.EndpointConfig"> & { + /** + * @generated from field: string name = 1; + */ + name: string; + + /** + * @generated from field: string url = 2; + */ + url: string; + + /** + * @generated from field: string method = 3; + */ + method: string; + + /** + * @generated from field: map headers = 4; + */ + headers: { [key: string]: string }; + + /** + * @generated from field: string body = 5; + */ + body: string; + + /** + * @generated from field: int32 expected_status = 6; + */ + expectedStatus: number; + + /** + * @generated from field: string json_path = 7; + */ + jsonPath: string; + + /** + * @generated from field: string username = 8; + */ + username: string; + + /** + * @generated from field: string password = 9; + */ + password: string; +}; + +/** + * Describes the message sentinel.service.v1.HTTPConfig.EndpointConfig. + * Use `create(HTTPConfig_EndpointConfigSchema)` to create a new message. + */ +export const HTTPConfig_EndpointConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_service_v1_service, 0, 0); + +/** + * @generated from message sentinel.service.v1.TCPConfig + */ +export type TCPConfig = Message<"sentinel.service.v1.TCPConfig"> & { + /** + * @generated from field: string endpoint = 1; + */ + endpoint: string; + + /** + * @generated from field: string send_data = 2; + */ + sendData: string; + + /** + * @generated from field: string expect_data = 3; + */ + expectData: string; +}; + +/** + * Describes the message sentinel.service.v1.TCPConfig. + * Use `create(TCPConfigSchema)` to create a new message. + */ +export const TCPConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_service_v1_service, 1); + +/** + * @generated from message sentinel.service.v1.GRPCConfig + */ +export type GRPCConfig = Message<"sentinel.service.v1.GRPCConfig"> & { + /** + * @generated from field: string endpoint = 1; + */ + endpoint: string; + + /** + * @generated from field: string check_type = 2; + */ + checkType: string; + + /** + * @generated from field: string service_name = 3; + */ + serviceName: string; + + /** + * @generated from field: bool tls = 4; + */ + tls: boolean; + + /** + * @generated from field: bool insecure_tls = 5; + */ + insecureTls: boolean; +}; + +/** + * Describes the message sentinel.service.v1.GRPCConfig. + * Use `create(GRPCConfigSchema)` to create a new message. + */ +export const GRPCConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_service_v1_service, 2); + +/** + * @generated from message sentinel.service.v1.ServiceConfig + */ +export type ServiceConfig = Message<"sentinel.service.v1.ServiceConfig"> & { + /** + * @generated from field: optional sentinel.service.v1.HTTPConfig http_config = 1; + */ + httpConfig?: HTTPConfig; + + /** + * @generated from field: optional sentinel.service.v1.TCPConfig tcp_config = 2; + */ + tcpConfig?: TCPConfig; + + /** + * @generated from field: optional sentinel.service.v1.GRPCConfig grpc_config = 3; + */ + grpcConfig?: GRPCConfig; +}; + +/** + * Describes the message sentinel.service.v1.ServiceConfig. + * Use `create(ServiceConfigSchema)` to create a new message. + */ +export const ServiceConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_service_v1_service, 3); + +/** + * @generated from message sentinel.service.v1.Service + */ +export type Service = Message<"sentinel.service.v1.Service"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string name = 2; + */ + name: string; + + /** + * @generated from field: sentinel.service.v1.ServiceProtocol protocol = 3; + */ + protocol: ServiceProtocol; + + /** + * @generated from field: int64 interval = 4; + */ + interval: bigint; + + /** + * @generated from field: int64 timeout = 5; + */ + timeout: bigint; + + /** + * @generated from field: int64 retries = 6; + */ + retries: bigint; + + /** + * @generated from field: repeated string tags = 7; + */ + tags: string[]; + + /** + * @generated from field: sentinel.service.v1.ServiceConfig config = 8; + */ + config?: ServiceConfig; + + /** + * @generated from field: bool is_notifications_enabled = 9; + */ + isNotificationsEnabled: boolean; + + /** + * @generated from field: bool is_enabled = 10; + */ + isEnabled: boolean; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 11; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 12; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.service.v1.Service. + * Use `create(ServiceSchema)` to create a new message. + */ +export const ServiceSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_service_v1_service, 4); + +/** + * @generated from enum sentinel.service.v1.ServiceProtocol + */ +export enum ServiceProtocol { + /** + * @generated from enum value: SERVICE_PROTOCOL_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: SERVICE_PROTOCOL_HTTP = 1; + */ + HTTP = 1, + + /** + * @generated from enum value: SERVICE_PROTOCOL_TCP = 2; + */ + TCP = 2, + + /** + * @generated from enum value: SERVICE_PROTOCOL_GRPC = 3; + */ + GRPC = 3, +} + +/** + * Describes the enum sentinel.service.v1.ServiceProtocol. + */ +export const ServiceProtocolSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_service_v1_service, 0); + +/** + * @generated from enum sentinel.service.v1.ServiceStatus + */ +export enum ServiceStatus { + /** + * @generated from enum value: SERVICE_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: SERVICE_STATUS_UNKNOWN = 1; + */ + UNKNOWN = 1, + + /** + * @generated from enum value: SERVICE_STATUS_UP = 2; + */ + UP = 2, + + /** + * @generated from enum value: SERVICE_STATUS_DOWN = 3; + */ + DOWN = 3, +} + +/** + * Describes the enum sentinel.service.v1.ServiceStatus. + */ +export const ServiceStatusSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_sentinel_service_v1_service, 1); + diff --git a/frontend/src/api/gen/sentinel/system/v1/service-SystemService_connectquery.ts b/frontend/src/api/gen/sentinel/system/v1/service-SystemService_connectquery.ts new file mode 100644 index 0000000..e71e2de --- /dev/null +++ b/frontend/src/api/gen/sentinel/system/v1/service-SystemService_connectquery.ts @@ -0,0 +1,25 @@ +// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts" +// @generated from file sentinel/system/v1/service.proto (package sentinel.system.v1, syntax proto3) +/* eslint-disable */ + +import { SystemService } from "./service_pb"; + +/** + * @generated from rpc sentinel.system.v1.SystemService.CheckIsInitialized + */ +export const checkIsInitialized = SystemService.method.checkIsInitialized; + +/** + * @generated from rpc sentinel.system.v1.SystemService.Initialize + */ +export const initialize = SystemService.method.initialize; + +/** + * @generated from rpc sentinel.system.v1.SystemService.GetSystemInfo + */ +export const getSystemInfo = SystemService.method.getSystemInfo; + +/** + * @generated from rpc sentinel.system.v1.SystemService.CheckForUpdates + */ +export const checkForUpdates = SystemService.method.checkForUpdates; diff --git a/frontend/src/api/gen/sentinel/system/v1/service_pb.ts b/frontend/src/api/gen/sentinel/system/v1/service_pb.ts new file mode 100644 index 0000000..b2fa285 --- /dev/null +++ b/frontend/src/api/gen/sentinel/system/v1/service_pb.ts @@ -0,0 +1,325 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/system/v1/service.proto (package sentinel.system.v1, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/system/v1/service.proto. + */ +export const file_sentinel_system_v1_service: GenFile = /*@__PURE__*/ + fileDesc("CiBzZW50aW5lbC9zeXN0ZW0vdjEvc2VydmljZS5wcm90bxISc2VudGluZWwuc3lzdGVtLnYxItoBCg9BdmFpbGFibGVVcGRhdGUSFwoPY3VycmVudF92ZXJzaW9uGAEgASgJEhQKDGlzX2F2YWlsYWJsZRgCIAEoCBI8CgdkZXRhaWxzGAMgASgLMisuc2VudGluZWwuc3lzdGVtLnYxLkF2YWlsYWJsZVVwZGF0ZS5EZXRhaWxzGloKB0RldGFpbHMSGwoTaXNfYXZhaWxhYmxlX21hbnVhbBgBIAEoCBIQCgh0YWdfbmFtZRgCIAEoCRILCgN1cmwYAyABKAkSEwoLZGVzY3JpcHRpb24YBCABKAkijQIKClN5c3RlbUluZm8SDwoHdmVyc2lvbhgBIAEoCRITCgtjb21taXRfaGFzaBgCIAEoCRISCgpidWlsZF9kYXRlGAMgASgJEhIKCmdvX3ZlcnNpb24YBCABKAkSFgoOc3FsaXRlX3ZlcnNpb24YBSABKAkSCgoCb3MYBiABKAkSDAoEYXJjaBgHIAEoCRIQCghob3N0bmFtZRgIIAEoCRIWCg5rZXJuZWxfdmVyc2lvbhgJIAEoCRIRCgljcHVfbW9kZWwYCiABKAkSEgoKaXBfYWRkcmVzcxgLIAEoCRIuCgpzdGFydGVkX2F0GAwgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCIbChlDaGVja0lzSW5pdGlhbGl6ZWRSZXF1ZXN0IjQKGkNoZWNrSXNJbml0aWFsaXplZFJlc3BvbnNlEhYKDmlzX2luaXRpYWxpemVkGAEgASgIIjQKEUluaXRpYWxpemVSZXF1ZXN0Eg0KBWVtYWlsGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIhQKEkluaXRpYWxpemVSZXNwb25zZSIWChRHZXRTeXN0ZW1JbmZvUmVxdWVzdCJFChVHZXRTeXN0ZW1JbmZvUmVzcG9uc2USLAoEaW5mbxgBIAEoCzIeLnNlbnRpbmVsLnN5c3RlbS52MS5TeXN0ZW1JbmZvIhgKFkNoZWNrRm9yVXBkYXRlc1JlcXVlc3QiTAoXQ2hlY2tGb3JVcGRhdGVzUmVzcG9uc2USMQoEaW5mbxgBIAEoCzIjLnNlbnRpbmVsLnN5c3RlbS52MS5BdmFpbGFibGVVcGRhdGUyswMKDVN5c3RlbVNlcnZpY2UScwoSQ2hlY2tJc0luaXRpYWxpemVkEi0uc2VudGluZWwuc3lzdGVtLnYxLkNoZWNrSXNJbml0aWFsaXplZFJlcXVlc3QaLi5zZW50aW5lbC5zeXN0ZW0udjEuQ2hlY2tJc0luaXRpYWxpemVkUmVzcG9uc2USWwoKSW5pdGlhbGl6ZRIlLnNlbnRpbmVsLnN5c3RlbS52MS5Jbml0aWFsaXplUmVxdWVzdBomLnNlbnRpbmVsLnN5c3RlbS52MS5Jbml0aWFsaXplUmVzcG9uc2USZAoNR2V0U3lzdGVtSW5mbxIoLnNlbnRpbmVsLnN5c3RlbS52MS5HZXRTeXN0ZW1JbmZvUmVxdWVzdBopLnNlbnRpbmVsLnN5c3RlbS52MS5HZXRTeXN0ZW1JbmZvUmVzcG9uc2USagoPQ2hlY2tGb3JVcGRhdGVzEiouc2VudGluZWwuc3lzdGVtLnYxLkNoZWNrRm9yVXBkYXRlc1JlcXVlc3QaKy5zZW50aW5lbC5zeXN0ZW0udjEuQ2hlY2tGb3JVcGRhdGVzUmVzcG9uc2VC5QEKFmNvbS5zZW50aW5lbC5zeXN0ZW0udjFCDFNlcnZpY2VQcm90b1ABWlNnaXRodWIuY29tL3N4d2ViZGV2L3NlbnRpbmVsL2ludGVybmFsL2h1Yi9odWJzZXJ2ZXIvYXBpL3NlbnRpbmVsL3N5c3RlbS92MTtzeXN0ZW12MaICA1NTWKoCElNlbnRpbmVsLlN5c3RlbS5WMcoCElNlbnRpbmVsXFN5c3RlbVxWMeICHlNlbnRpbmVsXFN5c3RlbVxWMVxHUEJNZXRhZGF0YeoCFFNlbnRpbmVsOjpTeXN0ZW06OlYxYgZwcm90bzM", [file_google_protobuf_timestamp]); + +/** + * AvailableUpdate represents information about an available update + * + * @generated from message sentinel.system.v1.AvailableUpdate + */ +export type AvailableUpdate = Message<"sentinel.system.v1.AvailableUpdate"> & { + /** + * @generated from field: string current_version = 1; + */ + currentVersion: string; + + /** + * @generated from field: bool is_available = 2; + */ + isAvailable: boolean; + + /** + * @generated from field: sentinel.system.v1.AvailableUpdate.Details details = 3; + */ + details?: AvailableUpdate_Details; +}; + +/** + * Describes the message sentinel.system.v1.AvailableUpdate. + * Use `create(AvailableUpdateSchema)` to create a new message. + */ +export const AvailableUpdateSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 0); + +/** + * @generated from message sentinel.system.v1.AvailableUpdate.Details + */ +export type AvailableUpdate_Details = Message<"sentinel.system.v1.AvailableUpdate.Details"> & { + /** + * @generated from field: bool is_available_manual = 1; + */ + isAvailableManual: boolean; + + /** + * @generated from field: string tag_name = 2; + */ + tagName: string; + + /** + * @generated from field: string url = 3; + */ + url: string; + + /** + * @generated from field: string description = 4; + */ + description: string; +}; + +/** + * Describes the message sentinel.system.v1.AvailableUpdate.Details. + * Use `create(AvailableUpdate_DetailsSchema)` to create a new message. + */ +export const AvailableUpdate_DetailsSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 0, 0); + +/** + * SystemInfo represents information about the server + * + * @generated from message sentinel.system.v1.SystemInfo + */ +export type SystemInfo = Message<"sentinel.system.v1.SystemInfo"> & { + /** + * @generated from field: string version = 1; + */ + version: string; + + /** + * @generated from field: string commit_hash = 2; + */ + commitHash: string; + + /** + * @generated from field: string build_date = 3; + */ + buildDate: string; + + /** + * @generated from field: string go_version = 4; + */ + goVersion: string; + + /** + * @generated from field: string sqlite_version = 5; + */ + sqliteVersion: string; + + /** + * @generated from field: string os = 6; + */ + os: string; + + /** + * @generated from field: string arch = 7; + */ + arch: string; + + /** + * @generated from field: string hostname = 8; + */ + hostname: string; + + /** + * @generated from field: string kernel_version = 9; + */ + kernelVersion: string; + + /** + * @generated from field: string cpu_model = 10; + */ + cpuModel: string; + + /** + * @generated from field: string ip_address = 11; + */ + ipAddress: string; + + /** + * @generated from field: google.protobuf.Timestamp started_at = 12; + */ + startedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.system.v1.SystemInfo. + * Use `create(SystemInfoSchema)` to create a new message. + */ +export const SystemInfoSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 1); + +/** + * CheckIsInitializedRequest + * + * @generated from message sentinel.system.v1.CheckIsInitializedRequest + */ +export type CheckIsInitializedRequest = Message<"sentinel.system.v1.CheckIsInitializedRequest"> & { +}; + +/** + * Describes the message sentinel.system.v1.CheckIsInitializedRequest. + * Use `create(CheckIsInitializedRequestSchema)` to create a new message. + */ +export const CheckIsInitializedRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 2); + +/** + * @generated from message sentinel.system.v1.CheckIsInitializedResponse + */ +export type CheckIsInitializedResponse = Message<"sentinel.system.v1.CheckIsInitializedResponse"> & { + /** + * @generated from field: bool is_initialized = 1; + */ + isInitialized: boolean; +}; + +/** + * Describes the message sentinel.system.v1.CheckIsInitializedResponse. + * Use `create(CheckIsInitializedResponseSchema)` to create a new message. + */ +export const CheckIsInitializedResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 3); + +/** + * InitializeRequest + * + * @generated from message sentinel.system.v1.InitializeRequest + */ +export type InitializeRequest = Message<"sentinel.system.v1.InitializeRequest"> & { + /** + * @generated from field: string email = 1; + */ + email: string; + + /** + * @generated from field: string password = 2; + */ + password: string; +}; + +/** + * Describes the message sentinel.system.v1.InitializeRequest. + * Use `create(InitializeRequestSchema)` to create a new message. + */ +export const InitializeRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 4); + +/** + * @generated from message sentinel.system.v1.InitializeResponse + */ +export type InitializeResponse = Message<"sentinel.system.v1.InitializeResponse"> & { +}; + +/** + * Describes the message sentinel.system.v1.InitializeResponse. + * Use `create(InitializeResponseSchema)` to create a new message. + */ +export const InitializeResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 5); + +/** + * GetSystemInfoRequest + * + * @generated from message sentinel.system.v1.GetSystemInfoRequest + */ +export type GetSystemInfoRequest = Message<"sentinel.system.v1.GetSystemInfoRequest"> & { +}; + +/** + * Describes the message sentinel.system.v1.GetSystemInfoRequest. + * Use `create(GetSystemInfoRequestSchema)` to create a new message. + */ +export const GetSystemInfoRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 6); + +/** + * @generated from message sentinel.system.v1.GetSystemInfoResponse + */ +export type GetSystemInfoResponse = Message<"sentinel.system.v1.GetSystemInfoResponse"> & { + /** + * @generated from field: sentinel.system.v1.SystemInfo info = 1; + */ + info?: SystemInfo; +}; + +/** + * Describes the message sentinel.system.v1.GetSystemInfoResponse. + * Use `create(GetSystemInfoResponseSchema)` to create a new message. + */ +export const GetSystemInfoResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 7); + +/** + * CheckForUpdatesRequest + * + * @generated from message sentinel.system.v1.CheckForUpdatesRequest + */ +export type CheckForUpdatesRequest = Message<"sentinel.system.v1.CheckForUpdatesRequest"> & { +}; + +/** + * Describes the message sentinel.system.v1.CheckForUpdatesRequest. + * Use `create(CheckForUpdatesRequestSchema)` to create a new message. + */ +export const CheckForUpdatesRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 8); + +/** + * @generated from message sentinel.system.v1.CheckForUpdatesResponse + */ +export type CheckForUpdatesResponse = Message<"sentinel.system.v1.CheckForUpdatesResponse"> & { + /** + * @generated from field: sentinel.system.v1.AvailableUpdate info = 1; + */ + info?: AvailableUpdate; +}; + +/** + * Describes the message sentinel.system.v1.CheckForUpdatesResponse. + * Use `create(CheckForUpdatesResponseSchema)` to create a new message. + */ +export const CheckForUpdatesResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_system_v1_service, 9); + +/** + * SystemService is the service for system information + * + * @generated from service sentinel.system.v1.SystemService + */ +export const SystemService: GenService<{ + /** + * @generated from rpc sentinel.system.v1.SystemService.CheckIsInitialized + */ + checkIsInitialized: { + methodKind: "unary"; + input: typeof CheckIsInitializedRequestSchema; + output: typeof CheckIsInitializedResponseSchema; + }, + /** + * @generated from rpc sentinel.system.v1.SystemService.Initialize + */ + initialize: { + methodKind: "unary"; + input: typeof InitializeRequestSchema; + output: typeof InitializeResponseSchema; + }, + /** + * @generated from rpc sentinel.system.v1.SystemService.GetSystemInfo + */ + getSystemInfo: { + methodKind: "unary"; + input: typeof GetSystemInfoRequestSchema; + output: typeof GetSystemInfoResponseSchema; + }, + /** + * @generated from rpc sentinel.system.v1.SystemService.CheckForUpdates + */ + checkForUpdates: { + methodKind: "unary"; + input: typeof CheckForUpdatesRequestSchema; + output: typeof CheckForUpdatesResponseSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_sentinel_system_v1_service, 0); + diff --git a/frontend/src/api/gen/sentinel/users/v1/users_pb.ts b/frontend/src/api/gen/sentinel/users/v1/users_pb.ts new file mode 100644 index 0000000..7f9f8fa --- /dev/null +++ b/frontend/src/api/gen/sentinel/users/v1/users_pb.ts @@ -0,0 +1,63 @@ +// @generated by protoc-gen-es v2.9.0 with parameter "target=ts" +// @generated from file sentinel/users/v1/users.proto (package sentinel.users.v1, syntax proto3) +/* eslint-disable */ + +import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2"; +import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2"; +import type { Timestamp } from "@bufbuild/protobuf/wkt"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; +import type { Message } from "@bufbuild/protobuf"; + +/** + * Describes the file sentinel/users/v1/users.proto. + */ +export const file_sentinel_users_v1_users: GenFile = /*@__PURE__*/ + fileDesc("Ch1zZW50aW5lbC91c2Vycy92MS91c2Vycy5wcm90bxIRc2VudGluZWwudXNlcnMudjEitgEKBFVzZXISCgoCaWQYASABKAkSDQoFZW1haWwYAiABKAkSEQoJZnVsbF9uYW1lGAMgASgJEgwKBHJvbGUYBCABKAkSEgoKYXZhdGFyX3VybBgFIAEoCRIuCgpjcmVhdGVkX2F0GAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GAcgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcELcAQoVY29tLnNlbnRpbmVsLnVzZXJzLnYxQgpVc2Vyc1Byb3RvUAFaUWdpdGh1Yi5jb20vc3h3ZWJkZXYvc2VudGluZWwvaW50ZXJuYWwvaHViL2h1YnNlcnZlci9hcGkvc2VudGluZWwvdXNlcnMvdjE7dXNlcnN2MaICA1NVWKoCEVNlbnRpbmVsLlVzZXJzLlYxygIRU2VudGluZWxcVXNlcnNcVjHiAh1TZW50aW5lbFxVc2Vyc1xWMVxHUEJNZXRhZGF0YeoCE1NlbnRpbmVsOjpVc2Vyczo6VjFiBnByb3RvMw", [file_google_protobuf_timestamp]); + +/** + * @generated from message sentinel.users.v1.User + */ +export type User = Message<"sentinel.users.v1.User"> & { + /** + * @generated from field: string id = 1; + */ + id: string; + + /** + * @generated from field: string email = 2; + */ + email: string; + + /** + * @generated from field: string full_name = 3; + */ + fullName: string; + + /** + * @generated from field: string role = 4; + */ + role: string; + + /** + * @generated from field: string avatar_url = 5; + */ + avatarUrl: string; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 6; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 7; + */ + updatedAt?: Timestamp; +}; + +/** + * Describes the message sentinel.users.v1.User. + * Use `create(UserSchema)` to create a new message. + */ +export const UserSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sentinel_users_v1_users, 0); + diff --git a/frontend/src/app/app.tsx b/frontend/src/app/app.tsx new file mode 100644 index 0000000..6f149b9 --- /dev/null +++ b/frontend/src/app/app.tsx @@ -0,0 +1,73 @@ +import { RouterProvider } from "@tanstack/react-router"; + +import { useAuthStore } from "./stores/auth"; +import { router } from "../router"; +import { useSystemStore } from "./stores/system"; +import PageLoader from "@/shared/components/pageLoader"; +import { useUserPreferenceStore } from "./stores/userPreferience"; +import { useEffect } from "react"; +import { useShallow } from "zustand/react/shallow"; + +const App = () => { + const { initTheme } = useUserPreferenceStore( + useShallow((s) => ({ initTheme: s.initTheme })), + ); + + const { + isLoading: isSystemLoading, + isSystemInitialized, + checkIsInitialized, + error, + } = useSystemStore( + useShallow((s) => ({ + isLoading: s.isLoading, + error: s.error, + isSystemInitialized: s.isSystemInitialized, + checkIsInitialized: s.checkIsInitialized, + })), + ); + + const { isLoading, isAuthenticated, init } = useAuthStore( + useShallow((s) => ({ + isLoading: s.isLoading, + isAuthenticated: s.isAuthenticated, + init: s.init, + })), + ); + + // Initialize theme on app load + useEffect(() => { + initTheme(); + }, [initTheme]); + + // Initialize system state on app load + useEffect(() => { + checkIsInitialized(); + }, [checkIsInitialized]); + + // Initialize auth on app load + useEffect(() => { + if (!isSystemLoading) return; + init(); + }, [isSystemLoading, init]); + + if (error) { + throw error; + } + + if (isSystemLoading || isLoading) { + return ; + } + + return ( + + ); +}; + +export default App; diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx new file mode 100644 index 0000000..89dfbb5 --- /dev/null +++ b/frontend/src/app/layout.tsx @@ -0,0 +1,34 @@ +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/shared/components/ui/sidebar"; +import { AppSidebar } from "@/shared/components/sidebar/sidebar"; +import { ThemeToggle } from "@/shared/components/theme-toggle"; +import { SystemInfo } from "@/features/apiInfo/systemInfo"; + +type LayoutProps = { + children: React.ReactNode; +}; + +export default function Layout({ children }: LayoutProps) { + return ( + + + +
+
+ +
+
+ + +
+
+
+
{children}
+
+
+
+ ); +} diff --git a/frontend/src/app/layouts/parts/Header.tsx b/frontend/src/app/layouts/parts/Header.tsx deleted file mode 100644 index bb9b647..0000000 --- a/frontend/src/app/layouts/parts/Header.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { Link } from "@tanstack/react-router"; - -import { HouseIcon, BookXIcon } from "lucide-react"; - -import { Button } from "@shared/components/ui/button"; -import { - NavigationMenu, - NavigationMenuItem, - NavigationMenuList, -} from "@shared/components/ui/navigation-menu"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@shared/components/ui/popover"; -import { cn } from "@/shared/lib/utils"; -import ServiceCreate from "@/pages/service/serviceCreate"; -import { UpdateBanner } from "@/features/apiInfo/update-banner"; -import { ServerInfo } from "@/features/apiInfo/server-info"; - -// Navigation links array -const navigationLinks = [ - { href: "/", label: "Dashboard", icon: HouseIcon, active: true }, - { href: "/incidents", label: "Incidents", icon: BookXIcon, active: true }, - // { href: "/certificates", label: "Certificates", icon: ShieldCheck }, -]; - -function NavigationMenuLink({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -export default function Component() { - return ( -
-
- {/* Left side */} -
- {/* Mobile menu trigger */} - - - - - - - - {navigationLinks.map((link, index) => { - const Icon = link.icon; - return ( - - - - - ); - })} - - - - - - - - {navigationLinks.map((link, index) => { - const Icon = link.icon; - return ( - - - - - ); - })} - - -
- - {/* Middle side: Logo */} - - - {/* Right side: Actions */} -
- - - {/* */} - - - - -
-
-
- ); -} diff --git a/frontend/src/app/protected.tsx b/frontend/src/app/protected.tsx new file mode 100644 index 0000000..71057d5 --- /dev/null +++ b/frontend/src/app/protected.tsx @@ -0,0 +1,36 @@ +import PageLoader from "@/shared/components/pageLoader"; +import Layout from "./layout"; +import { useProjectStore } from "./stores/projects"; +import ProjectCreate from "@/pages/projects/create"; +import { useShallow } from "zustand/react/shallow"; +import { useEffect } from "react"; + +type ProtectedWrapperProps = { + children: React.ReactNode; +}; + +const ProtectedWrapper = ({ children }: ProtectedWrapperProps) => { + const { isLoading, projects, loadProjects } = useProjectStore( + useShallow((s) => ({ + isLoading: s.isLoading, + projects: s.projects, + loadProjects: s.loadProjects, + })), + ); + + useEffect(() => { + loadProjects(); + }, [loadProjects]); + + if (isLoading) { + return ; + } + + if (projects.length === 0) { + return ; + } + + return {children}; +}; + +export default ProtectedWrapper; diff --git a/frontend/src/app/stores/auth.ts b/frontend/src/app/stores/auth.ts new file mode 100644 index 0000000..06ad6d5 --- /dev/null +++ b/frontend/src/app/stores/auth.ts @@ -0,0 +1,285 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import type { User } from "@/api/gen/sentinel/users/v1/users_pb"; +import { type AuthorizationResponse } from "@/api/gen/sentinel/auth/v1/auth_pb"; +import { + AuthorizationRequestSchema, + AuthenticateRequestSchema, + RefreshTokenRequestSchema, + LogoutRequestSchema, + DeviceInfoSchema, + DeviceType, +} from "@/api/gen/sentinel/auth/v1/auth_pb"; +import { Code, ConnectError } from "@connectrpc/connect"; +import { create as createMsg } from "@bufbuild/protobuf"; +import { timestampDate } from "@bufbuild/protobuf/wkt"; + +import { authClient } from "@/api/api"; +import { toast } from "sonner"; + +export type AuthSession = { + accessToken: string; + refreshToken: string; + accessTokenExpiredAt: string; + refreshTokenExpiredAt: string; + deviceId: string; +}; + +export type AuthError = { + name: string; + message: string; +}; + +export interface AuthStoreState { + // state + isLoading: boolean; + isAuthenticated: boolean; + user?: User; + session?: AuthSession | null; + + // actions + init: () => Promise; + authorization: (email: string, password: string) => Promise; + refreshToken: () => Promise; + logout: () => Promise; + clear: () => void; +} + +export const useAuthStore = create()( + persist( + (set, get) => { + // Interval for token refresh: checks every 30 seconds + let refreshInterval: ReturnType | null = null; + + const startRefreshInterval = () => { + if (refreshInterval) return; + + refreshInterval = setInterval(() => { + const session = get().session; + if (!session || !session.accessTokenExpiredAt) return; + + const expiresAt = new Date(session.accessTokenExpiredAt).getTime(); + const now = Date.now(); + const timeLeft = expiresAt - now; + + // If less than 5 minutes left, refresh token + if (timeLeft < 5 * 60 * 1000) { + get().refreshToken(); + } + }, 30 * 1000); + }; + + // Start interval immediately + startRefreshInterval(); + + return { + isLoading: false, + isAuthenticated: false, + user: undefined, + session: null, + + clear: () => { + // Stop refresh interval + if (refreshInterval) { + clearInterval(refreshInterval); + refreshInterval = null; + } + + set({ + isLoading: false, + isAuthenticated: false, + user: undefined, + session: null, + }); + }, + + init: async () => { + set({ isLoading: true }); + + const session = get().session; + if (!session) { + get().clear(); + return; + } + + // Refresh token expired -> clear + if (Date.now() >= new Date(session.refreshTokenExpiredAt).getTime()) { + get().clear(); + return; + } + + let currentSession = session; + // Access token expired -> try refresh + if ( + Date.now() >= + new Date(currentSession.accessTokenExpiredAt).getTime() + ) { + try { + const res = await authClient.refreshToken( + createMsg(RefreshTokenRequestSchema, { + refreshToken: currentSession.refreshToken, + }), + ); + if (!res.accessTokenExpiredAt || !res.refreshTokenExpiredAt) { + throw new Error("missing token expiration in response"); + } + currentSession = { + accessToken: res.accessToken, + refreshToken: res.refreshToken, + accessTokenExpiredAt: timestampDate( + res.accessTokenExpiredAt, + ).toISOString(), + refreshTokenExpiredAt: timestampDate( + res.refreshTokenExpiredAt, + ).toISOString(), + deviceId: currentSession.deviceId, + }; + set({ session: currentSession, isAuthenticated: true }); + } catch (e) { + if (e instanceof ConnectError) { + if (e.code === Code.Unauthenticated) { + get().clear(); + } + toast.error(e.rawMessage); + } + return; + } + } + + // Authenticate to fetch user + try { + const res = await authClient.authenticate( + createMsg(AuthenticateRequestSchema, { + accessToken: currentSession.accessToken, + }), + ); + set({ user: res.user, isAuthenticated: true, isLoading: false }); + } catch (e) { + if (e instanceof ConnectError) { + if (e.code === Code.Unauthenticated) { + get().clear(); + return; + } + toast.error(e.rawMessage); + } else { + toast.error( + `Failed to authenticate user ${(e as Error)?.message}`, + ); + } + set({ isLoading: false }); + } + }, + + authorization: async (email: string, password: string) => { + try { + const res: AuthorizationResponse = await authClient.authorization( + createMsg(AuthorizationRequestSchema, { + email, + password, + deviceInfo: createMsg(DeviceInfoSchema, { + deviceId: get().session?.deviceId || "", + deviceType: DeviceType.WEB, + deviceName: navigator.userAgent, + }), + }), + ); + if ( + !res.authPayload || + !res.authPayload.accessTokenExpiredAt || + !res.authPayload.refreshTokenExpiredAt + ) { + throw new Error("missing token expiration in response"); + } + const session: AuthSession = { + accessToken: res.authPayload.accessToken, + refreshToken: res.authPayload.refreshToken, + accessTokenExpiredAt: timestampDate( + res.authPayload.accessTokenExpiredAt, + ).toISOString(), + refreshTokenExpiredAt: timestampDate( + res.authPayload.refreshTokenExpiredAt, + ).toISOString(), + deviceId: res.authPayload.deviceId, + }; + set({ session, user: res.user, isAuthenticated: true }); + + // Restart refresh interval after successful authorization + startRefreshInterval(); + } catch (e) { + if (e instanceof ConnectError) { + toast.error(e.rawMessage); + } else if (e instanceof Error) { + toast.error(e.message); + } else { + toast.error("An unknown error occurred during authorization"); + } + throw e; + } + }, + + refreshToken: async (): Promise => { + const session = get().session; + if ( + !session || + !session.refreshToken || + !session.refreshTokenExpiredAt + ) { + get().clear(); + return; + } + + // If refresh token expired, signal Unauthenticated-like condition, but don't clear here + if (Date.now() >= new Date(session.refreshTokenExpiredAt).getTime()) { + get().clear(); + return; + } + + try { + const res = await authClient.refreshToken( + createMsg(RefreshTokenRequestSchema, { + refreshToken: session.refreshToken, + }), + ); + + if (!res.accessTokenExpiredAt || !res.refreshTokenExpiredAt) { + throw new Error("missing token expiration in response"); + } + + const newSession: AuthSession = { + accessToken: res.accessToken, + refreshToken: res.refreshToken, + accessTokenExpiredAt: timestampDate( + res.accessTokenExpiredAt, + ).toISOString(), + refreshTokenExpiredAt: timestampDate( + res.refreshTokenExpiredAt, + ).toISOString(), + deviceId: session.deviceId, + }; + set({ session: newSession, isAuthenticated: true }); + return; + } catch (e) { + console.log("refresh token error", e); + get().clear(); + window.location.href = "/login"; + } + }, + + logout: async () => { + try { + await authClient.logout(createMsg(LogoutRequestSchema)); + } catch { + // ignore network/logging errors + } finally { + get().clear(); + } + }, + }; + }, + { + name: "authStore", + version: 1, + partialize: (state: AuthStoreState) => ({ session: state.session }), + }, + ), +); diff --git a/frontend/src/app/stores/projects.ts b/frontend/src/app/stores/projects.ts new file mode 100644 index 0000000..0021fed --- /dev/null +++ b/frontend/src/app/stores/projects.ts @@ -0,0 +1,96 @@ +import { projectsClient } from "@/api/api"; +import { + ProjectCreateRequestSchema, + type Project, +} from "@/api/gen/sentinel/projects/v1/projects_pb"; +import { ConnectError } from "@connectrpc/connect"; +import { toast } from "sonner"; +import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { create as createProto } from "@bufbuild/protobuf"; + +type ProjectStore = { + isLoading: boolean; + selectedProjectId?: string; + projects: Project[]; + selectProject: (id: string | undefined) => void; + selectedProject: () => Project | undefined; + loadProjects: () => Promise; + createProject: (name: string, description: string) => Promise; +}; + +export const useProjectStore = create()( + persist( + (set, get) => ({ + isLoading: true, + projects: [], + selectProject: (id: string | undefined) => { + set({ selectedProjectId: id }); + window.location.reload(); + }, + selectedProject: () => { + const { selectedProjectId, projects } = get(); + const selectedProject = projects.find( + (p) => p.id === selectedProjectId, + ); + + if (selectedProject) { + return selectedProject; + } + + if (projects.length > 0) { + return projects[0]; + } + + return undefined; + }, + loadProjects: async () => { + try { + const data = await projectsClient.projectsList({}); + set({ projects: data.items }); + + const selectedProjectId = get().selectedProjectId; + if (!selectedProjectId && data.items.length > 0) { + set({ selectedProjectId: data.items[0].id }); + } + } catch (error) { + if (error instanceof ConnectError) { + toast.error(error.rawMessage); + } else { + console.error("Failed to load projects:", error); + } + } finally { + set({ isLoading: false }); + } + }, + createProject: async (name: string, description: string) => { + try { + const res = await projectsClient.projectCreate( + createProto(ProjectCreateRequestSchema, { + name, + description, + }), + ); + + await get().loadProjects(); + + set({ selectedProjectId: res.item?.id }); + + window.location.reload(); + } catch (error) { + if (error instanceof ConnectError) { + toast.error(error.rawMessage); + } else { + console.error("Failed to create project:", error); + } + } + }, + }), + { + name: "projectStore", + partialize: (state) => ({ + selectedProjectId: state.selectedProjectId, + }), + }, + ), +); diff --git a/frontend/src/app/stores/system.ts b/frontend/src/app/stores/system.ts new file mode 100644 index 0000000..2505d91 --- /dev/null +++ b/frontend/src/app/stores/system.ts @@ -0,0 +1,52 @@ +import { systemClient } from "@/api/api"; +import { ConnectError } from "@connectrpc/connect"; +import { toast } from "sonner"; +import { create } from "zustand"; +import { create as createProto } from "@bufbuild/protobuf"; +import { + CheckIsInitializedRequestSchema, + InitializeRequestSchema, +} from "@/api/gen/sentinel/system/v1/service_pb"; + +type SystemStore = { + isLoading: boolean; + error?: unknown; + isSystemInitialized: boolean; + initializeSystem: (email: string, password: string) => Promise; + checkIsInitialized: () => Promise; +}; + +export const useSystemStore = create()((set) => ({ + isLoading: true, + isSystemInitialized: false, + checkIsInitialized: async () => { + try { + const data = await systemClient.checkIsInitialized( + createProto(CheckIsInitializedRequestSchema), + ); + set({ isSystemInitialized: data.isInitialized }); + } catch (error) { + set({ error: error }); + } finally { + set({ isLoading: false }); + } + }, + initializeSystem: async (email: string, password: string) => { + try { + await systemClient.initialize( + createProto(InitializeRequestSchema, { + email, + password, + }), + ); + set({ isSystemInitialized: true }); + toast.success("System initialized successfully"); + } catch (error) { + if (error instanceof ConnectError) { + toast.error(error.rawMessage); + } else { + console.error("Failed to initialize system:", error); + } + } + }, +})); diff --git a/frontend/src/app/stores/userPreferience.ts b/frontend/src/app/stores/userPreferience.ts new file mode 100644 index 0000000..d5449f7 --- /dev/null +++ b/frontend/src/app/stores/userPreferience.ts @@ -0,0 +1,45 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +type Theme = "none" | "dark" | "light"; + +type UserPreferenceStore = { + theme: Theme; + toggleTheme: () => void; + initTheme: () => void; +}; + +export const useUserPreferenceStore = create()( + persist( + (set, get) => ({ + theme: "none", + toggleTheme: () => { + const currentTheme = get().theme; + const newTheme = currentTheme === "dark" ? "light" : "dark"; + set({ theme: newTheme }); + get().initTheme(); + }, + initTheme: () => { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + + if (get().theme === "none") { + set({ theme: systemTheme }); + } + + const root = window.document.documentElement; + + root.classList.remove("light", "dark"); + root.classList.add(get().theme); + }, + }), + { + name: "userPreferenceStore", + partialize: (state) => ({ + theme: state.theme, + }), + }, + ), +); diff --git a/frontend/src/entities/ActivityIndicatorSVG/ActivityIndicatorSVG.tsx b/frontend/src/entities/ActivityIndicatorSVG/ActivityIndicatorSVG.tsx index bc2c645..afe2561 100644 --- a/frontend/src/entities/ActivityIndicatorSVG/ActivityIndicatorSVG.tsx +++ b/frontend/src/entities/ActivityIndicatorSVG/ActivityIndicatorSVG.tsx @@ -1,6 +1,5 @@ export const ActivityIndicatorSVG = ({ active = true, size = 16 }) => { const color = active ? "#3b82f6" : "#ef4444"; - const pulseColor = active ? "#3b82f6" : "#ef4444"; return ( { viewBox="0 0 32 32" style={{ display: "inline-block", verticalAlign: "middle" }} > - + { return ( - setOpen(null)}> - - - {title} - {description} - + setOpen(null)}> + + + {title} + {description} + {content && content} - - + {type === "delete" && ( - + )} - {type === "default" && } - - - + {type === "default" && ( + + )} + + + ); }; diff --git a/frontend/src/entities/errorRouter/ErrorBoundary.tsx b/frontend/src/entities/errorRouter/ErrorBoundary.tsx new file mode 100644 index 0000000..c8b2681 --- /dev/null +++ b/frontend/src/entities/errorRouter/ErrorBoundary.tsx @@ -0,0 +1,91 @@ +import { Component, type ReactNode } from "react"; +import { Button } from "@/shared/components/ui"; +import { ConnectError } from "@connectrpc/connect"; + +export type ErrorData = { + status: number; + name?: string; + message: string; + details?: string; +}; + +interface ErrorBoundaryProps { + children: ReactNode; +} + +interface ErrorBoundaryState { + error: Error | null; +} + +export class ErrorBoundary extends Component< + ErrorBoundaryProps, + ErrorBoundaryState +> { + state: ErrorBoundaryState = { + error: null, + }; + + static getDerivedStateFromError(error: Error) { + return { error }; + } + + handleReload = () => { + this.setState({ error: null }); + window.location.reload(); + }; + + render() { + if (this.state.error) { + const errorData: ErrorData = { + status: 500, + name: this.state.error.name, + message: this.state.error.message, + // details: this.state.error.details, + }; + + if (this.state.error instanceof ConnectError) { + errorData.status = 500; + errorData.name = this.state.error.name; + errorData.message = + this.state.error.rawMessage || + this.state.error.message || + "Unknown error"; + errorData.details = this.state.error.details.join(", ") || undefined; + } + + if (errorData.message == "Failed to fetch") { + errorData.status = 503; + errorData.name = "Service Unavailable"; + errorData.message = + "The server is currently unreachable. Please try again later."; + errorData.details = undefined; + } + + return ( +
+
+
+
{errorData.status}
+
+ {errorData.name || "Something went wrong"} +
+
+ {errorData.message} + {errorData.details && ( +
+ {errorData.details} +
+ )} +
+
+ +
+
+
+
+ ); + } + + return this.props.children; + } +} diff --git a/frontend/src/entities/errorRouter/index.tsx b/frontend/src/entities/errorRouter/index.tsx index a6bfeb6..9bef98a 100644 --- a/frontend/src/entities/errorRouter/index.tsx +++ b/frontend/src/entities/errorRouter/index.tsx @@ -1,9 +1,8 @@ import { useRouter, type ErrorRouteComponent } from "@tanstack/react-router"; import { Button } from "@/shared/components/ui"; -import { isAxiosError, type AxiosError } from "axios"; -import type { WebErrorResponse } from "@/shared/types/model/webErrorResponse"; +import { ConnectError } from "@connectrpc/connect"; -type errorData = { +export type ErrorData = { status: number; message: string; details?: string; @@ -13,20 +12,16 @@ type errorData = { const ErrorRouter: ErrorRouteComponent = ({ error, reset }) => { const router = useRouter(); - const errorData: errorData = { + const errorData: ErrorData = { status: 500, message: error.name, details: error.message, }; - const axiosErr: AxiosError | undefined = isAxiosError(error) - ? error - : undefined; - - if (axiosErr) { - errorData.status = axiosErr.response?.status || 500; - errorData.message = axiosErr.message || "Unknown error"; - errorData.details = axiosErr.response?.data.error; + if (error instanceof ConnectError) { + errorData.status = error.code; + errorData.message = error.rawMessage || error.message || "Unknown error"; + errorData.details = error.details.join(", ") || undefined; } const handleReload = async () => { diff --git a/frontend/src/entities/infoStatsCard/infoCardStats.tsx b/frontend/src/entities/infoStatsCard/infoCardStats.tsx index aff7619..8be2da6 100644 --- a/frontend/src/entities/infoStatsCard/infoCardStats.tsx +++ b/frontend/src/entities/infoStatsCard/infoCardStats.tsx @@ -12,14 +12,16 @@ interface InfoCardStatsProps { export const InfoCardStats = ({ title, value }: InfoCardStatsProps) => { return ( - - + + {value} - -

{title}

+ +

+ {title} +

); diff --git a/frontend/src/features/apiInfo/server-info.tsx b/frontend/src/features/apiInfo/systemInfo.tsx similarity index 55% rename from frontend/src/features/apiInfo/server-info.tsx rename to frontend/src/features/apiInfo/systemInfo.tsx index c6ab628..13620f5 100644 --- a/frontend/src/features/apiInfo/server-info.tsx +++ b/frontend/src/features/apiInfo/systemInfo.tsx @@ -1,5 +1,5 @@ -import { useServerStore } from "@/pages/dashboard/store/useServerStore"; -import { Badge } from "@/shared/components/ui"; +import { getSystemInfo } from "@/api/gen/sentinel/system/v1/service-SystemService_connectquery"; +import { useQuery } from "@connectrpc/connect-query"; import { Button } from "@shared/components/ui/button"; import { Popover, @@ -8,25 +8,16 @@ import { } from "@shared/components/ui/popover"; import { Info } from "lucide-react"; -export const ServerInfo = () => { - const serverInfo = useServerStore((s) => s.serverInfo); +export const SystemInfo = () => { + const q = useQuery(getSystemInfo); - if (!serverInfo) return null; + if (!q.data) return null; return ( - @@ -34,38 +25,33 @@ export const ServerInfo = () => {
  • Sentinel version - {serverInfo.version} - {serverInfo.available_update ? ( - Outdated - ) : ( - Latest - )} + {q.data.info?.version}
  • Go version - {serverInfo.go_version} + {q.data.info?.goVersion}
  • SQLite version - {serverInfo.sqlite_version} + {q.data.info?.sqliteVersion}
  • OS - {serverInfo.os} + {q.data.info?.os}
  • Arch - {serverInfo.arch} + {q.data.info?.arch}
  • Build date - {serverInfo.build_date} + {q.data.info?.buildDate}
  • Commit hash - {serverInfo.commit_hash?.slice(0, 8) || "N/A"} + {q.data.info?.commitHash?.slice(0, 8) || "N/A"}
  • diff --git a/frontend/src/features/apiInfo/update-banner.tsx b/frontend/src/features/apiInfo/update-banner.tsx deleted file mode 100644 index 122b355..0000000 --- a/frontend/src/features/apiInfo/update-banner.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { - Button, - Dialog, - DialogClose, - DialogContent, - DialogFooter, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/shared/components/ui"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/shared/components/ui/alert-dialog"; -import Markdown from "react-markdown"; -import { LoaderCircleIcon, SparklesIcon } from "lucide-react"; -import remarkGfm from "remark-gfm"; -import { useServerStore } from "@/pages/dashboard/store/useServerStore"; - -export const UpdateBanner = () => { - const serverStore = useServerStore(); - - if (!serverStore.isUpdateAvailable) return null; - - return ( -
    - - - - - - - - 🚀 Available new update - - - - -
    - {/* Current version */} -
    - Current version:{" "} - - {serverStore.serverInfo?.version} - -
    - - {/* New version */} - - {serverStore.serverInfo?.available_update?.description} - -
    -
    - - - - - - - - - {serverStore.serverInfo?.available_update?.is_available_manual && ( - - - - - - - Are you sure? - - This will upgrade the server to the latest version. The - server will restart, and you will be redirected to the - dashboard after the upgrade is complete. -
    -
    - Please ensure you have a backup of your data before - proceeding. -
    -
    - - Cancel - serverStore.doUpgrade()}> - Let's do it! - - -
    -
    - )} -
    -
    -
    -
    - ); -}; diff --git a/frontend/src/features/apiInfo/updateBanner.tsx b/frontend/src/features/apiInfo/updateBanner.tsx new file mode 100644 index 0000000..38b2537 --- /dev/null +++ b/frontend/src/features/apiInfo/updateBanner.tsx @@ -0,0 +1,79 @@ +import { + Button, + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, + Badge, +} from "@/shared/components/ui"; +import Markdown from "react-markdown"; +import { SparklesIcon } from "lucide-react"; +import remarkGfm from "remark-gfm"; +import { useQuery } from "@connectrpc/connect-query"; +import { checkForUpdates } from "@/api/gen/sentinel/system/v1/service-SystemService_connectquery"; +import { + SidebarMenuButton, + SidebarMenuItem, +} from "@/shared/components/ui/sidebar"; + +export const UpdateBanner = () => { + const { data } = useQuery(checkForUpdates); + + if (!data || !data.info?.isAvailable) return null; + + return ( + + + + +
    + {" "} + Available update +
    + + {data.info?.details?.tagName} + +
    +
    +
    + + + + 🚀 Available new update + + + + +
    + {/* Current version */} +
    + Current version:{" "} + {data.info?.currentVersion} +
    + + {/* New version */} + + {data.info?.details?.description} + +
    +
    + + + + + + + + +
    +
    + ); +}; diff --git a/frontend/src/features/service/serviceForm.tsx b/frontend/src/features/service/serviceForm.tsx index 8c10305..8bc0af0 100644 --- a/frontend/src/features/service/serviceForm.tsx +++ b/frontend/src/features/service/serviceForm.tsx @@ -20,7 +20,7 @@ import { import { PlusIcon, TrashIcon } from "lucide-react"; import * as Yup from "yup"; import InputTag from "@/shared/components/ui/inputTag"; -import type { WebCreateUpdateServiceRequest } from "@/shared/types/model"; +import { toast } from "sonner"; export interface ServiceFormRef { submitForm: () => void; @@ -239,24 +239,21 @@ const HTTPForm = React.memo(