Compare commits

...
1 Commits
Author SHA1 Message Date
Denozordec 53b3c49612 refactor(settings): simplify settings query options and remove tenant dependency
CI / changes (push) Successful in 10s
CI / commitlint (push) Has been skipped
CI / openapi (push) Successful in 28s
CI / web (push) Successful in 1m0s
CI / go (push) Successful in 1m11s
CI / bird2 (push) Successful in 16s
CI / release (push) Successful in 4m5s
Updated the settings query options to eliminate the tenant ID parameter, streamlining the settings retrieval process. Adjusted the TenantSettingsComponent to reflect this change, ensuring it now queries settings without relying on tenant-specific data. This refactor enhances code clarity and reduces complexity in the settings management flow.
2026-07-06 23:28:14 +07:00
5 changed files with 45 additions and 15 deletions
+2 -4
View File
@@ -42,14 +42,12 @@ export const BOOLEAN_SETTING_KEYS = new Set<KnownSettingKey>(['runtime_logs_auto
export const settingsKeys = {
all: ['settings'] as const,
tenant: (tenantId: string) => [...settingsKeys.all, tenantId] as const,
}
export function settingsQueryOptions(tenantId?: string | null) {
export function settingsQueryOptions() {
return queryOptions<AppSettings>({
queryKey: settingsKeys.tenant(tenantId ?? ''),
queryKey: settingsKeys.all,
queryFn: () => apiJSON<AppSettings>('/v1/settings'),
enabled: Boolean(tenantId),
staleTime: 30_000,
})
}
@@ -39,7 +39,6 @@ import {
settingsQueryOptions,
type BirdSettingKey,
} from '@/queries/settings'
import { authSessionQueryOptions } from '@/queries/auth'
import { apiMutate } from '@/lib/api-client'
export const Route = createFileRoute('/_auth/tenant-settings')({
@@ -72,9 +71,7 @@ const BIRD_LABELS: Record<BirdSettingKey, string> = {
function TenantSettingsComponent() {
const search = useSearch({ from: '/_auth/tenant-settings' })
const sessionQ = useQuery(authSessionQueryOptions())
const tenantId = sessionQ.data?.tenant_id ?? null
const settingsQ = useQuery(settingsQueryOptions(tenantId))
const settingsQ = useQuery(settingsQueryOptions())
const qc = useQueryClient()
const partitioned = settingsQ.data ? partitionSettings(settingsQ.data) : null
+1 -1
View File
@@ -50,7 +50,7 @@ opkey|01ARZ3NDEKTSV4RRFFQ69G5FAV|operator,nodekey|01ARZ3NDEKTSV4RRFFQ69G5FAV|nod
Если в store доступен демо-tenant (`DemoIDs`, обычно `EVOBGP_SEED_DEMO` не равен `0`), заголовок **`Authorization: Bearer dev`** даёт роль **`operator`** для этого tenant. **Не зависит** от `EVOBGP_DEV_INSECURE`.
Если токен `dev` также задан в `EVOBGP_API_KEYS` или таблице `api_key`, **приоритет у явной записи** (production tenant), а не у demo-shortcut.
Без demo-tenant токен `dev` может быть задан в `EVOBGP_API_KEYS` (break-glass).
**Запрещено** в продакшене: не оставляйте demo-seed с известным токеном `dev` на боевых данных. Переменная `EVOBGP_DEV_INSECURE` в текущей версии **не влияет** на аутентификацию (оставлена в compose для совместимости; не включайте в production — см. SEC-02 в инженерных правилах).
+5 -4
View File
@@ -81,15 +81,16 @@ func authFromKeyRecord(raw string, rec apiKeyRecord) Auth {
}
// resolveAuth maps a bearer token to tenant identity.
// For the literal token "dev", env/DB keys take precedence over the demo shortcut (devAuth).
// For the literal token "dev", the demo shortcut (devAuth) takes precedence when demo-seed
// is available; env/DB mapping is used only when demo tenant is absent.
func (s *Server) resolveAuth(raw string) (Auth, bool) {
if raw == "dev" {
if rec, ok := s.keyResolver.Lookup(raw); ok {
return authFromKeyRecord(raw, rec), true
}
if a, ok := s.devAuth(); ok {
return a, true
}
if rec, ok := s.keyResolver.Lookup(raw); ok {
return authFromKeyRecord(raw, rec), true
}
return Auth{}, false
}
rec, ok := s.keyResolver.Lookup(raw)
+36 -2
View File
@@ -31,7 +31,7 @@ func TestBearerDevGetSettings(t *testing.T) {
}
}
func TestBearerDevPrefersEnvAPIKeyOverDemoTenant(t *testing.T) {
func TestBearerDevPrefersDemoTenantOverEnvKey(t *testing.T) {
srv, err := New(Options{SeedDemo: true, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
@@ -61,7 +61,41 @@ func TestBearerDevPrefersEnvAPIKeyOverDemoTenant(t *testing.T) {
t.Fatal(err)
}
got, _ := body["tenant_id"].(string)
if got != demoTenant {
t.Fatalf("tenant_id=%q want demo tenant %q (env=%q)", got, demoTenant, otherTenant)
}
}
func TestBearerDevFallsBackToEnvKeyWithoutDemo(t *testing.T) {
srv, err := New(Options{SeedDemo: false, BundleSeedHex: testBundleSeed})
if err != nil {
t.Fatal(err)
}
defer srv.Close()
otherTenant := "00000000-0000-4000-8000-000000000001"
mustSetTestAPIKeys(t, srv, "dev|"+otherTenant+"|operator")
ts := httptest.NewServer(srv.Handler())
defer ts.Close()
req, _ := http.NewRequest(http.MethodGet, ts.URL+"/v1/auth/session", nil)
req.Header.Set("Authorization", "Bearer dev")
resp, err := ts.Client().Do(req)
if err != nil {
t.Fatal(err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(resp.Body)
t.Fatalf("session status=%d body=%s", resp.StatusCode, b)
}
var body map[string]any
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
t.Fatal(err)
}
got, _ := body["tenant_id"].(string)
if got != otherTenant {
t.Fatalf("tenant_id=%q want env key tenant %q (demo=%q)", got, otherTenant, demoTenant)
t.Fatalf("tenant_id=%q want env key tenant %q", got, otherTenant)
}
}