mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-13 12:50:05 +01:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
165d2a2af1 | ||
|
|
fc1c83ba18 | ||
|
|
2be0eb8180 | ||
|
|
81043419e1 | ||
|
|
f2ff08993a | ||
|
|
fabf456786 | ||
|
|
23c60d4ce8 | ||
|
|
c9bc09ae92 | ||
|
|
6ec8305962 | ||
|
|
8d34198477 | ||
|
|
f79224b831 | ||
|
|
cd8e3f2d86 | ||
|
|
ad0ec48027 | ||
|
|
7a3e56b3ff | ||
|
|
015044abb1 | ||
|
|
afd8c8ceb2 | ||
|
|
b358911b1b | ||
|
|
ad8141a497 | ||
|
|
9485b2ae4f | ||
|
|
cc8e8b38c7 | ||
|
|
586e51b8cc | ||
|
|
ea4c625269 | ||
|
|
b4c108a5be | ||
|
|
3a1ce33bca | ||
|
|
dbb7ec398e | ||
|
|
82847e5027 | ||
|
|
4314599427 | ||
|
|
9108ee8f8f | ||
|
|
1feb3e29e1 | ||
|
|
0e9ce98624 | ||
|
|
96aef81959 | ||
|
|
62232e18f3 | ||
|
|
515327153c | ||
|
|
5f2e885a8e | ||
|
|
01b48efba0 | ||
|
|
1c87566c5b | ||
|
|
ad00a3c45d | ||
|
|
158fdce780 | ||
|
|
c4fd344364 | ||
|
|
953e724ba0 | ||
|
|
256acfd686 | ||
|
|
1dd0a39aad | ||
|
|
7ef0605c2b | ||
|
|
8a92727b88 | ||
|
|
1f9920a04c | ||
|
|
5fd96ec40f | ||
|
|
bc72df3c9a | ||
|
|
56ea963b47 | ||
|
|
c9d5080d50 | ||
|
|
4adad29c4f | ||
|
|
13c9ba0ec0 | ||
|
|
e80d818e86 | ||
|
|
f1533eb325 | ||
|
|
334df457ce | ||
|
|
c478a8d10f | ||
|
|
3463b2cb33 | ||
|
|
325e162d39 | ||
|
|
30e91bfd88 | ||
|
|
12f2c5e1c5 | ||
|
|
7dc7d1a1f1 | ||
|
|
6a16eccce5 | ||
|
|
adb3c01382 | ||
|
|
35b42c6b19 | ||
|
|
ac12c48c27 | ||
|
|
04fc75a699 | ||
|
|
f4c41cfed7 | ||
|
|
d7a87c3add | ||
|
|
6b824769ba |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
terraform
|
||||
terraform-provider-autoglue
|
||||
sdk
|
||||
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -137,4 +137,6 @@ notes.txt
|
||||
|
||||
.terraform
|
||||
.terraform.lock*
|
||||
terraform.tfstate*
|
||||
terraform.tfstate*
|
||||
|
||||
ui/src/sdk
|
||||
@@ -1,7 +1,7 @@
|
||||
#################################
|
||||
# Builder: Go + Node in one
|
||||
#################################
|
||||
FROM golang:1.25.3-alpine@sha256:aee43c3ccbf24fdffb7295693b6e33b21e01baec1b2a55acc351fde345e9ec34 AS builder
|
||||
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash git ca-certificates tzdata \
|
||||
@@ -15,7 +15,11 @@ RUN npm i -g yarn pnpm
|
||||
WORKDIR /src
|
||||
|
||||
COPY . .
|
||||
RUN make clean && make swagger && make -j3 sdk-all && make ui && make build
|
||||
RUN make clean
|
||||
RUN make swagger
|
||||
RUN make sdk-ts-ui
|
||||
RUN make ui
|
||||
RUN make build
|
||||
|
||||
#################################
|
||||
# Runtime
|
||||
|
||||
14
Makefile
14
Makefile
@@ -23,13 +23,13 @@ MODULE_PATH ?= $(GIT_HOST)/$(GIT_USER)/$(BIN)
|
||||
|
||||
# SDK / module settings (Go)
|
||||
SDK_REPO ?= $(BIN)-sdk-go # repo name used for module path
|
||||
SDK_OUTDIR ?= sdk/go # output directory (inside repo)
|
||||
SDK_OUTDIR ?= ../autoglue-sdk-go # output directory (inside repo)
|
||||
SDK_PKG ?= ${BIN} # package name inside the SDK
|
||||
|
||||
UI_SSG_ROUTES ?= /,/login,/docs,/pricing
|
||||
|
||||
# Go versioning (go.mod uses major.minor; you’re on 1.25.3)
|
||||
GO_VERSION ?= 1.25.3
|
||||
# Go versioning (go.mod uses major.minor; you’re on 1.25.4)
|
||||
GO_VERSION ?= 1.25.4
|
||||
|
||||
# SDK / package settings (TypeScript)
|
||||
SDK_TS_OUTDIR ?= sdk/ts
|
||||
@@ -70,7 +70,7 @@ export GO_POST_PROCESS_FILE := gofmt -w
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# --- version metadata (ldflags) ---
|
||||
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||||
VERSION := $(shell git describe --tags --always 2>/dev/null || echo "dev")
|
||||
COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "none")
|
||||
DATE := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
BUILT_BY := $(shell whoami)
|
||||
@@ -103,7 +103,7 @@ DOCS_YAML := docs/swagger.yaml
|
||||
# Prefer git for speed; fall back to find. Exclude UI dir.
|
||||
#GO_SRCS := $(shell (git ls-files '*.go' ':!$(UI_DIR)/**' 2>/dev/null || find . -name '*.go' -not -path './$(UI_DIR)/*' -type f))
|
||||
GO_SRCS := $(shell ( \
|
||||
git ls-files '*.go' ':!$(UI_DIR)/**' ':!docs/**' ':!sdk/**' 2>/dev/null \
|
||||
git ls-files '*.go' ':!$(UI_DIR)/**' ':!docs/**' ':!sdk/**' ':!terraform-provider-autoglue/**' 2>/dev/null \
|
||||
|| find . -name '*.go' -not -path './$(UI_DIR)/*' -not -path './docs/*' -type f \
|
||||
))
|
||||
|
||||
@@ -247,7 +247,6 @@ TS_PROPS := -p npmName=$(SDK_TS_NPM_NAME) -p npmVersion=$(SDK_TS_NPM_VER) $
|
||||
# --- sdk generation (Go) ---
|
||||
sdk-go: $(DOCS_JSON) validate-spec check-tags ## Generate Go SDK + tidy module
|
||||
@echo ">> Generating Go SDK (module $(GIT_HOST_CLEAN)/$(GIT_USER_CLEAN)/$(SDK_REPO_CLEAN), Go $(GO_VERSION))..."
|
||||
@rm -rf "$(SDK_OUTDIR_CLEAN)"; mkdir -p "$(SDK_OUTDIR_CLEAN)"
|
||||
@$(call OGC_GENERATE,go,$(SDK_OUTDIR_CLEAN),--additional-properties=packageName=$(SDK_PKG_CLEAN) $(OAG_GIT_PROPS))
|
||||
@cd "$(SDK_OUTDIR_CLEAN)"; \
|
||||
$(GOCMD) mod edit -go=$(GO_VERSION); \
|
||||
@@ -311,6 +310,9 @@ doctor: ## Print environment diagnostics (shell, versions, generator availabilit
|
||||
$(OGC_BIN) version || true; \
|
||||
}
|
||||
|
||||
fetch-pgweb: ## Fetch PGWeb Binaries for embedding
|
||||
go run ./tools/pgweb_fetch.go
|
||||
|
||||
help: ## Show this help
|
||||
@grep -hE '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
51
cmd/db.go
Normal file
51
cmd/db.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var dbCmd = &cobra.Command{
|
||||
Use: "db",
|
||||
Short: "Database utilities",
|
||||
}
|
||||
|
||||
var dbPsqlCmd = &cobra.Command{
|
||||
Use: "psql",
|
||||
Short: "Open a psql session to the app database",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cfg.DbURL == "" {
|
||||
return errors.New("database.url is empty")
|
||||
}
|
||||
psql := "psql"
|
||||
if runtime.GOOS == "windows" {
|
||||
psql = "psql.exe"
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 72*time.Hour)
|
||||
defer cancel()
|
||||
|
||||
psqlCmd := exec.CommandContext(ctx, psql, cfg.DbURL)
|
||||
psqlCmd.Stdin, psqlCmd.Stdout, psqlCmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
fmt.Println("Launching psql…")
|
||||
return psqlCmd.Run()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
dbCmd.AddCommand(dbPsqlCmd)
|
||||
|
||||
rootCmd.AddCommand(dbCmd)
|
||||
}
|
||||
110
cmd/serve.go
110
cmd/serve.go
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/glueops/autoglue/internal/auth"
|
||||
"github.com/glueops/autoglue/internal/bg"
|
||||
"github.com/glueops/autoglue/internal/config"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -38,6 +39,8 @@ var serveCmd = &cobra.Command{
|
||||
log.Fatalf("failed to init background jobs: %v", err)
|
||||
}
|
||||
|
||||
rt.DB.Where("status IN ?", []string{"scheduled", "queued", "pending"}).Delete(&models.Job{})
|
||||
|
||||
// Start workers in background ONCE
|
||||
go func() {
|
||||
if err := jobs.Start(); err != nil {
|
||||
@@ -50,7 +53,7 @@ var serveCmd = &cobra.Command{
|
||||
{
|
||||
// schedule next 03:30 local time
|
||||
next := time.Now().Truncate(24 * time.Hour).Add(24 * time.Hour).Add(3*time.Hour + 30*time.Minute)
|
||||
_, _ = jobs.Enqueue(
|
||||
_, err = jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"archer_cleanup",
|
||||
@@ -58,10 +61,13 @@ var serveCmd = &cobra.Command{
|
||||
archer.WithScheduleTime(next),
|
||||
archer.WithMaxRetries(1),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to enqueue archer cleanup job: %v", err)
|
||||
}
|
||||
|
||||
// schedule next 03:45 local time
|
||||
next2 := time.Now().Truncate(24 * time.Hour).Add(24 * time.Hour).Add(3*time.Hour + 45*time.Minute)
|
||||
_, _ = jobs.Enqueue(
|
||||
_, err = jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"tokens_cleanup",
|
||||
@@ -69,46 +75,47 @@ var serveCmd = &cobra.Command{
|
||||
archer.WithScheduleTime(next2),
|
||||
archer.WithMaxRetries(1),
|
||||
)
|
||||
}
|
||||
|
||||
// Periodic scheduler
|
||||
schedCtx, schedCancel := context.WithCancel(context.Background())
|
||||
defer schedCancel()
|
||||
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
_, err := jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"bootstrap_bastion",
|
||||
bg.BastionBootstrapArgs{},
|
||||
archer.WithMaxRetries(3),
|
||||
// while debugging, avoid extra schedule delay:
|
||||
archer.WithScheduleTime(time.Now().Add(60*time.Second)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("failed to enqueue bootstrap_bastion: %v", err)
|
||||
}
|
||||
/*
|
||||
_, _ = jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"tokens_cleanup",
|
||||
bg.TokensCleanupArgs{},
|
||||
archer.WithMaxRetries(3),
|
||||
archer.WithScheduleTime(time.Now().Add(10*time.Second)),
|
||||
)
|
||||
*/
|
||||
case <-schedCtx.Done():
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("failed to enqueue token cleanup job: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"db_backup_s3",
|
||||
bg.DbBackupArgs{IntervalS: 3600},
|
||||
archer.WithMaxRetries(1),
|
||||
archer.WithScheduleTime(time.Now().Add(1*time.Hour)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to enqueue backup jobs: %v", err)
|
||||
}
|
||||
|
||||
_, err = jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"dns_reconcile",
|
||||
bg.DNSReconcileArgs{MaxDomains: 25, MaxRecords: 100, IntervalS: 10},
|
||||
archer.WithScheduleTime(time.Now().Add(5*time.Second)),
|
||||
archer.WithMaxRetries(1),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to enqueue dns reconcile: %v", err)
|
||||
}
|
||||
|
||||
_, err := jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"bootstrap_bastion",
|
||||
bg.BastionBootstrapArgs{IntervalS: 10},
|
||||
archer.WithMaxRetries(3),
|
||||
// while debugging, avoid extra schedule delay:
|
||||
archer.WithScheduleTime(time.Now().Add(60*time.Second)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("failed to enqueue bootstrap_bastion: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
_ = auth.Refresh(rt.DB, rt.Cfg.JWTPrivateEncKey)
|
||||
go func() {
|
||||
@@ -119,7 +126,26 @@ var serveCmd = &cobra.Command{
|
||||
}
|
||||
}()
|
||||
|
||||
r := api.NewRouter(rt.DB, jobs)
|
||||
r := api.NewRouter(rt.DB, jobs, nil)
|
||||
|
||||
if cfg.DBStudioEnabled {
|
||||
dbURL := cfg.DbURLRO
|
||||
if dbURL == "" {
|
||||
dbURL = cfg.DbURL
|
||||
}
|
||||
|
||||
studio, err := api.PgwebHandler(
|
||||
dbURL,
|
||||
"db-studio",
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to init db studio: %v", err)
|
||||
} else {
|
||||
r = api.NewRouter(rt.DB, jobs, studio)
|
||||
log.Printf("pgweb mounted at /db-studio/")
|
||||
}
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.Port)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
- postgres
|
||||
|
||||
mailpit:
|
||||
image: axllent/mailpit@sha256:6abc8e633df15eaf785cfcf38bae48e66f64beecdc03121e249d0f9ec15f0707
|
||||
image: axllent/mailpit@sha256:e22dce5b36f93c77082e204a3942fb6b283b7896e057458400a4c88344c3df68
|
||||
restart: always
|
||||
ports:
|
||||
- "1025:1025"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2107
docs/swagger.yaml
2107
docs/swagger.yaml
File diff suppressed because it is too large
Load Diff
76
go.mod
76
go.mod
@@ -1,24 +1,32 @@
|
||||
module github.com/glueops/autoglue
|
||||
|
||||
go 1.25.3
|
||||
go 1.25.4
|
||||
|
||||
require (
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.18
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.22
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.59.4
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0
|
||||
github.com/coreos/go-oidc/v3 v3.16.0
|
||||
github.com/dyaksa/archer v1.1.3
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/go-chi/chi/v5 v5.2.3
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-chi/httprate v0.15.0
|
||||
github.com/go-playground/validator/v10 v10.28.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/sosedoff/pgweb v0.16.2
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/oauth2 v0.32.0
|
||||
golang.org/x/crypto v0.44.0
|
||||
golang.org/x/oauth2 v0.33.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/datatypes v1.2.7
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
@@ -27,32 +35,74 @@ require (
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/BurntSushi/toml v1.1.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 // indirect
|
||||
github.com/aws/smithy-go v1.23.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.9 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
@@ -61,12 +111,20 @@ require (
|
||||
github.com/sv-tools/openapi v0.2.1 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/swaggo/swag v1.8.1 // indirect
|
||||
github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
github.com/zeebo/xxh3 v1.0.2 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gorm.io/driver/mysql v1.5.6 // indirect
|
||||
)
|
||||
|
||||
199
go.sum
199
go.sum
@@ -1,13 +1,89 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 h1:VauE2GcJNZFun2Och6tIT2zJZK1v6jxALQDA9BIji/E=
|
||||
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o=
|
||||
github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
|
||||
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
|
||||
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
|
||||
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3 h1:DHctwEM8P8iTXFxC/QK0MRjwEpWQeM9yzidCRjldUz0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.3/go.mod h1:xdCzcZEtnSTKVDOmUZs4l/j3pSV6rpo1WXl5ugNsL8Y=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.18 h1:RouG3AcF2fLFhw+Z0qbnuIl9HZ0Kh4E/U9sKwTMRpMI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.18/go.mod h1:aXZ13mSQC8S2VEHwGfL1COMuJ1Zty6pX5xU7hyqjvCg=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.19 h1:qdUtOw4JhZr2YcKO3g0ho/IcFXfXrrb8xlX05Y6EvSw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.19/go.mod h1:tMJ8bur01t8eEm0atLadkIIFA154OJ4JCKZeQ+o+R7k=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20 h1:/jWF4Wu90EhKCgjTdy1DGxcbcbNrjfBHvksEL79tfQc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.20/go.mod h1:95Hh1Tc5VYKL9NJ7tAkDcqeKt+MCXQB1hQZaRdJIZE0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.22 h1:hyIVGBHhQPaNP9D4BaVRwpjLMCwMMdAkHqB3gGMiykU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.22/go.mod h1:B9E2qHs3/YGfeQZ4jrIE/nPvqxtyafZrJ5EQiZBG6pk=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.23 h1:IQILcxVgMO2BVLaJ2aAv21dKWvE1MduNrbvuK43XL2Q=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.23/go.mod h1:JRodHszhVdh5TPUknxDzJzrMiznG+M+FfR3WSWKgCI8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24 h1:iJ2FmPT35EaIB0+kMa6TnQ+PwG5A1prEdAw+PsMzfHg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.24/go.mod h1:U91+DrfjAiXPDEGYhh/x29o4p0qHX5HDqG7y5VViv64=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13 h1:eg/WYAa12vqTphzIdWMzqYRVKKnCboVPRlvaybNCqPA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.13/go.mod h1:/FDdxWhz1486obGrKKC1HONd7krpk38LBt+dutLcN9k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4 h1:NvMjwvv8hpGUILarKw7Z4Q0w1H9anXKsesMxtw++MA4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.4/go.mod h1:455WPHSwaGj2waRSpQp7TsnpOnBfw8iDfPfbwl7KPJE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13 h1:zhBJXdhWIFZ1acfDYIhu4+LCzdUS2Vbcum7D01dXlHQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.13/go.mod h1:JaaOeCE368qn2Hzi3sEzY6FgAZVCIYcC2nwbro2QCh8=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.59.4 h1:KEszjusgJ2dAqE5nSJY+5AHBkakfah8Sx6Vk3pjgrq8=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.59.4/go.mod h1:TUbfYOisWZWyT2qjmlMh93ERw1Ry8G4q/yT2Q8TsDag=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0 h1:ef6gIJR+xv/JQWwpa5FYirzoQctfSJm7tuDe3SZsUf8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.0/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.1 h1:kKJk9r6iLMfCGy8RL9GWg3n9gUE1IpSwqYP3/5bdL1s=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.1/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2 h1:DhdbtDl4FdNlj31+xiRXANxEE+eC7n8JQz+/ilwQ8Uc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.90.2/go.mod h1:+wArOOrcHUevqdto9k1tKOF5++YTe9JEcPSc9Tx2ZSw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.2 h1:/p6MxkbQoCzaGQT3WO0JwG0FlQyG9RD8VmdmoKc5xqU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.2/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3 h1:NjShtS1t8r5LUfFVtFeI8xLAHQNTa7UI0VawXlrBMFQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.3/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6 h1:0dES42T2dhICCbVB3JSTTn7+Bz93wfJEK1b7jksZIyQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7 h1:gTsnx0xXNQ6SBbymoDvcoRHL+q4l/dAFsQuKfDWSaGc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.7/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0 h1:ZGDJVmlpPFiNFCb/I42nYVKUanJAdFUiSmUo/32AqPQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.0/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.1 h1:5sbIM57lHLaEaNWdIx23JH30LNBsSDkjN/QXGcRLAFc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2 h1:HK5ON3KmQV2HcAunnx4sKLB9aPf3gKGwVAf7xnx0QT0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.40.2/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk=
|
||||
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
@@ -16,12 +92,20 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
|
||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
|
||||
github.com/dyaksa/archer v1.1.3 h1:jfe51tSNzzscFpu+Vilm4SKb0Lnv6FR1yaGspjab4x4=
|
||||
github.com/dyaksa/archer v1.1.3/go.mod h1:IYSp67u14JHTNuvvy6gG1eaX2TPywXvfk1FiyZwVEK4=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
@@ -43,6 +127,15 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
@@ -50,6 +143,8 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
@@ -57,8 +152,9 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -71,16 +167,22 @@ github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -89,6 +191,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@@ -99,12 +204,23 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
||||
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
|
||||
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
|
||||
@@ -114,14 +230,30 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sosedoff/pgweb v0.16.2 h1:1F1CWlCLSEgSctMva+nYuUibdhyiCUzlXyU5MQUJbFM=
|
||||
github.com/sosedoff/pgweb v0.16.2/go.mod h1:ER7fsBddI3h7MQKO5RsUPi7Q/PWZYSKcI61kTp369Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
@@ -141,6 +273,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -160,38 +293,56 @@ github.com/swaggo/swag v1.8.1 h1:JuARzFX1Z1njbCGz+ZytBR15TFJwF2Q7fu8puJHhQYI=
|
||||
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE=
|
||||
github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 h1:yL0l/u242MzDP6D0B5vGC+wxm5WRY+alQZy+dJk3bFI=
|
||||
github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948/go.mod h1:a06d/M1pxWi51qiSrfGMHaEydtuXT06nha8N2aNQuXk=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -199,30 +350,32 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -49,6 +49,14 @@ func AuthMiddleware(db *gorm.DB, requireOrg bool) func(http.Handler) http.Handle
|
||||
} else if appKey := r.Header.Get("X-APP-KEY"); appKey != "" {
|
||||
secret := r.Header.Get("X-APP-SECRET")
|
||||
user = auth.ValidateAppKeyPair(appKey, secret, db)
|
||||
} else if c, err := r.Cookie("ag_jwt"); err == nil {
|
||||
tok := strings.TrimSpace(c.Value)
|
||||
if strings.HasPrefix(strings.ToLower(tok), "bearer ") {
|
||||
tok = tok[7:]
|
||||
}
|
||||
if tok != "" {
|
||||
user = auth.ValidateJWT(tok, db)
|
||||
}
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
|
||||
26
internal/api/mount_admin_routes.go
Normal file
26
internal/api/mount_admin_routes.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/bg"
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountAdminRoutes(r chi.Router, db *gorm.DB, jobs *bg.Jobs, authUser func(http.Handler) http.Handler) {
|
||||
r.Route("/admin", func(admin chi.Router) {
|
||||
admin.Route("/archer", func(archer chi.Router) {
|
||||
archer.Use(authUser)
|
||||
archer.Use(httpmiddleware.RequirePlatformAdmin())
|
||||
|
||||
archer.Get("/jobs", handlers.AdminListArcherJobs(db))
|
||||
archer.Post("/jobs", handlers.AdminEnqueueArcherJob(db, jobs))
|
||||
archer.Post("/jobs/{id}/retry", handlers.AdminRetryArcherJob(db))
|
||||
archer.Post("/jobs/{id}/cancel", handlers.AdminCancelArcherJob(db))
|
||||
archer.Get("/queues", handlers.AdminListArcherQueues(db))
|
||||
})
|
||||
})
|
||||
}
|
||||
20
internal/api/mount_annotation_routes.go
Normal file
20
internal/api/mount_annotation_routes.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountAnnotationRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/annotations", func(a chi.Router) {
|
||||
a.Use(authOrg)
|
||||
a.Get("/", handlers.ListAnnotations(db))
|
||||
a.Post("/", handlers.CreateAnnotation(db))
|
||||
a.Get("/{id}", handlers.GetAnnotation(db))
|
||||
a.Patch("/{id}", handlers.UpdateAnnotation(db))
|
||||
a.Delete("/{id}", handlers.DeleteAnnotation(db))
|
||||
})
|
||||
}
|
||||
37
internal/api/mount_api_routes.go
Normal file
37
internal/api/mount_api_routes.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/bg"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountAPIRoutes(r chi.Router, db *gorm.DB, jobs *bg.Jobs) {
|
||||
r.Route("/api", func(api chi.Router) {
|
||||
api.Route("/v1", func(v1 chi.Router) {
|
||||
authUser := httpmiddleware.AuthMiddleware(db, false)
|
||||
authOrg := httpmiddleware.AuthMiddleware(db, true)
|
||||
|
||||
// shared basics
|
||||
mountMetaRoutes(v1)
|
||||
mountAuthRoutes(v1, db)
|
||||
|
||||
// admin
|
||||
mountAdminRoutes(v1, db, jobs, authUser)
|
||||
|
||||
// user/org scoped
|
||||
mountMeRoutes(v1, db, authUser)
|
||||
mountOrgRoutes(v1, db, authUser, authOrg)
|
||||
|
||||
mountCredentialRoutes(v1, db, authOrg)
|
||||
mountSSHRoutes(v1, db, authOrg)
|
||||
mountServerRoutes(v1, db, authOrg)
|
||||
mountTaintRoutes(v1, db, authOrg)
|
||||
mountLabelRoutes(v1, db, authOrg)
|
||||
mountAnnotationRoutes(v1, db, authOrg)
|
||||
mountNodePoolRoutes(v1, db, authOrg)
|
||||
mountDNSRoutes(v1, db, authOrg)
|
||||
})
|
||||
})
|
||||
}
|
||||
16
internal/api/mount_auth_routes.go
Normal file
16
internal/api/mount_auth_routes.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountAuthRoutes(r chi.Router, db *gorm.DB) {
|
||||
r.Route("/auth", func(a chi.Router) {
|
||||
a.Post("/{provider}/start", handlers.AuthStart(db))
|
||||
a.Get("/{provider}/callback", handlers.AuthCallback(db))
|
||||
a.Post("/refresh", handlers.Refresh(db))
|
||||
a.Post("/logout", handlers.Logout(db))
|
||||
})
|
||||
}
|
||||
21
internal/api/mount_credential_routes.go
Normal file
21
internal/api/mount_credential_routes.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountCredentialRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/credentials", func(c chi.Router) {
|
||||
c.Use(authOrg)
|
||||
c.Get("/", handlers.ListCredentials(db))
|
||||
c.Post("/", handlers.CreateCredential(db))
|
||||
c.Get("/{id}", handlers.GetCredential(db))
|
||||
c.Patch("/{id}", handlers.UpdateCredential(db))
|
||||
c.Delete("/{id}", handlers.DeleteCredential(db))
|
||||
c.Post("/{id}/reveal", handlers.RevealCredential(db))
|
||||
})
|
||||
}
|
||||
53
internal/api/mount_db_studio.go
Normal file
53
internal/api/mount_db_studio.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
pgapi "github.com/sosedoff/pgweb/pkg/api"
|
||||
pgclient "github.com/sosedoff/pgweb/pkg/client"
|
||||
pgcmd "github.com/sosedoff/pgweb/pkg/command"
|
||||
)
|
||||
|
||||
func PgwebHandler(dbURL, prefix string, readonly bool) (http.Handler, error) {
|
||||
// Normalize prefix for pgweb:
|
||||
// - no leading slash
|
||||
// - always trailing slash if not empty
|
||||
prefix = strings.Trim(prefix, "/")
|
||||
if prefix != "" {
|
||||
prefix = prefix + "/"
|
||||
}
|
||||
|
||||
pgcmd.Opts = pgcmd.Options{
|
||||
URL: dbURL,
|
||||
Prefix: prefix, // e.g. "db-studio/"
|
||||
ReadOnly: readonly,
|
||||
Sessions: false,
|
||||
LockSession: true,
|
||||
SkipOpen: true,
|
||||
}
|
||||
|
||||
cli, err := pgclient.NewFromUrl(dbURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if readonly {
|
||||
_ = cli.SetReadOnlyMode()
|
||||
}
|
||||
|
||||
if err := cli.Test(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pgapi.DbClient = cli
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
g := gin.New()
|
||||
g.Use(gin.Recovery())
|
||||
|
||||
pgapi.SetupRoutes(g)
|
||||
pgapi.SetupMetrics(g)
|
||||
|
||||
return g, nil
|
||||
}
|
||||
26
internal/api/mount_dns_routes.go
Normal file
26
internal/api/mount_dns_routes.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountDNSRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/dns", func(d chi.Router) {
|
||||
d.Use(authOrg)
|
||||
|
||||
d.Get("/domains", handlers.ListDomains(db))
|
||||
d.Post("/domains", handlers.CreateDomain(db))
|
||||
d.Get("/domains/{id}", handlers.GetDomain(db))
|
||||
d.Patch("/domains/{id}", handlers.UpdateDomain(db))
|
||||
d.Delete("/domains/{id}", handlers.DeleteDomain(db))
|
||||
|
||||
d.Get("/domains/{domain_id}/records", handlers.ListRecordSets(db))
|
||||
d.Post("/domains/{domain_id}/records", handlers.CreateRecordSet(db))
|
||||
d.Patch("/records/{id}", handlers.UpdateRecordSet(db))
|
||||
d.Delete("/records/{id}", handlers.DeleteRecordSet(db))
|
||||
})
|
||||
}
|
||||
20
internal/api/mount_label_routes.go
Normal file
20
internal/api/mount_label_routes.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountLabelRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/labels", func(l chi.Router) {
|
||||
l.Use(authOrg)
|
||||
l.Get("/", handlers.ListLabels(db))
|
||||
l.Post("/", handlers.CreateLabel(db))
|
||||
l.Get("/{id}", handlers.GetLabel(db))
|
||||
l.Patch("/{id}", handlers.UpdateLabel(db))
|
||||
l.Delete("/{id}", handlers.DeleteLabel(db))
|
||||
})
|
||||
}
|
||||
22
internal/api/mount_me_routes.go
Normal file
22
internal/api/mount_me_routes.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountMeRoutes(r chi.Router, db *gorm.DB, authUser func(http.Handler) http.Handler) {
|
||||
r.Route("/me", func(me chi.Router) {
|
||||
me.Use(authUser)
|
||||
|
||||
me.Get("/", handlers.GetMe(db))
|
||||
me.Patch("/", handlers.UpdateMe(db))
|
||||
|
||||
me.Get("/api-keys", handlers.ListUserAPIKeys(db))
|
||||
me.Post("/api-keys", handlers.CreateUserAPIKey(db))
|
||||
me.Delete("/api-keys/{id}", handlers.DeleteUserAPIKey(db))
|
||||
})
|
||||
}
|
||||
13
internal/api/mount_meta_routes.go
Normal file
13
internal/api/mount_meta_routes.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func mountMetaRoutes(r chi.Router) {
|
||||
// Versioned JWKS for swagger
|
||||
r.Get("/.well-known/jwks.json", handlers.JWKSHandler)
|
||||
r.Get("/healthz", handlers.HealthCheck)
|
||||
r.Get("/version", handlers.Version)
|
||||
}
|
||||
40
internal/api/mount_node_pool_routes.go
Normal file
40
internal/api/mount_node_pool_routes.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountNodePoolRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/node-pools", func(n chi.Router) {
|
||||
n.Use(authOrg)
|
||||
n.Get("/", handlers.ListNodePools(db))
|
||||
n.Post("/", handlers.CreateNodePool(db))
|
||||
n.Get("/{id}", handlers.GetNodePool(db))
|
||||
n.Patch("/{id}", handlers.UpdateNodePool(db))
|
||||
n.Delete("/{id}", handlers.DeleteNodePool(db))
|
||||
|
||||
// Servers
|
||||
n.Get("/{id}/servers", handlers.ListNodePoolServers(db))
|
||||
n.Post("/{id}/servers", handlers.AttachNodePoolServers(db))
|
||||
n.Delete("/{id}/servers/{serverId}", handlers.DetachNodePoolServer(db))
|
||||
|
||||
// Taints
|
||||
n.Get("/{id}/taints", handlers.ListNodePoolTaints(db))
|
||||
n.Post("/{id}/taints", handlers.AttachNodePoolTaints(db))
|
||||
n.Delete("/{id}/taints/{taintId}", handlers.DetachNodePoolTaint(db))
|
||||
|
||||
// Labels
|
||||
n.Get("/{id}/labels", handlers.ListNodePoolLabels(db))
|
||||
n.Post("/{id}/labels", handlers.AttachNodePoolLabels(db))
|
||||
n.Delete("/{id}/labels/{labelId}", handlers.DetachNodePoolLabel(db))
|
||||
|
||||
// Annotations
|
||||
n.Get("/{id}/annotations", handlers.ListNodePoolAnnotations(db))
|
||||
n.Post("/{id}/annotations", handlers.AttachNodePoolAnnotations(db))
|
||||
n.Delete("/{id}/annotations/{annotationId}", handlers.DetachNodePoolAnnotation(db))
|
||||
})
|
||||
}
|
||||
35
internal/api/mount_org_routes.go
Normal file
35
internal/api/mount_org_routes.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountOrgRoutes(r chi.Router, db *gorm.DB, authUser, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/orgs", func(o chi.Router) {
|
||||
o.Use(authUser)
|
||||
o.Get("/", handlers.ListMyOrgs(db))
|
||||
o.Post("/", handlers.CreateOrg(db))
|
||||
|
||||
o.Group(func(og chi.Router) {
|
||||
og.Use(authOrg)
|
||||
|
||||
og.Get("/{id}", handlers.GetOrg(db))
|
||||
og.Patch("/{id}", handlers.UpdateOrg(db))
|
||||
og.Delete("/{id}", handlers.DeleteOrg(db))
|
||||
|
||||
// members
|
||||
og.Get("/{id}/members", handlers.ListMembers(db))
|
||||
og.Post("/{id}/members", handlers.AddOrUpdateMember(db))
|
||||
og.Delete("/{id}/members/{user_id}", handlers.RemoveMember(db))
|
||||
|
||||
// org-scoped key/secret pair
|
||||
og.Get("/{id}/api-keys", handlers.ListOrgKeys(db))
|
||||
og.Post("/{id}/api-keys", handlers.CreateOrgKey(db))
|
||||
og.Delete("/{id}/api-keys/{key_id}", handlers.DeleteOrgKey(db))
|
||||
})
|
||||
})
|
||||
}
|
||||
24
internal/api/mount_pprof_routes.go
Normal file
24
internal/api/mount_pprof_routes.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
httpPprof "net/http/pprof"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func mountPprofRoutes(r chi.Router) {
|
||||
r.Route("/debug/pprof", func(pr chi.Router) {
|
||||
pr.Get("/", httpPprof.Index)
|
||||
pr.Get("/cmdline", httpPprof.Cmdline)
|
||||
pr.Get("/profile", httpPprof.Profile)
|
||||
pr.Get("/symbol", httpPprof.Symbol)
|
||||
pr.Get("/trace", httpPprof.Trace)
|
||||
|
||||
pr.Handle("/allocs", httpPprof.Handler("allocs"))
|
||||
pr.Handle("/block", httpPprof.Handler("block"))
|
||||
pr.Handle("/goroutine", httpPprof.Handler("goroutine"))
|
||||
pr.Handle("/heap", httpPprof.Handler("heap"))
|
||||
pr.Handle("/mutex", httpPprof.Handler("mutex"))
|
||||
pr.Handle("/threadcreate", httpPprof.Handler("threadcreate"))
|
||||
})
|
||||
}
|
||||
21
internal/api/mount_server_routes.go
Normal file
21
internal/api/mount_server_routes.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountServerRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/servers", func(s chi.Router) {
|
||||
s.Use(authOrg)
|
||||
s.Get("/", handlers.ListServers(db))
|
||||
s.Post("/", handlers.CreateServer(db))
|
||||
s.Get("/{id}", handlers.GetServer(db))
|
||||
s.Patch("/{id}", handlers.UpdateServer(db))
|
||||
s.Delete("/{id}", handlers.DeleteServer(db))
|
||||
s.Post("/{id}/reset-hostkey", handlers.ResetServerHostKey(db))
|
||||
})
|
||||
}
|
||||
20
internal/api/mount_ssh_routes.go
Normal file
20
internal/api/mount_ssh_routes.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountSSHRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/ssh", func(s chi.Router) {
|
||||
s.Use(authOrg)
|
||||
s.Get("/", handlers.ListPublicSshKeys(db))
|
||||
s.Post("/", handlers.CreateSSHKey(db))
|
||||
s.Get("/{id}", handlers.GetSSHKey(db))
|
||||
s.Delete("/{id}", handlers.DeleteSSHKey(db))
|
||||
s.Get("/{id}/download", handlers.DownloadSSHKey(db))
|
||||
})
|
||||
}
|
||||
15
internal/api/mount_swagger_routes.go
Normal file
15
internal/api/mount_swagger_routes.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/glueops/autoglue/docs"
|
||||
"github.com/go-chi/chi/v5"
|
||||
httpSwagger "github.com/swaggo/http-swagger/v2"
|
||||
)
|
||||
|
||||
func mountSwaggerRoutes(r chi.Router) {
|
||||
r.Get("/swagger/*", httpSwagger.Handler(
|
||||
httpSwagger.URL("swagger.json"),
|
||||
))
|
||||
r.Get("/swagger/swagger.json", serveSwaggerFromEmbed(docs.SwaggerJSON, "application/json"))
|
||||
r.Get("/swagger/swagger.yaml", serveSwaggerFromEmbed(docs.SwaggerYAML, "application/x-yaml"))
|
||||
}
|
||||
20
internal/api/mount_taint_routes.go
Normal file
20
internal/api/mount_taint_routes.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue/internal/handlers"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func mountTaintRoutes(r chi.Router, db *gorm.DB, authOrg func(http.Handler) http.Handler) {
|
||||
r.Route("/taints", func(t chi.Router) {
|
||||
t.Use(authOrg)
|
||||
t.Get("/", handlers.ListTaints(db))
|
||||
t.Post("/", handlers.CreateTaint(db))
|
||||
t.Get("/{id}", handlers.GetTaint(db))
|
||||
t.Patch("/{id}", handlers.UpdateTaint(db))
|
||||
t.Delete("/{id}", handlers.DeleteTaint(db))
|
||||
})
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func SecurityHeaders(next http.Handler) http.Handler {
|
||||
// Google font files
|
||||
"font-src 'self' data: https://fonts.gstatic.com",
|
||||
// HMR connections
|
||||
"connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080",
|
||||
"connect-src 'self' http://localhost:5173 ws://localhost:5173 ws://localhost:8080 https://api.github.com",
|
||||
"frame-ancestors 'none'",
|
||||
}, "; "))
|
||||
} else {
|
||||
@@ -49,11 +49,11 @@ func SecurityHeaders(next http.Handler) http.Handler {
|
||||
"default-src 'self'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"script-src 'self'",
|
||||
"script-src 'self' 'unsafe-inline'",
|
||||
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self' data: https://fonts.gstatic.com",
|
||||
"connect-src 'self'",
|
||||
"connect-src 'self' ws://localhost:8080 https://api.github.com",
|
||||
"frame-ancestors 'none'",
|
||||
}, "; "))
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
httpPprof "net/http/pprof"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/docs"
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/bg"
|
||||
"github.com/glueops/autoglue/internal/config"
|
||||
@@ -23,11 +22,9 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
httpSwagger "github.com/swaggo/http-swagger/v2"
|
||||
)
|
||||
|
||||
func NewRouter(db *gorm.DB, jobs *bg.Jobs) http.Handler {
|
||||
func NewRouter(db *gorm.DB, jobs *bg.Jobs, studio http.Handler) http.Handler {
|
||||
zerolog.TimeFieldFormat = time.RFC3339
|
||||
|
||||
l := log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "15:04:05"})
|
||||
@@ -59,145 +56,44 @@ func NewRouter(db *gorm.DB, jobs *bg.Jobs) http.Handler {
|
||||
MaxAge: 600,
|
||||
}))
|
||||
|
||||
r.Use(middleware.AllowContentType("application/json"))
|
||||
r.Use(middleware.Maybe(
|
||||
middleware.AllowContentType("application/json"),
|
||||
func(r *http.Request) bool {
|
||||
// return true => run AllowContentType
|
||||
// return false => skip AllowContentType for this request
|
||||
return !strings.HasPrefix(r.URL.Path, "/db-studio")
|
||||
}))
|
||||
//r.Use(middleware.AllowContentType("application/json"))
|
||||
|
||||
// Unversioned, non-auth endpoints
|
||||
r.Get("/.well-known/jwks.json", handlers.JWKSHandler)
|
||||
r.Route("/api", func(api chi.Router) {
|
||||
api.Route("/v1", func(v1 chi.Router) {
|
||||
|
||||
// Versioned API
|
||||
mountAPIRoutes(r, db, jobs)
|
||||
|
||||
// Optional DB studio
|
||||
if studio != nil {
|
||||
r.Group(func(gr chi.Router) {
|
||||
authUser := httpmiddleware.AuthMiddleware(db, false)
|
||||
authOrg := httpmiddleware.AuthMiddleware(db, true)
|
||||
|
||||
// Also serving a versioned JWKS for swagger, which uses BasePath
|
||||
v1.Get("/.well-known/jwks.json", handlers.JWKSHandler)
|
||||
|
||||
v1.Get("/healthz", handlers.HealthCheck)
|
||||
|
||||
v1.Route("/auth", func(a chi.Router) {
|
||||
a.Post("/{provider}/start", handlers.AuthStart(db))
|
||||
a.Get("/{provider}/callback", handlers.AuthCallback(db))
|
||||
a.Post("/refresh", handlers.Refresh(db))
|
||||
a.Post("/logout", handlers.Logout(db))
|
||||
})
|
||||
|
||||
v1.Route("/admin/archer", func(a chi.Router) {
|
||||
a.Use(authUser)
|
||||
a.Use(httpmiddleware.RequirePlatformAdmin())
|
||||
|
||||
a.Get("/jobs", handlers.AdminListArcherJobs(db))
|
||||
a.Post("/jobs", handlers.AdminEnqueueArcherJob(db, jobs))
|
||||
a.Post("/jobs/{id}/retry", handlers.AdminRetryArcherJob(db))
|
||||
a.Post("/jobs/{id}/cancel", handlers.AdminCancelArcherJob(db))
|
||||
a.Get("/queues", handlers.AdminListArcherQueues(db))
|
||||
})
|
||||
|
||||
v1.Route("/me", func(me chi.Router) {
|
||||
me.Use(authUser)
|
||||
|
||||
me.Get("/", handlers.GetMe(db))
|
||||
me.Patch("/", handlers.UpdateMe(db))
|
||||
|
||||
me.Get("/api-keys", handlers.ListUserAPIKeys(db))
|
||||
me.Post("/api-keys", handlers.CreateUserAPIKey(db))
|
||||
me.Delete("/api-keys/{id}", handlers.DeleteUserAPIKey(db))
|
||||
})
|
||||
|
||||
v1.Route("/orgs", func(o chi.Router) {
|
||||
o.Use(authUser)
|
||||
o.Get("/", handlers.ListMyOrgs(db))
|
||||
o.Post("/", handlers.CreateOrg(db))
|
||||
|
||||
o.Group(func(og chi.Router) {
|
||||
og.Use(authOrg)
|
||||
og.Get("/{id}", handlers.GetOrg(db))
|
||||
og.Patch("/{id}", handlers.UpdateOrg(db))
|
||||
og.Delete("/{id}", handlers.DeleteOrg(db))
|
||||
|
||||
// members
|
||||
og.Get("/{id}/members", handlers.ListMembers(db))
|
||||
og.Post("/{id}/members", handlers.AddOrUpdateMember(db))
|
||||
og.Delete("/{id}/members/{user_id}", handlers.RemoveMember(db))
|
||||
|
||||
// org-scoped key/secret pair
|
||||
og.Get("/{id}/api-keys", handlers.ListOrgKeys(db))
|
||||
og.Post("/{id}/api-keys", handlers.CreateOrgKey(db))
|
||||
og.Delete("/{id}/api-keys/{key_id}", handlers.DeleteOrgKey(db))
|
||||
})
|
||||
})
|
||||
|
||||
v1.Route("/ssh", func(s chi.Router) {
|
||||
s.Use(authOrg)
|
||||
s.Get("/", handlers.ListPublicSshKeys(db))
|
||||
s.Post("/", handlers.CreateSSHKey(db))
|
||||
s.Get("/{id}", handlers.GetSSHKey(db))
|
||||
s.Delete("/{id}", handlers.DeleteSSHKey(db))
|
||||
s.Get("/{id}/download", handlers.DownloadSSHKey(db))
|
||||
})
|
||||
|
||||
v1.Route("/servers", func(s chi.Router) {
|
||||
s.Use(authOrg)
|
||||
s.Get("/", handlers.ListServers(db))
|
||||
s.Post("/", handlers.CreateServer(db))
|
||||
s.Get("/{id}", handlers.GetServer(db))
|
||||
s.Patch("/{id}", handlers.UpdateServer(db))
|
||||
s.Delete("/{id}", handlers.DeleteServer(db))
|
||||
})
|
||||
|
||||
v1.Route("/taints", func(s chi.Router) {
|
||||
s.Use(authOrg)
|
||||
s.Get("/", handlers.ListTaints(db))
|
||||
s.Post("/", handlers.CreateTaint(db))
|
||||
s.Get("/{id}", handlers.GetTaint(db))
|
||||
s.Patch("/{id}", handlers.UpdateTaint(db))
|
||||
s.Delete("/{id}", handlers.DeleteTaint(db))
|
||||
})
|
||||
|
||||
v1.Route("/labels", func(l chi.Router) {
|
||||
l.Use(authOrg)
|
||||
l.Get("/", handlers.ListLabels(db))
|
||||
l.Post("/", handlers.CreateLabel(db))
|
||||
l.Get("/{id}", handlers.GetLabel(db))
|
||||
l.Patch("/{id}", handlers.UpdateLabel(db))
|
||||
l.Delete("/{id}", handlers.DeleteLabel(db))
|
||||
})
|
||||
|
||||
v1.Route("/annotations", func(a chi.Router) {
|
||||
a.Use(authOrg)
|
||||
a.Get("/", handlers.ListAnnotations(db))
|
||||
a.Post("/", handlers.CreateAnnotation(db))
|
||||
a.Get("/{id}", handlers.GetAnnotation(db))
|
||||
a.Patch("/{id}", handlers.UpdateAnnotation(db))
|
||||
a.Delete("/{id}", handlers.DeleteAnnotation(db))
|
||||
})
|
||||
adminOnly := httpmiddleware.RequirePlatformAdmin()
|
||||
gr.Use(authUser, adminOnly)
|
||||
gr.Mount("/db-studio", studio)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// pprof
|
||||
if config.IsDebug() {
|
||||
r.Route("/debug/pprof", func(pr chi.Router) {
|
||||
pr.Get("/", httpPprof.Index)
|
||||
pr.Get("/cmdline", httpPprof.Cmdline)
|
||||
pr.Get("/profile", httpPprof.Profile)
|
||||
pr.Get("/symbol", httpPprof.Symbol)
|
||||
pr.Get("/trace", httpPprof.Trace)
|
||||
|
||||
pr.Handle("/allocs", httpPprof.Handler("allocs"))
|
||||
pr.Handle("/block", httpPprof.Handler("block"))
|
||||
pr.Handle("/goroutine", httpPprof.Handler("goroutine"))
|
||||
pr.Handle("/heap", httpPprof.Handler("heap"))
|
||||
pr.Handle("/mutex", httpPprof.Handler("mutex"))
|
||||
pr.Handle("/threadcreate", httpPprof.Handler("threadcreate"))
|
||||
})
|
||||
mountPprofRoutes(r)
|
||||
}
|
||||
|
||||
// Swagger
|
||||
if config.IsSwaggerEnabled() {
|
||||
r.Get("/swagger/*", httpSwagger.Handler(
|
||||
httpSwagger.URL("swagger.json"),
|
||||
))
|
||||
r.Get("/swagger/swagger.json", serveSwaggerFromEmbed(docs.SwaggerJSON, "application/json"))
|
||||
r.Get("/swagger/swagger.yaml", serveSwaggerFromEmbed(docs.SwaggerYAML, "application/x-yaml"))
|
||||
mountSwaggerRoutes(r)
|
||||
}
|
||||
|
||||
// UI dev/prod
|
||||
if config.IsUIDev() {
|
||||
fmt.Println("Running in development mode")
|
||||
// Dev: isolate proxy from chi middlewares so WS upgrade can hijack.
|
||||
proxy, err := web.DevProxy("http://localhost:5173")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("dev proxy init failed")
|
||||
@@ -205,22 +101,20 @@ func NewRouter(db *gorm.DB, jobs *bg.Jobs) http.Handler {
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
// Send API/Swagger/pprof to chi
|
||||
mux.Handle("/api/", r)
|
||||
mux.Handle("/api", r)
|
||||
mux.Handle("/swagger/", r)
|
||||
mux.Handle("/db-studio/", r)
|
||||
mux.Handle("/debug/pprof/", r)
|
||||
// Everything else (/, /brand-preview, assets) → proxy (no middlewares)
|
||||
mux.Handle("/", proxy)
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
fmt.Println("Running in production mode")
|
||||
if h, err := web.SPAHandler(); err == nil {
|
||||
r.NotFound(h.ServeHTTP)
|
||||
} else {
|
||||
fmt.Println("Running in production mode")
|
||||
if h, err := web.SPAHandler(); err == nil {
|
||||
r.NotFound(h.ServeHTTP)
|
||||
} else {
|
||||
log.Error().Err(err).Msg("spa handler init failed")
|
||||
}
|
||||
log.Error().Err(err).Msg("spa handler init failed")
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
@@ -38,7 +38,13 @@ func NewRuntime() *Runtime {
|
||||
&models.Taint{},
|
||||
&models.Label{},
|
||||
&models.Annotation{},
|
||||
&models.NodePool{},
|
||||
&models.Cluster{},
|
||||
&models.Credential{},
|
||||
&models.Domain{},
|
||||
&models.RecordSet{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error initializing database: %v", err)
|
||||
}
|
||||
|
||||
264
internal/bg/backup_s3.go
Normal file
264
internal/bg/backup_s3.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package bg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/dyaksa/archer"
|
||||
"github.com/dyaksa/archer/job"
|
||||
"github.com/glueops/autoglue/internal/config"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DbBackupArgs struct {
|
||||
IntervalS int `json:"interval_seconds,omitempty"`
|
||||
}
|
||||
|
||||
type s3Scope struct {
|
||||
Service string `json:"service"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type encAWS struct {
|
||||
AccessKeyID string `json:"access_key_id"`
|
||||
SecretAccessKey string `json:"secret_access_key"`
|
||||
}
|
||||
|
||||
func DbBackupWorker(db *gorm.DB, jobs *Jobs) archer.WorkerFn {
|
||||
return func(ctx context.Context, j job.Job) (any, error) {
|
||||
args := DbBackupArgs{IntervalS: 3600}
|
||||
_ = j.ParseArguments(&args)
|
||||
|
||||
if args.IntervalS <= 0 {
|
||||
args.IntervalS = 3600
|
||||
}
|
||||
|
||||
if err := DbBackup(ctx, db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queue := j.QueueName
|
||||
if strings.TrimSpace(queue) == "" {
|
||||
queue = "db_backup_s3"
|
||||
}
|
||||
|
||||
next := time.Now().Add(time.Duration(args.IntervalS) * time.Second)
|
||||
|
||||
payload := DbBackupArgs{}
|
||||
|
||||
opts := []archer.FnOptions{
|
||||
archer.WithScheduleTime(next),
|
||||
archer.WithMaxRetries(1),
|
||||
}
|
||||
|
||||
if _, err := jobs.Enqueue(ctx, uuid.NewString(), queue, payload, opts...); err != nil {
|
||||
log.Error().Err(err).Str("queue", queue).Time("next", next).Msg("failed to enqueue next db backup")
|
||||
} else {
|
||||
log.Info().Str("queue", queue).Time("next", next).Msg("scheduled next db backup")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func DbBackup(ctx context.Context, db *gorm.DB) error {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("load config: %w", err)
|
||||
}
|
||||
|
||||
cred, sc, err := loadS3Credential(ctx, db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load credential: %w", err)
|
||||
}
|
||||
|
||||
ak, sk, err := decryptAwsAccessKeys(ctx, db, cred)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt aws keys: %w", err)
|
||||
}
|
||||
|
||||
region := sc.Region
|
||||
|
||||
if strings.TrimSpace(region) == "" {
|
||||
region = cred.Region
|
||||
if strings.TrimSpace(region) == "" {
|
||||
region = "us-west-1"
|
||||
}
|
||||
}
|
||||
|
||||
bucket := strings.ToLower(fmt.Sprintf("%s-autoglue-backups-%s", cred.OrganizationID, region))
|
||||
|
||||
s3cli, err := makeS3Client(ctx, ak, sk, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureBucket(ctx, s3cli, bucket, region); err != nil {
|
||||
return fmt.Errorf("ensure bucket: %w", err)
|
||||
}
|
||||
|
||||
tmpDir := os.TempDir()
|
||||
now := time.Now().UTC()
|
||||
key := fmt.Sprintf("%04d/%02d/%02d/backup-%02d.sql", now.Year(), now.Month(), now.Day(), now.Hour())
|
||||
outPath := filepath.Join(tmpDir, "autoglue-backup-"+now.Format("20060102T150405Z")+".sql")
|
||||
|
||||
if err := runPgDump(ctx, cfg.DbURL, outPath); err != nil {
|
||||
return fmt.Errorf("pg_dump: %w", err)
|
||||
}
|
||||
defer os.Remove(outPath)
|
||||
|
||||
if err := uploadFileToS3(ctx, s3cli, bucket, key, outPath); err != nil {
|
||||
return fmt.Errorf("s3 upload: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Str("bucket", bucket).Str("key", key).Msg("backup uploaded")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Helpers
|
||||
|
||||
func loadS3Credential(ctx context.Context, db *gorm.DB) (models.Credential, s3Scope, error) {
|
||||
var c models.Credential
|
||||
err := db.
|
||||
WithContext(ctx).
|
||||
Where("provider = ? AND kind = ? AND scope_kind = ?", "aws", "aws_access_key", "service").
|
||||
Where("scope ->> 'service' = ?", "s3").
|
||||
Order("created_at DESC").
|
||||
First(&c).Error
|
||||
if err != nil {
|
||||
return models.Credential{}, s3Scope{}, fmt.Errorf("load credential: %w", err)
|
||||
}
|
||||
|
||||
var sc s3Scope
|
||||
_ = json.Unmarshal(c.Scope, &sc)
|
||||
return c, sc, nil
|
||||
}
|
||||
|
||||
func decryptAwsAccessKeys(ctx context.Context, db *gorm.DB, c models.Credential) (string, string, error) {
|
||||
plain, err := utils.DecryptForOrg(c.OrganizationID, c.EncryptedData, c.IV, c.Tag, db)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var payload encAWS
|
||||
if err := json.Unmarshal([]byte(plain), &payload); err != nil {
|
||||
return "", "", fmt.Errorf("parse decrypted payload: %w", err)
|
||||
}
|
||||
|
||||
if payload.AccessKeyID == "" || payload.SecretAccessKey == "" {
|
||||
return "", "", errors.New("decrypted payload missing keys")
|
||||
}
|
||||
return payload.AccessKeyID, payload.SecretAccessKey, nil
|
||||
}
|
||||
|
||||
func makeS3Client(ctx context.Context, accessKey, secret, region string) (*s3.Client, error) {
|
||||
staticCredentialsProvider := credentials.NewStaticCredentialsProvider(accessKey, secret, "")
|
||||
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithCredentialsProvider(staticCredentialsProvider), awsconfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("aws config: %w", err)
|
||||
}
|
||||
return s3.NewFromConfig(cfg), nil
|
||||
}
|
||||
|
||||
func ensureBucket(ctx context.Context, s3cli *s3.Client, bucket, region string) error {
|
||||
_, err := s3cli.HeadBucket(ctx, &s3.HeadBucketInput{Bucket: aws.String(bucket)})
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if out, err := s3cli.GetBucketLocation(ctx, &s3.GetBucketLocationInput{Bucket: aws.String(bucket)}); err == nil {
|
||||
existing := string(out.LocationConstraint)
|
||||
if existing == "" {
|
||||
existing = "us-east-1"
|
||||
}
|
||||
if existing != region {
|
||||
return fmt.Errorf("bucket %q already exists in region %q (requested %q)", bucket, existing, region)
|
||||
}
|
||||
}
|
||||
|
||||
// Create; LocationConstraint except us-east-1
|
||||
in := &s3.CreateBucketInput{Bucket: aws.String(bucket)}
|
||||
if region != "us-east-1" {
|
||||
in.CreateBucketConfiguration = &s3types.CreateBucketConfiguration{
|
||||
LocationConstraint: s3types.BucketLocationConstraint(region),
|
||||
}
|
||||
}
|
||||
if _, err := s3cli.CreateBucket(ctx, in); err != nil {
|
||||
return fmt.Errorf("create bucket: %w", err)
|
||||
}
|
||||
|
||||
// default SSE (best-effort)
|
||||
_, _ = s3cli.PutBucketEncryption(ctx, &s3.PutBucketEncryptionInput{
|
||||
Bucket: aws.String(bucket),
|
||||
ServerSideEncryptionConfiguration: &s3types.ServerSideEncryptionConfiguration{
|
||||
Rules: []s3types.ServerSideEncryptionRule{
|
||||
{ApplyServerSideEncryptionByDefault: &s3types.ServerSideEncryptionByDefault{
|
||||
SSEAlgorithm: s3types.ServerSideEncryptionAes256,
|
||||
}},
|
||||
},
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPgDump(ctx context.Context, dbURL, outPath string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"--no-owner",
|
||||
"--no-privileges",
|
||||
"--format=plain",
|
||||
"--file", outPath,
|
||||
dbURL,
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "pg_dump", args...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("pg_dump failed: %v | %s", err, stderr.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uploadFileToS3(ctx context.Context, s3cli *s3.Client, bucket, key, path string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
info, _ := f.Stat()
|
||||
_, err = s3cli.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
Body: f,
|
||||
ContentLength: aws.Int64(info.Size()),
|
||||
ContentType: aws.String(mime.TypeByExtension(filepath.Ext(path))),
|
||||
ServerSideEncryption: s3types.ServerSideEncryptionAes256,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -2,8 +2,8 @@ package bg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -13,13 +13,16 @@ import (
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ----- Public types -----
|
||||
|
||||
type BastionBootstrapArgs struct{}
|
||||
type BastionBootstrapArgs struct {
|
||||
IntervalS int `json:"interval_seconds,omitempty"`
|
||||
}
|
||||
|
||||
type BastionBootstrapFailure struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
@@ -39,11 +42,17 @@ type BastionBootstrapResult struct {
|
||||
|
||||
// ----- Worker -----
|
||||
|
||||
func BastionBootstrapWorker(db *gorm.DB) archer.WorkerFn {
|
||||
func BastionBootstrapWorker(db *gorm.DB, jobs *Jobs) archer.WorkerFn {
|
||||
return func(ctx context.Context, j job.Job) (any, error) {
|
||||
args := BastionBootstrapArgs{IntervalS: 120}
|
||||
jobID := j.ID
|
||||
start := time.Now()
|
||||
|
||||
_ = j.ParseArguments(&args)
|
||||
if args.IntervalS <= 0 {
|
||||
args.IntervalS = 120
|
||||
}
|
||||
|
||||
var servers []models.Server
|
||||
if err := db.
|
||||
Preload("SshKey").
|
||||
@@ -105,7 +114,7 @@ func BastionBootstrapWorker(db *gorm.DB) archer.WorkerFn {
|
||||
// 4) SSH + install docker
|
||||
host := net.JoinHostPort(*s.PublicIPAddress, "22")
|
||||
runCtx, cancel := context.WithTimeout(ctx, perHostTimeout)
|
||||
out, err := sshInstallDockerWithOutput(runCtx, host, s.SSHUser, []byte(privKey))
|
||||
out, err := sshInstallDockerWithOutput(runCtx, db, s, host, s.SSHUser, []byte(privKey))
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
@@ -147,9 +156,17 @@ func BastionBootstrapWorker(db *gorm.DB) archer.WorkerFn {
|
||||
Failures: failures,
|
||||
}
|
||||
|
||||
// log.Printf("[bastion] level=INFO job=%s step=finish processed=%d ready=%d failed=%d elapsed_ms=%d",
|
||||
// jobID, proc, ok, fail, res.ElapsedMs)
|
||||
log.Debug().Int("processed", proc).Int("ready", ok).Int("failed", fail).Msg("[bastion] reconcile tick ok")
|
||||
|
||||
next := time.Now().Add(time.Duration(args.IntervalS) * time.Second)
|
||||
_, _ = jobs.Enqueue(
|
||||
ctx,
|
||||
uuid.NewString(),
|
||||
"bootstrap_bastion",
|
||||
args,
|
||||
archer.WithScheduleTime(next),
|
||||
archer.WithMaxRetries(1),
|
||||
)
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
@@ -187,16 +204,24 @@ func logHostInfo(jobID string, s *models.Server, step, msg string, kv ...any) {
|
||||
// ----- SSH & command execution -----
|
||||
|
||||
// returns combined stdout/stderr so caller can log it on error
|
||||
func sshInstallDockerWithOutput(ctx context.Context, host, user string, privateKeyPEM []byte) (string, error) {
|
||||
func sshInstallDockerWithOutput(
|
||||
ctx context.Context,
|
||||
db *gorm.DB,
|
||||
s *models.Server,
|
||||
host, user string,
|
||||
privateKeyPEM []byte,
|
||||
) (string, error) {
|
||||
signer, err := ssh.ParsePrivateKey(privateKeyPEM)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse private key: %w", err)
|
||||
}
|
||||
|
||||
hkcb := makeDBHostKeyCallback(db, s)
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // TODO: known_hosts verification
|
||||
HostKeyCallback: hkcb,
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
@@ -225,19 +250,242 @@ func sshInstallDockerWithOutput(ctx context.Context, host, user string, privateK
|
||||
script := `
|
||||
set -euxo pipefail
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
# ----------- toggles (set to 0 to skip) -----------
|
||||
: "${BASELINE_PKGS:=1}"
|
||||
: "${INSTALL_DOCKER:=1}"
|
||||
: "${SSH_HARDEN:=1}"
|
||||
: "${FIREWALL:=1}"
|
||||
: "${AUTO_UPDATES:=1}"
|
||||
: "${TIME_SYNC:=1}"
|
||||
: "${FAIL2BAN:=1}"
|
||||
: "${BANNER:=1}"
|
||||
|
||||
# ----------- helpers -----------
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
pm=""
|
||||
if have apt-get; then pm="apt"
|
||||
elif have dnf; then pm="dnf"
|
||||
elif have yum; then pm="yum"
|
||||
elif have zypper; then pm="zypper"
|
||||
elif have apk; then pm="apk"
|
||||
fi
|
||||
|
||||
# try to enable/start (handles distros with systemd)
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
sudo systemctl enable --now docker || true
|
||||
pm_update_install() {
|
||||
case "$pm" in
|
||||
apt)
|
||||
sudo apt-get update -y
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@"
|
||||
;;
|
||||
dnf) sudo dnf install -y "$@" ;;
|
||||
yum) sudo yum install -y "$@" ;;
|
||||
zypper) sudo zypper --non-interactive install -y "$@" || true ;;
|
||||
apk) sudo apk add --no-cache "$@" ;;
|
||||
*)
|
||||
echo "Unsupported distro: couldn't detect package manager" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
systemd_enable_now() {
|
||||
if have systemctl; then
|
||||
sudo systemctl enable --now "$1" || true
|
||||
fi
|
||||
}
|
||||
|
||||
sshd_reload() {
|
||||
if have systemctl && systemctl is-enabled ssh >/dev/null 2>&1; then
|
||||
sudo systemctl reload ssh || true
|
||||
elif have systemctl && systemctl is-enabled sshd >/dev/null 2>&1; then
|
||||
sudo systemctl reload sshd || true
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------- baseline packages -----------
|
||||
if [ "$BASELINE_PKGS" = "1" ] && [ -n "$pm" ]; then
|
||||
pkgs_common="curl ca-certificates gnupg git jq unzip tar vim tmux htop net-tools"
|
||||
case "$pm" in
|
||||
apt) pkgs="$pkgs_common ufw openssh-client" ;;
|
||||
dnf|yum) pkgs="$pkgs_common firewalld openssh-clients" ;;
|
||||
zypper) pkgs="$pkgs_common firewalld openssh" ;;
|
||||
apk) pkgs="$pkgs_common openssh-client" ;;
|
||||
esac
|
||||
pm_update_install $pkgs || true
|
||||
fi
|
||||
|
||||
# add current ssh user to docker group if exists
|
||||
if getent group docker >/dev/null 2>&1; then
|
||||
sudo usermod -aG docker "$(id -un)" || true
|
||||
# ----------- docker & compose v2 -----------
|
||||
if [ "$INSTALL_DOCKER" = "1" ]; then
|
||||
if ! have docker; then
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
fi
|
||||
|
||||
# try to enable/start (handles distros with systemd)
|
||||
if have systemctl; then
|
||||
sudo systemctl enable --now docker || true
|
||||
fi
|
||||
|
||||
# add current ssh user to docker group if exists
|
||||
if getent group docker >/dev/null 2>&1; then
|
||||
sudo usermod -aG docker "$(id -un)" || true
|
||||
fi
|
||||
|
||||
# docker compose v2 (plugin) if missing
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
# Try package first (Debian/Ubuntu name)
|
||||
if [ "$pm" = "apt" ]; then
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y docker-compose-plugin || true
|
||||
fi
|
||||
|
||||
# Fallback: install static plugin binary under ~/.docker/cli-plugins
|
||||
if ! docker compose version >/dev/null 2>&1; then
|
||||
mkdir -p ~/.docker/cli-plugins
|
||||
arch="$(uname -m)"
|
||||
case "$arch" in
|
||||
x86_64|amd64) arch="x86_64" ;;
|
||||
aarch64|arm64) arch="aarch64" ;;
|
||||
esac
|
||||
curl -fsSL -o ~/.docker/cli-plugins/docker-compose \
|
||||
"https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-$(uname -s)-$arch"
|
||||
chmod +x ~/.docker/cli-plugins/docker-compose
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------- SSH hardening (non-destructive: separate conf file) -----------
|
||||
if [ "$SSH_HARDEN" = "1" ]; then
|
||||
confd="/etc/ssh/sshd_config.d"
|
||||
if [ -d "$confd" ] && [ -w "$confd" ]; then
|
||||
sudo tee "$confd/10-bastion.conf" >/dev/null <<'EOF'
|
||||
# Bastion hardening
|
||||
PasswordAuthentication no
|
||||
ChallengeResponseAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
UsePAM yes
|
||||
PermitEmptyPasswords no
|
||||
PubkeyAuthentication yes
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
LoginGraceTime 20
|
||||
MaxAuthTries 3
|
||||
MaxSessions 10
|
||||
AllowAgentForwarding no
|
||||
X11Forwarding no
|
||||
EOF
|
||||
sshd_reload
|
||||
else
|
||||
echo "Skipping SSH hardening: $confd not present or not writable" >&2
|
||||
fi
|
||||
|
||||
# lock root password (no effect if already locked)
|
||||
if have passwd; then
|
||||
sudo passwd -l root || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------- firewall -----------
|
||||
if [ "$FIREWALL" = "1" ]; then
|
||||
if have ufw; then
|
||||
# Keep it minimal: allow SSH and rate-limit
|
||||
sudo ufw --force reset || true
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
sudo ufw allow OpenSSH || sudo ufw allow 22/tcp
|
||||
sudo ufw limit OpenSSH || true
|
||||
sudo ufw --force enable
|
||||
elif have firewall-cmd; then
|
||||
systemd_enable_now firewalld
|
||||
sudo firewall-cmd --permanent --add-service=ssh || sudo firewall-cmd --permanent --add-port=22/tcp
|
||||
sudo firewall-cmd --reload || true
|
||||
else
|
||||
echo "No supported firewall tool detected; skipping." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------- unattended / automatic updates -----------
|
||||
if [ "$AUTO_UPDATES" = "1" ] && [ -n "$pm" ]; then
|
||||
case "$pm" in
|
||||
apt)
|
||||
pm_update_install unattended-upgrades apt-listchanges || true
|
||||
sudo dpkg-reconfigure -f noninteractive unattended-upgrades || true
|
||||
sudo tee /etc/apt/apt.conf.d/20auto-upgrades >/dev/null <<'EOF'
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
EOF
|
||||
;;
|
||||
dnf)
|
||||
pm_update_install dnf-automatic || true
|
||||
sudo sed -i 's/^apply_updates = .*/apply_updates = yes/' /etc/dnf/automatic.conf || true
|
||||
systemd_enable_now dnf-automatic.timer
|
||||
;;
|
||||
yum)
|
||||
pm_update_install yum-cron || true
|
||||
sudo sed -i 's/apply_updates = no/apply_updates = yes/' /etc/yum/yum-cron.conf || true
|
||||
systemd_enable_now yum-cron
|
||||
;;
|
||||
zypper)
|
||||
pm_update_install pkgconf-pkg-config || true
|
||||
# SUSE has automatic updates via transactional-update / yast2-online-update; skipping heavy config.
|
||||
;;
|
||||
apk)
|
||||
# Alpine: no official unattended updater; consider periodic 'apk upgrade' via cron (skipped by default).
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ----------- time sync -----------
|
||||
if [ "$TIME_SYNC" = "1" ]; then
|
||||
if have timedatectl; then
|
||||
# Prefer systemd-timesyncd if available; else install/enable chrony
|
||||
if [ -f /lib/systemd/system/systemd-timesyncd.service ] || [ -f /usr/lib/systemd/system/systemd-timesyncd.service ]; then
|
||||
systemd_enable_now systemd-timesyncd
|
||||
else
|
||||
pm_update_install chrony || true
|
||||
systemd_enable_now chronyd || systemd_enable_now chrony || true
|
||||
fi
|
||||
timedatectl set-ntp true || true
|
||||
else
|
||||
pm_update_install chrony || true
|
||||
systemd_enable_now chronyd || systemd_enable_now chrony || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------- fail2ban (basic sshd jail) -----------
|
||||
if [ "$FAIL2BAN" = "1" ]; then
|
||||
pm_update_install fail2ban || true
|
||||
if [ -d /etc/fail2ban ]; then
|
||||
sudo tee /etc/fail2ban/jail.d/sshd.local >/dev/null <<'EOF'
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = ssh
|
||||
logpath = %(sshd_log)s
|
||||
maxretry = 4
|
||||
bantime = 1h
|
||||
findtime = 10m
|
||||
EOF
|
||||
systemd_enable_now fail2ban
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------- SSH banner / MOTD -----------
|
||||
if [ "$BANNER" = "1" ]; then
|
||||
if [ -w /etc/issue.net ] || sudo test -w /etc/issue.net; then
|
||||
sudo tee /etc/issue.net >/dev/null <<'EOF'
|
||||
NOTICE: Authorized use only. Activity may be monitored and reported.
|
||||
EOF
|
||||
# Ensure banner is enabled via our bastion conf
|
||||
if [ -d /etc/ssh/sshd_config.d ]; then
|
||||
if ! grep -q '^Banner ' /etc/ssh/sshd_config.d/10-bastion.conf 2>/dev/null; then
|
||||
echo 'Banner /etc/issue.net' | sudo tee -a /etc/ssh/sshd_config.d/10-bastion.conf >/dev/null
|
||||
sshd_reload
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Bootstrap complete. If you were added to the docker group, log out and back in to apply."
|
||||
`
|
||||
|
||||
// Send script via stdin to avoid quoting/escaping issues
|
||||
@@ -271,3 +519,38 @@ func wrapSSHError(err error, output string) error {
|
||||
func sshEscape(s string) string {
|
||||
return fmt.Sprintf("%q", s)
|
||||
}
|
||||
|
||||
// makeDBHostKeyCallback returns a HostKeyCallback bound to a specific server row.
|
||||
// TOFU semantics:
|
||||
// - If s.SSHHostKey is empty: store the current key in DB and accept.
|
||||
// - If s.SSHHostKey is set: require exact match, else error (possible MITM/reinstall).
|
||||
func makeDBHostKeyCallback(db *gorm.DB, s *models.Server) ssh.HostKeyCallback {
|
||||
return func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
algo := key.Type()
|
||||
enc := base64.StdEncoding.EncodeToString(key.Marshal())
|
||||
|
||||
// First-time connect: persist key (TOFU).
|
||||
if s.SSHHostKey == "" {
|
||||
if err := db.Model(&models.Server{}).
|
||||
Where("id = ? AND (ssh_host_key IS NULL or ssh_host_key = '')", s.ID).
|
||||
Updates(map[string]any{
|
||||
"ssh_host_key": enc,
|
||||
"ssh_host_key_algo": algo,
|
||||
}).Error; err != nil {
|
||||
return fmt.Errorf("store new host key for %s (%s): %w", hostname, s.ID, err)
|
||||
}
|
||||
|
||||
s.SSHHostKey = enc
|
||||
s.SSHHostKeyAlgo = algo
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.SSHHostKeyAlgo != algo || s.SSHHostKey != enc {
|
||||
return fmt.Errorf(
|
||||
"host key mismatch for %s (server_id=%s, stored=%s/%s, got=%s/%s) - POSSIBLE MITM or host reinstalled",
|
||||
hostname, s.ID, s.SSHHostKeyAlgo, s.SSHHostKey, algo, enc,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func NewJobs(gdb *gorm.DB, dbUrl string) (*Jobs, error) {
|
||||
archer.WithSetTableName("jobs"), // <- ensure correct table
|
||||
archer.WithSleepInterval(1*time.Second), // fast poll while debugging
|
||||
archer.WithErrHandler(func(err error) { // bubble up worker SQL errors
|
||||
log.Printf("[archer] ERROR: %v", err)
|
||||
log.Error().Err(err).Msg("[archer] worker error")
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -75,7 +75,7 @@ func NewJobs(gdb *gorm.DB, dbUrl string) (*Jobs, error) {
|
||||
|
||||
c.Register(
|
||||
"bootstrap_bastion",
|
||||
BastionBootstrapWorker(gdb),
|
||||
BastionBootstrapWorker(gdb, jobs),
|
||||
archer.WithInstances(instances),
|
||||
archer.WithTimeout(time.Duration(timeoutSec)*time.Second),
|
||||
)
|
||||
@@ -94,6 +94,19 @@ func NewJobs(gdb *gorm.DB, dbUrl string) (*Jobs, error) {
|
||||
archer.WithTimeout(5*time.Minute),
|
||||
)
|
||||
|
||||
c.Register(
|
||||
"db_backup_s3",
|
||||
DbBackupWorker(gdb, jobs),
|
||||
archer.WithInstances(1),
|
||||
archer.WithTimeout(15*time.Minute),
|
||||
)
|
||||
|
||||
c.Register(
|
||||
"dns_reconcile",
|
||||
DNSReconsileWorker(gdb, jobs),
|
||||
archer.WithInstances(1),
|
||||
archer.WithTimeout(2*time.Minute),
|
||||
)
|
||||
return jobs, nil
|
||||
}
|
||||
|
||||
|
||||
597
internal/bg/dns.go
Normal file
597
internal/bg/dns.go
Normal file
@@ -0,0 +1,597 @@
|
||||
package bg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dyaksa/archer"
|
||||
"github.com/dyaksa/archer/job"
|
||||
"github.com/glueops/autoglue/internal/handlers/dto"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
r53 "github.com/aws/aws-sdk-go-v2/service/route53"
|
||||
r53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
|
||||
)
|
||||
|
||||
/************* args & small DTOs *************/
|
||||
|
||||
type DNSReconcileArgs struct {
|
||||
MaxDomains int `json:"max_domains,omitempty"`
|
||||
MaxRecords int `json:"max_records,omitempty"`
|
||||
IntervalS int `json:"interval_seconds,omitempty"`
|
||||
}
|
||||
|
||||
// TXT marker content (compact)
|
||||
type ownershipMarker struct {
|
||||
Ver string `json:"v"` // "ag1"
|
||||
Org string `json:"org"` // org UUID
|
||||
Rec string `json:"rec"` // record UUID
|
||||
Fp string `json:"fp"` // short fp (first 16 of sha256)
|
||||
}
|
||||
|
||||
// ExternalDNS poison owner id – MUST NOT match any real external-dns --txt-owner-id
|
||||
const externalDNSPoisonOwner = "autoglue-lock"
|
||||
|
||||
// ExternalDNS poison content – fake owner so real external-dns skips it.
|
||||
const externalDNSPoisonValue = "heritage=external-dns,external-dns/owner=" + externalDNSPoisonOwner + ",external-dns/resource=manual/autoglue"
|
||||
|
||||
/************* entrypoint worker *************/
|
||||
|
||||
func DNSReconsileWorker(db *gorm.DB, jobs *Jobs) archer.WorkerFn {
|
||||
return func(ctx context.Context, j job.Job) (any, error) {
|
||||
args := DNSReconcileArgs{MaxDomains: 25, MaxRecords: 100, IntervalS: 30}
|
||||
_ = j.ParseArguments(&args)
|
||||
|
||||
if args.MaxDomains <= 0 {
|
||||
args.MaxDomains = 25
|
||||
}
|
||||
if args.MaxRecords <= 0 {
|
||||
args.MaxRecords = 100
|
||||
}
|
||||
if args.IntervalS <= 0 {
|
||||
args.IntervalS = 30
|
||||
}
|
||||
|
||||
processedDomains, processedRecords, err := reconcileDNSOnce(ctx, db, args)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("[dns] reconcile tick failed")
|
||||
} else {
|
||||
log.Debug().
|
||||
Int("domains", processedDomains).
|
||||
Int("records", processedRecords).
|
||||
Msg("[dns] reconcile tick ok")
|
||||
}
|
||||
|
||||
next := time.Now().Add(time.Duration(args.IntervalS) * time.Second)
|
||||
_, _ = jobs.Enqueue(ctx, uuid.NewString(), "dns_reconcile", args,
|
||||
archer.WithScheduleTime(next),
|
||||
archer.WithMaxRetries(1),
|
||||
)
|
||||
|
||||
return map[string]any{
|
||||
"domains_processed": processedDomains,
|
||||
"records_processed": processedRecords,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
/************* core tick *************/
|
||||
|
||||
func reconcileDNSOnce(ctx context.Context, db *gorm.DB, args DNSReconcileArgs) (int, int, error) {
|
||||
var domains []models.Domain
|
||||
|
||||
// 1) validate/backfill pending domains
|
||||
if err := db.
|
||||
Where("status = ?", "pending").
|
||||
Order("created_at ASC").
|
||||
Limit(args.MaxDomains).
|
||||
Find(&domains).Error; err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
domainsProcessed := 0
|
||||
for i := range domains {
|
||||
if err := processDomain(ctx, db, &domains[i]); err != nil {
|
||||
log.Error().Err(err).Str("domain", domains[i].DomainName).Msg("[dns] domain processing failed")
|
||||
} else {
|
||||
domainsProcessed++
|
||||
}
|
||||
}
|
||||
|
||||
// 2) apply pending record sets for ready domains
|
||||
var readyDomains []models.Domain
|
||||
if err := db.Where("status = ?", "ready").Find(&readyDomains).Error; err != nil {
|
||||
return domainsProcessed, 0, err
|
||||
}
|
||||
|
||||
recordsProcessed := 0
|
||||
for i := range readyDomains {
|
||||
n, err := processPendingRecordsForDomain(ctx, db, &readyDomains[i], args.MaxRecords)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("domain", readyDomains[i].DomainName).Msg("[dns] record processing failed")
|
||||
continue
|
||||
}
|
||||
recordsProcessed += n
|
||||
}
|
||||
|
||||
return domainsProcessed, recordsProcessed, nil
|
||||
}
|
||||
|
||||
/************* domain processing *************/
|
||||
|
||||
func processDomain(ctx context.Context, db *gorm.DB, d *models.Domain) error {
|
||||
orgID := d.OrganizationID
|
||||
|
||||
// 1) Load credential (org-guarded)
|
||||
var cred models.Credential
|
||||
if err := db.Where("id = ? AND organization_id = ?", d.CredentialID, orgID).First(&cred).Error; err != nil {
|
||||
return setDomainFailed(db, d, fmt.Errorf("credential not found: %w", err))
|
||||
}
|
||||
|
||||
// 2) Decrypt → dto.AWSCredential
|
||||
secret, err := utils.DecryptForOrg(orgID, cred.EncryptedData, cred.IV, cred.Tag, db)
|
||||
if err != nil {
|
||||
return setDomainFailed(db, d, fmt.Errorf("decrypt: %w", err))
|
||||
}
|
||||
var awsCred dto.AWSCredential
|
||||
if err := jsonUnmarshalStrict([]byte(secret), &awsCred); err != nil {
|
||||
return setDomainFailed(db, d, fmt.Errorf("secret decode: %w", err))
|
||||
}
|
||||
|
||||
// 3) Client
|
||||
r53c, _, err := newRoute53Client(ctx, awsCred)
|
||||
if err != nil {
|
||||
return setDomainFailed(db, d, err)
|
||||
}
|
||||
|
||||
// 4) Backfill zone id if missing
|
||||
zoneID := strings.TrimSpace(d.ZoneID)
|
||||
if zoneID == "" {
|
||||
zid, err := findHostedZoneID(ctx, r53c, d.DomainName)
|
||||
if err != nil {
|
||||
return setDomainFailed(db, d, fmt.Errorf("discover zone id: %w", err))
|
||||
}
|
||||
zoneID = zid
|
||||
d.ZoneID = zoneID
|
||||
}
|
||||
|
||||
// 5) Sanity: can fetch zone
|
||||
if _, err := r53c.GetHostedZone(ctx, &r53.GetHostedZoneInput{Id: aws.String(zoneID)}); err != nil {
|
||||
return setDomainFailed(db, d, fmt.Errorf("get hosted zone: %w", err))
|
||||
}
|
||||
|
||||
// 6) Mark ready
|
||||
d.Status = "ready"
|
||||
d.LastError = ""
|
||||
if err := db.Save(d).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setDomainFailed(db *gorm.DB, d *models.Domain, cause error) error {
|
||||
d.Status = "failed"
|
||||
d.LastError = truncateErr(cause.Error())
|
||||
_ = db.Save(d).Error
|
||||
return cause
|
||||
}
|
||||
|
||||
/************* record processing *************/
|
||||
|
||||
func processPendingRecordsForDomain(ctx context.Context, db *gorm.DB, d *models.Domain, max int) (int, error) {
|
||||
orgID := d.OrganizationID
|
||||
|
||||
// reload credential
|
||||
var cred models.Credential
|
||||
if err := db.Where("id = ? AND organization_id = ?", d.CredentialID, orgID).First(&cred).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
secret, err := utils.DecryptForOrg(orgID, cred.EncryptedData, cred.IV, cred.Tag, db)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var awsCred dto.AWSCredential
|
||||
if err := jsonUnmarshalStrict([]byte(secret), &awsCred); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r53c, _, err := newRoute53Client(ctx, awsCred)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var records []models.RecordSet
|
||||
if err := db.
|
||||
Where("domain_id = ? AND status = ?", d.ID, "pending").
|
||||
Order("created_at ASC").
|
||||
Limit(max).
|
||||
Find(&records).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
applied := 0
|
||||
for i := range records {
|
||||
if err := applyRecord(ctx, db, r53c, d, &records[i]); err != nil {
|
||||
log.Error().Err(err).Str("rr", records[i].Name).Msg("[dns] apply record failed")
|
||||
_ = setRecordFailed(db, &records[i], err)
|
||||
continue
|
||||
}
|
||||
applied++
|
||||
}
|
||||
return applied, nil
|
||||
}
|
||||
|
||||
// core write + ownership + external-dns hardening
|
||||
|
||||
func applyRecord(ctx context.Context, db *gorm.DB, r53c *r53.Client, d *models.Domain, r *models.RecordSet) error {
|
||||
zoneID := strings.TrimSpace(d.ZoneID)
|
||||
if zoneID == "" {
|
||||
return errors.New("domain has no zone_id")
|
||||
}
|
||||
|
||||
rt := strings.ToUpper(r.Type)
|
||||
|
||||
// FQDN & marker
|
||||
fq := recordFQDN(r.Name, d.DomainName) // ends with "."
|
||||
mname := markerName(fq)
|
||||
expected := buildMarkerValue(d.OrganizationID.String(), r.ID.String(), r.Fingerprint)
|
||||
|
||||
// ---- ExternalDNS preflight ----
|
||||
extOwned, err := hasExternalDNSOwnership(ctx, r53c, zoneID, fq, rt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("external_dns_lookup: %w", err)
|
||||
}
|
||||
if extOwned {
|
||||
r.Owner = "external"
|
||||
_ = db.Save(r).Error
|
||||
return fmt.Errorf("ownership_conflict: external-dns claims %s; refusing to modify", strings.TrimSuffix(fq, "."))
|
||||
}
|
||||
|
||||
// ---- Autoglue ownership preflight via _autoglue.<fqdn> TXT ----
|
||||
markerVals, err := getMarkerTXTValues(ctx, r53c, zoneID, mname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marker lookup: %w", err)
|
||||
}
|
||||
hasForeignOwner := false
|
||||
hasOurExact := false
|
||||
for _, v := range markerVals {
|
||||
mk, ok := parseMarkerValue(v)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case mk.Org == d.OrganizationID.String() && mk.Rec == r.ID.String() && mk.Fp == shortFP(r.Fingerprint):
|
||||
hasOurExact = true
|
||||
case mk.Org != d.OrganizationID.String() || mk.Rec != r.ID.String():
|
||||
hasForeignOwner = true
|
||||
}
|
||||
}
|
||||
if hasForeignOwner {
|
||||
r.Owner = "external"
|
||||
_ = db.Save(r).Error
|
||||
return fmt.Errorf("ownership_conflict: marker for %s is owned by another controller; refusing to modify", strings.TrimSuffix(fq, "."))
|
||||
}
|
||||
|
||||
// Build RR change (UPSERT)
|
||||
rrChange := r53types.Change{
|
||||
Action: r53types.ChangeActionUpsert,
|
||||
ResourceRecordSet: &r53types.ResourceRecordSet{
|
||||
Name: aws.String(fq),
|
||||
Type: r53types.RRType(rt),
|
||||
},
|
||||
}
|
||||
|
||||
// Decode user values
|
||||
var userVals []string
|
||||
if len(r.Values) > 0 {
|
||||
if err := jsonUnmarshalStrict([]byte(r.Values), &userVals); err != nil {
|
||||
return fmt.Errorf("values decode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Quote TXT values as required by Route53
|
||||
recs := make([]r53types.ResourceRecord, 0, len(userVals))
|
||||
for _, v := range userVals {
|
||||
v = strings.TrimSpace(v)
|
||||
if rt == "TXT" && !(strings.HasPrefix(v, `"`) && strings.HasSuffix(v, `"`)) {
|
||||
v = strconv.Quote(v)
|
||||
}
|
||||
recs = append(recs, r53types.ResourceRecord{Value: aws.String(v)})
|
||||
}
|
||||
rrChange.ResourceRecordSet.ResourceRecords = recs
|
||||
if r.TTL != nil {
|
||||
ttl := int64(*r.TTL)
|
||||
rrChange.ResourceRecordSet.TTL = aws.Int64(ttl)
|
||||
}
|
||||
|
||||
// Build marker TXT change (UPSERT)
|
||||
markerChange := r53types.Change{
|
||||
Action: r53types.ChangeActionUpsert,
|
||||
ResourceRecordSet: &r53types.ResourceRecordSet{
|
||||
Name: aws.String(mname),
|
||||
Type: r53types.RRTypeTxt,
|
||||
TTL: aws.Int64(300),
|
||||
ResourceRecords: []r53types.ResourceRecord{
|
||||
{Value: aws.String(strconv.Quote(expected))},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Build external-dns poison TXT changes
|
||||
poisonChanges := buildExternalDNSPoisonTXTChanges(fq, rt)
|
||||
|
||||
// Apply all in one batch (atomic-ish)
|
||||
changes := []r53types.Change{rrChange, markerChange}
|
||||
changes = append(changes, poisonChanges...)
|
||||
|
||||
_, err = r53c.ChangeResourceRecordSets(ctx, &r53.ChangeResourceRecordSetsInput{
|
||||
HostedZoneId: aws.String(zoneID),
|
||||
ChangeBatch: &r53types.ChangeBatch{Changes: changes},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Success → mark ready & ownership
|
||||
r.Status = "ready"
|
||||
r.LastError = ""
|
||||
r.Owner = "autoglue"
|
||||
if err := db.Save(r).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
_ = hasOurExact // could be used to skip marker write in future
|
||||
return nil
|
||||
}
|
||||
|
||||
func setRecordFailed(db *gorm.DB, r *models.RecordSet, cause error) error {
|
||||
msg := truncateErr(cause.Error())
|
||||
r.Status = "failed"
|
||||
r.LastError = msg
|
||||
// classify ownership on conflict
|
||||
if strings.HasPrefix(msg, "ownership_conflict") {
|
||||
r.Owner = "external"
|
||||
} else if r.Owner == "" || r.Owner == "unknown" {
|
||||
r.Owner = "unknown"
|
||||
}
|
||||
_ = db.Save(r).Error
|
||||
return cause
|
||||
}
|
||||
|
||||
/************* AWS helpers *************/
|
||||
|
||||
func newRoute53Client(ctx context.Context, cred dto.AWSCredential) (*r53.Client, *aws.Config, error) {
|
||||
// Route53 is global, but config still wants a region
|
||||
region := strings.TrimSpace(cred.Region)
|
||||
if region == "" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
cfg, err := config.LoadDefaultConfig(ctx,
|
||||
config.WithRegion(region),
|
||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
|
||||
cred.AccessKeyID, cred.SecretAccessKey, "",
|
||||
)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return r53.NewFromConfig(cfg), &cfg, nil
|
||||
}
|
||||
|
||||
func findHostedZoneID(ctx context.Context, c *r53.Client, domain string) (string, error) {
|
||||
d := normalizeDomain(domain)
|
||||
out, err := c.ListHostedZonesByName(ctx, &r53.ListHostedZonesByNameInput{
|
||||
DNSName: aws.String(d),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, hz := range out.HostedZones {
|
||||
if strings.TrimSuffix(aws.ToString(hz.Name), ".") == d {
|
||||
return trimZoneID(aws.ToString(hz.Id)), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("hosted zone not found for %q", d)
|
||||
}
|
||||
|
||||
func trimZoneID(id string) string {
|
||||
return strings.TrimPrefix(id, "/hostedzone/")
|
||||
}
|
||||
|
||||
func normalizeDomain(s string) string {
|
||||
s = strings.TrimSpace(strings.ToLower(s))
|
||||
return strings.TrimSuffix(s, ".")
|
||||
}
|
||||
|
||||
func recordFQDN(name, domain string) string {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" || name == "@" {
|
||||
return normalizeDomain(domain) + "."
|
||||
}
|
||||
if strings.HasSuffix(name, ".") {
|
||||
return name
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.", name, normalizeDomain(domain))
|
||||
}
|
||||
|
||||
/************* TXT marker / external-dns helpers *************/
|
||||
|
||||
func markerName(fqdn string) string {
|
||||
trimmed := strings.TrimSuffix(fqdn, ".")
|
||||
return "_autoglue." + trimmed + "."
|
||||
}
|
||||
|
||||
func shortFP(full string) string {
|
||||
if len(full) > 16 {
|
||||
return full[:16]
|
||||
}
|
||||
return full
|
||||
}
|
||||
|
||||
func buildMarkerValue(orgID, recID, fp string) string {
|
||||
return "v=ag1 org=" + orgID + " rec=" + recID + " fp=" + shortFP(fp)
|
||||
}
|
||||
|
||||
func parseMarkerValue(s string) (ownershipMarker, bool) {
|
||||
out := ownershipMarker{}
|
||||
fields := strings.Fields(s)
|
||||
if len(fields) < 4 {
|
||||
return out, false
|
||||
}
|
||||
kv := map[string]string{}
|
||||
for _, f := range fields {
|
||||
parts := strings.SplitN(f, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
kv[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
if kv["v"] == "" || kv["org"] == "" || kv["rec"] == "" || kv["fp"] == "" {
|
||||
return out, false
|
||||
}
|
||||
out.Ver, out.Org, out.Rec, out.Fp = kv["v"], kv["org"], kv["rec"], kv["fp"]
|
||||
return out, true
|
||||
}
|
||||
|
||||
func getMarkerTXTValues(ctx context.Context, c *r53.Client, zoneID, marker string) ([]string, error) {
|
||||
return getTXTValues(ctx, c, zoneID, marker)
|
||||
}
|
||||
|
||||
// generic TXT fetcher
|
||||
func getTXTValues(ctx context.Context, c *r53.Client, zoneID, name string) ([]string, error) {
|
||||
out, err := c.ListResourceRecordSets(ctx, &r53.ListResourceRecordSetsInput{
|
||||
HostedZoneId: aws.String(zoneID),
|
||||
StartRecordName: aws.String(name),
|
||||
StartRecordType: r53types.RRTypeTxt,
|
||||
MaxItems: aws.Int32(1),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(out.ResourceRecordSets) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
rrset := out.ResourceRecordSets[0]
|
||||
if aws.ToString(rrset.Name) != name || rrset.Type != r53types.RRTypeTxt {
|
||||
return nil, nil
|
||||
}
|
||||
vals := make([]string, 0, len(rrset.ResourceRecords))
|
||||
for _, rr := range rrset.ResourceRecords {
|
||||
vals = append(vals, aws.ToString(rr.Value))
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// detect external-dns-style ownership for this fqdn/type
|
||||
func hasExternalDNSOwnership(ctx context.Context, c *r53.Client, zoneID, fqdn, rrType string) (bool, error) {
|
||||
base := strings.TrimSuffix(fqdn, ".")
|
||||
candidates := []string{
|
||||
// with txtPrefix=extdns-, external-dns writes both:
|
||||
// extdns-<fqdn> and extdns-<rrtype-lc>-<fqdn>
|
||||
"extdns-" + base + ".",
|
||||
"extdns-" + strings.ToLower(rrType) + "-" + base + ".",
|
||||
}
|
||||
for _, name := range candidates {
|
||||
vals, err := getTXTValues(ctx, c, zoneID, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, raw := range vals {
|
||||
v := strings.TrimSpace(raw)
|
||||
// strip surrounding quotes if present
|
||||
if len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' {
|
||||
if unq, err := strconv.Unquote(v); err == nil {
|
||||
v = unq
|
||||
}
|
||||
}
|
||||
meta := parseExternalDNSMeta(v)
|
||||
if meta == nil {
|
||||
continue
|
||||
}
|
||||
if meta["heritage"] == "external-dns" &&
|
||||
meta["external-dns/owner"] != "" &&
|
||||
meta["external-dns/owner"] != externalDNSPoisonOwner {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// parseExternalDNSMeta parses the comma-separated external-dns TXT format into a small map.
|
||||
func parseExternalDNSMeta(v string) map[string]string {
|
||||
parts := strings.Split(v, ",")
|
||||
if len(parts) == 0 {
|
||||
return nil
|
||||
}
|
||||
meta := make(map[string]string, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
kv := strings.SplitN(p, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
meta[kv[0]] = kv[1]
|
||||
}
|
||||
if len(meta) == 0 {
|
||||
return nil
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
// build poison TXT records so external-dns thinks some *other* owner manages this
|
||||
func buildExternalDNSPoisonTXTChanges(fqdn, rrType string) []r53types.Change {
|
||||
base := strings.TrimSuffix(fqdn, ".")
|
||||
names := []string{
|
||||
"extdns-" + base + ".",
|
||||
"extdns-" + strings.ToLower(rrType) + "-" + base + ".",
|
||||
}
|
||||
val := strconv.Quote(externalDNSPoisonValue)
|
||||
changes := make([]r53types.Change, 0, len(names))
|
||||
for _, n := range names {
|
||||
changes = append(changes, r53types.Change{
|
||||
Action: r53types.ChangeActionUpsert,
|
||||
ResourceRecordSet: &r53types.ResourceRecordSet{
|
||||
Name: aws.String(n),
|
||||
Type: r53types.RRTypeTxt,
|
||||
TTL: aws.Int64(300),
|
||||
ResourceRecords: []r53types.ResourceRecord{
|
||||
{Value: aws.String(val)},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
/************* misc utils *************/
|
||||
|
||||
func truncateErr(s string) string {
|
||||
const max = 2000
|
||||
if len(s) > max {
|
||||
return s[:max]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Strict unmarshal that treats "null" -> zero value correctly.
|
||||
func jsonUnmarshalStrict(b []byte, dst any) error {
|
||||
if len(b) == 0 {
|
||||
return errors.New("empty json")
|
||||
}
|
||||
return json.Unmarshal(b, dst)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
type Config struct {
|
||||
DbURL string
|
||||
DbURLRO string
|
||||
Port string
|
||||
Host string
|
||||
JWTIssuer string
|
||||
@@ -24,10 +25,17 @@ type Config struct {
|
||||
GithubClientID string
|
||||
GithubClientSecret string
|
||||
|
||||
UIDev bool
|
||||
Env string
|
||||
Debug bool
|
||||
Swagger bool
|
||||
UIDev bool
|
||||
Env string
|
||||
Debug bool
|
||||
Swagger bool
|
||||
SwaggerHost string
|
||||
|
||||
DBStudioEnabled bool
|
||||
DBStudioBind string
|
||||
DBStudioPort string
|
||||
DBStudioUser string
|
||||
DBStudioPass string
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -47,11 +55,18 @@ func Load() (Config, error) {
|
||||
v.SetDefault("bind.address", "127.0.0.1")
|
||||
v.SetDefault("bind.port", "8080")
|
||||
v.SetDefault("database.url", "postgres://user:pass@localhost:5432/db?sslmode=disable")
|
||||
v.SetDefault("database.url_ro", "")
|
||||
v.SetDefault("db_studio.enabled", false)
|
||||
v.SetDefault("db_studio.bind", "127.0.0.1")
|
||||
v.SetDefault("db_studio.port", "0") // 0 = random
|
||||
v.SetDefault("db_studio.user", "")
|
||||
v.SetDefault("db_studio.pass", "")
|
||||
|
||||
v.SetDefault("ui.dev", false)
|
||||
v.SetDefault("env", "development")
|
||||
v.SetDefault("debug", false)
|
||||
v.SetDefault("swagger", false)
|
||||
v.SetDefault("swagger.host", "localhost:8080")
|
||||
|
||||
// Env setup and binding
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
@@ -61,6 +76,7 @@ func Load() (Config, error) {
|
||||
"bind.address",
|
||||
"bind.port",
|
||||
"database.url",
|
||||
"database.url_ro",
|
||||
"jwt.issuer",
|
||||
"jwt.audience",
|
||||
"jwt.private.enc.key",
|
||||
@@ -73,6 +89,12 @@ func Load() (Config, error) {
|
||||
"env",
|
||||
"debug",
|
||||
"swagger",
|
||||
"swagger.host",
|
||||
"db_studio.enabled",
|
||||
"db_studio.bind",
|
||||
"db_studio.port",
|
||||
"db_studio.user",
|
||||
"db_studio.pass",
|
||||
}
|
||||
for _, k := range keys {
|
||||
_ = v.BindEnv(k)
|
||||
@@ -81,6 +103,7 @@ func Load() (Config, error) {
|
||||
// Build config
|
||||
cfg := Config{
|
||||
DbURL: v.GetString("database.url"),
|
||||
DbURLRO: v.GetString("database.url_ro"),
|
||||
Port: v.GetString("bind.port"),
|
||||
Host: v.GetString("bind.address"),
|
||||
JWTIssuer: v.GetString("jwt.issuer"),
|
||||
@@ -92,10 +115,17 @@ func Load() (Config, error) {
|
||||
GithubClientID: v.GetString("github.client.id"),
|
||||
GithubClientSecret: v.GetString("github.client.secret"),
|
||||
|
||||
UIDev: v.GetBool("ui.dev"),
|
||||
Env: v.GetString("env"),
|
||||
Debug: v.GetBool("debug"),
|
||||
Swagger: v.GetBool("swagger"),
|
||||
UIDev: v.GetBool("ui.dev"),
|
||||
Env: v.GetString("env"),
|
||||
Debug: v.GetBool("debug"),
|
||||
Swagger: v.GetBool("swagger"),
|
||||
SwaggerHost: v.GetString("swagger.host"),
|
||||
|
||||
DBStudioEnabled: v.GetBool("db_studio.enabled"),
|
||||
DBStudioBind: v.GetString("db_studio.bind"),
|
||||
DBStudioPort: v.GetString("db_studio.port"),
|
||||
DBStudioUser: v.GetString("db_studio.user"),
|
||||
DBStudioPass: v.GetString("db_studio.pass"),
|
||||
}
|
||||
|
||||
// Validate
|
||||
|
||||
@@ -273,6 +273,21 @@ func AuthCallback(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
secure := strings.HasPrefix(cfg.OAuthRedirectBase, "https://")
|
||||
if xf := r.Header.Get("X-Forwarded-Proto"); xf != "" {
|
||||
secure = strings.EqualFold(xf, "https")
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "ag_jwt",
|
||||
Value: "Bearer " + access,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Secure: secure,
|
||||
MaxAge: int((time.Hour * 8).Seconds()),
|
||||
})
|
||||
|
||||
// If the state indicates SPA popup mode, postMessage tokens to the opener and close
|
||||
state := r.URL.Query().Get("state")
|
||||
if strings.Contains(state, "mode=spa") {
|
||||
@@ -356,6 +371,21 @@ func Refresh(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
secure := strings.HasPrefix(cfg.OAuthRedirectBase, "https://")
|
||||
if xf := r.Header.Get("X-Forwarded-Proto"); xf != "" {
|
||||
secure = strings.EqualFold(xf, "https")
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "ag_jwt",
|
||||
Value: "Bearer " + access,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Secure: secure,
|
||||
MaxAge: int((time.Hour * 8).Seconds()),
|
||||
})
|
||||
|
||||
utils.WriteJSON(w, 200, dto.TokenPair{
|
||||
AccessToken: access,
|
||||
RefreshToken: newPair.Plain,
|
||||
@@ -377,6 +407,7 @@ func Refresh(db *gorm.DB) http.HandlerFunc {
|
||||
// @Router /auth/logout [post]
|
||||
func Logout(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
cfg, _ := config.Load()
|
||||
var req dto.LogoutRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
utils.WriteError(w, 400, "invalid_json", err.Error())
|
||||
@@ -385,13 +416,27 @@ func Logout(db *gorm.DB) http.HandlerFunc {
|
||||
rec, err := auth.ValidateRefreshToken(db, req.RefreshToken)
|
||||
if err != nil {
|
||||
w.WriteHeader(204) // already invalid/revoked
|
||||
return
|
||||
goto clearCookie
|
||||
}
|
||||
if err := auth.RevokeFamily(db, rec.FamilyID); err != nil {
|
||||
utils.WriteError(w, 500, "revoke_failed", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
clearCookie:
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "ag_jwt",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
MaxAge: -1,
|
||||
Expires: time.Unix(0, 0),
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Secure: strings.HasPrefix(cfg.OAuthRedirectBase, "https"),
|
||||
})
|
||||
|
||||
w.WriteHeader(204)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
186
internal/handlers/clusters.go
Normal file
186
internal/handlers/clusters.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/common"
|
||||
"github.com/glueops/autoglue/internal/handlers/dto"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ListClusters godoc
|
||||
//
|
||||
// @ID ListClusters
|
||||
// @Summary List clusters (org scoped)
|
||||
// @Description Returns clusters for the organization in X-Org-ID. Filter by `q` (name contains).
|
||||
// @Tags Clusters
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param q query string false "Name contains (case-insensitive)"
|
||||
// @Success 200 {array} dto.ClusterResponse
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "failed to list clusters"
|
||||
// @Router /clusters [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func ListClusters(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
|
||||
q := db.Where("organization_id = ?", orgID)
|
||||
if needle := strings.TrimSpace(r.URL.Query().Get("q")); needle != "" {
|
||||
q = q.Where(`name ILIKE ?`, "%"+needle+"%")
|
||||
}
|
||||
|
||||
var rows []models.Cluster
|
||||
if err := q.
|
||||
Preload("NodePools").
|
||||
Preload("NodePools.Labels").
|
||||
Preload("NodePools.Annotations").
|
||||
Preload("NodePools.Labels").
|
||||
Preload("NodePools.Taints").
|
||||
Preload("NodePools.Servers").
|
||||
Preload("BastionServer").
|
||||
Find(&rows).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
|
||||
out := make([]dto.ClusterResponse, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, clusterToDTO(row))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CreateCluster godoc
|
||||
//
|
||||
// @ID CreateCluster
|
||||
// @Summary Create cluster (org scoped)
|
||||
// @Description Creates a cluster. If `kubeconfig` is provided, it will be encrypted per-organization and stored securely (never returned).
|
||||
// @Tags Clusters
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param body body dto.CreateClusterRequest true "payload"
|
||||
// @Success 201 {object} dto.ClusterResponse
|
||||
// @Failure 400 {string} string "invalid json"
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "create failed"
|
||||
// @Router /clusters [post]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func CreateCluster(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// -- Helpers
|
||||
|
||||
func clusterToDTO(c models.Cluster) dto.ClusterResponse {
|
||||
var bastion *dto.ServerResponse
|
||||
if c.BastionServer != nil {
|
||||
b := serverToDTO(*c.BastionServer)
|
||||
bastion = &b
|
||||
}
|
||||
|
||||
nps := make([]dto.NodePoolResponse, 0, len(c.NodePools))
|
||||
for _, np := range c.NodePools {
|
||||
nps = append(nps, nodePoolToDTO(np))
|
||||
}
|
||||
|
||||
return dto.ClusterResponse{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Provider: c.Provider,
|
||||
Region: c.Region,
|
||||
Status: c.Status,
|
||||
CaptainDomain: c.CaptainDomain,
|
||||
//ClusterLoadBalancer: c.ClusterLoadBalancer,
|
||||
RandomToken: c.RandomToken,
|
||||
CertificateKey: c.CertificateKey,
|
||||
//ControlLoadBalancer: c.ControlLoadBalancer,
|
||||
NodePools: nps,
|
||||
BastionServer: bastion,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func nodePoolToDTO(np models.NodePool) dto.NodePoolResponse {
|
||||
labels := make([]dto.LabelResponse, 0, len(np.Labels))
|
||||
for _, l := range np.Labels {
|
||||
labels = append(labels, dto.LabelResponse{
|
||||
Key: l.Key,
|
||||
Value: l.Value,
|
||||
})
|
||||
}
|
||||
|
||||
annotations := make([]dto.AnnotationResponse, 0, len(np.Annotations))
|
||||
for _, a := range np.Annotations {
|
||||
annotations = append(annotations, dto.AnnotationResponse{
|
||||
Key: a.Key,
|
||||
Value: a.Value,
|
||||
})
|
||||
}
|
||||
|
||||
taints := make([]dto.TaintResponse, 0, len(np.Taints))
|
||||
for _, t := range np.Taints {
|
||||
taints = append(taints, dto.TaintResponse{
|
||||
Key: t.Key,
|
||||
Value: t.Value,
|
||||
Effect: t.Effect,
|
||||
})
|
||||
}
|
||||
|
||||
servers := make([]dto.ServerResponse, 0, len(np.Servers))
|
||||
for _, s := range np.Servers {
|
||||
servers = append(servers, serverToDTO(s))
|
||||
}
|
||||
|
||||
return dto.NodePoolResponse{
|
||||
AuditFields: common.AuditFields{
|
||||
ID: np.ID,
|
||||
OrganizationID: np.OrganizationID,
|
||||
CreatedAt: np.CreatedAt,
|
||||
UpdatedAt: np.UpdatedAt,
|
||||
},
|
||||
Name: np.Name,
|
||||
Role: dto.NodeRole(np.Role),
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
Taints: taints,
|
||||
Servers: servers,
|
||||
}
|
||||
}
|
||||
|
||||
func serverToDTO(s models.Server) dto.ServerResponse {
|
||||
return dto.ServerResponse{
|
||||
ID: s.ID,
|
||||
Hostname: s.Hostname,
|
||||
PrivateIPAddress: s.PrivateIPAddress,
|
||||
PublicIPAddress: s.PublicIPAddress,
|
||||
Role: s.Role,
|
||||
Status: s.Status,
|
||||
SSHUser: s.SSHUser,
|
||||
SshKeyID: s.SshKeyID,
|
||||
CreatedAt: s.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: s.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
561
internal/handlers/credentials.go
Normal file
561
internal/handlers/credentials.go
Normal file
@@ -0,0 +1,561 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/handlers/dto"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ListCredentials godoc
|
||||
// @ID ListCredentials
|
||||
// @Summary List credentials (metadata only)
|
||||
// @Description Returns credential metadata for the current org. Secrets are never returned.
|
||||
// @Tags Credentials
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization ID (UUID)"
|
||||
// @Param provider query string false "Filter by provider (e.g., aws)"
|
||||
// @Param kind query string false "Filter by kind (e.g., aws_access_key)"
|
||||
// @Param scope_kind query string false "Filter by scope kind (provider/service/resource)"
|
||||
// @Success 200 {array} dto.CredentialOut
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "internal server error"
|
||||
// @Router /credentials [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func ListCredentials(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
q := db.Where("organization_id = ?", orgID)
|
||||
if v := r.URL.Query().Get("provider"); v != "" {
|
||||
q = q.Where("provider = ?", v)
|
||||
}
|
||||
if v := r.URL.Query().Get("kind"); v != "" {
|
||||
q = q.Where("kind = ?", v)
|
||||
}
|
||||
if v := r.URL.Query().Get("scope_kind"); v != "" {
|
||||
q = q.Where("scope_kind = ?", v)
|
||||
}
|
||||
|
||||
var rows []models.Credential
|
||||
if err := q.Order("updated_at DESC").Find(&rows).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
out := make([]dto.CredentialOut, 0, len(rows))
|
||||
for i := range rows {
|
||||
out = append(out, credOut(&rows[i]))
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCredential godoc
|
||||
// @ID GetCredential
|
||||
// @Summary Get credential by ID (metadata only)
|
||||
// @Tags Credentials
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization ID (UUID)"
|
||||
// @Param id path string true "Credential ID (UUID)"
|
||||
// @Success 200 {object} dto.CredentialOut
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "internal server error"
|
||||
// @Router /credentials/{id} [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func GetCredential(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
|
||||
var row models.Credential
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, id).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "credential not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, credOut(&row))
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCredential godoc
|
||||
// @ID CreateCredential
|
||||
// @Summary Create a credential (encrypts secret)
|
||||
// @Tags Credentials
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization ID (UUID)"
|
||||
// @Param body body dto.CreateCredentialRequest true "Credential payload"
|
||||
// @Success 201 {object} dto.CredentialOut
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "internal server error"
|
||||
// @Router /credentials [post]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func CreateCredential(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
|
||||
var in dto.CreateCredentialRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_json", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := dto.Validate.Struct(in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cred, err := SaveCredentialWithScope(
|
||||
r.Context(), db, orgID,
|
||||
in.Provider, in.Kind, in.SchemaVersion,
|
||||
in.ScopeKind, in.ScopeVersion, json.RawMessage(in.Scope), json.RawMessage(in.Secret),
|
||||
in.Name, in.AccountID, in.Region,
|
||||
)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "save_failed", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusCreated, credOut(cred))
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateCredential godoc
|
||||
// @ID UpdateCredential
|
||||
// @Summary Update credential metadata and/or rotate secret
|
||||
// @Tags Credentials
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization ID (UUID)"
|
||||
// @Param id path string true "Credential ID (UUID)"
|
||||
// @Param body body dto.UpdateCredentialRequest true "Fields to update"
|
||||
// @Success 200 {object} dto.CredentialOut
|
||||
// @Failure 403 {string} string "X-Org-ID required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /credentials/{id} [patch]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func UpdateCredential(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
|
||||
var row models.Credential
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, id).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "credential not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var in dto.UpdateCredentialRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_json", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update metadata
|
||||
if in.Name != nil {
|
||||
row.Name = *in.Name
|
||||
}
|
||||
if in.AccountID != nil {
|
||||
row.AccountID = *in.AccountID
|
||||
}
|
||||
if in.Region != nil {
|
||||
row.Region = *in.Region
|
||||
}
|
||||
|
||||
// Update scope (re-validate + fingerprint)
|
||||
if in.ScopeKind != nil || in.Scope != nil || in.ScopeVersion != nil {
|
||||
newKind := row.ScopeKind
|
||||
if in.ScopeKind != nil {
|
||||
newKind = *in.ScopeKind
|
||||
}
|
||||
newVersion := row.ScopeVersion
|
||||
if in.ScopeVersion != nil {
|
||||
newVersion = *in.ScopeVersion
|
||||
}
|
||||
if in.Scope == nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", "scope must be provided when changing scope kind/version")
|
||||
return
|
||||
}
|
||||
prScopes := dto.ScopeRegistry[row.Provider]
|
||||
kScopes := prScopes[newKind]
|
||||
sdef := kScopes[newVersion]
|
||||
dst := sdef.New()
|
||||
if err := json.Unmarshal(*in.Scope, dst); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "invalid_scope_json", err.Error())
|
||||
return
|
||||
}
|
||||
if err := sdef.Validate(dst); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "invalid_scope", err.Error())
|
||||
return
|
||||
}
|
||||
canonScope, err := canonicalJSON(dst)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "canon_error", err.Error())
|
||||
return
|
||||
}
|
||||
row.Scope = canonScope
|
||||
row.ScopeKind = newKind
|
||||
row.ScopeVersion = newVersion
|
||||
row.ScopeFingerprint = sha256Hex(canonScope)
|
||||
}
|
||||
|
||||
// Rotate secret
|
||||
if in.Secret != nil {
|
||||
// validate against current Provider/Kind/SchemaVersion
|
||||
def := dto.CredentialRegistry[row.Provider][row.Kind][row.SchemaVersion]
|
||||
dst := def.New()
|
||||
if err := json.Unmarshal(*in.Secret, dst); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "invalid_secret_json", err.Error())
|
||||
return
|
||||
}
|
||||
if err := def.Validate(dst); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "invalid_secret", err.Error())
|
||||
return
|
||||
}
|
||||
canonSecret, err := canonicalJSON(dst)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "canon_error", err.Error())
|
||||
return
|
||||
}
|
||||
cipher, iv, tag, err := utils.EncryptForOrg(orgID, canonSecret, db)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "encrypt_error", err.Error())
|
||||
return
|
||||
}
|
||||
row.EncryptedData = cipher
|
||||
row.IV = iv
|
||||
row.Tag = tag
|
||||
}
|
||||
|
||||
if err := db.Save(&row).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, credOut(&row))
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCredential godoc
|
||||
// @ID DeleteCredential
|
||||
// @Summary Delete credential
|
||||
// @Tags Credentials
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization ID (UUID)"
|
||||
// @Param id path string true "Credential ID (UUID)"
|
||||
// @Success 204
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /credentials/{id} [delete]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func DeleteCredential(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
res := db.Where("organization_id = ? AND id = ?", orgID, id).Delete(&models.Credential{})
|
||||
if res.Error != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", res.Error.Error())
|
||||
return
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "credential not found")
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// RevealCredential godoc
|
||||
// @ID RevealCredential
|
||||
// @Summary Reveal decrypted secret (one-time read)
|
||||
// @Tags Credentials
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization ID (UUID)"
|
||||
// @Param id path string true "Credential ID (UUID)"
|
||||
// @Success 200 {object} map[string]any
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /credentials/{id}/reveal [post]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func RevealCredential(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
|
||||
var row models.Credential
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, id).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "credential not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
plain, err := utils.DecryptForOrg(orgID, row.EncryptedData, row.IV, row.Tag, db)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "decrypt_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
utils.WriteJSON(w, http.StatusOK, plain)
|
||||
}
|
||||
}
|
||||
|
||||
// -- Helpers
|
||||
|
||||
func canonicalJSON(v any) ([]byte, error) {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var m any
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return marshalSorted(m)
|
||||
}
|
||||
|
||||
func marshalSorted(v any) ([]byte, error) {
|
||||
switch vv := v.(type) {
|
||||
case map[string]any:
|
||||
keys := make([]string, 0, len(vv))
|
||||
for k := range vv {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
buf := bytes.NewBufferString("{")
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
kb, _ := json.Marshal(k)
|
||||
buf.Write(kb)
|
||||
buf.WriteByte(':')
|
||||
b, err := marshalSorted(vv[k])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
case []any:
|
||||
buf := bytes.NewBufferString("[")
|
||||
for i, e := range vv {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
b, err := marshalSorted(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.Bytes(), nil
|
||||
default:
|
||||
return json.Marshal(v)
|
||||
}
|
||||
}
|
||||
|
||||
func sha256Hex(b []byte) string {
|
||||
sum := sha256.Sum256(b)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// SaveCredentialWithScope validates secret+scope, encrypts, fingerprints, and stores.
|
||||
func SaveCredentialWithScope(
|
||||
ctx context.Context,
|
||||
db *gorm.DB,
|
||||
orgID uuid.UUID,
|
||||
provider, kind string,
|
||||
schemaVersion int,
|
||||
scopeKind string,
|
||||
scopeVersion int,
|
||||
rawScope json.RawMessage,
|
||||
rawSecret json.RawMessage,
|
||||
name, accountID, region string,
|
||||
) (*models.Credential, error) {
|
||||
// 1) secret shape
|
||||
pv, ok := dto.CredentialRegistry[provider]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown provider %q", provider)
|
||||
}
|
||||
kv, ok := pv[kind]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown kind %q for provider %q", kind, provider)
|
||||
}
|
||||
def, ok := kv[schemaVersion]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported schema version %d for %s/%s", schemaVersion, provider, kind)
|
||||
}
|
||||
|
||||
secretDst := def.New()
|
||||
if err := json.Unmarshal(rawSecret, secretDst); err != nil {
|
||||
return nil, fmt.Errorf("payload is not valid JSON for %s/%s: %w", provider, kind, err)
|
||||
}
|
||||
if err := def.Validate(secretDst); err != nil {
|
||||
return nil, fmt.Errorf("invalid %s/%s: %w", provider, kind, err)
|
||||
}
|
||||
|
||||
// 2) scope shape
|
||||
prScopes, ok := dto.ScopeRegistry[provider]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no scopes registered for provider %q", provider)
|
||||
}
|
||||
kScopes, ok := prScopes[scopeKind]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid scope_kind %q for provider %q", scopeKind, provider)
|
||||
}
|
||||
sdef, ok := kScopes[scopeVersion]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported scope version %d for %s/%s", scopeVersion, provider, scopeKind)
|
||||
}
|
||||
|
||||
scopeDst := sdef.New()
|
||||
if err := json.Unmarshal(rawScope, scopeDst); err != nil {
|
||||
return nil, fmt.Errorf("invalid scope JSON: %w", err)
|
||||
}
|
||||
if err := sdef.Validate(scopeDst); err != nil {
|
||||
return nil, fmt.Errorf("invalid scope: %w", err)
|
||||
}
|
||||
|
||||
// 3) canonicalize scope (also what we persist in plaintext)
|
||||
canonScope, err := canonicalJSON(scopeDst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fp := sha256Hex(canonScope) // or HMAC if you have a server-side key
|
||||
|
||||
// 4) canonicalize + encrypt secret
|
||||
canonSecret, err := canonicalJSON(secretDst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipher, iv, tag, err := utils.EncryptForOrg(orgID, canonSecret, db)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encrypt: %w", err)
|
||||
}
|
||||
|
||||
cred := &models.Credential{
|
||||
OrganizationID: orgID,
|
||||
Provider: provider,
|
||||
Kind: kind,
|
||||
SchemaVersion: schemaVersion,
|
||||
Name: name,
|
||||
ScopeKind: scopeKind,
|
||||
Scope: datatypes.JSON(canonScope),
|
||||
ScopeVersion: scopeVersion,
|
||||
AccountID: accountID,
|
||||
Region: region,
|
||||
ScopeFingerprint: fp,
|
||||
EncryptedData: cipher,
|
||||
IV: iv,
|
||||
Tag: tag,
|
||||
}
|
||||
|
||||
if err := db.WithContext(ctx).Create(cred).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
// credOut converts model → response DTO
|
||||
func credOut(c *models.Credential) dto.CredentialOut {
|
||||
return dto.CredentialOut{
|
||||
ID: c.ID.String(),
|
||||
Provider: c.Provider,
|
||||
Kind: c.Kind,
|
||||
SchemaVersion: c.SchemaVersion,
|
||||
Name: c.Name,
|
||||
ScopeKind: c.ScopeKind,
|
||||
ScopeVersion: c.ScopeVersion,
|
||||
Scope: dto.RawJSON(c.Scope),
|
||||
AccountID: c.AccountID,
|
||||
Region: c.Region,
|
||||
CreatedAt: c.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: c.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
802
internal/handlers/dns.go
Normal file
802
internal/handlers/dns.go
Normal file
@@ -0,0 +1,802 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/handlers/dto"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ---------- Helpers ----------
|
||||
|
||||
func normLowerNoDot(s string) string {
|
||||
s = strings.TrimSpace(strings.ToLower(s))
|
||||
return strings.TrimSuffix(s, ".")
|
||||
}
|
||||
|
||||
func fqdn(domain string, rel string) string {
|
||||
d := normLowerNoDot(domain)
|
||||
r := normLowerNoDot(rel)
|
||||
if r == "" || r == "@" {
|
||||
return d
|
||||
}
|
||||
return r + "." + d
|
||||
}
|
||||
|
||||
func canonicalJSONAny(v any) ([]byte, error) {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var anyv any
|
||||
if err := json.Unmarshal(b, &anyv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return marshalSortedDNS(anyv)
|
||||
}
|
||||
|
||||
func marshalSortedDNS(v any) ([]byte, error) {
|
||||
switch vv := v.(type) {
|
||||
case map[string]any:
|
||||
keys := make([]string, 0, len(vv))
|
||||
for k := range vv {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sortStrings(keys)
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('{')
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
kb, _ := json.Marshal(k)
|
||||
buf.Write(kb)
|
||||
buf.WriteByte(':')
|
||||
b, err := marshalSortedDNS(vv[k])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
case []any:
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('[')
|
||||
for i, e := range vv {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
b, err := marshalSortedDNS(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(b)
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.Bytes(), nil
|
||||
default:
|
||||
return json.Marshal(v)
|
||||
}
|
||||
}
|
||||
|
||||
func sortStrings(a []string) {
|
||||
for i := 0; i < len(a); i++ {
|
||||
for j := i + 1; j < len(a); j++ {
|
||||
if a[j] < a[i] {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sha256HexBytes(b []byte) string {
|
||||
sum := sha256.Sum256(b)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
/* Fingerprint (provider-agnostic) */
|
||||
type desiredRecord struct {
|
||||
ZoneID string `json:"zone_id"`
|
||||
FQDN string `json:"fqdn"`
|
||||
Type string `json:"type"`
|
||||
TTL *int `json:"ttl,omitempty"`
|
||||
Values []string `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
func computeFingerprint(zoneID, fqdn, typ string, ttl *int, values datatypes.JSON) (string, error) {
|
||||
var vals []string
|
||||
if len(values) > 0 && string(values) != "null" {
|
||||
if err := json.Unmarshal(values, &vals); err != nil {
|
||||
return "", err
|
||||
}
|
||||
sortStrings(vals)
|
||||
}
|
||||
payload := &desiredRecord{
|
||||
ZoneID: zoneID, FQDN: fqdn, Type: strings.ToUpper(typ), TTL: ttl, Values: vals,
|
||||
}
|
||||
can, err := canonicalJSONAny(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sha256HexBytes(can), nil
|
||||
}
|
||||
|
||||
func mustSameOrgDomainWithCredential(db *gorm.DB, orgID uuid.UUID, credID uuid.UUID) error {
|
||||
var cred models.Credential
|
||||
if err := db.Where("id = ? AND organization_id = ?", credID, orgID).First(&cred).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fmt.Errorf("credential not found or belongs to different org")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if cred.Provider != "aws" || cred.ScopeKind != "service" {
|
||||
return fmt.Errorf("credential must be AWS Route 53 service scoped")
|
||||
}
|
||||
var scope map[string]any
|
||||
if err := json.Unmarshal(cred.Scope, &scope); err != nil {
|
||||
return fmt.Errorf("credential scope invalid json: %w", err)
|
||||
}
|
||||
if strings.ToLower(fmt.Sprint(scope["service"])) != "route53" {
|
||||
return fmt.Errorf("credential scope.service must be route53")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------- Domain Handlers ----------
|
||||
|
||||
// ListDomains godoc
|
||||
//
|
||||
// @ID ListDomains
|
||||
// @Summary List domains (org scoped)
|
||||
// @Description Returns domains for X-Org-ID. Filters: `domain_name`, `status`, `q` (contains).
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param domain_name query string false "Exact domain name (lowercase, no trailing dot)"
|
||||
// @Param status query string false "pending|provisioning|ready|failed"
|
||||
// @Param q query string false "Domain contains (case-insensitive)"
|
||||
// @Success 200 {array} dto.DomainResponse
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "db error"
|
||||
// @Router /dns/domains [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func ListDomains(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
q := db.Model(&models.Domain{}).Where("organization_id = ?", orgID)
|
||||
if v := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("domain_name"))); v != "" {
|
||||
q = q.Where("LOWER(domain_name) = ?", v)
|
||||
}
|
||||
if v := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("status"))); v != "" {
|
||||
q = q.Where("status = ?", v)
|
||||
}
|
||||
if needle := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("q"))); needle != "" {
|
||||
q = q.Where("LOWER(domain_name) LIKE ?", "%"+needle+"%")
|
||||
}
|
||||
|
||||
var rows []models.Domain
|
||||
if err := q.Order("created_at DESC").Find(&rows).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
out := make([]dto.DomainResponse, 0, len(rows))
|
||||
for i := range rows {
|
||||
out = append(out, domainOut(&rows[i]))
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
|
||||
// GetDomain godoc
|
||||
//
|
||||
// @ID GetDomain
|
||||
// @Summary Get a domain (org scoped)
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param id path string true "Domain ID (UUID)"
|
||||
// @Success 200 {object} dto.DomainResponse
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /dns/domains/{id} [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func GetDomain(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
var row models.Domain
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, id).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "domain not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, domainOut(&row))
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDomain godoc
|
||||
//
|
||||
// @ID CreateDomain
|
||||
// @Summary Create a domain (org scoped)
|
||||
// @Description Creates a domain bound to a Route 53 scoped credential. Archer will backfill ZoneID if omitted.
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param body body dto.CreateDomainRequest true "Domain payload"
|
||||
// @Success 201 {object} dto.DomainResponse
|
||||
// @Failure 400 {string} string "validation error"
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "db error"
|
||||
// @Router /dns/domains [post]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func CreateDomain(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
var in dto.CreateDomainRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_json", err.Error())
|
||||
return
|
||||
}
|
||||
if err := dto.DNSValidate(in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", err.Error())
|
||||
return
|
||||
}
|
||||
credID, _ := uuid.Parse(in.CredentialID)
|
||||
if err := mustSameOrgDomainWithCredential(db, orgID, credID); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "invalid_credential", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
row := &models.Domain{
|
||||
OrganizationID: orgID,
|
||||
DomainName: normLowerNoDot(in.DomainName),
|
||||
ZoneID: strings.TrimSpace(in.ZoneID),
|
||||
Status: "pending",
|
||||
LastError: "",
|
||||
CredentialID: credID,
|
||||
}
|
||||
if err := db.Create(row).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusCreated, domainOut(row))
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateDomain godoc
|
||||
//
|
||||
// @ID UpdateDomain
|
||||
// @Summary Update a domain (org scoped)
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param id path string true "Domain ID (UUID)"
|
||||
// @Param body body dto.UpdateDomainRequest true "Fields to update"
|
||||
// @Success 200 {object} dto.DomainResponse
|
||||
// @Failure 400 {string} string "validation error"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /dns/domains/{id} [patch]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func UpdateDomain(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
var row models.Domain
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, id).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "domain not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
var in dto.UpdateDomainRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_json", err.Error())
|
||||
return
|
||||
}
|
||||
if err := dto.DNSValidate(in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", err.Error())
|
||||
return
|
||||
}
|
||||
if in.DomainName != nil {
|
||||
row.DomainName = normLowerNoDot(*in.DomainName)
|
||||
}
|
||||
if in.CredentialID != nil {
|
||||
credID, _ := uuid.Parse(*in.CredentialID)
|
||||
if err := mustSameOrgDomainWithCredential(db, orgID, credID); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "invalid_credential", err.Error())
|
||||
return
|
||||
}
|
||||
row.CredentialID = credID
|
||||
row.Status = "pending"
|
||||
row.LastError = ""
|
||||
}
|
||||
if in.ZoneID != nil {
|
||||
row.ZoneID = strings.TrimSpace(*in.ZoneID)
|
||||
}
|
||||
if in.Status != nil {
|
||||
row.Status = *in.Status
|
||||
if row.Status == "pending" {
|
||||
row.LastError = ""
|
||||
}
|
||||
}
|
||||
if err := db.Save(&row).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, domainOut(&row))
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteDomain godoc
|
||||
//
|
||||
// @ID DeleteDomain
|
||||
// @Summary Delete a domain
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param id path string true "Domain ID (UUID)"
|
||||
// @Success 204
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /dns/domains/{id} [delete]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func DeleteDomain(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
res := db.Where("organization_id = ? AND id = ?", orgID, id).Delete(&models.Domain{})
|
||||
if res.Error != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", res.Error.Error())
|
||||
return
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "domain not found")
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Record Set Handlers ----------
|
||||
|
||||
// ListRecordSets godoc
|
||||
//
|
||||
// @ID ListRecordSets
|
||||
// @Summary List record sets for a domain
|
||||
// @Description Filters: `name`, `type`, `status`.
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param domain_id path string true "Domain ID (UUID)"
|
||||
// @Param name query string false "Exact relative name or FQDN (server normalizes)"
|
||||
// @Param type query string false "RR type (A, AAAA, CNAME, TXT, MX, NS, SRV, CAA)"
|
||||
// @Param status query string false "pending|provisioning|ready|failed"
|
||||
// @Success 200 {array} dto.RecordSetResponse
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "domain not found"
|
||||
// @Router /dns/domains/{domain_id}/records [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func ListRecordSets(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
did, err := uuid.Parse(chi.URLParam(r, "domain_id"))
|
||||
if err != nil {
|
||||
log.Info().Msg(err.Error())
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid domain UUID:")
|
||||
return
|
||||
}
|
||||
var domain models.Domain
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, did).First(&domain).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "domain not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
|
||||
q := db.Model(&models.RecordSet{}).Where("domain_id = ?", did)
|
||||
if v := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("name"))); v != "" {
|
||||
dn := strings.ToLower(domain.DomainName)
|
||||
rel := v
|
||||
// normalize apex or FQDN into relative
|
||||
if v == dn || v == dn+"." {
|
||||
rel = ""
|
||||
} else {
|
||||
rel = strings.TrimSuffix(v, "."+dn)
|
||||
rel = normLowerNoDot(rel)
|
||||
}
|
||||
q = q.Where("LOWER(name) = ?", rel)
|
||||
}
|
||||
if v := strings.TrimSpace(strings.ToUpper(r.URL.Query().Get("type"))); v != "" {
|
||||
q = q.Where("type = ?", v)
|
||||
}
|
||||
if v := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("status"))); v != "" {
|
||||
q = q.Where("status = ?", v)
|
||||
}
|
||||
|
||||
var rows []models.RecordSet
|
||||
if err := q.Order("created_at DESC").Find(&rows).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
out := make([]dto.RecordSetResponse, 0, len(rows))
|
||||
for i := range rows {
|
||||
out = append(out, recordOut(&rows[i]))
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateRecordSet godoc
|
||||
//
|
||||
// @ID CreateRecordSet
|
||||
// @Summary Create a record set (pending; Archer will UPSERT to Route 53)
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param domain_id path string true "Domain ID (UUID)"
|
||||
// @Param body body dto.CreateRecordSetRequest true "Record set payload"
|
||||
// @Success 201 {object} dto.RecordSetResponse
|
||||
// @Failure 400 {string} string "validation error"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "domain not found"
|
||||
// @Router /dns/domains/{domain_id}/records [post]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func CreateRecordSet(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
did, err := uuid.Parse(chi.URLParam(r, "domain_id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid domain UUID")
|
||||
return
|
||||
}
|
||||
var domain models.Domain
|
||||
if err := db.Where("organization_id = ? AND id = ?", orgID, did).First(&domain).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "domain not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var in dto.CreateRecordSetRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_json", err.Error())
|
||||
return
|
||||
}
|
||||
if err := dto.DNSValidate(in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", err.Error())
|
||||
return
|
||||
}
|
||||
t := strings.ToUpper(in.Type)
|
||||
if t == "CNAME" && len(in.Values) != 1 {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", "CNAME requires exactly one value")
|
||||
return
|
||||
}
|
||||
|
||||
rel := normLowerNoDot(in.Name)
|
||||
fq := fqdn(domain.DomainName, rel)
|
||||
|
||||
// Pre-flight: block duplicate tuple and protect from non-autoglue rows
|
||||
var existing models.RecordSet
|
||||
if err := db.Where("domain_id = ? AND LOWER(name) = ? AND type = ?",
|
||||
domain.ID, strings.ToLower(rel), t).First(&existing).Error; err == nil {
|
||||
if existing.Owner != "" && existing.Owner != "autoglue" {
|
||||
utils.WriteError(w, http.StatusConflict, "ownership_conflict",
|
||||
"record with the same (name,type) exists but is not owned by autoglue")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusConflict, "already_exists",
|
||||
"a record with the same (name,type) already exists; use PATCH to modify")
|
||||
return
|
||||
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
valuesJSON, _ := json.Marshal(in.Values)
|
||||
fp, err := computeFingerprint(domain.ZoneID, fq, t, in.TTL, datatypes.JSON(valuesJSON))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "fingerprint_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
row := &models.RecordSet{
|
||||
DomainID: domain.ID,
|
||||
Name: rel,
|
||||
Type: t,
|
||||
TTL: in.TTL,
|
||||
Values: datatypes.JSON(valuesJSON),
|
||||
Fingerprint: fp,
|
||||
Status: "pending",
|
||||
LastError: "",
|
||||
Owner: "autoglue",
|
||||
}
|
||||
if err := db.Create(row).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusCreated, recordOut(row))
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRecordSet godoc
|
||||
//
|
||||
// @ID UpdateRecordSet
|
||||
// @Summary Update a record set (flips to pending for reconciliation)
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param id path string true "Record Set ID (UUID)"
|
||||
// @Param body body dto.UpdateRecordSetRequest true "Fields to update"
|
||||
// @Success 200 {object} dto.RecordSetResponse
|
||||
// @Failure 400 {string} string "validation error"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /dns/records/{id} [patch]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func UpdateRecordSet(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
|
||||
var row models.RecordSet
|
||||
if err := db.
|
||||
Joins("Domain").
|
||||
Where(`record_sets.id = ? AND "Domain"."organization_id" = ?`, id, orgID).
|
||||
First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "record set not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
var domain models.Domain
|
||||
if err := db.Where("id = ?", row.DomainID).First(&domain).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var in dto.UpdateRecordSetRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_json", err.Error())
|
||||
return
|
||||
}
|
||||
if err := dto.DNSValidate(in); err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", err.Error())
|
||||
return
|
||||
}
|
||||
if row.Owner != "" && row.Owner != "autoglue" {
|
||||
utils.WriteError(w, http.StatusConflict, "ownership_conflict",
|
||||
"record is not owned by autoglue; refuse to modify")
|
||||
return
|
||||
}
|
||||
|
||||
// Mutations
|
||||
if in.Name != nil {
|
||||
row.Name = normLowerNoDot(*in.Name)
|
||||
}
|
||||
if in.Type != nil {
|
||||
row.Type = strings.ToUpper(*in.Type)
|
||||
}
|
||||
if in.TTL != nil {
|
||||
row.TTL = in.TTL
|
||||
}
|
||||
if in.Values != nil {
|
||||
t := row.Type
|
||||
if in.Type != nil {
|
||||
t = strings.ToUpper(*in.Type)
|
||||
}
|
||||
if t == "CNAME" && len(*in.Values) != 1 {
|
||||
utils.WriteError(w, http.StatusBadRequest, "validation_error", "CNAME requires exactly one value")
|
||||
return
|
||||
}
|
||||
b, _ := json.Marshal(*in.Values)
|
||||
row.Values = datatypes.JSON(b)
|
||||
}
|
||||
|
||||
if in.Status != nil {
|
||||
row.Status = *in.Status
|
||||
} else {
|
||||
row.Status = "pending"
|
||||
row.LastError = ""
|
||||
}
|
||||
|
||||
fq := fqdn(domain.DomainName, row.Name)
|
||||
fp, err := computeFingerprint(domain.ZoneID, fq, row.Type, row.TTL, row.Values)
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "fingerprint_error", err.Error())
|
||||
return
|
||||
}
|
||||
row.Fingerprint = fp
|
||||
|
||||
if err := db.Save(&row).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, recordOut(&row))
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteRecordSet godoc
|
||||
//
|
||||
// @ID DeleteRecordSet
|
||||
// @Summary Delete a record set (API removes row; worker can optionally handle external deletion policy)
|
||||
// @Tags DNS
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param id path string true "Record Set ID (UUID)"
|
||||
// @Success 204
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Router /dns/records/{id} [delete]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func DeleteRecordSet(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "bad_id", "invalid UUID")
|
||||
return
|
||||
}
|
||||
sub := db.Model(&models.RecordSet{}).
|
||||
Select("record_sets.id").
|
||||
Joins("JOIN domains ON domains.id = record_sets.domain_id").
|
||||
Where("record_sets.id = ? AND domains.organization_id = ?", id, orgID)
|
||||
|
||||
res := db.Where("id IN (?)", sub).Delete(&models.RecordSet{})
|
||||
if res.Error != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", res.Error.Error())
|
||||
return
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
utils.WriteError(w, http.StatusNotFound, "not_found", "record set not found")
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Out mappers ----------
|
||||
|
||||
func domainOut(m *models.Domain) dto.DomainResponse {
|
||||
return dto.DomainResponse{
|
||||
ID: m.ID.String(),
|
||||
OrganizationID: m.OrganizationID.String(),
|
||||
DomainName: m.DomainName,
|
||||
ZoneID: m.ZoneID,
|
||||
Status: m.Status,
|
||||
LastError: m.LastError,
|
||||
CredentialID: m.CredentialID.String(),
|
||||
CreatedAt: m.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: m.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
func recordOut(r *models.RecordSet) dto.RecordSetResponse {
|
||||
vals := r.Values
|
||||
if len(vals) == 0 {
|
||||
vals = datatypes.JSON("[]")
|
||||
}
|
||||
return dto.RecordSetResponse{
|
||||
ID: r.ID.String(),
|
||||
DomainID: r.DomainID.String(),
|
||||
Name: r.Name,
|
||||
Type: r.Type,
|
||||
TTL: r.TTL,
|
||||
Values: []byte(vals),
|
||||
Fingerprint: r.Fingerprint,
|
||||
Status: r.Status,
|
||||
LastError: r.LastError,
|
||||
Owner: r.Owner,
|
||||
CreatedAt: r.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: r.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
34
internal/handlers/dto/clusters.go
Normal file
34
internal/handlers/dto/clusters.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ClusterResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Region string `json:"region"`
|
||||
Status string `json:"status"`
|
||||
CaptainDomain string `json:"captain_domain"`
|
||||
ClusterLoadBalancer string `json:"cluster_load_balancer"`
|
||||
RandomToken string `json:"random_token"`
|
||||
CertificateKey string `json:"certificate_key"`
|
||||
ControlLoadBalancer string `json:"control_load_balancer"`
|
||||
NodePools []NodePoolResponse `json:"node_pools,omitempty"`
|
||||
BastionServer *ServerResponse `json:"bastion_server,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type CreateClusterRequest struct {
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Region string `json:"region"`
|
||||
Status string `json:"status"`
|
||||
CaptainDomain string `json:"captain_domain"`
|
||||
ClusterLoadBalancer *string `json:"cluster_load_balancer"`
|
||||
ControlLoadBalancer *string `json:"control_load_balancer"`
|
||||
}
|
||||
138
internal/handlers/dto/credentials.go
Normal file
138
internal/handlers/dto/credentials.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// RawJSON is a swagger-friendly wrapper for json.RawMessage.
|
||||
type RawJSON = json.RawMessage
|
||||
|
||||
var Validate = validator.New()
|
||||
|
||||
func init() {
|
||||
_ = Validate.RegisterValidation("awsarn", func(fl validator.FieldLevel) bool {
|
||||
v := fl.Field().String()
|
||||
return len(v) > 10 && len(v) < 2048 && len(v) >= 4 && v[:4] == "arn:"
|
||||
})
|
||||
}
|
||||
|
||||
/*** Shapes for secrets ***/
|
||||
|
||||
type AWSCredential struct {
|
||||
AccessKeyID string `json:"access_key_id" validate:"required,alphanum,len=20"`
|
||||
SecretAccessKey string `json:"secret_access_key" validate:"required"`
|
||||
Region string `json:"region" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type BasicAuth struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
type APIToken struct {
|
||||
Token string `json:"token" validate:"required"`
|
||||
}
|
||||
|
||||
type OAuth2Credential struct {
|
||||
ClientID string `json:"client_id" validate:"required"`
|
||||
ClientSecret string `json:"client_secret" validate:"required"`
|
||||
RefreshToken string `json:"refresh_token" validate:"required"`
|
||||
}
|
||||
|
||||
/*** Shapes for scopes ***/
|
||||
|
||||
type AWSProviderScope struct{}
|
||||
|
||||
type AWSServiceScope struct {
|
||||
Service string `json:"service" validate:"required,oneof=route53 s3 ec2 iam rds dynamodb"`
|
||||
}
|
||||
|
||||
type AWSResourceScope struct {
|
||||
ARN string `json:"arn" validate:"required,awsarn"`
|
||||
}
|
||||
|
||||
/*** Registries ***/
|
||||
|
||||
type ProviderDef struct {
|
||||
New func() any
|
||||
Validate func(any) error
|
||||
}
|
||||
|
||||
type ScopeDef struct {
|
||||
New func() any
|
||||
Validate func(any) error
|
||||
Specificity int // 0=provider, 1=service, 2=resource
|
||||
}
|
||||
|
||||
// Secret shapes per provider/kind/version
|
||||
|
||||
var CredentialRegistry = map[string]map[string]map[int]ProviderDef{
|
||||
"aws": {
|
||||
"aws_access_key": {
|
||||
1: {New: func() any { return &AWSCredential{} }, Validate: func(x any) error { return Validate.Struct(x) }},
|
||||
},
|
||||
},
|
||||
"cloudflare": {"api_token": {1: {New: func() any { return &APIToken{} }, Validate: func(x any) error { return Validate.Struct(x) }}}},
|
||||
"hetzner": {"api_token": {1: {New: func() any { return &APIToken{} }, Validate: func(x any) error { return Validate.Struct(x) }}}},
|
||||
"digitalocean": {"api_token": {1: {New: func() any { return &APIToken{} }, Validate: func(x any) error { return Validate.Struct(x) }}}},
|
||||
"generic": {
|
||||
"basic_auth": {1: {New: func() any { return &BasicAuth{} }, Validate: func(x any) error { return Validate.Struct(x) }}},
|
||||
"oauth2": {1: {New: func() any { return &OAuth2Credential{} }, Validate: func(x any) error { return Validate.Struct(x) }}},
|
||||
},
|
||||
}
|
||||
|
||||
// Scope shapes per provider/scopeKind/version
|
||||
|
||||
var ScopeRegistry = map[string]map[string]map[int]ScopeDef{
|
||||
"aws": {
|
||||
"provider": {1: {New: func() any { return &AWSProviderScope{} }, Validate: func(any) error { return nil }, Specificity: 0}},
|
||||
"service": {1: {New: func() any { return &AWSServiceScope{} }, Validate: func(x any) error { return Validate.Struct(x) }, Specificity: 1}},
|
||||
"resource": {1: {New: func() any { return &AWSResourceScope{} }, Validate: func(x any) error { return Validate.Struct(x) }, Specificity: 2}},
|
||||
},
|
||||
}
|
||||
|
||||
/*** API DTOs used by swagger ***/
|
||||
|
||||
// CreateCredentialRequest represents the POST /credentials payload
|
||||
type CreateCredentialRequest struct {
|
||||
Provider string `json:"provider" validate:"required,oneof=aws cloudflare hetzner digitalocean generic"`
|
||||
Kind string `json:"kind" validate:"required"` // aws_access_key, api_token, basic_auth, oauth2
|
||||
SchemaVersion int `json:"schema_version" validate:"required,gte=1"` // secret schema version
|
||||
Name string `json:"name" validate:"omitempty,max=100"` // human label
|
||||
ScopeKind string `json:"scope_kind" validate:"required,oneof=provider service resource"`
|
||||
ScopeVersion int `json:"scope_version" validate:"required,gte=1"` // scope schema version
|
||||
Scope RawJSON `json:"scope" validate:"required" swaggertype:"object"` // {"service":"route53"} or {"arn":"..."}
|
||||
AccountID string `json:"account_id,omitempty" validate:"omitempty,max=32"`
|
||||
Region string `json:"region,omitempty" validate:"omitempty,max=32"`
|
||||
Secret RawJSON `json:"secret" validate:"required" swaggertype:"object"` // encrypted later
|
||||
}
|
||||
|
||||
// UpdateCredentialRequest represents PATCH /credentials/{id}
|
||||
type UpdateCredentialRequest struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
AccountID *string `json:"account_id,omitempty"`
|
||||
Region *string `json:"region,omitempty"`
|
||||
ScopeKind *string `json:"scope_kind,omitempty"`
|
||||
ScopeVersion *int `json:"scope_version,omitempty"`
|
||||
Scope *RawJSON `json:"scope,omitempty" swaggertype:"object"`
|
||||
Secret *RawJSON `json:"secret,omitempty" swaggertype:"object"` // set if rotating
|
||||
|
||||
}
|
||||
|
||||
// CredentialOut is what we return (no secrets)
|
||||
type CredentialOut struct {
|
||||
ID string `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
Kind string `json:"kind"`
|
||||
SchemaVersion int `json:"schema_version"`
|
||||
Name string `json:"name"`
|
||||
ScopeKind string `json:"scope_kind"`
|
||||
ScopeVersion int `json:"scope_version"`
|
||||
Scope RawJSON `json:"scope" swaggertype:"object"`
|
||||
AccountID string `json:"account_id,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
103
internal/handlers/dto/dns.go
Normal file
103
internal/handlers/dto/dns.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
var dnsValidate = validator.New()
|
||||
|
||||
func init() {
|
||||
_ = dnsValidate.RegisterValidation("fqdn", func(fl validator.FieldLevel) bool {
|
||||
s := strings.TrimSpace(fl.Field().String())
|
||||
if s == "" || len(s) > 253 {
|
||||
return false
|
||||
}
|
||||
// Minimal: lower-cased, no trailing dot in our API (normalize server-side)
|
||||
// You can add stricter checks later.
|
||||
return !strings.HasPrefix(s, ".") && !strings.Contains(s, "..")
|
||||
})
|
||||
_ = dnsValidate.RegisterValidation("rrtype", func(fl validator.FieldLevel) bool {
|
||||
switch strings.ToUpper(fl.Field().String()) {
|
||||
case "A", "AAAA", "CNAME", "TXT", "MX", "NS", "SRV", "CAA":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ---- Domains ----
|
||||
|
||||
type CreateDomainRequest struct {
|
||||
DomainName string `json:"domain_name" validate:"required,fqdn"`
|
||||
CredentialID string `json:"credential_id" validate:"required,uuid4"`
|
||||
ZoneID string `json:"zone_id,omitempty" validate:"omitempty,max=128"`
|
||||
}
|
||||
|
||||
type UpdateDomainRequest struct {
|
||||
CredentialID *string `json:"credential_id,omitempty" validate:"omitempty,uuid4"`
|
||||
ZoneID *string `json:"zone_id,omitempty" validate:"omitempty,max=128"`
|
||||
Status *string `json:"status,omitempty" validate:"omitempty,oneof=pending provisioning ready failed"`
|
||||
DomainName *string `json:"domain_name,omitempty" validate:"omitempty,fqdn"`
|
||||
}
|
||||
|
||||
type DomainResponse struct {
|
||||
ID string `json:"id"`
|
||||
OrganizationID string `json:"organization_id"`
|
||||
DomainName string `json:"domain_name"`
|
||||
ZoneID string `json:"zone_id"`
|
||||
Status string `json:"status"`
|
||||
LastError string `json:"last_error"`
|
||||
CredentialID string `json:"credential_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ---- Record Sets ----
|
||||
|
||||
type AliasTarget struct {
|
||||
HostedZoneID string `json:"hosted_zone_id" validate:"required"`
|
||||
DNSName string `json:"dns_name" validate:"required"`
|
||||
EvaluateTargetHealth bool `json:"evaluate_target_health"`
|
||||
}
|
||||
|
||||
type CreateRecordSetRequest struct {
|
||||
// Name relative to domain ("endpoint") OR FQDN ("endpoint.example.com").
|
||||
// Server normalizes to relative.
|
||||
Name string `json:"name" validate:"required,max=253"`
|
||||
Type string `json:"type" validate:"required,rrtype"`
|
||||
TTL *int `json:"ttl,omitempty" validate:"omitempty,gte=1,lte=86400"`
|
||||
Values []string `json:"values" validate:"omitempty,dive,min=1,max=1024"`
|
||||
}
|
||||
|
||||
type UpdateRecordSetRequest struct {
|
||||
// Any change flips status back to pending (worker will UPSERT)
|
||||
Name *string `json:"name,omitempty" validate:"omitempty,max=253"`
|
||||
Type *string `json:"type,omitempty" validate:"omitempty,rrtype"`
|
||||
TTL *int `json:"ttl,omitempty" validate:"omitempty,gte=1,lte=86400"`
|
||||
Values *[]string `json:"values,omitempty" validate:"omitempty,dive,min=1,max=1024"`
|
||||
Status *string `json:"status,omitempty" validate:"omitempty,oneof=pending provisioning ready failed"`
|
||||
}
|
||||
|
||||
type RecordSetResponse struct {
|
||||
ID string `json:"id"`
|
||||
DomainID string `json:"domain_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TTL *int `json:"ttl,omitempty"`
|
||||
Values json.RawMessage `json:"values" swaggertype:"object"` // []string JSON
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
Status string `json:"status"`
|
||||
LastError string `json:"last_error"`
|
||||
Owner string `json:"owner"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// DNSValidate Quick helper to validate DTOs in handlers
|
||||
func DNSValidate(i any) error {
|
||||
return dnsValidate.Struct(i)
|
||||
}
|
||||
@@ -57,6 +57,6 @@ type PageJob struct {
|
||||
type EnqueueRequest struct {
|
||||
Queue string `json:"queue" example:"default"`
|
||||
Type string `json:"type" example:"email.send"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
Payload json.RawMessage `json:"payload" swaggertype:"object"`
|
||||
RunAt *time.Time `json:"run_at" example:"2025-11-05T08:00:00Z"`
|
||||
}
|
||||
|
||||
46
internal/handlers/dto/node_pools.go
Normal file
46
internal/handlers/dto/node_pools.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package dto
|
||||
|
||||
import "github.com/glueops/autoglue/internal/common"
|
||||
|
||||
type NodeRole string
|
||||
|
||||
const (
|
||||
NodeRoleMaster NodeRole = "master"
|
||||
NodeRoleWorker NodeRole = "worker"
|
||||
)
|
||||
|
||||
type CreateNodePoolRequest struct {
|
||||
Name string `json:"name"`
|
||||
Role NodeRole `json:"role" enums:"master,worker" swaggertype:"string"`
|
||||
}
|
||||
|
||||
type UpdateNodePoolRequest struct {
|
||||
Name *string `json:"name"`
|
||||
Role *NodeRole `json:"role" enums:"master,worker" swaggertype:"string"`
|
||||
}
|
||||
|
||||
type NodePoolResponse struct {
|
||||
common.AuditFields
|
||||
Name string `json:"name"`
|
||||
Role NodeRole `json:"role" enums:"master,worker" swaggertype:"string"`
|
||||
Servers []ServerResponse `json:"servers"`
|
||||
Annotations []AnnotationResponse `json:"annotations"`
|
||||
Labels []LabelResponse `json:"labels"`
|
||||
Taints []TaintResponse `json:"taints"`
|
||||
}
|
||||
|
||||
type AttachServersRequest struct {
|
||||
ServerIDs []string `json:"server_ids"`
|
||||
}
|
||||
|
||||
type AttachTaintsRequest struct {
|
||||
TaintIDs []string `json:"taint_ids"`
|
||||
}
|
||||
|
||||
type AttachLabelsRequest struct {
|
||||
LabelIDs []string `json:"label_ids"`
|
||||
}
|
||||
|
||||
type AttachAnnotationsRequest struct {
|
||||
AnnotationIDs []string `json:"annotation_ids"`
|
||||
}
|
||||
@@ -8,8 +8,8 @@ type CreateServerRequest struct {
|
||||
PrivateIPAddress string `json:"private_ip_address"`
|
||||
SSHUser string `json:"ssh_user"`
|
||||
SshKeyID string `json:"ssh_key_id"`
|
||||
Role string `json:"role" example:"master|worker|bastion"`
|
||||
Status string `json:"status,omitempty" example:"pending|provisioning|ready|failed"`
|
||||
Role string `json:"role" example:"master|worker|bastion" enums:"master,worker,bastion"`
|
||||
Status string `json:"status,omitempty" example:"pending|provisioning|ready|failed" enums:"pending,provisioning,ready,failed"`
|
||||
}
|
||||
|
||||
type UpdateServerRequest struct {
|
||||
@@ -18,8 +18,8 @@ type UpdateServerRequest struct {
|
||||
PrivateIPAddress *string `json:"private_ip_address,omitempty"`
|
||||
SSHUser *string `json:"ssh_user,omitempty"`
|
||||
SshKeyID *string `json:"ssh_key_id,omitempty"`
|
||||
Role *string `json:"role,omitempty" example:"master|worker|bastion"`
|
||||
Status *string `json:"status,omitempty" example:"pending|provisioning|ready|failed"`
|
||||
Role *string `json:"role" example:"master|worker|bastion" enums:"master,worker,bastion"`
|
||||
Status *string `json:"status,omitempty" example:"pending|provisioning|ready|failed" enums:"pending,provisioning,ready,failed"`
|
||||
}
|
||||
|
||||
type ServerResponse struct {
|
||||
@@ -30,8 +30,8 @@ type ServerResponse struct {
|
||||
PrivateIPAddress string `json:"private_ip_address"`
|
||||
SSHUser string `json:"ssh_user"`
|
||||
SshKeyID uuid.UUID `json:"ssh_key_id"`
|
||||
Role string `json:"role"`
|
||||
Status string `json:"status"`
|
||||
Role string `json:"role" example:"master|worker|bastion" enums:"master,worker,bastion"`
|
||||
Status string `json:"status,omitempty" example:"pending|provisioning|ready|failed" enums:"pending,provisioning,ready,failed"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
1270
internal/handlers/node_pools.go
Normal file
1270
internal/handlers/node_pools.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -370,6 +370,63 @@ func DeleteServer(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// ResetServerHostKey godoc
|
||||
//
|
||||
// @ID ResetServerHostKey
|
||||
// @Summary Reset SSH host key (org scoped)
|
||||
// @Description Clears the stored SSH host key for this server. The next SSH connection will re-learn the host key (trust-on-first-use).
|
||||
// @Tags Servers
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param id path string true "Server ID (UUID)"
|
||||
// @Success 200 {object} dto.ServerResponse
|
||||
// @Failure 400 {string} string "invalid id"
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 404 {string} string "not found"
|
||||
// @Failure 500 {string} string "reset failed"
|
||||
// @Router /servers/{id}/reset-hostkey [post]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
func ResetServerHostKey(db *gorm.DB) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
||||
if !ok {
|
||||
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
||||
return
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
utils.WriteError(w, http.StatusBadRequest, "id_invalid", "invalid id")
|
||||
return
|
||||
}
|
||||
|
||||
var server models.Server
|
||||
if err := db.Where("id = ? AND organization_id = ?", id, orgID).First(&server).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.WriteError(w, http.StatusNotFound, "server_not_found", "server not found")
|
||||
return
|
||||
}
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "failed to get server")
|
||||
return
|
||||
}
|
||||
|
||||
// Clear stored host key so next SSH handshake will TOFU and persist a new one.
|
||||
server.SSHHostKey = ""
|
||||
server.SSHHostKeyAlgo = ""
|
||||
|
||||
if err := db.Save(&server).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "failed to reset host key")
|
||||
return
|
||||
}
|
||||
|
||||
utils.WriteJSON(w, http.StatusOK, server)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
func validStatus(status string) bool {
|
||||
|
||||
@@ -62,6 +62,9 @@ func ListPublicSshKeys(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if out == nil {
|
||||
out = []dto.SshResponse{}
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
@@ -379,7 +382,6 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
|
||||
if mode == "json" {
|
||||
prefix := keyFilenamePrefix(key.PublicKey)
|
||||
resp := dto.SshMaterialJSON{
|
||||
ID: key.ID.String(),
|
||||
Name: key.Name,
|
||||
@@ -389,7 +391,7 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
case "public":
|
||||
pub := key.PublicKey
|
||||
resp.PublicKey = &pub
|
||||
resp.Filenames = []string{fmt.Sprintf("%s_%s.pub", prefix, key.ID.String())}
|
||||
resp.Filenames = []string{fmt.Sprintf("%s.pub", key.ID.String())}
|
||||
utils.WriteJSON(w, http.StatusOK, resp)
|
||||
return
|
||||
|
||||
@@ -400,7 +402,7 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
resp.PrivatePEM = &plain
|
||||
resp.Filenames = []string{fmt.Sprintf("%s_%s.pem", prefix, key.ID.String())}
|
||||
resp.Filenames = []string{fmt.Sprintf("%s.pem", key.ID.String())}
|
||||
utils.WriteJSON(w, http.StatusOK, resp)
|
||||
return
|
||||
|
||||
@@ -413,16 +415,16 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
|
||||
var buf bytes.Buffer
|
||||
zw := zip.NewWriter(&buf)
|
||||
_ = toZipFile(fmt.Sprintf("%s_%s.pem", prefix, key.ID.String()), []byte(plain), zw)
|
||||
_ = toZipFile(fmt.Sprintf("%s_%s.pub", prefix, key.ID.String()), []byte(key.PublicKey), zw)
|
||||
_ = toZipFile(fmt.Sprintf("%s.pem", key.ID.String()), []byte(plain), zw)
|
||||
_ = toZipFile(fmt.Sprintf("%s.pub", key.ID.String()), []byte(key.PublicKey), zw)
|
||||
_ = zw.Close()
|
||||
|
||||
b64 := utils.EncodeB64(buf.Bytes())
|
||||
resp.ZipBase64 = &b64
|
||||
resp.Filenames = []string{
|
||||
fmt.Sprintf("%s_%s.zip", prefix, key.ID.String()),
|
||||
fmt.Sprintf("%s_%s.pem", prefix, key.ID.String()),
|
||||
fmt.Sprintf("%s_%s.pub", prefix, key.ID.String()),
|
||||
fmt.Sprintf("%s.zip", key.ID.String()),
|
||||
fmt.Sprintf("%s.pem", key.ID.String()),
|
||||
fmt.Sprintf("%s.pub", key.ID.String()),
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, resp)
|
||||
return
|
||||
@@ -433,11 +435,9 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
prefix := keyFilenamePrefix(key.PublicKey)
|
||||
|
||||
switch part {
|
||||
case "public":
|
||||
filename := fmt.Sprintf("%s_%s.pub", prefix, key.ID.String())
|
||||
filename := fmt.Sprintf("%s.pub", key.ID.String())
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
|
||||
_, _ = w.Write([]byte(key.PublicKey))
|
||||
@@ -449,7 +449,7 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "failed to decrypt ssh key")
|
||||
return
|
||||
}
|
||||
filename := fmt.Sprintf("%s_%s.pem", prefix, key.ID.String())
|
||||
filename := fmt.Sprintf("%s.pem", key.ID.String())
|
||||
w.Header().Set("Content-Type", "application/x-pem-file")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
|
||||
_, _ = w.Write([]byte(plain))
|
||||
@@ -464,8 +464,8 @@ func DownloadSSHKey(db *gorm.DB) http.HandlerFunc {
|
||||
|
||||
var buf bytes.Buffer
|
||||
zw := zip.NewWriter(&buf)
|
||||
_ = toZipFile(fmt.Sprintf("%s_%s.pem", prefix, key.ID.String()), []byte(plain), zw)
|
||||
_ = toZipFile(fmt.Sprintf("%s_%s.pub", prefix, key.ID.String()), []byte(key.PublicKey), zw)
|
||||
_ = toZipFile(fmt.Sprintf("%s.pem", key.ID.String()), []byte(plain), zw)
|
||||
_ = toZipFile(fmt.Sprintf("%s.pub", key.ID.String()), []byte(key.PublicKey), zw)
|
||||
_ = zw.Close()
|
||||
|
||||
filename := fmt.Sprintf("ssh_key_%s.zip", key.ID.String())
|
||||
|
||||
65
internal/handlers/version.go
Normal file
65
internal/handlers/version.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
"github.com/glueops/autoglue/internal/version"
|
||||
)
|
||||
|
||||
type VersionResponse struct {
|
||||
Version string `json:"version" example:"1.4.2"`
|
||||
Commit string `json:"commit" example:"a1b2c3d"`
|
||||
Built string `json:"built" example:"2025-11-08T12:34:56Z"`
|
||||
BuiltBy string `json:"builtBy" example:"ci"`
|
||||
Go string `json:"go" example:"go1.23.3"`
|
||||
GOOS string `json:"goOS" example:"linux"`
|
||||
GOARCH string `json:"goArch" example:"amd64"`
|
||||
VCS string `json:"vcs,omitempty" example:"git"`
|
||||
Revision string `json:"revision,omitempty" example:"a1b2c3d4e5f6abcdef"`
|
||||
CommitTime string `json:"commitTime,omitempty" example:"2025-11-08T12:31:00Z"`
|
||||
Modified *bool `json:"modified,omitempty" example:"false"`
|
||||
}
|
||||
|
||||
// Version godoc
|
||||
//
|
||||
// @Summary Service version information
|
||||
// @Description Returns build/runtime metadata for the running service.
|
||||
// @Tags Meta
|
||||
// @ID Version // operationId
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} VersionResponse
|
||||
// @Router /version [get]
|
||||
func Version(w http.ResponseWriter, r *http.Request) {
|
||||
resp := VersionResponse{
|
||||
Version: version.Version,
|
||||
Commit: version.Commit,
|
||||
Built: version.Date,
|
||||
BuiltBy: version.BuiltBy,
|
||||
Go: runtime.Version(),
|
||||
GOOS: runtime.GOOS,
|
||||
GOARCH: runtime.GOARCH,
|
||||
}
|
||||
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
for _, s := range bi.Settings {
|
||||
switch s.Key {
|
||||
case "vcs":
|
||||
resp.VCS = s.Value
|
||||
case "vcs.revision":
|
||||
resp.Revision = s.Value
|
||||
case "vcs.time":
|
||||
resp.CommitTime = s.Value
|
||||
case "vcs.modified":
|
||||
if b, err := strconv.ParseBool(s.Value); err == nil {
|
||||
resp.Modified = &b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
@@ -9,4 +9,5 @@ type Annotation struct {
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Key string `gorm:"not null" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
NodePools []NodePool `gorm:"many2many:node_annotations;constraint:OnDelete:CASCADE" json:"node_pools,omitempty"`
|
||||
}
|
||||
|
||||
18
internal/models/backup.go
Normal file
18
internal/models/backup.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Backup struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index;uniqueIndex:uniq_org_credential,priority:1"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Enabled bool `gorm:"not null;default:false" json:"enabled"`
|
||||
CredentialID uuid.UUID `gorm:"type:uuid;not null;uniqueIndex:uniq_org_credential,priority:2" json:"credential_id"`
|
||||
Credential Credential `gorm:"foreignKey:CredentialID" json:"credential,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty" gorm:"type:timestamptz;column:created_at;not null;default:now()"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty" gorm:"type:timestamptz;autoUpdateTime;column:updated_at;not null;default:now()"`
|
||||
}
|
||||
33
internal/models/cluster.go
Normal file
33
internal/models/cluster.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Cluster struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;default:gen_random_uuid();primaryKey" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
Region string `json:"region"`
|
||||
Status string `json:"status"`
|
||||
CaptainDomain string `gorm:"not null" json:"captain_domain"` // nonprod.earth.onglueops.rocks
|
||||
AppsLoadBalancer string `json:"cluster_load_balancer"` // {public_ip: 1.2.3.4, private_ip: 10.0.30.1, name: apps.CaqptainDomain}
|
||||
GlueOpsLoadBalancer string `json:"control_load_balancer"` // {public_ip: 5.6.7.8, private_ip: 10.0.22.1, name: CaptainDomain}
|
||||
|
||||
ControlPlane string `json:"control_plane"` // <- dns cntlpn
|
||||
|
||||
RandomToken string `json:"random_token"`
|
||||
CertificateKey string `json:"certificate_key"`
|
||||
EncryptedKubeconfig string `gorm:"type:text" json:"-"`
|
||||
KubeIV string `json:"-"`
|
||||
KubeTag string `json:"-"`
|
||||
NodePools []NodePool `gorm:"many2many:cluster_node_pools;constraint:OnDelete:CASCADE" json:"node_pools,omitempty"`
|
||||
BastionServerID *uuid.UUID `gorm:"type:uuid" json:"bastion_server_id,omitempty"`
|
||||
BastionServer *Server `gorm:"foreignKey:BastionServerID" json:"bastion_server,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty" gorm:"type:timestamptz;column:created_at;not null;default:now()"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty" gorm:"type:timestamptz;autoUpdateTime;column:updated_at;not null;default:now()"`
|
||||
}
|
||||
29
internal/models/credential.go
Normal file
29
internal/models/credential.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index;uniqueIndex:uniq_org_provider_scopekind_scope,priority:1" json:"organization_id"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Provider string `gorm:"type:varchar(50);not null;uniqueIndex:uniq_org_provider_scopekind_scope,priority:2;index:idx_provider_kind"`
|
||||
Kind string `gorm:"type:varchar(50);not null;index:idx_provider_kind;index:idx_kind_scope"`
|
||||
ScopeKind string `gorm:"type:varchar(20);not null;uniqueIndex:uniq_org_provider_scopekind_scope,priority:3"`
|
||||
Scope datatypes.JSON `gorm:"type:jsonb;not null;default:'{}';index:idx_kind_scope"`
|
||||
ScopeFingerprint string `gorm:"type:char(64);not null;uniqueIndex:uniq_org_provider_scopekind_scope,priority:4;index"`
|
||||
SchemaVersion int `gorm:"not null;default:1"`
|
||||
Name string `gorm:"type:varchar(100);not null;default:''"`
|
||||
ScopeVersion int `gorm:"not null;default:1"`
|
||||
AccountID string `gorm:"type:varchar(32)"`
|
||||
Region string `gorm:"type:varchar(32)"`
|
||||
EncryptedData string `gorm:"not null"`
|
||||
IV string `gorm:"not null"`
|
||||
Tag string `gorm:"not null"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty" gorm:"type:timestamptz;column:created_at;not null;default:now()" format:"date-time"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty" gorm:"type:timestamptz;autoUpdateTime;column:updated_at;not null;default:now()" format:"date-time"`
|
||||
}
|
||||
41
internal/models/domain.go
Normal file
41
internal/models/domain.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type Domain struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index;uniqueIndex:uniq_org_domain,priority:1"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
DomainName string `gorm:"type:varchar(253);not null;uniqueIndex:uniq_org_domain,priority:2"`
|
||||
ZoneID string `gorm:"type:varchar(128);not null;default:''"` // backfilled for R53 (e.g. "/hostedzone/Z123...")
|
||||
Status string `gorm:"type:varchar(20);not null;default:'pending'"` // pending, provisioning, ready, failed
|
||||
LastError string `gorm:"type:text;not null;default:''"`
|
||||
CredentialID uuid.UUID `gorm:"type:uuid;not null" json:"credential_id"`
|
||||
Credential Credential `gorm:"foreignKey:CredentialID" json:"credential,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty" gorm:"type:timestamptz;column:created_at;not null;default:now()"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty" gorm:"type:timestamptz;autoUpdateTime;column:updated_at;not null;default:now()"`
|
||||
}
|
||||
|
||||
type RecordSet struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
|
||||
DomainID uuid.UUID `gorm:"type:uuid;not null;index"`
|
||||
Domain Domain `gorm:"foreignKey:DomainID;constraint:OnDelete:CASCADE"`
|
||||
Name string `gorm:"type:varchar(253);not null"` // e.g. "endpoint" (relative to DomainName)
|
||||
Type string `gorm:"type:varchar(10);not null;index"` // A, AAAA, CNAME, TXT, MX, SRV, NS, CAA...
|
||||
TTL *int `gorm:""` // nil for alias targets (Route 53 ignores TTL for alias)
|
||||
Values datatypes.JSON `gorm:"type:jsonb;not null;default:'[]'"`
|
||||
Fingerprint string `gorm:"type:char(64);not null;index"` // sha256 of canonical(name,type,ttl,values|alias)
|
||||
Status string `gorm:"type:varchar(20);not null;default:'pending'"`
|
||||
Owner string `gorm:"type:varchar(16);not null;default:'unknown'"` // 'autoglue' | 'external' | 'unknown'
|
||||
LastError string `gorm:"type:text;not null;default:''"`
|
||||
_ struct{} `gorm:"uniqueIndex:uniq_domain_name_type,priority:1"` // tag holder
|
||||
_ struct{} `gorm:"uniqueIndex:uniq_domain_name_type,priority:2"`
|
||||
_ struct{} `gorm:"uniqueIndex:uniq_domain_name_type,priority:3"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty" gorm:"type:timestamptz;column:created_at;not null;default:now()"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty" gorm:"type:timestamptz;autoUpdateTime;column:updated_at;not null;default:now()"`
|
||||
}
|
||||
@@ -9,5 +9,5 @@ type Label struct {
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Key string `gorm:"not null" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
NodePools []NodePool `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"servers,omitempty"`
|
||||
NodePools []NodePool `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"node_pools,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/glueops/autoglue/internal/common"
|
||||
)
|
||||
|
||||
type NodePool struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Servers []Server `gorm:"many2many:node_servers;constraint:OnDelete:CASCADE" json:"servers,omitempty"`
|
||||
Annotations []Annotation `gorm:"many2many:node_annotations;constraint:OnDelete:CASCADE" json:"annotations,omitempty"`
|
||||
Labels []Label `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"labels,omitempty"`
|
||||
Taints []Taint `gorm:"many2many:node_taints;constraint:OnDelete:CASCADE" json:"taints,omitempty"`
|
||||
//Clusters []Cluster `gorm:"many2many:cluster_node_pools;constraint:OnDelete:CASCADE" json:"clusters,omitempty"`
|
||||
common.AuditFields
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Servers []Server `gorm:"many2many:node_servers;constraint:OnDelete:CASCADE" json:"servers,omitempty"`
|
||||
Annotations []Annotation `gorm:"many2many:node_annotations;constraint:OnDelete:CASCADE" json:"annotations,omitempty"`
|
||||
Labels []Label `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"labels,omitempty"`
|
||||
Taints []Taint `gorm:"many2many:node_taints;constraint:OnDelete:CASCADE" json:"taints,omitempty"`
|
||||
Clusters []Cluster `gorm:"many2many:cluster_node_pools;constraint:OnDelete:CASCADE" json:"clusters,omitempty"`
|
||||
//Topology string `gorm:"not null,default:'stacked'" json:"topology,omitempty"` // stacked or external
|
||||
Role string `gorm:"not null,default:'worker'" json:"role,omitempty"` // master, worker, or etcd (etcd only if topology = external
|
||||
CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at" format:"date-time"`
|
||||
UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at" format:"date-time"`
|
||||
Role string `gorm:"not null,default:'worker'" json:"role,omitempty"` // master, worker, or etcd (etcd only if topology = external
|
||||
}
|
||||
|
||||
@@ -19,8 +19,11 @@ type Server struct {
|
||||
SSHUser string `gorm:"not null" json:"ssh_user"`
|
||||
SshKeyID uuid.UUID `gorm:"type:uuid;not null" json:"ssh_key_id"`
|
||||
SshKey SshKey `gorm:"foreignKey:SshKeyID" json:"ssh_key"`
|
||||
Role string `gorm:"not null" json:"role"` // e.g., "master", "worker", "bastion"
|
||||
Status string `gorm:"default:'pending'" json:"status"` // pending, provisioning, ready, failed
|
||||
Role string `gorm:"not null" json:"role" enums:"master,worker,bastion"` // e.g., "master", "worker", "bastion"
|
||||
Status string `gorm:"default:'pending'" json:"status" enums:"pending, provisioning, ready, failed"` // pending, provisioning, ready, failed
|
||||
NodePools []NodePool `gorm:"many2many:node_servers;constraint:OnDelete:CASCADE" json:"node_pools,omitempty"`
|
||||
SSHHostKey string `gorm:"column:ssh_host_key"`
|
||||
SSHHostKeyAlgo string `gorm:"column:ssh_host_key_algo"`
|
||||
CreatedAt time.Time `gorm:"not null;default:now()" json:"created_at" format:"date-time"`
|
||||
UpdatedAt time.Time `gorm:"not null;default:now()" json:"updated_at" format:"date-time"`
|
||||
}
|
||||
|
||||
79
internal/web/dist/assets/index-52pog1DZ.js
vendored
Normal file
79
internal/web/dist/assets/index-52pog1DZ.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
internal/web/dist/assets/index-52pog1DZ.js.br
vendored
Normal file
BIN
internal/web/dist/assets/index-52pog1DZ.js.br
vendored
Normal file
Binary file not shown.
BIN
internal/web/dist/assets/index-52pog1DZ.js.gz
vendored
Normal file
BIN
internal/web/dist/assets/index-52pog1DZ.js.gz
vendored
Normal file
Binary file not shown.
1
internal/web/dist/assets/index-52pog1DZ.js.map
vendored
Normal file
1
internal/web/dist/assets/index-52pog1DZ.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
internal/web/dist/assets/index-CjaTfP6H.css
vendored
2
internal/web/dist/assets/index-CjaTfP6H.css
vendored
File diff suppressed because one or more lines are too long
BIN
internal/web/dist/assets/index-CjaTfP6H.css.br
vendored
BIN
internal/web/dist/assets/index-CjaTfP6H.css.br
vendored
Binary file not shown.
BIN
internal/web/dist/assets/index-CjaTfP6H.css.gz
vendored
BIN
internal/web/dist/assets/index-CjaTfP6H.css.gz
vendored
Binary file not shown.
79
internal/web/dist/assets/index-DbPirwVN.js
vendored
79
internal/web/dist/assets/index-DbPirwVN.js
vendored
File diff suppressed because one or more lines are too long
BIN
internal/web/dist/assets/index-DbPirwVN.js.br
vendored
BIN
internal/web/dist/assets/index-DbPirwVN.js.br
vendored
Binary file not shown.
BIN
internal/web/dist/assets/index-DbPirwVN.js.gz
vendored
BIN
internal/web/dist/assets/index-DbPirwVN.js.gz
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
2
internal/web/dist/assets/index-tX4seA_J.css
vendored
Normal file
2
internal/web/dist/assets/index-tX4seA_J.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
internal/web/dist/assets/index-tX4seA_J.css.br
vendored
Normal file
BIN
internal/web/dist/assets/index-tX4seA_J.css.br
vendored
Normal file
Binary file not shown.
BIN
internal/web/dist/assets/index-tX4seA_J.css.gz
vendored
Normal file
BIN
internal/web/dist/assets/index-tX4seA_J.css.gz
vendored
Normal file
Binary file not shown.
BIN
internal/web/dist/assets/react-B75e6Si-.js.gz
vendored
BIN
internal/web/dist/assets/react-B75e6Si-.js.gz
vendored
Binary file not shown.
4
internal/web/dist/index.html
vendored
4
internal/web/dist/index.html
vendored
@@ -5,9 +5,9 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>AutoGlue</title>
|
||||
<script type="module" crossorigin src="/assets/index-DbPirwVN.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-52pog1DZ.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/react-B75e6Si-.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CjaTfP6H.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-tX4seA_J.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
BIN
internal/web/dist/index.html.br
vendored
BIN
internal/web/dist/index.html.br
vendored
Binary file not shown.
BIN
internal/web/dist/index.html.gz
vendored
BIN
internal/web/dist/index.html.gz
vendored
Binary file not shown.
3
internal/web/dist/vite.svg
vendored
3
internal/web/dist/vite.svg
vendored
@@ -1 +1,2 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88"
|
||||
height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -61,6 +61,7 @@ func SPAHandler() (http.Handler, error) {
|
||||
if strings.HasPrefix(r.URL.Path, "/api/") ||
|
||||
r.URL.Path == "/api" ||
|
||||
strings.HasPrefix(r.URL.Path, "/swagger") ||
|
||||
strings.HasPrefix(r.URL.Path, "/db-studio") ||
|
||||
strings.HasPrefix(r.URL.Path, "/debug/pprof") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
|
||||
11
main.go
11
main.go
@@ -1,6 +1,12 @@
|
||||
package main
|
||||
|
||||
import "github.com/glueops/autoglue/cmd"
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/glueops/autoglue/cmd"
|
||||
"github.com/glueops/autoglue/docs"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
// @title AutoGlue API
|
||||
// @version 1.0
|
||||
@@ -10,7 +16,6 @@ import "github.com/glueops/autoglue/cmd"
|
||||
|
||||
// @BasePath /api/v1
|
||||
// @schemes http https
|
||||
// @host localhost:8080
|
||||
|
||||
// @securityDefinitions.apikey BearerAuth
|
||||
// @in header
|
||||
@@ -33,5 +38,7 @@ import "github.com/glueops/autoglue/cmd"
|
||||
// @description Org-level secret
|
||||
|
||||
func main() {
|
||||
_ = godotenv.Load()
|
||||
docs.SwaggerInfo.Host = os.Getenv("SWAGGER_HOST")
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM postgres:latest@sha256:feff5b24fedd610975a1f5e743c51a4b360437f4dc3a11acf740dcd708f413f6
|
||||
FROM postgres:17.7@sha256:44640f16641cf36716cabd011e2f7eb4742b6b6b19f4488ddcbb7c250e5c9753
|
||||
|
||||
RUN cd /var/lib/postgresql/ && \
|
||||
openssl req -new -text -passout pass:abcd -subj /CN=localhost -out server.req -keyout privkey.pem && \
|
||||
|
||||
24
sdk/go/.gitignore
vendored
24
sdk/go/.gitignore
vendored
@@ -1,24 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
@@ -1,23 +0,0 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
||||
@@ -1,127 +0,0 @@
|
||||
.gitignore
|
||||
.openapi-generator-ignore
|
||||
.travis.yml
|
||||
README.md
|
||||
api/openapi.yaml
|
||||
api_annotations.go
|
||||
api_archer_admin.go
|
||||
api_auth.go
|
||||
api_health.go
|
||||
api_labels.go
|
||||
api_me.go
|
||||
api_me_api_keys.go
|
||||
api_orgs.go
|
||||
api_servers.go
|
||||
api_ssh.go
|
||||
api_taints.go
|
||||
client.go
|
||||
configuration.go
|
||||
docs/AnnotationsAPI.md
|
||||
docs/ArcherAdminAPI.md
|
||||
docs/AuthAPI.md
|
||||
docs/DtoAnnotationResponse.md
|
||||
docs/DtoAuthStartResponse.md
|
||||
docs/DtoCreateAnnotationRequest.md
|
||||
docs/DtoCreateLabelRequest.md
|
||||
docs/DtoCreateSSHRequest.md
|
||||
docs/DtoCreateServerRequest.md
|
||||
docs/DtoCreateTaintRequest.md
|
||||
docs/DtoJWK.md
|
||||
docs/DtoJWKS.md
|
||||
docs/DtoJob.md
|
||||
docs/DtoJobStatus.md
|
||||
docs/DtoLabelResponse.md
|
||||
docs/DtoLogoutRequest.md
|
||||
docs/DtoPageJob.md
|
||||
docs/DtoQueueInfo.md
|
||||
docs/DtoRefreshRequest.md
|
||||
docs/DtoServerResponse.md
|
||||
docs/DtoSshResponse.md
|
||||
docs/DtoSshRevealResponse.md
|
||||
docs/DtoTaintResponse.md
|
||||
docs/DtoTokenPair.md
|
||||
docs/DtoUpdateAnnotationRequest.md
|
||||
docs/DtoUpdateLabelRequest.md
|
||||
docs/DtoUpdateServerRequest.md
|
||||
docs/DtoUpdateTaintRequest.md
|
||||
docs/HandlersCreateUserKeyRequest.md
|
||||
docs/HandlersHealthStatus.md
|
||||
docs/HandlersMeResponse.md
|
||||
docs/HandlersMemberOut.md
|
||||
docs/HandlersMemberUpsertReq.md
|
||||
docs/HandlersOrgCreateReq.md
|
||||
docs/HandlersOrgKeyCreateReq.md
|
||||
docs/HandlersOrgKeyCreateResp.md
|
||||
docs/HandlersOrgUpdateReq.md
|
||||
docs/HandlersUpdateMeRequest.md
|
||||
docs/HandlersUserAPIKeyOut.md
|
||||
docs/HealthAPI.md
|
||||
docs/LabelsAPI.md
|
||||
docs/MeAPI.md
|
||||
docs/MeAPIKeysAPI.md
|
||||
docs/ModelsAPIKey.md
|
||||
docs/ModelsOrganization.md
|
||||
docs/ModelsUser.md
|
||||
docs/ModelsUserEmail.md
|
||||
docs/OrgsAPI.md
|
||||
docs/ServersAPI.md
|
||||
docs/SshAPI.md
|
||||
docs/TaintsAPI.md
|
||||
docs/UtilsErrorResponse.md
|
||||
git_push.sh
|
||||
go.mod
|
||||
go.sum
|
||||
model_dto_annotation_response.go
|
||||
model_dto_auth_start_response.go
|
||||
model_dto_create_annotation_request.go
|
||||
model_dto_create_label_request.go
|
||||
model_dto_create_server_request.go
|
||||
model_dto_create_ssh_request.go
|
||||
model_dto_create_taint_request.go
|
||||
model_dto_job.go
|
||||
model_dto_job_status.go
|
||||
model_dto_jwk.go
|
||||
model_dto_jwks.go
|
||||
model_dto_label_response.go
|
||||
model_dto_logout_request.go
|
||||
model_dto_page_job.go
|
||||
model_dto_queue_info.go
|
||||
model_dto_refresh_request.go
|
||||
model_dto_server_response.go
|
||||
model_dto_ssh_response.go
|
||||
model_dto_ssh_reveal_response.go
|
||||
model_dto_taint_response.go
|
||||
model_dto_token_pair.go
|
||||
model_dto_update_annotation_request.go
|
||||
model_dto_update_label_request.go
|
||||
model_dto_update_server_request.go
|
||||
model_dto_update_taint_request.go
|
||||
model_handlers_create_user_key_request.go
|
||||
model_handlers_health_status.go
|
||||
model_handlers_me_response.go
|
||||
model_handlers_member_out.go
|
||||
model_handlers_member_upsert_req.go
|
||||
model_handlers_org_create_req.go
|
||||
model_handlers_org_key_create_req.go
|
||||
model_handlers_org_key_create_resp.go
|
||||
model_handlers_org_update_req.go
|
||||
model_handlers_update_me_request.go
|
||||
model_handlers_user_api_key_out.go
|
||||
model_models_api_key.go
|
||||
model_models_organization.go
|
||||
model_models_user.go
|
||||
model_models_user_email.go
|
||||
model_utils_error_response.go
|
||||
response.go
|
||||
test/api_annotations_test.go
|
||||
test/api_archer_admin_test.go
|
||||
test/api_auth_test.go
|
||||
test/api_health_test.go
|
||||
test/api_labels_test.go
|
||||
test/api_me_api_keys_test.go
|
||||
test/api_me_test.go
|
||||
test/api_orgs_test.go
|
||||
test/api_servers_test.go
|
||||
test/api_ssh_test.go
|
||||
test/api_taints_test.go
|
||||
utils.go
|
||||
@@ -1 +0,0 @@
|
||||
7.17.0
|
||||
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
|
||||
install:
|
||||
- go get -d -v .
|
||||
|
||||
script:
|
||||
- go build -v ./
|
||||
|
||||
288
sdk/go/README.md
288
sdk/go/README.md
@@ -1,288 +0,0 @@
|
||||
# Go API client for autoglue
|
||||
|
||||
API for managing K3s clusters across cloud providers
|
||||
|
||||
## Overview
|
||||
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client.
|
||||
|
||||
- API version: 1.0
|
||||
- Package version: 1.0.0
|
||||
- Generator version: 7.17.0
|
||||
- Build package: org.openapitools.codegen.languages.GoClientCodegen
|
||||
|
||||
## Installation
|
||||
|
||||
Install the following dependencies:
|
||||
|
||||
```sh
|
||||
go get github.com/stretchr/testify/assert
|
||||
go get golang.org/x/net/context
|
||||
```
|
||||
|
||||
Put the package under your project folder and add the following in import:
|
||||
|
||||
```go
|
||||
import autoglue "github.com/glueops/autoglue-sdk-go"
|
||||
```
|
||||
|
||||
To use a proxy, set the environment variable `HTTP_PROXY`:
|
||||
|
||||
```go
|
||||
os.Setenv("HTTP_PROXY", "http://proxy_name:proxy_port")
|
||||
```
|
||||
|
||||
## Configuration of Server URL
|
||||
|
||||
Default configuration comes with `Servers` field that contains server objects as defined in the OpenAPI specification.
|
||||
|
||||
### Select Server Configuration
|
||||
|
||||
For using other server than the one defined on index 0 set context value `autoglue.ContextServerIndex` of type `int`.
|
||||
|
||||
```go
|
||||
ctx := context.WithValue(context.Background(), autoglue.ContextServerIndex, 1)
|
||||
```
|
||||
|
||||
### Templated Server URL
|
||||
|
||||
Templated server URL is formatted using default variables from configuration or from context value `autoglue.ContextServerVariables` of type `map[string]string`.
|
||||
|
||||
```go
|
||||
ctx := context.WithValue(context.Background(), autoglue.ContextServerVariables, map[string]string{
|
||||
"basePath": "v2",
|
||||
})
|
||||
```
|
||||
|
||||
Note, enum values are always validated and all unused variables are silently ignored.
|
||||
|
||||
### URLs Configuration per Operation
|
||||
|
||||
Each operation can use different server URL defined using `OperationServers` map in the `Configuration`.
|
||||
An operation is uniquely identified by `"{classname}Service.{nickname}"` string.
|
||||
Similar rules for overriding default operation server index and variables applies by using `autoglue.ContextOperationServerIndices` and `autoglue.ContextOperationServerVariables` context maps.
|
||||
|
||||
```go
|
||||
ctx := context.WithValue(context.Background(), autoglue.ContextOperationServerIndices, map[string]int{
|
||||
"{classname}Service.{nickname}": 2,
|
||||
})
|
||||
ctx = context.WithValue(context.Background(), autoglue.ContextOperationServerVariables, map[string]map[string]string{
|
||||
"{classname}Service.{nickname}": {
|
||||
"port": "8443",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Documentation for API Endpoints
|
||||
|
||||
All URIs are relative to *http://localhost:8080/api/v1*
|
||||
|
||||
Class | Method | HTTP request | Description
|
||||
------------ | ------------- | ------------- | -------------
|
||||
*AnnotationsAPI* | [**CreateAnnotation**](docs/AnnotationsAPI.md#createannotation) | **Post** /annotations | Create annotation (org scoped)
|
||||
*AnnotationsAPI* | [**DeleteAnnotation**](docs/AnnotationsAPI.md#deleteannotation) | **Delete** /annotations/{id} | Delete annotation (org scoped)
|
||||
*AnnotationsAPI* | [**GetAnnotation**](docs/AnnotationsAPI.md#getannotation) | **Get** /annotations/{id} | Get annotation by ID (org scoped)
|
||||
*AnnotationsAPI* | [**ListAnnotations**](docs/AnnotationsAPI.md#listannotations) | **Get** /annotations | List annotations (org scoped)
|
||||
*AnnotationsAPI* | [**UpdateAnnotation**](docs/AnnotationsAPI.md#updateannotation) | **Patch** /annotations/{id} | Update annotation (org scoped)
|
||||
*ArcherAdminAPI* | [**AdminCancelArcherJob**](docs/ArcherAdminAPI.md#admincancelarcherjob) | **Post** /admin/archer/jobs/{id}/cancel | Cancel an Archer job (admin)
|
||||
*ArcherAdminAPI* | [**AdminEnqueueArcherJob**](docs/ArcherAdminAPI.md#adminenqueuearcherjob) | **Post** /admin/archer/jobs | Enqueue a new Archer job (admin)
|
||||
*ArcherAdminAPI* | [**AdminListArcherJobs**](docs/ArcherAdminAPI.md#adminlistarcherjobs) | **Get** /admin/archer/jobs | List Archer jobs (admin)
|
||||
*ArcherAdminAPI* | [**AdminListArcherQueues**](docs/ArcherAdminAPI.md#adminlistarcherqueues) | **Get** /admin/archer/queues | List Archer queues (admin)
|
||||
*ArcherAdminAPI* | [**AdminRetryArcherJob**](docs/ArcherAdminAPI.md#adminretryarcherjob) | **Post** /admin/archer/jobs/{id}/retry | Retry a failed/canceled Archer job (admin)
|
||||
*AuthAPI* | [**AuthCallback**](docs/AuthAPI.md#authcallback) | **Get** /auth/{provider}/callback | Handle social login callback
|
||||
*AuthAPI* | [**AuthStart**](docs/AuthAPI.md#authstart) | **Post** /auth/{provider}/start | Begin social login
|
||||
*AuthAPI* | [**GetJWKS**](docs/AuthAPI.md#getjwks) | **Get** /.well-known/jwks.json | Get JWKS
|
||||
*AuthAPI* | [**Logout**](docs/AuthAPI.md#logout) | **Post** /auth/logout | Revoke refresh token family (logout everywhere)
|
||||
*AuthAPI* | [**Refresh**](docs/AuthAPI.md#refresh) | **Post** /auth/refresh | Rotate refresh token
|
||||
*HealthAPI* | [**HealthCheckOperationId**](docs/HealthAPI.md#healthcheckoperationid) | **Get** /healthz | Basic health check
|
||||
*LabelsAPI* | [**CreateLabel**](docs/LabelsAPI.md#createlabel) | **Post** /labels | Create label (org scoped)
|
||||
*LabelsAPI* | [**DeleteLabel**](docs/LabelsAPI.md#deletelabel) | **Delete** /labels/{id} | Delete label (org scoped)
|
||||
*LabelsAPI* | [**GetLabel**](docs/LabelsAPI.md#getlabel) | **Get** /labels/{id} | Get label by ID (org scoped)
|
||||
*LabelsAPI* | [**ListLabels**](docs/LabelsAPI.md#listlabels) | **Get** /labels | List node labels (org scoped)
|
||||
*LabelsAPI* | [**UpdateLabel**](docs/LabelsAPI.md#updatelabel) | **Patch** /labels/{id} | Update label (org scoped)
|
||||
*MeAPI* | [**GetMe**](docs/MeAPI.md#getme) | **Get** /me | Get current user profile
|
||||
*MeAPI* | [**UpdateMe**](docs/MeAPI.md#updateme) | **Patch** /me | Update current user profile
|
||||
*MeAPIKeysAPI* | [**CreateUserAPIKey**](docs/MeAPIKeysAPI.md#createuserapikey) | **Post** /me/api-keys | Create a new user API key
|
||||
*MeAPIKeysAPI* | [**DeleteUserAPIKey**](docs/MeAPIKeysAPI.md#deleteuserapikey) | **Delete** /me/api-keys/{id} | Delete a user API key
|
||||
*MeAPIKeysAPI* | [**ListUserAPIKeys**](docs/MeAPIKeysAPI.md#listuserapikeys) | **Get** /me/api-keys | List my API keys
|
||||
*OrgsAPI* | [**AddOrUpdateMember**](docs/OrgsAPI.md#addorupdatemember) | **Post** /orgs/{id}/members | Add or update a member (owner/admin)
|
||||
*OrgsAPI* | [**CreateOrg**](docs/OrgsAPI.md#createorg) | **Post** /orgs | Create organization
|
||||
*OrgsAPI* | [**CreateOrgKey**](docs/OrgsAPI.md#createorgkey) | **Post** /orgs/{id}/api-keys | Create org key/secret pair (owner/admin)
|
||||
*OrgsAPI* | [**DeleteOrg**](docs/OrgsAPI.md#deleteorg) | **Delete** /orgs/{id} | Delete organization (owner)
|
||||
*OrgsAPI* | [**DeleteOrgKey**](docs/OrgsAPI.md#deleteorgkey) | **Delete** /orgs/{id}/api-keys/{key_id} | Delete org key (owner/admin)
|
||||
*OrgsAPI* | [**GetOrg**](docs/OrgsAPI.md#getorg) | **Get** /orgs/{id} | Get organization
|
||||
*OrgsAPI* | [**ListMembers**](docs/OrgsAPI.md#listmembers) | **Get** /orgs/{id}/members | List members in org
|
||||
*OrgsAPI* | [**ListMyOrgs**](docs/OrgsAPI.md#listmyorgs) | **Get** /orgs | List organizations I belong to
|
||||
*OrgsAPI* | [**ListOrgKeys**](docs/OrgsAPI.md#listorgkeys) | **Get** /orgs/{id}/api-keys | List org-scoped API keys (no secrets)
|
||||
*OrgsAPI* | [**RemoveMember**](docs/OrgsAPI.md#removemember) | **Delete** /orgs/{id}/members/{user_id} | Remove a member (owner/admin)
|
||||
*OrgsAPI* | [**UpdateOrg**](docs/OrgsAPI.md#updateorg) | **Patch** /orgs/{id} | Update organization (owner/admin)
|
||||
*ServersAPI* | [**CreateServer**](docs/ServersAPI.md#createserver) | **Post** /servers | Create server (org scoped)
|
||||
*ServersAPI* | [**DeleteServer**](docs/ServersAPI.md#deleteserver) | **Delete** /servers/{id} | Delete server (org scoped)
|
||||
*ServersAPI* | [**GetServer**](docs/ServersAPI.md#getserver) | **Get** /servers/{id} | Get server by ID (org scoped)
|
||||
*ServersAPI* | [**ListServers**](docs/ServersAPI.md#listservers) | **Get** /servers | List servers (org scoped)
|
||||
*ServersAPI* | [**UpdateServer**](docs/ServersAPI.md#updateserver) | **Patch** /servers/{id} | Update server (org scoped)
|
||||
*SshAPI* | [**CreateSSHKey**](docs/SshAPI.md#createsshkey) | **Post** /ssh | Create ssh keypair (org scoped)
|
||||
*SshAPI* | [**DeleteSSHKey**](docs/SshAPI.md#deletesshkey) | **Delete** /ssh/{id} | Delete ssh keypair (org scoped)
|
||||
*SshAPI* | [**DownloadSSHKey**](docs/SshAPI.md#downloadsshkey) | **Get** /ssh/{id}/download | Download ssh key files by ID (org scoped)
|
||||
*SshAPI* | [**GetSSHKey**](docs/SshAPI.md#getsshkey) | **Get** /ssh/{id} | Get ssh key by ID (org scoped)
|
||||
*SshAPI* | [**ListPublicSshKeys**](docs/SshAPI.md#listpublicsshkeys) | **Get** /ssh | List ssh keys (org scoped)
|
||||
*TaintsAPI* | [**CreateTaint**](docs/TaintsAPI.md#createtaint) | **Post** /taints | Create node taint (org scoped)
|
||||
*TaintsAPI* | [**DeleteTaint**](docs/TaintsAPI.md#deletetaint) | **Delete** /taints/{id} | Delete taint (org scoped)
|
||||
*TaintsAPI* | [**GetTaint**](docs/TaintsAPI.md#gettaint) | **Get** /taints/{id} | Get node taint by ID (org scoped)
|
||||
*TaintsAPI* | [**ListTaints**](docs/TaintsAPI.md#listtaints) | **Get** /taints | List node pool taints (org scoped)
|
||||
*TaintsAPI* | [**UpdateTaint**](docs/TaintsAPI.md#updatetaint) | **Patch** /taints/{id} | Update node taint (org scoped)
|
||||
|
||||
|
||||
## Documentation For Models
|
||||
|
||||
- [DtoAnnotationResponse](docs/DtoAnnotationResponse.md)
|
||||
- [DtoAuthStartResponse](docs/DtoAuthStartResponse.md)
|
||||
- [DtoCreateAnnotationRequest](docs/DtoCreateAnnotationRequest.md)
|
||||
- [DtoCreateLabelRequest](docs/DtoCreateLabelRequest.md)
|
||||
- [DtoCreateSSHRequest](docs/DtoCreateSSHRequest.md)
|
||||
- [DtoCreateServerRequest](docs/DtoCreateServerRequest.md)
|
||||
- [DtoCreateTaintRequest](docs/DtoCreateTaintRequest.md)
|
||||
- [DtoJWK](docs/DtoJWK.md)
|
||||
- [DtoJWKS](docs/DtoJWKS.md)
|
||||
- [DtoJob](docs/DtoJob.md)
|
||||
- [DtoJobStatus](docs/DtoJobStatus.md)
|
||||
- [DtoLabelResponse](docs/DtoLabelResponse.md)
|
||||
- [DtoLogoutRequest](docs/DtoLogoutRequest.md)
|
||||
- [DtoPageJob](docs/DtoPageJob.md)
|
||||
- [DtoQueueInfo](docs/DtoQueueInfo.md)
|
||||
- [DtoRefreshRequest](docs/DtoRefreshRequest.md)
|
||||
- [DtoServerResponse](docs/DtoServerResponse.md)
|
||||
- [DtoSshResponse](docs/DtoSshResponse.md)
|
||||
- [DtoSshRevealResponse](docs/DtoSshRevealResponse.md)
|
||||
- [DtoTaintResponse](docs/DtoTaintResponse.md)
|
||||
- [DtoTokenPair](docs/DtoTokenPair.md)
|
||||
- [DtoUpdateAnnotationRequest](docs/DtoUpdateAnnotationRequest.md)
|
||||
- [DtoUpdateLabelRequest](docs/DtoUpdateLabelRequest.md)
|
||||
- [DtoUpdateServerRequest](docs/DtoUpdateServerRequest.md)
|
||||
- [DtoUpdateTaintRequest](docs/DtoUpdateTaintRequest.md)
|
||||
- [HandlersCreateUserKeyRequest](docs/HandlersCreateUserKeyRequest.md)
|
||||
- [HandlersHealthStatus](docs/HandlersHealthStatus.md)
|
||||
- [HandlersMeResponse](docs/HandlersMeResponse.md)
|
||||
- [HandlersMemberOut](docs/HandlersMemberOut.md)
|
||||
- [HandlersMemberUpsertReq](docs/HandlersMemberUpsertReq.md)
|
||||
- [HandlersOrgCreateReq](docs/HandlersOrgCreateReq.md)
|
||||
- [HandlersOrgKeyCreateReq](docs/HandlersOrgKeyCreateReq.md)
|
||||
- [HandlersOrgKeyCreateResp](docs/HandlersOrgKeyCreateResp.md)
|
||||
- [HandlersOrgUpdateReq](docs/HandlersOrgUpdateReq.md)
|
||||
- [HandlersUpdateMeRequest](docs/HandlersUpdateMeRequest.md)
|
||||
- [HandlersUserAPIKeyOut](docs/HandlersUserAPIKeyOut.md)
|
||||
- [ModelsAPIKey](docs/ModelsAPIKey.md)
|
||||
- [ModelsOrganization](docs/ModelsOrganization.md)
|
||||
- [ModelsUser](docs/ModelsUser.md)
|
||||
- [ModelsUserEmail](docs/ModelsUserEmail.md)
|
||||
- [UtilsErrorResponse](docs/UtilsErrorResponse.md)
|
||||
|
||||
|
||||
## Documentation For Authorization
|
||||
|
||||
|
||||
Authentication schemes defined for the API:
|
||||
### ApiKeyAuth
|
||||
|
||||
- **Type**: API key
|
||||
- **API key parameter name**: X-API-KEY
|
||||
- **Location**: HTTP header
|
||||
|
||||
Note, each API key must be added to a map of `map[string]APIKey` where the key is: ApiKeyAuth and passed in as the auth context for each request.
|
||||
|
||||
Example
|
||||
|
||||
```go
|
||||
auth := context.WithValue(
|
||||
context.Background(),
|
||||
autoglue.ContextAPIKeys,
|
||||
map[string]autoglue.APIKey{
|
||||
"ApiKeyAuth": {Key: "API_KEY_STRING"},
|
||||
},
|
||||
)
|
||||
r, err := client.Service.Operation(auth, args)
|
||||
```
|
||||
|
||||
### BearerAuth
|
||||
|
||||
- **Type**: API key
|
||||
- **API key parameter name**: Authorization
|
||||
- **Location**: HTTP header
|
||||
|
||||
Note, each API key must be added to a map of `map[string]APIKey` where the key is: BearerAuth and passed in as the auth context for each request.
|
||||
|
||||
Example
|
||||
|
||||
```go
|
||||
auth := context.WithValue(
|
||||
context.Background(),
|
||||
autoglue.ContextAPIKeys,
|
||||
map[string]autoglue.APIKey{
|
||||
"BearerAuth": {Key: "API_KEY_STRING"},
|
||||
},
|
||||
)
|
||||
r, err := client.Service.Operation(auth, args)
|
||||
```
|
||||
|
||||
### OrgKeyAuth
|
||||
|
||||
- **Type**: API key
|
||||
- **API key parameter name**: X-ORG-KEY
|
||||
- **Location**: HTTP header
|
||||
|
||||
Note, each API key must be added to a map of `map[string]APIKey` where the key is: OrgKeyAuth and passed in as the auth context for each request.
|
||||
|
||||
Example
|
||||
|
||||
```go
|
||||
auth := context.WithValue(
|
||||
context.Background(),
|
||||
autoglue.ContextAPIKeys,
|
||||
map[string]autoglue.APIKey{
|
||||
"OrgKeyAuth": {Key: "API_KEY_STRING"},
|
||||
},
|
||||
)
|
||||
r, err := client.Service.Operation(auth, args)
|
||||
```
|
||||
|
||||
### OrgSecretAuth
|
||||
|
||||
- **Type**: API key
|
||||
- **API key parameter name**: X-ORG-SECRET
|
||||
- **Location**: HTTP header
|
||||
|
||||
Note, each API key must be added to a map of `map[string]APIKey` where the key is: OrgSecretAuth and passed in as the auth context for each request.
|
||||
|
||||
Example
|
||||
|
||||
```go
|
||||
auth := context.WithValue(
|
||||
context.Background(),
|
||||
autoglue.ContextAPIKeys,
|
||||
map[string]autoglue.APIKey{
|
||||
"OrgSecretAuth": {Key: "API_KEY_STRING"},
|
||||
},
|
||||
)
|
||||
r, err := client.Service.Operation(auth, args)
|
||||
```
|
||||
|
||||
|
||||
## Documentation for Utility Methods
|
||||
|
||||
Due to the fact that model structure members are all pointers, this package contains
|
||||
a number of utility functions to easily obtain pointers to values of basic types.
|
||||
Each of these functions takes a value of the given basic type and returns a pointer to it:
|
||||
|
||||
* `PtrBool`
|
||||
* `PtrInt`
|
||||
* `PtrInt32`
|
||||
* `PtrInt64`
|
||||
* `PtrFloat`
|
||||
* `PtrFloat32`
|
||||
* `PtrFloat64`
|
||||
* `PtrString`
|
||||
* `PtrTime`
|
||||
|
||||
## Author
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,864 +0,0 @@
|
||||
/*
|
||||
AutoGlue API
|
||||
|
||||
API for managing K3s clusters across cloud providers
|
||||
|
||||
API version: 1.0
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package autoglue
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ArcherAdminAPIService ArcherAdminAPI service
|
||||
type ArcherAdminAPIService service
|
||||
|
||||
type ApiAdminCancelArcherJobRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ArcherAdminAPIService
|
||||
id string
|
||||
}
|
||||
|
||||
func (r ApiAdminCancelArcherJobRequest) Execute() (*DtoJob, *http.Response, error) {
|
||||
return r.ApiService.AdminCancelArcherJobExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AdminCancelArcherJob Cancel an Archer job (admin)
|
||||
|
||||
Set job status to canceled if cancellable. For running jobs, this only affects future picks; wire to Archer if you need active kill.
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param id Job ID
|
||||
@return ApiAdminCancelArcherJobRequest
|
||||
*/
|
||||
func (a *ArcherAdminAPIService) AdminCancelArcherJob(ctx context.Context, id string) ApiAdminCancelArcherJobRequest {
|
||||
return ApiAdminCancelArcherJobRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoJob
|
||||
func (a *ArcherAdminAPIService) AdminCancelArcherJobExecute(r ApiAdminCancelArcherJobRequest) (*DtoJob, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoJob
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ArcherAdminAPIService.AdminCancelArcherJob")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/admin/archer/jobs/{id}/cancel"
|
||||
localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1)
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 400 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 401 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 403 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 404 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiAdminEnqueueArcherJobRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ArcherAdminAPIService
|
||||
body *map[string]interface{}
|
||||
}
|
||||
|
||||
// Job parameters
|
||||
func (r ApiAdminEnqueueArcherJobRequest) Body(body map[string]interface{}) ApiAdminEnqueueArcherJobRequest {
|
||||
r.body = &body
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiAdminEnqueueArcherJobRequest) Execute() (*DtoJob, *http.Response, error) {
|
||||
return r.ApiService.AdminEnqueueArcherJobExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AdminEnqueueArcherJob Enqueue a new Archer job (admin)
|
||||
|
||||
Create a job immediately or schedule it for the future via `run_at`.
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiAdminEnqueueArcherJobRequest
|
||||
*/
|
||||
func (a *ArcherAdminAPIService) AdminEnqueueArcherJob(ctx context.Context) ApiAdminEnqueueArcherJobRequest {
|
||||
return ApiAdminEnqueueArcherJobRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoJob
|
||||
func (a *ArcherAdminAPIService) AdminEnqueueArcherJobExecute(r ApiAdminEnqueueArcherJobRequest) (*DtoJob, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoJob
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ArcherAdminAPIService.AdminEnqueueArcherJob")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/admin/archer/jobs"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
if r.body == nil {
|
||||
return localVarReturnValue, nil, reportError("body is required and must be specified")
|
||||
}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{"application/json"}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
// body params
|
||||
localVarPostBody = r.body
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 400 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 401 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 403 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 500 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiAdminListArcherJobsRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ArcherAdminAPIService
|
||||
status *string
|
||||
queue *string
|
||||
q *string
|
||||
page *int32
|
||||
pageSize *int32
|
||||
}
|
||||
|
||||
// Filter by status
|
||||
func (r ApiAdminListArcherJobsRequest) Status(status string) ApiAdminListArcherJobsRequest {
|
||||
r.status = &status
|
||||
return r
|
||||
}
|
||||
|
||||
// Filter by queue name / worker name
|
||||
func (r ApiAdminListArcherJobsRequest) Queue(queue string) ApiAdminListArcherJobsRequest {
|
||||
r.queue = &queue
|
||||
return r
|
||||
}
|
||||
|
||||
// Free-text search
|
||||
func (r ApiAdminListArcherJobsRequest) Q(q string) ApiAdminListArcherJobsRequest {
|
||||
r.q = &q
|
||||
return r
|
||||
}
|
||||
|
||||
// Page number
|
||||
func (r ApiAdminListArcherJobsRequest) Page(page int32) ApiAdminListArcherJobsRequest {
|
||||
r.page = &page
|
||||
return r
|
||||
}
|
||||
|
||||
// Items per page
|
||||
func (r ApiAdminListArcherJobsRequest) PageSize(pageSize int32) ApiAdminListArcherJobsRequest {
|
||||
r.pageSize = &pageSize
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiAdminListArcherJobsRequest) Execute() (*DtoPageJob, *http.Response, error) {
|
||||
return r.ApiService.AdminListArcherJobsExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AdminListArcherJobs List Archer jobs (admin)
|
||||
|
||||
Paginated background jobs with optional filters. Search `q` may match id, type, error, payload (implementation-dependent).
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiAdminListArcherJobsRequest
|
||||
*/
|
||||
func (a *ArcherAdminAPIService) AdminListArcherJobs(ctx context.Context) ApiAdminListArcherJobsRequest {
|
||||
return ApiAdminListArcherJobsRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoPageJob
|
||||
func (a *ArcherAdminAPIService) AdminListArcherJobsExecute(r ApiAdminListArcherJobsRequest) (*DtoPageJob, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoPageJob
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ArcherAdminAPIService.AdminListArcherJobs")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/admin/archer/jobs"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
if r.status != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "status", r.status, "", "")
|
||||
}
|
||||
if r.queue != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "queue", r.queue, "", "")
|
||||
}
|
||||
if r.q != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "q", r.q, "", "")
|
||||
}
|
||||
if r.page != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "", "")
|
||||
} else {
|
||||
var defaultValue int32 = 1
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "", "")
|
||||
r.page = &defaultValue
|
||||
}
|
||||
if r.pageSize != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "page_size", r.pageSize, "", "")
|
||||
} else {
|
||||
var defaultValue int32 = 25
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "page_size", defaultValue, "", "")
|
||||
r.pageSize = &defaultValue
|
||||
}
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 401 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 403 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 500 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiAdminListArcherQueuesRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ArcherAdminAPIService
|
||||
}
|
||||
|
||||
func (r ApiAdminListArcherQueuesRequest) Execute() ([]DtoQueueInfo, *http.Response, error) {
|
||||
return r.ApiService.AdminListArcherQueuesExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AdminListArcherQueues List Archer queues (admin)
|
||||
|
||||
Summary metrics per queue (pending, running, failed, scheduled).
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiAdminListArcherQueuesRequest
|
||||
*/
|
||||
func (a *ArcherAdminAPIService) AdminListArcherQueues(ctx context.Context) ApiAdminListArcherQueuesRequest {
|
||||
return ApiAdminListArcherQueuesRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return []DtoQueueInfo
|
||||
func (a *ArcherAdminAPIService) AdminListArcherQueuesExecute(r ApiAdminListArcherQueuesRequest) ([]DtoQueueInfo, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue []DtoQueueInfo
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ArcherAdminAPIService.AdminListArcherQueues")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/admin/archer/queues"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 401 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 403 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 500 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiAdminRetryArcherJobRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ArcherAdminAPIService
|
||||
id string
|
||||
}
|
||||
|
||||
func (r ApiAdminRetryArcherJobRequest) Execute() (*DtoJob, *http.Response, error) {
|
||||
return r.ApiService.AdminRetryArcherJobExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AdminRetryArcherJob Retry a failed/canceled Archer job (admin)
|
||||
|
||||
Marks the job retriable (DB flip). Swap this for an Archer admin call if you expose one.
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param id Job ID
|
||||
@return ApiAdminRetryArcherJobRequest
|
||||
*/
|
||||
func (a *ArcherAdminAPIService) AdminRetryArcherJob(ctx context.Context, id string) ApiAdminRetryArcherJobRequest {
|
||||
return ApiAdminRetryArcherJobRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoJob
|
||||
func (a *ArcherAdminAPIService) AdminRetryArcherJobExecute(r ApiAdminRetryArcherJobRequest) (*DtoJob, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoJob
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ArcherAdminAPIService.AdminRetryArcherJob")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/admin/archer/jobs/{id}/retry"
|
||||
localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1)
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 400 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 401 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 403 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 404 {
|
||||
var v string
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
/*
|
||||
AutoGlue API
|
||||
|
||||
API for managing K3s clusters across cloud providers
|
||||
|
||||
API version: 1.0
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package autoglue
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AuthAPIService AuthAPI service
|
||||
type AuthAPIService service
|
||||
|
||||
type ApiAuthCallbackRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *AuthAPIService
|
||||
provider string
|
||||
}
|
||||
|
||||
func (r ApiAuthCallbackRequest) Execute() (*DtoTokenPair, *http.Response, error) {
|
||||
return r.ApiService.AuthCallbackExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AuthCallback Handle social login callback
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param provider google|github
|
||||
@return ApiAuthCallbackRequest
|
||||
*/
|
||||
func (a *AuthAPIService) AuthCallback(ctx context.Context, provider string) ApiAuthCallbackRequest {
|
||||
return ApiAuthCallbackRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoTokenPair
|
||||
func (a *AuthAPIService) AuthCallbackExecute(r ApiAuthCallbackRequest) (*DtoTokenPair, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoTokenPair
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuthAPIService.AuthCallback")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/auth/{provider}/callback"
|
||||
localVarPath = strings.Replace(localVarPath, "{"+"provider"+"}", url.PathEscape(parameterValueToString(r.provider, "provider")), -1)
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiAuthStartRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *AuthAPIService
|
||||
provider string
|
||||
}
|
||||
|
||||
func (r ApiAuthStartRequest) Execute() (*DtoAuthStartResponse, *http.Response, error) {
|
||||
return r.ApiService.AuthStartExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
AuthStart Begin social login
|
||||
|
||||
Returns provider authorization URL for the frontend to redirect
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param provider google|github
|
||||
@return ApiAuthStartRequest
|
||||
*/
|
||||
func (a *AuthAPIService) AuthStart(ctx context.Context, provider string) ApiAuthStartRequest {
|
||||
return ApiAuthStartRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoAuthStartResponse
|
||||
func (a *AuthAPIService) AuthStartExecute(r ApiAuthStartRequest) (*DtoAuthStartResponse, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoAuthStartResponse
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuthAPIService.AuthStart")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/auth/{provider}/start"
|
||||
localVarPath = strings.Replace(localVarPath, "{"+"provider"+"}", url.PathEscape(parameterValueToString(r.provider, "provider")), -1)
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiGetJWKSRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *AuthAPIService
|
||||
}
|
||||
|
||||
func (r ApiGetJWKSRequest) Execute() (*DtoJWKS, *http.Response, error) {
|
||||
return r.ApiService.GetJWKSExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
GetJWKS Get JWKS
|
||||
|
||||
Returns the JSON Web Key Set for token verification
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiGetJWKSRequest
|
||||
*/
|
||||
func (a *AuthAPIService) GetJWKS(ctx context.Context) ApiGetJWKSRequest {
|
||||
return ApiGetJWKSRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoJWKS
|
||||
func (a *AuthAPIService) GetJWKSExecute(r ApiGetJWKSRequest) (*DtoJWKS, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoJWKS
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuthAPIService.GetJWKS")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/.well-known/jwks.json"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiLogoutRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *AuthAPIService
|
||||
body *DtoLogoutRequest
|
||||
}
|
||||
|
||||
// Refresh token
|
||||
func (r ApiLogoutRequest) Body(body DtoLogoutRequest) ApiLogoutRequest {
|
||||
r.body = &body
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiLogoutRequest) Execute() (*http.Response, error) {
|
||||
return r.ApiService.LogoutExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
Logout Revoke refresh token family (logout everywhere)
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiLogoutRequest
|
||||
*/
|
||||
func (a *AuthAPIService) Logout(ctx context.Context) ApiLogoutRequest {
|
||||
return ApiLogoutRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
func (a *AuthAPIService) LogoutExecute(r ApiLogoutRequest) (*http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuthAPIService.Logout")
|
||||
if err != nil {
|
||||
return nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/auth/logout"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
if r.body == nil {
|
||||
return nil, reportError("body is required and must be specified")
|
||||
}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{"application/json"}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
// body params
|
||||
localVarPostBody = r.body
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiRefreshRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *AuthAPIService
|
||||
body *DtoRefreshRequest
|
||||
}
|
||||
|
||||
// Refresh token
|
||||
func (r ApiRefreshRequest) Body(body DtoRefreshRequest) ApiRefreshRequest {
|
||||
r.body = &body
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiRefreshRequest) Execute() (*DtoTokenPair, *http.Response, error) {
|
||||
return r.ApiService.RefreshExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
Refresh Rotate refresh token
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiRefreshRequest
|
||||
*/
|
||||
func (a *AuthAPIService) Refresh(ctx context.Context) ApiRefreshRequest {
|
||||
return ApiRefreshRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return DtoTokenPair
|
||||
func (a *AuthAPIService) RefreshExecute(r ApiRefreshRequest) (*DtoTokenPair, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *DtoTokenPair
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuthAPIService.Refresh")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/auth/refresh"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
if r.body == nil {
|
||||
return localVarReturnValue, nil, reportError("body is required and must be specified")
|
||||
}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{"application/json"}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
// body params
|
||||
localVarPostBody = r.body
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
AutoGlue API
|
||||
|
||||
API for managing K3s clusters across cloud providers
|
||||
|
||||
API version: 1.0
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package autoglue
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// HealthAPIService HealthAPI service
|
||||
type HealthAPIService service
|
||||
|
||||
type ApiHealthCheckOperationIdRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *HealthAPIService
|
||||
}
|
||||
|
||||
func (r ApiHealthCheckOperationIdRequest) Execute() (*HandlersHealthStatus, *http.Response, error) {
|
||||
return r.ApiService.HealthCheckOperationIdExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
HealthCheckOperationId Basic health check
|
||||
|
||||
Returns 200 OK when the service is up
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiHealthCheckOperationIdRequest
|
||||
*/
|
||||
func (a *HealthAPIService) HealthCheckOperationId(ctx context.Context) ApiHealthCheckOperationIdRequest {
|
||||
return ApiHealthCheckOperationIdRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return HandlersHealthStatus
|
||||
func (a *HealthAPIService) HealthCheckOperationIdExecute(r ApiHealthCheckOperationIdRequest) (*HandlersHealthStatus, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *HandlersHealthStatus
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "HealthAPIService.HealthCheckOperationId")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/healthz"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
1075
sdk/go/api_labels.go
1075
sdk/go/api_labels.go
File diff suppressed because it is too large
Load Diff
286
sdk/go/api_me.go
286
sdk/go/api_me.go
@@ -1,286 +0,0 @@
|
||||
/*
|
||||
AutoGlue API
|
||||
|
||||
API for managing K3s clusters across cloud providers
|
||||
|
||||
API version: 1.0
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package autoglue
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// MeAPIService MeAPI service
|
||||
type MeAPIService service
|
||||
|
||||
type ApiGetMeRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeAPIService
|
||||
}
|
||||
|
||||
func (r ApiGetMeRequest) Execute() (*HandlersMeResponse, *http.Response, error) {
|
||||
return r.ApiService.GetMeExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
GetMe Get current user profile
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiGetMeRequest
|
||||
*/
|
||||
func (a *MeAPIService) GetMe(ctx context.Context) ApiGetMeRequest {
|
||||
return ApiGetMeRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return HandlersMeResponse
|
||||
func (a *MeAPIService) GetMeExecute(r ApiGetMeRequest) (*HandlersMeResponse, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *HandlersMeResponse
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MeAPIService.GetMe")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/me"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["ApiKeyAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["X-API-KEY"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiUpdateMeRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeAPIService
|
||||
body *HandlersUpdateMeRequest
|
||||
}
|
||||
|
||||
// Patch profile
|
||||
func (r ApiUpdateMeRequest) Body(body HandlersUpdateMeRequest) ApiUpdateMeRequest {
|
||||
r.body = &body
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiUpdateMeRequest) Execute() (*ModelsUser, *http.Response, error) {
|
||||
return r.ApiService.UpdateMeExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
UpdateMe Update current user profile
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiUpdateMeRequest
|
||||
*/
|
||||
func (a *MeAPIService) UpdateMe(ctx context.Context) ApiUpdateMeRequest {
|
||||
return ApiUpdateMeRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return ModelsUser
|
||||
func (a *MeAPIService) UpdateMeExecute(r ApiUpdateMeRequest) (*ModelsUser, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPatch
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *ModelsUser
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MeAPIService.UpdateMe")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/me"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
if r.body == nil {
|
||||
return localVarReturnValue, nil, reportError("body is required and must be specified")
|
||||
}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{"application/json"}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
// body params
|
||||
localVarPostBody = r.body
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["ApiKeyAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["X-API-KEY"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
/*
|
||||
AutoGlue API
|
||||
|
||||
API for managing K3s clusters across cloud providers
|
||||
|
||||
API version: 1.0
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package autoglue
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MeAPIKeysAPIService MeAPIKeysAPI service
|
||||
type MeAPIKeysAPIService service
|
||||
|
||||
type ApiCreateUserAPIKeyRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeAPIKeysAPIService
|
||||
body *HandlersCreateUserKeyRequest
|
||||
}
|
||||
|
||||
// Key options
|
||||
func (r ApiCreateUserAPIKeyRequest) Body(body HandlersCreateUserKeyRequest) ApiCreateUserAPIKeyRequest {
|
||||
r.body = &body
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiCreateUserAPIKeyRequest) Execute() (*HandlersUserAPIKeyOut, *http.Response, error) {
|
||||
return r.ApiService.CreateUserAPIKeyExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
CreateUserAPIKey Create a new user API key
|
||||
|
||||
Returns the plaintext key once. Store it securely on the client side.
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiCreateUserAPIKeyRequest
|
||||
*/
|
||||
func (a *MeAPIKeysAPIService) CreateUserAPIKey(ctx context.Context) ApiCreateUserAPIKeyRequest {
|
||||
return ApiCreateUserAPIKeyRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return HandlersUserAPIKeyOut
|
||||
func (a *MeAPIKeysAPIService) CreateUserAPIKeyExecute(r ApiCreateUserAPIKeyRequest) (*HandlersUserAPIKeyOut, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodPost
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *HandlersUserAPIKeyOut
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MeAPIKeysAPIService.CreateUserAPIKey")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/me/api-keys"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
if r.body == nil {
|
||||
return localVarReturnValue, nil, reportError("body is required and must be specified")
|
||||
}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{"application/json"}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
// body params
|
||||
localVarPostBody = r.body
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["ApiKeyAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["X-API-KEY"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiDeleteUserAPIKeyRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeAPIKeysAPIService
|
||||
id string
|
||||
}
|
||||
|
||||
func (r ApiDeleteUserAPIKeyRequest) Execute() (*http.Response, error) {
|
||||
return r.ApiService.DeleteUserAPIKeyExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
DeleteUserAPIKey Delete a user API key
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param id Key ID (UUID)
|
||||
@return ApiDeleteUserAPIKeyRequest
|
||||
*/
|
||||
func (a *MeAPIKeysAPIService) DeleteUserAPIKey(ctx context.Context, id string) ApiDeleteUserAPIKeyRequest {
|
||||
return ApiDeleteUserAPIKeyRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
func (a *MeAPIKeysAPIService) DeleteUserAPIKeyExecute(r ApiDeleteUserAPIKeyRequest) (*http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodDelete
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MeAPIKeysAPIService.DeleteUserAPIKey")
|
||||
if err != nil {
|
||||
return nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/me/api-keys/{id}"
|
||||
localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1)
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiListUserAPIKeysRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *MeAPIKeysAPIService
|
||||
}
|
||||
|
||||
func (r ApiListUserAPIKeysRequest) Execute() ([]HandlersUserAPIKeyOut, *http.Response, error) {
|
||||
return r.ApiService.ListUserAPIKeysExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
ListUserAPIKeys List my API keys
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiListUserAPIKeysRequest
|
||||
*/
|
||||
func (a *MeAPIKeysAPIService) ListUserAPIKeys(ctx context.Context) ApiListUserAPIKeysRequest {
|
||||
return ApiListUserAPIKeysRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return []HandlersUserAPIKeyOut
|
||||
func (a *MeAPIKeysAPIService) ListUserAPIKeysExecute(r ApiListUserAPIKeysRequest) ([]HandlersUserAPIKeyOut, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue []HandlersUserAPIKeyOut
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "MeAPIKeysAPIService.ListUserAPIKeys")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/me/api-keys"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["ApiKeyAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["X-API-KEY"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.ctx != nil {
|
||||
// API Key Authentication
|
||||
if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok {
|
||||
if apiKey, ok := auth["BearerAuth"]; ok {
|
||||
var key string
|
||||
if apiKey.Prefix != "" {
|
||||
key = apiKey.Prefix + " " + apiKey.Key
|
||||
} else {
|
||||
key = apiKey.Key
|
||||
}
|
||||
localVarHeaderParams["Authorization"] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
1476
sdk/go/api_orgs.go
1476
sdk/go/api_orgs.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1055
sdk/go/api_ssh.go
1055
sdk/go/api_ssh.go
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user