mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-13 21:00:06 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85f37cd113 | ||
|
|
fd1a81ecd8 | ||
|
|
793daf3ac3 | ||
|
|
7bef4ef6f1 | ||
|
|
9fa9cd169b | ||
|
|
8812b43346 | ||
|
|
21a6d7d5a1 | ||
|
|
da332c89dd | ||
|
|
fd25825f34 |
15
cmd/serve.go
15
cmd/serve.go
@@ -156,6 +156,21 @@ var serveCmd = &cobra.Command{
|
||||
if err != nil {
|
||||
log.Printf("failed to enqueue cluster bootstrap: %v", err)
|
||||
}
|
||||
|
||||
_, err = jobs.Enqueue(
|
||||
context.Background(),
|
||||
uuid.NewString(),
|
||||
"org_key_sweeper",
|
||||
bg.OrgKeySweeperArgs{
|
||||
IntervalS: 3600,
|
||||
RetentionDays: 10,
|
||||
},
|
||||
archer.WithMaxRetries(1),
|
||||
archer.WithScheduleTime(time.Now()),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("failed to enqueue org_key_sweeper: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
_ = auth.Refresh(rt.DB, rt.Cfg.JWTPrivateEncKey)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -104,6 +104,8 @@ components:
|
||||
$ref: '#/components/schemas/dto.LoadBalancerResponse'
|
||||
id:
|
||||
type: string
|
||||
kubeconfig:
|
||||
type: string
|
||||
last_error:
|
||||
type: string
|
||||
name:
|
||||
@@ -113,6 +115,10 @@ components:
|
||||
$ref: '#/components/schemas/dto.NodePoolResponse'
|
||||
type: array
|
||||
uniqueItems: false
|
||||
org_key:
|
||||
type: string
|
||||
org_secret:
|
||||
type: string
|
||||
random_token:
|
||||
type: string
|
||||
region:
|
||||
@@ -1037,6 +1043,8 @@ components:
|
||||
type: object
|
||||
models.APIKey:
|
||||
properties:
|
||||
cluster_id:
|
||||
type: string
|
||||
created_at:
|
||||
format: date-time
|
||||
type: string
|
||||
@@ -1046,6 +1054,8 @@ components:
|
||||
id:
|
||||
format: uuid
|
||||
type: string
|
||||
is_ephemeral:
|
||||
type: boolean
|
||||
last_used_at:
|
||||
format: date-time
|
||||
type: string
|
||||
@@ -1056,6 +1066,8 @@ components:
|
||||
type: string
|
||||
prefix:
|
||||
type: string
|
||||
purpose:
|
||||
type: string
|
||||
revoked:
|
||||
type: boolean
|
||||
scope:
|
||||
|
||||
@@ -129,6 +129,12 @@ func NewJobs(gdb *gorm.DB, dbUrl string) (*Jobs, error) {
|
||||
archer.WithTimeout(60*time.Minute),
|
||||
)
|
||||
|
||||
c.Register(
|
||||
"org_key_sweeper",
|
||||
OrgKeySweeperWorker(gdb, jobs),
|
||||
archer.WithInstances(1),
|
||||
archer.WithTimeout(5*time.Minute),
|
||||
)
|
||||
return jobs, nil
|
||||
}
|
||||
|
||||
|
||||
95
internal/bg/org_key_sweeper.go
Normal file
95
internal/bg/org_key_sweeper.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package bg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/dyaksa/archer"
|
||||
"github.com/dyaksa/archer/job"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OrgKeySweeperArgs struct {
|
||||
IntervalS int `json:"interval_seconds,omitempty"`
|
||||
RetentionDays int `json:"retention_days,omitempty"`
|
||||
}
|
||||
|
||||
type OrgKeySweeperResult struct {
|
||||
Status string `json:"status"`
|
||||
MarkedRevoked int `json:"marked_revoked"`
|
||||
DeletedEphemeral int `json:"deleted_ephemeral"`
|
||||
ElapsedMs int `json:"elapsed_ms"`
|
||||
}
|
||||
|
||||
func OrgKeySweeperWorker(db *gorm.DB, jobs *Jobs) archer.WorkerFn {
|
||||
return func(ctx context.Context, j job.Job) (any, error) {
|
||||
args := OrgKeySweeperArgs{
|
||||
IntervalS: 3600,
|
||||
RetentionDays: 10,
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
_ = j.ParseArguments(&args)
|
||||
if args.IntervalS <= 0 {
|
||||
args.IntervalS = 3600
|
||||
}
|
||||
if args.RetentionDays <= 0 {
|
||||
args.RetentionDays = 10
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// 1) Mark expired keys as revoked
|
||||
res1 := db.Model(&models.APIKey{}).
|
||||
Where("expires_at IS NOT NULL AND expires_at <= ? AND revoked = false", now).
|
||||
Updates(map[string]any{
|
||||
"revoked": true,
|
||||
"updated_at": now,
|
||||
})
|
||||
|
||||
if res1.Error != nil {
|
||||
log.Error().Err(res1.Error).Msg("[org_key_sweeper] mark expired revoked failed")
|
||||
return nil, res1.Error
|
||||
}
|
||||
markedRevoked := int(res1.RowsAffected)
|
||||
|
||||
// 2) Hard-delete ephemeral keys that are revoked and older than retention
|
||||
cutoff := now.Add(-time.Duration(args.RetentionDays) * 24 * time.Hour)
|
||||
res2 := db.
|
||||
Where("is_ephemeral = ? AND revoked = ? AND updated_at <= ?", true, true, cutoff).
|
||||
Delete(&models.APIKey{})
|
||||
|
||||
if res2.Error != nil {
|
||||
log.Error().Err(res2.Error).Msg("[org_key_sweeper] delete revoked ephemeral keys failed")
|
||||
return nil, res2.Error
|
||||
}
|
||||
deletedEphemeral := int(res2.RowsAffected)
|
||||
|
||||
out := OrgKeySweeperResult{
|
||||
Status: "ok",
|
||||
MarkedRevoked: markedRevoked,
|
||||
DeletedEphemeral: deletedEphemeral,
|
||||
ElapsedMs: int(time.Since(start).Milliseconds()),
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Int("marked_revoked", markedRevoked).
|
||||
Int("deleted_ephemeral", deletedEphemeral).
|
||||
Msg("[org_key_sweeper] cleanup tick ok")
|
||||
|
||||
// Re-enqueue the sweeper
|
||||
next := time.Now().Add(time.Duration(args.IntervalS) * time.Second)
|
||||
_, _ = jobs.Enqueue(
|
||||
ctx,
|
||||
uuid.NewString(),
|
||||
"org_key_sweeper",
|
||||
args,
|
||||
archer.WithScheduleTime(next),
|
||||
archer.WithMaxRetries(1),
|
||||
)
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package bg
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
|
||||
"github.com/dyaksa/archer"
|
||||
"github.com/dyaksa/archer/job"
|
||||
"github.com/glueops/autoglue/internal/auth"
|
||||
"github.com/glueops/autoglue/internal/mapper"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
@@ -156,6 +158,29 @@ func ClusterPrepareWorker(db *gorm.DB, jobs *Jobs) archer.WorkerFn {
|
||||
dtoCluster.Kubeconfig = &kubeconfig
|
||||
}
|
||||
|
||||
orgKey, orgSecret, err := findOrCreateClusterAutomationKey(
|
||||
db,
|
||||
c.OrganizationID,
|
||||
c.ID,
|
||||
24*time.Hour,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fail++
|
||||
failedIDs = append(failedIDs, c.ID)
|
||||
failures = append(failures, ClusterPrepareFailure{
|
||||
ClusterID: c.ID,
|
||||
Step: "create_org_key",
|
||||
Reason: err.Error(),
|
||||
})
|
||||
clusterLog.Error().Err(err).Msg("[cluster_prepare] create org key for payload failed")
|
||||
_ = setClusterStatus(db, c.ID, clusterStatusFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
dtoCluster.OrgKey = &orgKey
|
||||
dtoCluster.OrgSecret = &orgSecret
|
||||
|
||||
payloadJSON, err := json.MarshalIndent(dtoCluster, "", " ")
|
||||
if err != nil {
|
||||
fail++
|
||||
@@ -551,3 +576,75 @@ func runMakeOnBastion(
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func randomB64URL(n int) (string, error) {
|
||||
b := make([]byte, n)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func findOrCreateClusterAutomationKey(
|
||||
db *gorm.DB,
|
||||
orgID uuid.UUID,
|
||||
clusterID uuid.UUID,
|
||||
ttl time.Duration,
|
||||
) (orgKey string, orgSecret string, err error) {
|
||||
now := time.Now()
|
||||
name := fmt.Sprintf("cluster-%s-bastion", clusterID.String())
|
||||
|
||||
// 1) Delete any existing ephemeral cluster-bastion key for this org+cluster
|
||||
if err := db.Where(
|
||||
"org_id = ? AND scope = ? AND purpose = ? AND cluster_id = ? AND is_ephemeral = ?",
|
||||
orgID, "org", "cluster_bastion", clusterID, true,
|
||||
).Delete(&models.APIKey{}).Error; err != nil {
|
||||
return "", "", fmt.Errorf("delete existing cluster key: %w", err)
|
||||
}
|
||||
|
||||
// 2) Mint a fresh keypair
|
||||
keySuffix, err := randomB64URL(16)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("entropy_error: %w", err)
|
||||
}
|
||||
sec, err := randomB64URL(32)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("entropy_error: %w", err)
|
||||
}
|
||||
|
||||
orgKey = "org_" + keySuffix
|
||||
orgSecret = sec
|
||||
|
||||
keyHash := auth.SHA256Hex(orgKey)
|
||||
secretHash, err := auth.HashSecretArgon2id(orgSecret)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("hash_error: %w", err)
|
||||
}
|
||||
|
||||
exp := now.Add(ttl)
|
||||
|
||||
prefix := orgKey
|
||||
if len(prefix) > 12 {
|
||||
prefix = prefix[:12]
|
||||
}
|
||||
|
||||
rec := models.APIKey{
|
||||
OrgID: &orgID,
|
||||
Scope: "org",
|
||||
Purpose: "cluster_bastion",
|
||||
ClusterID: &clusterID,
|
||||
IsEphemeral: true,
|
||||
Name: name,
|
||||
KeyHash: keyHash,
|
||||
SecretHash: &secretHash,
|
||||
ExpiresAt: &exp,
|
||||
Revoked: false,
|
||||
Prefix: &prefix,
|
||||
}
|
||||
|
||||
if err := db.Create(&rec).Error; err != nil {
|
||||
return "", "", fmt.Errorf("db_error: %w", err)
|
||||
}
|
||||
|
||||
return orgKey, orgSecret, nil
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ type ClusterResponse struct {
|
||||
DockerImage string `json:"docker_image"`
|
||||
DockerTag string `json:"docker_tag"`
|
||||
Kubeconfig *string `json:"kubeconfig,omitempty"`
|
||||
OrgKey *string `json:"org_key,omitempty"`
|
||||
OrgSecret *string `json:"org_secret,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -585,13 +585,22 @@ func CreateOrgKey(db *gorm.DB) http.HandlerFunc {
|
||||
exp = &e
|
||||
}
|
||||
|
||||
prefix := orgKey
|
||||
if len(prefix) > 12 {
|
||||
prefix = prefix[:12]
|
||||
}
|
||||
|
||||
rec := models.APIKey{
|
||||
OrgID: &oid,
|
||||
Scope: "org",
|
||||
Name: req.Name,
|
||||
KeyHash: keyHash,
|
||||
SecretHash: &secretHash,
|
||||
ExpiresAt: exp,
|
||||
OrgID: &oid,
|
||||
Scope: "org",
|
||||
Purpose: "user",
|
||||
IsEphemeral: false,
|
||||
Name: req.Name,
|
||||
KeyHash: keyHash,
|
||||
SecretHash: &secretHash,
|
||||
ExpiresAt: exp,
|
||||
Revoked: false,
|
||||
Prefix: &prefix,
|
||||
}
|
||||
if err := db.Create(&rec).Error; err != nil {
|
||||
utils.WriteError(w, 500, "db_error", err.Error())
|
||||
|
||||
@@ -7,17 +7,20 @@ import (
|
||||
)
|
||||
|
||||
type APIKey struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id" format:"uuid"`
|
||||
Name string `gorm:"not null;default:''" json:"name"`
|
||||
KeyHash string `gorm:"uniqueIndex;not null" json:"-"`
|
||||
Scope string `gorm:"not null;default:''" json:"scope"`
|
||||
UserID *uuid.UUID `json:"user_id,omitempty" format:"uuid"`
|
||||
OrgID *uuid.UUID `json:"org_id,omitempty" format:"uuid"`
|
||||
SecretHash *string `json:"-"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty" format:"date-time"`
|
||||
Revoked bool `gorm:"not null;default:false" json:"revoked"`
|
||||
Prefix *string `json:"prefix,omitempty"`
|
||||
LastUsedAt *time.Time `json:"last_used_at,omitempty" format:"date-time"`
|
||||
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"`
|
||||
ID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id" format:"uuid"`
|
||||
OrgID *uuid.UUID `json:"org_id,omitempty" format:"uuid"`
|
||||
Scope string `gorm:"not null;default:''" json:"scope"`
|
||||
Purpose string `json:"purpose"`
|
||||
ClusterID *uuid.UUID `json:"cluster_id,omitempty"`
|
||||
IsEphemeral bool `json:"is_ephemeral"`
|
||||
Name string `gorm:"not null;default:''" json:"name"`
|
||||
KeyHash string `gorm:"uniqueIndex;not null" json:"-"`
|
||||
SecretHash *string `json:"-"`
|
||||
UserID *uuid.UUID `json:"user_id,omitempty" format:"uuid"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty" format:"date-time"`
|
||||
Revoked bool `gorm:"not null;default:false" json:"revoked"`
|
||||
Prefix *string `json:"prefix,omitempty"`
|
||||
LastUsedAt *time.Time `json:"last_used_at,omitempty" format:"date-time"`
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -1,67 +1,56 @@
|
||||
;
|
||||
|
||||
// src/pages/ClustersPage.tsx
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { clustersApi } from "@/api/clusters";
|
||||
import { dnsApi } from "@/api/dns";
|
||||
import { loadBalancersApi } from "@/api/loadbalancers";
|
||||
import { nodePoolsApi } from "@/api/node_pools";
|
||||
import { serversApi } from "@/api/servers";
|
||||
import type { DtoClusterResponse, DtoDomainResponse, DtoLoadBalancerResponse, DtoNodePoolResponse, DtoRecordSetResponse, DtoServerResponse } from "@/sdk";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { AlertCircle, CheckCircle2, CircleSlash2, FileCode2, Globe2, Loader2, MapPin, Pencil, Plus, Search, Server, Wrench } from "lucide-react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
|
||||
|
||||
|
||||
import { truncateMiddle } from "@/lib/utils";
|
||||
import { Badge } from "@/components/ui/badge.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog.tsx";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { Label } from "@/components/ui/label.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table.tsx";
|
||||
import { Textarea } from "@/components/ui/textarea.tsx";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { clustersApi } from "@/api/clusters"
|
||||
import { dnsApi } from "@/api/dns"
|
||||
import { loadBalancersApi } from "@/api/loadbalancers"
|
||||
import { nodePoolsApi } from "@/api/node_pools"
|
||||
import { serversApi } from "@/api/servers"
|
||||
import type {
|
||||
DtoClusterResponse,
|
||||
DtoDomainResponse,
|
||||
DtoLoadBalancerResponse,
|
||||
DtoNodePoolResponse,
|
||||
DtoRecordSetResponse,
|
||||
DtoServerResponse,
|
||||
} from "@/sdk"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import {
|
||||
AlertCircle,
|
||||
CheckCircle2,
|
||||
CircleSlash2,
|
||||
FileCode2,
|
||||
Globe2,
|
||||
Loader2,
|
||||
MapPin,
|
||||
Pencil,
|
||||
Plus,
|
||||
Search,
|
||||
Server,
|
||||
Wrench,
|
||||
} from "lucide-react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
import { z } from "zod"
|
||||
|
||||
import { truncateMiddle } from "@/lib/utils"
|
||||
import { Badge } from "@/components/ui/badge.tsx"
|
||||
import { Button } from "@/components/ui/button.tsx"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog.tsx"
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form.tsx"
|
||||
import { Input } from "@/components/ui/input.tsx"
|
||||
import { Label } from "@/components/ui/label.tsx"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select.tsx"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table.tsx"
|
||||
import { Textarea } from "@/components/ui/textarea.tsx"
|
||||
|
||||
// --- Schemas ---
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
import { z } from "zod"
|
||||
|
||||
import { Badge } from "@/components/ui/badge.tsx"
|
||||
import { Button } from "@/components/ui/button.tsx"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"
|
||||
import {
|
||||
@@ -35,10 +36,12 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table.tsx"
|
||||
|
||||
// 1) No coerce; we’ll do the conversion in onChange
|
||||
const createSchema = z.object({
|
||||
name: z.string(),
|
||||
expires_in_hours: z.number().min(1).max(43800),
|
||||
expires_in_hours: z.number().int().min(1).max(43800),
|
||||
})
|
||||
|
||||
type CreateValues = z.infer<typeof createSchema>
|
||||
|
||||
export const OrgApiKeys = () => {
|
||||
@@ -52,6 +55,7 @@ export const OrgApiKeys = () => {
|
||||
queryFn: () => withRefresh(() => api.listOrgKeys({ id: orgId! })),
|
||||
})
|
||||
|
||||
// 2) Form holds numbers directly
|
||||
const form = useForm<CreateValues>({
|
||||
resolver: zodResolver(createSchema),
|
||||
defaultValues: {
|
||||
@@ -71,7 +75,7 @@ export const OrgApiKeys = () => {
|
||||
void qc.invalidateQueries({ queryKey: ["org:keys", orgId] })
|
||||
setShowSecret({ key: resp.org_key, secret: resp.org_secret })
|
||||
toast.success("Key created")
|
||||
form.reset({ name: "", expires_in_hours: undefined })
|
||||
form.reset({ name: "", expires_in_hours: 720 })
|
||||
},
|
||||
onError: (e: any) => toast.error(e?.message ?? "Failed to create key"),
|
||||
})
|
||||
@@ -124,7 +128,17 @@ export const OrgApiKeys = () => {
|
||||
<FormItem>
|
||||
<FormLabel>Expires In (hours)</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="e.g. 720" {...field} />
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="e.g. 720"
|
||||
{...field}
|
||||
// 3) Convert string → number (or undefined if empty)
|
||||
value={field.value ?? ""}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
field.onChange(v === "" ? undefined : Number(v))
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -148,6 +162,7 @@ export const OrgApiKeys = () => {
|
||||
<TableHead>Scope</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead>Expires</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead className="w-28" />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -160,6 +175,33 @@ export const OrgApiKeys = () => {
|
||||
<TableCell>
|
||||
{k.expires_at ? new Date(k.expires_at).toLocaleString() : "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{(() => {
|
||||
const isExpired = k.expires_at ? new Date(k.expires_at) <= new Date() : false
|
||||
|
||||
if (k.revoked) {
|
||||
return (
|
||||
<Badge variant="destructive" className="font-mono">
|
||||
Revoked
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
|
||||
if (isExpired) {
|
||||
return (
|
||||
<Badge variant="outline" className="font-mono">
|
||||
Expired
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge variant="secondary" className="font-mono">
|
||||
Active
|
||||
</Badge>
|
||||
)
|
||||
})()}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button variant="destructive" size="sm" onClick={() => deleteMut.mutate(k.id!)}>
|
||||
Delete
|
||||
|
||||
194
ui/yarn.lock
194
ui/yarn.lock
@@ -310,7 +310,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@ecies/ciphers/-/ciphers-0.2.5.tgz#754ff2f821645f0465d18a1a68198eb15d16c2a0"
|
||||
integrity sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A==
|
||||
|
||||
"@emnapi/core@^1.6.0", "@emnapi/core@^1.7.1":
|
||||
"@emnapi/core@^1.7.1":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.7.1.tgz#3a79a02dbc84f45884a1806ebb98e5746bdfaac4"
|
||||
integrity sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==
|
||||
@@ -318,7 +318,7 @@
|
||||
"@emnapi/wasi-threads" "1.1.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/runtime@^1.6.0", "@emnapi/runtime@^1.7.1":
|
||||
"@emnapi/runtime@^1.7.1":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.1.tgz#a73784e23f5d57287369c808197288b52276b791"
|
||||
integrity sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==
|
||||
@@ -725,7 +725,7 @@
|
||||
outvariant "^1.4.3"
|
||||
strict-event-emitter "^0.5.1"
|
||||
|
||||
"@napi-rs/wasm-runtime@^1.0.7":
|
||||
"@napi-rs/wasm-runtime@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz#c0180393d7862cff0d412e3e1a7c3bd5ea6d9b2f"
|
||||
integrity sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==
|
||||
@@ -2003,10 +2003,10 @@
|
||||
dependencies:
|
||||
apg-lite "^1.0.4"
|
||||
|
||||
"@tailwindcss/node@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.1.17.tgz#ec40a37293246f4eeab2dac00e4425af9272f600"
|
||||
integrity sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==
|
||||
"@tailwindcss/node@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.1.18.tgz#9863be0d26178638794a38d6c7c14666fb992e8a"
|
||||
integrity sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==
|
||||
dependencies:
|
||||
"@jridgewell/remapping" "^2.3.4"
|
||||
enhanced-resolve "^5.18.3"
|
||||
@@ -2014,101 +2014,101 @@
|
||||
lightningcss "1.30.2"
|
||||
magic-string "^0.30.21"
|
||||
source-map-js "^1.2.1"
|
||||
tailwindcss "4.1.17"
|
||||
tailwindcss "4.1.18"
|
||||
|
||||
"@tailwindcss/oxide-android-arm64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz#17f0dc901f88a979c5bff618181bce596dff596d"
|
||||
integrity sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==
|
||||
"@tailwindcss/oxide-android-arm64@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz#79717f87e90135e5d3d23a3d3aecde4ca5595dd5"
|
||||
integrity sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz#63e12e62b83f6949dbb10b5a7f6e441606835efc"
|
||||
integrity sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==
|
||||
"@tailwindcss/oxide-darwin-arm64@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz#7fa47608d62d60e9eb020682249d20159667fbb0"
|
||||
integrity sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz#6dad270d2777508f55e2b73eca0eaef625bc45a7"
|
||||
integrity sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==
|
||||
"@tailwindcss/oxide-darwin-x64@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz#c05991c85aa2af47bf9d1f8172fe9e4636591e79"
|
||||
integrity sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz#e7628b4602ac7d73c11a9922ecb83c24337eff55"
|
||||
integrity sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==
|
||||
"@tailwindcss/oxide-freebsd-x64@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz#3d48e8d79fd08ece0e02af8e72d5059646be34d0"
|
||||
integrity sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz#4d96a6fe4c7ed20e7a013101ee46f46caca2233e"
|
||||
integrity sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz#982ecd1a65180807ccfde67dc17c6897f2e50aa8"
|
||||
integrity sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz#adc3c01cd73610870bfc21db5713571e08fb2210"
|
||||
integrity sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz#df49357bc9737b2e9810ea950c1c0647ba6573c3"
|
||||
integrity sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz#39ceda30407af56a1ee125b2c5ce856c6d29250f"
|
||||
integrity sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==
|
||||
"@tailwindcss/oxide-linux-arm64-musl@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz#b266c12822bf87883cf152615f8fffb8519d689c"
|
||||
integrity sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz#a3d4bd876c04d09856af0c394f5095fbc8a2b14c"
|
||||
integrity sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==
|
||||
"@tailwindcss/oxide-linux-x64-gnu@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz#5c737f13dd9529b25b314e6000ff54e05b3811da"
|
||||
integrity sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz#bdc20aa4fb2d28cc928f2cfffff7a9cd03a51d5b"
|
||||
integrity sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==
|
||||
"@tailwindcss/oxide-linux-x64-musl@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz#3380e17f7be391f1ef924be9f0afe1f304fe3478"
|
||||
integrity sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz#7c0804748935928751838f86ff4f879c38f8a6d7"
|
||||
integrity sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==
|
||||
"@tailwindcss/oxide-wasm32-wasi@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz#9464df0e28a499aab1c55e97682be37b3a656c88"
|
||||
integrity sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==
|
||||
dependencies:
|
||||
"@emnapi/core" "^1.6.0"
|
||||
"@emnapi/runtime" "^1.6.0"
|
||||
"@emnapi/core" "^1.7.1"
|
||||
"@emnapi/runtime" "^1.7.1"
|
||||
"@emnapi/wasi-threads" "^1.1.0"
|
||||
"@napi-rs/wasm-runtime" "^1.0.7"
|
||||
"@napi-rs/wasm-runtime" "^1.1.0"
|
||||
"@tybys/wasm-util" "^0.10.1"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz#7222fc2ceee9d45ebe5aebf38707ee9833a20475"
|
||||
integrity sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==
|
||||
"@tailwindcss/oxide-win32-arm64-msvc@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz#bbcdd59c628811f6a0a4d5b09616967d8fb0c4d4"
|
||||
integrity sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz#ac79087f451dfcd5c3099589027a5732b045a3bf"
|
||||
integrity sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==
|
||||
"@tailwindcss/oxide-win32-x64-msvc@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz#9c628d04623aa4c3536c508289f58d58ba4b3fb1"
|
||||
integrity sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==
|
||||
|
||||
"@tailwindcss/oxide@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.1.17.tgz#6c063b40a022f4fbdac557c0586cfb9ae08a3dfe"
|
||||
integrity sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==
|
||||
"@tailwindcss/oxide@4.1.18":
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.1.18.tgz#c8335cd0a83e9880caecd60abf7904f43ebab582"
|
||||
integrity sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==
|
||||
optionalDependencies:
|
||||
"@tailwindcss/oxide-android-arm64" "4.1.17"
|
||||
"@tailwindcss/oxide-darwin-arm64" "4.1.17"
|
||||
"@tailwindcss/oxide-darwin-x64" "4.1.17"
|
||||
"@tailwindcss/oxide-freebsd-x64" "4.1.17"
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.17"
|
||||
"@tailwindcss/oxide-linux-arm64-gnu" "4.1.17"
|
||||
"@tailwindcss/oxide-linux-arm64-musl" "4.1.17"
|
||||
"@tailwindcss/oxide-linux-x64-gnu" "4.1.17"
|
||||
"@tailwindcss/oxide-linux-x64-musl" "4.1.17"
|
||||
"@tailwindcss/oxide-wasm32-wasi" "4.1.17"
|
||||
"@tailwindcss/oxide-win32-arm64-msvc" "4.1.17"
|
||||
"@tailwindcss/oxide-win32-x64-msvc" "4.1.17"
|
||||
"@tailwindcss/oxide-android-arm64" "4.1.18"
|
||||
"@tailwindcss/oxide-darwin-arm64" "4.1.18"
|
||||
"@tailwindcss/oxide-darwin-x64" "4.1.18"
|
||||
"@tailwindcss/oxide-freebsd-x64" "4.1.18"
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.18"
|
||||
"@tailwindcss/oxide-linux-arm64-gnu" "4.1.18"
|
||||
"@tailwindcss/oxide-linux-arm64-musl" "4.1.18"
|
||||
"@tailwindcss/oxide-linux-x64-gnu" "4.1.18"
|
||||
"@tailwindcss/oxide-linux-x64-musl" "4.1.18"
|
||||
"@tailwindcss/oxide-wasm32-wasi" "4.1.18"
|
||||
"@tailwindcss/oxide-win32-arm64-msvc" "4.1.18"
|
||||
"@tailwindcss/oxide-win32-x64-msvc" "4.1.18"
|
||||
|
||||
"@tailwindcss/vite@^4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/vite/-/vite-4.1.17.tgz#c316b3817b21e175c37261249550790b1b909f93"
|
||||
integrity sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/vite/-/vite-4.1.18.tgz#614b9d5483559518c72d31bca05d686f8df28e9a"
|
||||
integrity sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==
|
||||
dependencies:
|
||||
"@tailwindcss/node" "4.1.17"
|
||||
"@tailwindcss/oxide" "4.1.17"
|
||||
tailwindcss "4.1.17"
|
||||
"@tailwindcss/node" "4.1.18"
|
||||
"@tailwindcss/oxide" "4.1.18"
|
||||
tailwindcss "4.1.18"
|
||||
|
||||
"@tanstack/query-core@5.90.12":
|
||||
version "5.90.12"
|
||||
@@ -2987,9 +2987,9 @@ encodeurl@^2.0.0:
|
||||
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
|
||||
|
||||
enhanced-resolve@^5.18.3:
|
||||
version "5.18.3"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz#9b5f4c5c076b8787c78fe540392ce76a88855b44"
|
||||
integrity sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==
|
||||
version "5.18.4"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz#c22d33055f3952035ce6a144ce092447c525f828"
|
||||
integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
@@ -3644,9 +3644,9 @@ human-signals@^8.0.1:
|
||||
integrity sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==
|
||||
|
||||
iconv-lite@^0.7.0, iconv-lite@~0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e"
|
||||
integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.1.tgz#d4af1d2092f2bb05aab6296e5e7cd286d2f15432"
|
||||
integrity sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
@@ -4609,9 +4609,9 @@ react-day-picker@^9.12.0:
|
||||
date-fns-jalali "^4.1.0-0"
|
||||
|
||||
react-dom@^19.2.1:
|
||||
version "19.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.1.tgz#ce3527560bda4f997e47d10dab754825b3061f59"
|
||||
integrity sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==
|
||||
version "19.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17"
|
||||
integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==
|
||||
dependencies:
|
||||
scheduler "^0.27.0"
|
||||
|
||||
@@ -4707,9 +4707,9 @@ react-transition-group@^4.4.5:
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react@^19.2.1:
|
||||
version "19.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-19.2.1.tgz#8600fa205e58e2e807f6ef431c9f6492591a2700"
|
||||
integrity sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==
|
||||
version "19.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8"
|
||||
integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==
|
||||
|
||||
recast@^0.23.11:
|
||||
version "0.23.11"
|
||||
@@ -5132,10 +5132,10 @@ tailwind-merge@^3.4.0:
|
||||
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-3.4.0.tgz#5a264e131a096879965f1175d11f8c36e6b64eca"
|
||||
integrity sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==
|
||||
|
||||
tailwindcss@4.1.17, tailwindcss@^4.1.17:
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.17.tgz#e6dcb7a9c60cef7522169b5f207ffec2fd652286"
|
||||
integrity sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==
|
||||
tailwindcss@4.1.18, tailwindcss@^4.1.17:
|
||||
version "4.1.18"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.18.tgz#f488ba47853abdb5354daf9679d3e7791fc4f4e3"
|
||||
integrity sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==
|
||||
|
||||
tapable@^2.2.0:
|
||||
version "2.3.0"
|
||||
|
||||
Reference in New Issue
Block a user