Compare commits

...

21 Commits

Author SHA1 Message Date
allanice001
ea4c625269 fix: db-studio prefix fixes 2025-11-11 04:01:57 +00:00
allanice001
b4c108a5be Merge branch 'main' of github.com:GlueOps/autoglue 2025-11-11 03:20:10 +00:00
allanice001
3a1ce33bca feat: adding embedded db-studio
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-11 03:19:09 +00:00
public-glueops-renovatebot[bot]
dbb7ec398e chore: lock file maintenance (#259)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-11 03:03:05 +00:00
public-glueops-renovatebot[bot]
82847e5027 chore: lock file maintenance (#258)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-10 21:03:26 +00:00
public-glueops-renovatebot[bot]
4314599427 chore: lock file maintenance (#256)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-10 17:24:20 +00:00
public-glueops-renovatebot[bot]
9108ee8f8f breaking: the dependency sigstore/cosign-installer has been updated to a new major version (v4.0.0), which may include breaking changes. #major (#253)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-10 15:14:40 +00:00
allanice001
1feb3e29e1 chore: prettier 2025-11-10 14:47:39 +00:00
allanice001
0e9ce98624 fix: credentials page bugfix
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 14:47:24 +00:00
allanice001
96aef81959 fix: credentials page bugfix
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 14:46:56 +00:00
allanice001
62232e18f3 chore: prettier 2025-11-10 14:42:30 +00:00
allanice001
515327153c fix: credentials bugfix
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 14:41:54 +00:00
allanice001
5f2e885a8e Merge remote-tracking branch 'origin/main' 2025-11-10 14:41:30 +00:00
allanice001
01b48efba0 fix: credentials bugfix
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 14:41:16 +00:00
public-glueops-renovatebot[bot]
1c87566c5b chore: lock file maintenance (#255)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-10 09:12:44 +00:00
public-glueops-renovatebot[bot]
ad00a3c45d chore(fallback): update axllent/mailpit (#252)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-10 00:17:33 +00:00
public-glueops-renovatebot[bot]
158fdce780 chore(pin): update @types/react to #patch (#251)
Co-authored-by: public-glueops-renovatebot[bot] <186083205+public-glueops-renovatebot[bot]@users.noreply.github.com>
2025-11-10 00:17:24 +00:00
allanice001
c4fd344364 fix: prettier
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 00:15:34 +00:00
allanice001
953e724ba0 fix: types fixed - credentials page
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 00:14:49 +00:00
allanice001
256acfd686 fix: types fixed - credentials
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 00:13:53 +00:00
allanice001
1dd0a39aad fix: types fixed - credentials
Signed-off-by: allanice001 <allanice001@gmail.com>
2025-11-10 00:05:54 +00:00
28 changed files with 758 additions and 219 deletions

View File

@@ -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'

View File

@@ -310,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
View 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)
}

View File

@@ -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/web"
"github.com/google/uuid"
"github.com/spf13/cobra"
)
@@ -33,6 +34,8 @@ var serveCmd = &cobra.Command{
return err
}
var pgwebInst *web.Pgweb
jobs, err := bg.NewJobs(rt.DB, cfg.DbURL)
if err != nil {
log.Fatalf("failed to init background jobs: %v", err)
@@ -119,7 +122,31 @@ var serveCmd = &cobra.Command{
}
}()
r := api.NewRouter(rt.DB, jobs)
var studioHandler http.Handler
r := api.NewRouter(rt.DB, jobs, nil)
if cfg.DBStudioEnabled {
dbURL := cfg.DbURLRO
if dbURL == "" {
dbURL = cfg.DbURL
}
pgwebInst, err = web.StartPgweb(
dbURL,
cfg.DBStudioBind,
cfg.DBStudioPort,
true,
cfg.DBStudioUser,
cfg.DBStudioPass,
)
if err != nil {
log.Printf("pgweb failed to start: %v", err)
} else {
studioHandler = http.HandlerFunc(pgwebInst.Proxy())
r = api.NewRouter(rt.DB, jobs, studioHandler)
log.Printf("pgweb running on http://%s:%s (proxied at /db-studio/)", cfg.DBStudioBind, pgwebInst.Port())
}
}
addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.Port)
@@ -143,6 +170,9 @@ var serveCmd = &cobra.Command{
<-ctx.Done()
fmt.Println("\n⏳ Shutting down...")
if pgwebInst != nil {
_ = pgwebInst.Stop(context.Background())
}
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return srv.Shutdown(shutdownCtx)

View File

@@ -42,7 +42,7 @@ services:
- postgres
mailpit:
image: axllent/mailpit@sha256:6abc8e633df15eaf785cfcf38bae48e66f64beecdc03121e249d0f9ec15f0707
image: axllent/mailpit@sha256:e22dce5b36f93c77082e204a3942fb6b283b7896e057458400a4c88344c3df68
restart: always
ports:
- "1025:1025"

View File

@@ -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 {

View File

@@ -27,7 +27,7 @@ import (
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"})
@@ -212,6 +212,17 @@ func NewRouter(db *gorm.DB, jobs *bg.Jobs) http.Handler {
})
})
})
if studio != nil {
r.Group(func(gr chi.Router) {
authUser := httpmiddleware.AuthMiddleware(db, false)
adminOnly := httpmiddleware.RequirePlatformAdmin()
gr.Use(authUser)
gr.Use(adminOnly)
gr.Mount("/db-studio", studio)
})
}
if config.IsDebug() {
r.Route("/debug/pprof", func(pr chi.Router) {
pr.Get("/", httpPprof.Index)
@@ -251,6 +262,7 @@ func NewRouter(db *gorm.DB, jobs *bg.Jobs) http.Handler {
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)

View File

@@ -13,6 +13,7 @@ import (
type Config struct {
DbURL string
DbURLRO string
Port string
Host string
JWTIssuer string
@@ -29,6 +30,12 @@ type Config struct {
Debug bool
Swagger bool
SwaggerHost string
DBStudioEnabled bool
DBStudioBind string
DBStudioPort string
DBStudioUser string
DBStudioPass string
}
var (
@@ -48,6 +55,12 @@ 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")
@@ -63,6 +76,7 @@ func Load() (Config, error) {
"bind.address",
"bind.port",
"database.url",
"database.url_ro",
"jwt.issuer",
"jwt.audience",
"jwt.private.enc.key",
@@ -76,6 +90,11 @@ func Load() (Config, error) {
"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)
@@ -84,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"),
@@ -100,6 +120,12 @@ func Load() (Config, error) {
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

View File

@@ -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") {
@@ -377,6 +392,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 +401,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)
}
}

View File

@@ -112,10 +112,10 @@ func clusterToDTO(c models.Cluster) dto.ClusterResponse {
Region: c.Region,
Status: c.Status,
CaptainDomain: c.CaptainDomain,
ClusterLoadBalancer: c.ClusterLoadBalancer,
//ClusterLoadBalancer: c.ClusterLoadBalancer,
RandomToken: c.RandomToken,
CertificateKey: c.CertificateKey,
ControlLoadBalancer: c.ControlLoadBalancer,
//ControlLoadBalancer: c.ControlLoadBalancer,
NodePools: nps,
BastionServer: bastion,
CreatedAt: c.CreatedAt,

View File

@@ -14,9 +14,12 @@ type Cluster struct {
Provider string `json:"provider"`
Region string `json:"region"`
Status string `json:"status"`
CaptainDomain string `gorm:"not null" json:"captain_domain"`
ClusterLoadBalancer string `json:"cluster_load_balancer"`
ControlLoadBalancer string `json:"control_load_balancer"`
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:"-"`

View File

@@ -9,18 +9,18 @@ import (
type Credential struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;uniqueIndex:idx_credentials_org_provider" json:"organization_id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
Provider string `gorm:"type:varchar(50);not null;index"`
Kind string `gorm:"type:varchar(50);not null;index"` // "aws_access_key", "api_token", "basic_auth", ...
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:''"` // human label, lets you have multiple for same service
ScopeKind string `gorm:"type:varchar(20);not null"` // "provider" | "service" | "resource"
Scope datatypes.JSON `gorm:"type:jsonb;not null;default:'{}'"` // e.g. {"service":"route53"} or {"arn":"arn:aws:s3:::my-bucket"}
Name string `gorm:"type:varchar(100);not null;default:''"`
ScopeVersion int `gorm:"not null;default:1"`
AccountID string `gorm:"type:varchar(32)"` // AWS account ID if applicable
Region string `gorm:"type:varchar(32)"` // default region (non-secret)
ScopeFingerprint string `gorm:"type:char(64);not null;index"`
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"`

View File

@@ -1,20 +0,0 @@
package models
import (
"time"
"github.com/google/uuid"
)
type Dns struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;uniqueIndex:idx_credentials_org_provider" json:"organization_id"`
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
ClusterID *uuid.UUID `gorm:"type:uuid" json:"cluster_id,omitempty"`
Cluster *Cluster `gorm:"foreignKey:ClusterID" json:"cluster,omitempty"`
Type string `gorm:"not null" json:"type,omitempty"`
Name string `gorm:"not null" json:"name,omitempty"`
Content string `gorm:"not null" json:"content,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()"`
}

21
internal/models/domain.go Normal file
View File

@@ -0,0 +1,21 @@
package models
import (
"time"
"github.com/google/uuid"
)
type Domain struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;uniqueIndex:idx_credentials_org_provider" json:"organization_id"`
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
ClusterID *uuid.UUID `gorm:"type:uuid" json:"cluster_id,omitempty"`
Cluster *Cluster `gorm:"foreignKey:ClusterID" json:"cluster,omitempty"`
DomainName string `gorm:"not null;index" json:"domain_name,omitempty"`
DomainID string
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()"`
}

View File

@@ -0,0 +1,85 @@
package web
import (
"crypto/sha256"
"embed"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
)
//go:embed pgwebbin/*
var pgwebFS embed.FS
type pgwebAsset struct {
Path string
SHA256 string
}
var pgwebIndex = map[string]pgwebAsset{
"linux/amd64": {Path: "pgwebbin/pgweb-linux-amd64", SHA256: ""},
"linux/arm64": {Path: "pgwebbin/pgweb-linux-arm64", SHA256: ""},
"darwin/amd64": {Path: "pgwebbin/pgweb-darwin-amd64", SHA256: ""},
"darwin/arm64": {Path: "pgwebbin/pgweb-darwin-arm64", SHA256: ""},
}
func ExtractPgweb() (string, error) {
key := runtime.GOOS + "/" + runtime.GOARCH
as, ok := pgwebIndex[key]
if !ok {
return "", fmt.Errorf("pgweb not embedded for %s", key)
}
f, err := pgwebFS.Open(as.Path)
if err != nil {
return "", fmt.Errorf("embedded pgweb missing: %w", err)
}
defer f.Close()
tmpDir, err := os.MkdirTemp("", "pgweb-*")
if err != nil {
return "", err
}
filename := "pgweb"
if runtime.GOOS == "windows" {
filename += ".exe"
}
outPath := filepath.Join(tmpDir, filename)
out, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o700)
if err != nil {
return "", err
}
defer out.Close()
h := sha256.New()
if _, err = io.Copy(io.MultiWriter(out, h), f); err != nil {
return "", err
}
if as.SHA256 != "" {
got := hex.EncodeToString(h.Sum(nil))
if got != as.SHA256 {
return "", fmt.Errorf("pgweb checksum mismatch: got=%s want=%s", got, as.SHA256)
}
}
// Make sure its executable on Unix; Windows ignores this.
_ = os.Chmod(outPath, 0o700)
return outPath, nil
}
func CleanupPgweb(pgwebPath string) error {
if pgwebPath == "" {
return nil
}
dir := filepath.Dir(pgwebPath)
if dir == "" || dir == "/" || dir == "." {
return errors.New("refusing to remove suspicious directory")
}
return os.RemoveAll(dir)
}

107
internal/web/pgweb_proxy.go Normal file
View File

@@ -0,0 +1,107 @@
package web
import (
"context"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"time"
)
type Pgweb struct {
cmd *exec.Cmd
host string
port string
bin string
}
func StartPgweb(dbURL, host, port string, readonly bool, user, pass string) (*Pgweb, error) {
// pick random port if 0/empty
if port == "" || port == "0" {
l, err := net.Listen("tcp", net.JoinHostPort(host, "0"))
if err != nil {
return nil, err
}
defer l.Close()
_, p, _ := net.SplitHostPort(l.Addr().String())
port = p
}
args := []string{
"--url", dbURL,
"--bind", host,
"--listen", port,
"--prefix", "/db-studio",
"--skip-open",
}
if readonly {
args = append(args, "--readonly")
}
if user != "" && pass != "" {
args = append(args, "--auth-user", user, "--auth-pass", pass)
}
pgwebBinary, err := ExtractPgweb()
if err != nil {
return nil, fmt.Errorf("pgweb extract: %w", err)
}
cmd := exec.Command(pgwebBinary, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return nil, err
}
// wait for port to be ready
deadline := time.Now().Add(4 * time.Second)
for time.Now().Before(deadline) {
c, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), 200*time.Millisecond)
if err == nil {
_ = c.Close()
return &Pgweb{cmd: cmd, host: host, port: port}, nil
}
time.Sleep(120 * time.Millisecond)
}
// still return object so caller can Stop()
//return &Pgweb{cmd: cmd, host: host, port: port, bin: pgwebBinary}, nil
return nil, fmt.Errorf("pgweb did not become ready on %s:%s", host, port)
}
func (p *Pgweb) Proxy() http.HandlerFunc {
target, _ := url.Parse("http://" + net.JoinHostPort(p.host, p.port))
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.FlushInterval = 100 * time.Millisecond
return func(w http.ResponseWriter, r *http.Request) {
r.Host = target.Host
// Let pgweb handle its paths; we mount it at a prefix.
proxy.ServeHTTP(w, r)
}
}
func (p *Pgweb) Stop(ctx context.Context) error {
if p == nil || p.cmd == nil || p.cmd.Process == nil {
return nil
}
_ = p.cmd.Process.Kill()
done := make(chan struct{})
go func() { _, _ = p.cmd.Process.Wait(); close(done) }()
select {
case <-done:
if p.bin != "" {
_ = CleanupPgweb(p.bin)
}
case <-ctx.Done():
return ctx.Err()
}
return nil
}
func (p *Pgweb) Port() string {
return p.port
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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

171
tools/pgweb_fetch.go Normal file
View File

@@ -0,0 +1,171 @@
//go:build ignore
// +build ignore
package main
import (
"archive/zip"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
)
type Target struct {
Name string
URL string
SHA256 string
}
const version = "0.16.2"
func main() {
targets := []Target{
{
Name: "pgweb-linux-amd64",
URL: fmt.Sprintf("https://github.com/sosedoff/pgweb/releases/download/v%s/pgweb_linux_amd64.zip", version),
SHA256: "",
},
{
Name: "pgweb-linux-arm64",
URL: fmt.Sprintf("https://github.com/sosedoff/pgweb/releases/download/v%s/pgweb_linux_arm64.zip", version),
SHA256: "",
},
{
Name: "pgweb-darwin-amd64",
URL: fmt.Sprintf("https://github.com/sosedoff/pgweb/releases/download/v%s/pgweb_darwin_amd64.zip", version),
SHA256: "",
},
{
Name: "pgweb-darwin-arm64",
URL: fmt.Sprintf("https://github.com/sosedoff/pgweb/releases/download/v%s/pgweb_darwin_arm64.zip", version),
SHA256: "",
},
}
outDir := filepath.Join("internal", "web", "pgwebbin")
_ = os.MkdirAll(outDir, 0o755)
for _, t := range targets {
destZip := filepath.Join(outDir, t.Name+".zip")
fmt.Printf("Downloading %s...\n", t.URL)
if err := downloadFile(destZip, t.URL); err != nil {
panic(err)
}
binPath := filepath.Join(outDir, t.Name)
if err := unzipSingle(destZip, binPath); err != nil {
panic(err)
}
_ = os.Remove(destZip)
// Make executable
if err := os.Chmod(binPath, 0o755); err != nil {
panic(err)
}
fmt.Printf("Saved %s\n", binPath)
// Compute checksum
sum, _ := fileSHA256(binPath)
fmt.Printf(" SHA256: %s\n", sum)
}
}
func downloadFile(dest, url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("bad status: %s", resp.Status)
}
out, err := os.Create(dest)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return err
}
func fileSHA256(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
func unzipSingle(zipPath, outPath string) error {
// minimal unzip: because pgweb zip has only one binary
r, err := os.Open(zipPath)
if err != nil {
return err
}
defer r.Close()
// use archive/zip
stat, err := os.Stat(zipPath)
if err != nil {
return err
}
return unzipFile(zipPath, outPath, stat.Size())
}
func unzipFile(zipFile, outFile string, _ int64) error {
r, err := os.Open(zipFile)
if err != nil {
return err
}
defer r.Close()
fi, _ := r.Stat()
// rely on standard zip reader
data, err := io.ReadAll(r)
if err != nil {
return err
}
tmpZip := filepath.Join(os.TempDir(), fi.Name())
if err := os.WriteFile(tmpZip, data, 0o644); err != nil {
return err
}
defer os.Remove(tmpZip)
zr, err := os.Open(tmpZip)
if err != nil {
return err
}
defer zr.Close()
// extract using standard lib
zr2, err := zip.OpenReader(tmpZip)
if err != nil {
return err
}
defer zr2.Close()
for _, f := range zr2.File {
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
out, err := os.Create(outFile)
if err != nil {
return err
}
if _, err := io.Copy(out, rc); err != nil {
out.Close()
return err
}
out.Close()
break
}
return nil
}

View File

@@ -65,8 +65,8 @@
"@eslint/js": "9.39.1",
"@ianvs/prettier-plugin-sort-imports": "4.7.0",
"@types/node": "24.10.0",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.2",
"@vitejs/plugin-react": "5.1.0",
"eslint": "9.39.1",
"eslint-plugin-react-hooks": "7.0.1",

View File

@@ -2,7 +2,6 @@ import { withRefresh } from "@/api/with-refresh.ts"
import type { DtoCreateLabelRequest, DtoUpdateLabelRequest } from "@/sdk"
import { makeLabelsApi } from "@/sdkClient.ts"
const labels = makeLabelsApi()
export const labelsApi = {

View File

@@ -172,7 +172,7 @@ const createCredentialSchema = z
}
})
type CreateCredentialValues = z.infer<typeof createCredentialSchema>
type CreateCredentialValues = z.input<typeof createCredentialSchema>
const updateCredentialSchema = createCredentialSchema.partial().extend({
name: z.string().min(1, "Name is required").max(100).optional(),
})
@@ -181,7 +181,7 @@ const updateCredentialSchema = createCredentialSchema.partial().extend({
function pretty(obj: unknown) {
try {
return JSON.stringify(obj, null, 2)
return JSON.stringify(JSON.parse(obj as string), null, 2)
} catch {
return ""
}

View File

@@ -8,7 +8,6 @@ import { useForm } from "react-hook-form"
import { toast } from "sonner"
import { z } from "zod"
import { Button } from "@/components/ui/button.tsx"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"
import {

View File

@@ -1,7 +1,7 @@
import path from "path"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
// https://vite.dev/config/
export default defineConfig({
@@ -9,15 +9,16 @@ export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
}
},
},
server: {
port: 5173,
proxy: {
"/api": "http://localhost:8080",
"/swagger": "http://localhost:8080",
"/db-studio": "http://localhost:8080",
},
allowedHosts: ['.getexposed.io']
allowedHosts: [".getexposed.io"],
},
build: {
chunkSizeWarningLimit: 1000,
@@ -25,7 +26,9 @@ export default defineConfig({
emptyOutDir: true,
sourcemap: true,
cssMinify: "lightningcss",
rollupOptions: { output: { manualChunks: { react: ["react","react-dom","react-router-dom"] } } }
rollupOptions: {
output: { manualChunks: { react: ["react", "react-dom", "react-router-dom"] } },
},
esbuild: { legalComments: "none" }
},
esbuild: { legalComments: "none" },
})

View File

@@ -1370,115 +1370,115 @@
resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz#fa8249860113711ad3c8053bc79cb07c79b77f62"
integrity sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==
"@rollup/rollup-android-arm-eabi@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.1.tgz#63f6bdc496180079976e655473d5bea99b21f3ff"
integrity sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==
"@rollup/rollup-android-arm-eabi@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz#7131f3d364805067fd5596302aad9ebef1434b32"
integrity sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==
"@rollup/rollup-android-arm64@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.1.tgz#177f5e504d2f332edd0ddd3682f91ab72528fb60"
integrity sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==
"@rollup/rollup-android-arm64@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz#7ede14d7fcf7c57821a2731c04b29ccc03145d82"
integrity sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==
"@rollup/rollup-darwin-arm64@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.1.tgz#ffdbe0cc43c88a35be2821f99cdff4c7a5ee2116"
integrity sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==
"@rollup/rollup-darwin-arm64@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz#d59bf9ed582b38838e86a17f91720c17db6575b9"
integrity sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==
"@rollup/rollup-darwin-x64@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.1.tgz#27a4852923010abbcd1f028c7e8bd6bf0ccbe755"
integrity sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==
"@rollup/rollup-darwin-x64@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz#a76278d9b9da9f84ea7909a14d93b915d5bbe01e"
integrity sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==
"@rollup/rollup-freebsd-arm64@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.1.tgz#a02b83018e487674ab445198786bef9b41cad9f0"
integrity sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==
"@rollup/rollup-freebsd-arm64@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz#1a94821a1f565b9eaa74187632d482e4c59a1707"
integrity sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==
"@rollup/rollup-freebsd-x64@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.1.tgz#fe898a4f0ff7c30f8377c3976ae76b89720c41da"
integrity sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==
"@rollup/rollup-freebsd-x64@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz#aad2274680106b2b6549b1e35e5d3a7a9f1f16af"
integrity sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==
"@rollup/rollup-linux-arm-gnueabihf@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.1.tgz#be5a731a9f7bd7bc707457a768940b6107a9215e"
integrity sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==
"@rollup/rollup-linux-arm-gnueabihf@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz#100fe4306399ffeec47318a3c9b8c0e5e8b07ddb"
integrity sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==
"@rollup/rollup-linux-arm-musleabihf@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.1.tgz#30ce6548a9e3591303507c37280300edb0cd1d14"
integrity sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==
"@rollup/rollup-linux-arm-musleabihf@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz#b84634952604b950e18fa11fddebde898c5928d8"
integrity sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==
"@rollup/rollup-linux-arm64-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.1.tgz#ec76f4223335e86cd61b0d596f34e97223f4f711"
integrity sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==
"@rollup/rollup-linux-arm64-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz#dad6f2fb41c2485f29a98e40e9bd78253255dbf3"
integrity sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==
"@rollup/rollup-linux-arm64-musl@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.1.tgz#9d4d87c2988ec8e4bb3cf4516dda7ef6d09dcd3d"
integrity sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==
"@rollup/rollup-linux-arm64-musl@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz#0f3f77c8ce9fbf982f8a8378b70a73dc6704a706"
integrity sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==
"@rollup/rollup-linux-loong64-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.1.tgz#584bc6f3c33b30c3dbfdad36ac9c7792e4df5199"
integrity sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==
"@rollup/rollup-linux-loong64-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz#870bb94e9dad28bb3124ba49bd733deaa6aa2635"
integrity sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==
"@rollup/rollup-linux-ppc64-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.1.tgz#3e9a3b095a7d7da6043cb9caa54439d3b598aaf5"
integrity sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==
"@rollup/rollup-linux-ppc64-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz#188427d11abefc6c9926e3870b3e032170f5577c"
integrity sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==
"@rollup/rollup-linux-riscv64-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.1.tgz#f3c3d6523d246eef4aa1eed265f1ba31b9eef7c8"
integrity sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==
"@rollup/rollup-linux-riscv64-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz#9dec6eadbbb5abd3b76fe624dc4f006913ff4a7f"
integrity sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==
"@rollup/rollup-linux-riscv64-musl@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.1.tgz#0a8944b4f29a1ba923fb9c2ddb829e621f004988"
integrity sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==
"@rollup/rollup-linux-riscv64-musl@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz#b26ba1c80b6f104dc5bd83ed83181fc0411a0c38"
integrity sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==
"@rollup/rollup-linux-s390x-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.1.tgz#bcb48f2d509ef6b33ba89f7d76a2f3805be8d4c8"
integrity sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==
"@rollup/rollup-linux-s390x-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz#dc83647189b68ad8d56a956a6fcaa4ee9c728190"
integrity sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==
"@rollup/rollup-linux-x64-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.1.tgz#ca9045e3b8e8dc0797e55d0229d5c664211bf366"
integrity sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==
"@rollup/rollup-linux-x64-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz#42c3b8c94e9de37bd103cb2e26fb715118ef6459"
integrity sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==
"@rollup/rollup-linux-x64-musl@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.1.tgz#740876db76078e37bd43cc8584ff1c7f6b382df8"
integrity sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==
"@rollup/rollup-linux-x64-musl@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz#d0e216ee1ea16bfafe35681b899b6a05258988e5"
integrity sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==
"@rollup/rollup-openharmony-arm64@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.1.tgz#3ff19213afe46b806fb6ec105f2664e4027e4cbc"
integrity sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==
"@rollup/rollup-openharmony-arm64@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz#3acd0157cb8976f659442bfd8a99aca46f8a2931"
integrity sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==
"@rollup/rollup-win32-arm64-msvc@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.1.tgz#cbba39610831747f8050a306811776534df1030d"
integrity sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==
"@rollup/rollup-win32-arm64-msvc@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz#3eb9e7d4d0e1d2e0850c4ee9aa2d0ddf89a8effa"
integrity sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==
"@rollup/rollup-win32-ia32-msvc@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.1.tgz#5453c7ebba95d2bbfcc94c744c05586d587fb640"
integrity sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==
"@rollup/rollup-win32-ia32-msvc@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz#d69280bc6680fe19e0956e965811946d542f6365"
integrity sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==
"@rollup/rollup-win32-x64-gnu@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.1.tgz#01e1acb0dacb220d13c8992340f7bc868a564832"
integrity sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==
"@rollup/rollup-win32-x64-gnu@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz#d182ce91e342bad9cbb8b284cf33ac542b126ead"
integrity sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==
"@rollup/rollup-win32-x64-msvc@4.53.1":
version "4.53.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.1.tgz#56eeb602545ec03ce84633b331c2e3ece07b99c3"
integrity sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==
"@rollup/rollup-win32-x64-msvc@4.53.2":
version "4.53.2"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz#d9ab606437fd072b2cb7df7e54bcdc7f1ccbe8b4"
integrity sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==
"@sec-ant/readable-stream@^0.4.1":
version "0.4.1"
@@ -1731,12 +1731,12 @@
dependencies:
undici-types "~7.16.0"
"@types/react-dom@^19.2.2":
"@types/react-dom@19.2.2":
version "19.2.2"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.2.tgz#a4cc874797b7ddc9cb180ef0d5dc23f596fc2332"
integrity sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==
"@types/react@^19.2.2":
"@types/react@19.2.2":
version "19.2.2"
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.2.tgz#ba123a75d4c2a51158697160a4ea2ff70aa6bf36"
integrity sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==
@@ -1791,11 +1791,16 @@
"@typescript-eslint/types" "8.46.3"
"@typescript-eslint/visitor-keys" "8.46.3"
"@typescript-eslint/tsconfig-utils@8.46.3", "@typescript-eslint/tsconfig-utils@^8.46.3":
"@typescript-eslint/tsconfig-utils@8.46.3":
version "8.46.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz#cad33398c762c97fe56a8defda00c16505abefa3"
integrity sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==
"@typescript-eslint/tsconfig-utils@^8.46.3":
version "8.46.4"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz#989a338093b6b91b0552f1f51331d89ec6980382"
integrity sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==
"@typescript-eslint/type-utils@8.46.3":
version "8.46.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz#71188df833d7697ecff256cd1d3889a20552d78c"
@@ -1807,11 +1812,16 @@
debug "^4.3.4"
ts-api-utils "^2.1.0"
"@typescript-eslint/types@8.46.3", "@typescript-eslint/types@^8.46.3":
"@typescript-eslint/types@8.46.3":
version "8.46.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.3.tgz#da05ea40e91359b4275dbb3a489f2f7907a02245"
integrity sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==
"@typescript-eslint/types@^8.46.3":
version "8.46.4"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.4.tgz#38022bfda051be80e4120eeefcd2b6e3e630a69b"
integrity sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==
"@typescript-eslint/typescript-estree@8.46.3":
version "8.46.3"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz#c12406afba707f9779ce0c0151a08c33b3a96d41"
@@ -1954,7 +1964,7 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
baseline-browser-mapping@^2.8.19:
baseline-browser-mapping@^2.8.25:
version "2.8.25"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz#947dc6f81778e0fa0424a2ab9ea09a3033e71109"
integrity sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==
@@ -1997,14 +2007,14 @@ braces@^3.0.3:
fill-range "^7.1.1"
browserslist@^4.24.0, browserslist@^4.26.2:
version "4.27.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.27.0.tgz#755654744feae978fbb123718b2f139bc0fa6697"
integrity sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==
version "4.28.0"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929"
integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==
dependencies:
baseline-browser-mapping "^2.8.19"
caniuse-lite "^1.0.30001751"
electron-to-chromium "^1.5.238"
node-releases "^2.0.26"
baseline-browser-mapping "^2.8.25"
caniuse-lite "^1.0.30001754"
electron-to-chromium "^1.5.249"
node-releases "^2.0.27"
update-browserslist-db "^1.1.4"
bytes@3.1.2, bytes@^3.1.2:
@@ -2033,7 +2043,7 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caniuse-lite@^1.0.30001751:
caniuse-lite@^1.0.30001754:
version "1.0.30001754"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz#7758299d9a72cce4e6b038788a15b12b44002759"
integrity sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==
@@ -2365,10 +2375,10 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
electron-to-chromium@^1.5.238:
version "1.5.249"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.249.tgz#e4fc3a3e60bb347361e4e876bb31903a9132a447"
integrity sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==
electron-to-chromium@^1.5.249:
version "1.5.250"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz#0b40436fa41ae7cbac3d2f60ef0411a698eb72a7"
integrity sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==
embla-carousel-react@^8.6.0:
version "8.6.0"
@@ -3526,7 +3536,7 @@ node-fetch@^3.3.2:
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
node-releases@^2.0.26:
node-releases@^2.0.27:
version "2.0.27"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e"
integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==
@@ -3983,34 +3993,34 @@ reusify@^1.0.4:
integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==
rollup@^4.43.0:
version "4.53.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.1.tgz#84d1d378584a15dedfcdcff7767a8b9d92d8d3d9"
integrity sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==
version "4.53.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.2.tgz#98e73ee51e119cb9d88b07d026c959522416420a"
integrity sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==
dependencies:
"@types/estree" "1.0.8"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.53.1"
"@rollup/rollup-android-arm64" "4.53.1"
"@rollup/rollup-darwin-arm64" "4.53.1"
"@rollup/rollup-darwin-x64" "4.53.1"
"@rollup/rollup-freebsd-arm64" "4.53.1"
"@rollup/rollup-freebsd-x64" "4.53.1"
"@rollup/rollup-linux-arm-gnueabihf" "4.53.1"
"@rollup/rollup-linux-arm-musleabihf" "4.53.1"
"@rollup/rollup-linux-arm64-gnu" "4.53.1"
"@rollup/rollup-linux-arm64-musl" "4.53.1"
"@rollup/rollup-linux-loong64-gnu" "4.53.1"
"@rollup/rollup-linux-ppc64-gnu" "4.53.1"
"@rollup/rollup-linux-riscv64-gnu" "4.53.1"
"@rollup/rollup-linux-riscv64-musl" "4.53.1"
"@rollup/rollup-linux-s390x-gnu" "4.53.1"
"@rollup/rollup-linux-x64-gnu" "4.53.1"
"@rollup/rollup-linux-x64-musl" "4.53.1"
"@rollup/rollup-openharmony-arm64" "4.53.1"
"@rollup/rollup-win32-arm64-msvc" "4.53.1"
"@rollup/rollup-win32-ia32-msvc" "4.53.1"
"@rollup/rollup-win32-x64-gnu" "4.53.1"
"@rollup/rollup-win32-x64-msvc" "4.53.1"
"@rollup/rollup-android-arm-eabi" "4.53.2"
"@rollup/rollup-android-arm64" "4.53.2"
"@rollup/rollup-darwin-arm64" "4.53.2"
"@rollup/rollup-darwin-x64" "4.53.2"
"@rollup/rollup-freebsd-arm64" "4.53.2"
"@rollup/rollup-freebsd-x64" "4.53.2"
"@rollup/rollup-linux-arm-gnueabihf" "4.53.2"
"@rollup/rollup-linux-arm-musleabihf" "4.53.2"
"@rollup/rollup-linux-arm64-gnu" "4.53.2"
"@rollup/rollup-linux-arm64-musl" "4.53.2"
"@rollup/rollup-linux-loong64-gnu" "4.53.2"
"@rollup/rollup-linux-ppc64-gnu" "4.53.2"
"@rollup/rollup-linux-riscv64-gnu" "4.53.2"
"@rollup/rollup-linux-riscv64-musl" "4.53.2"
"@rollup/rollup-linux-s390x-gnu" "4.53.2"
"@rollup/rollup-linux-x64-gnu" "4.53.2"
"@rollup/rollup-linux-x64-musl" "4.53.2"
"@rollup/rollup-openharmony-arm64" "4.53.2"
"@rollup/rollup-win32-arm64-msvc" "4.53.2"
"@rollup/rollup-win32-ia32-msvc" "4.53.2"
"@rollup/rollup-win32-x64-gnu" "4.53.2"
"@rollup/rollup-win32-x64-msvc" "4.53.2"
fsevents "~2.3.2"
router@^2.2.0: