@k2b/cloud 0.47.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/_internal/app-access-manager-sql.ts +10 -0
- package/src/_internal/app-access-resources.ts +2 -1
- package/src/_internal/app-capabilities.ts +73 -0
- package/src/_internal/app-event-catalog.ts +1 -1
- package/src/_internal/app-queries.ts +1 -1
- package/src/_internal/application-package.ts +2 -1
- package/src/_internal/bounded-json.ts +7 -1
- package/src/_internal/capabilities.ts +25 -2
- package/src/_internal/define-app.ts +33 -2
- package/src/_internal/heartbeat.ts +2 -0
- package/src/_internal/nats-connection.ts +10 -9
- package/src/_internal/process-sync.ts +6 -6
- package/src/_internal/public-surface.ts +76 -0
- package/src/_internal/purpose-name.ts +7 -0
- package/src/_internal/registry-validation.ts +1 -0
- package/src/_internal/registry.ts +49 -3
- package/src/_internal/workload-credential.ts +2 -1
- package/src/ai/app-skill-store.ts +2 -1
- package/src/ai/app-skills.ts +2 -1
- package/src/ai/approvals.ts +2 -1
- package/src/ai/chat-tasks.ts +2 -1
- package/src/ai/credentials.ts +2 -1
- package/src/ai/dictation-runtime.ts +1 -1
- package/src/ai/dictations.ts +1 -1
- package/src/ai/files-store.ts +2 -1
- package/src/ai/inference-calls.ts +2 -1
- package/src/ai/map-store.ts +1 -1
- package/src/ai/map-tool.ts +1 -1
- package/src/ai/memories.ts +1 -1
- package/src/ai/memory-learning-runs.ts +1 -1
- package/src/ai/memory-learning.ts +1 -1
- package/src/ai/memory-workflow-evidence.ts +1 -1
- package/src/ai/message-queue.ts +2 -1
- package/src/ai/migrate.ts +1 -1
- package/src/ai/model-access-migrate.ts +1 -1
- package/src/ai/model-access.ts +1 -1
- package/src/ai/model-catalog.ts +1 -1
- package/src/ai/model-delete.ts +1 -1
- package/src/ai/model-usage.ts +2 -1
- package/src/ai/open-questions.ts +1 -1
- package/src/ai/openai-api-slots.ts +2 -1
- package/src/ai/prefs.ts +2 -1
- package/src/ai/projects.ts +2 -1
- package/src/ai/provider-connections.ts +1 -1
- package/src/ai/quota-limits.ts +2 -1
- package/src/ai/quota-provider.ts +2 -1
- package/src/ai/quota-report.ts +2 -1
- package/src/ai/quotas-migrate.ts +2 -1
- package/src/ai/quotas.ts +2 -1
- package/src/ai/request-headers.ts +2 -1
- package/src/ai/review-content.ts +2 -1
- package/src/ai/skill-search.ts +2 -1
- package/src/ai/skills.ts +2 -1
- package/src/ai/store.ts +2 -1
- package/src/ai/structured-runs.ts +2 -1
- package/src/ai/tool-audit.ts +2 -1
- package/src/ai/turn-timing.ts +1 -1
- package/src/ai/usage.ts +2 -1
- package/src/ai/web-provenance.ts +2 -1
- package/src/api/admin-ai-quotas.ts +1 -1
- package/src/api/admin-ai.ts +1 -1
- package/src/api/admin-app-domains.ts +62 -0
- package/src/api/admin-app-keys.ts +47 -3
- package/src/api/admin-core-settings.ts +1 -1
- package/src/api/admin-outgoing-mail.ts +1 -1
- package/src/api/app-bootstrap.ts +153 -0
- package/src/api/app-events.ts +1 -1
- package/src/api/auth.ts +1 -1
- package/src/api/authenticate-app-request.ts +129 -0
- package/src/api/capabilities.ts +87 -6
- package/src/api/index.ts +7 -0
- package/src/api/me.ts +1 -1
- package/src/api/search.bench.ts +1 -1
- package/src/app-pwa/service.ts +2 -1
- package/src/app-pwa/transaction.ts +4 -2
- package/src/capabilities/claims.ts +2 -1
- package/src/capabilities/executions.ts +8 -4
- package/src/capabilities/geo-client.ts +1 -1
- package/src/capabilities/migrate.ts +3 -1
- package/src/capabilities/resource-readers.ts +2 -2
- package/src/capabilities/response.ts +2 -1
- package/src/capabilities/server-invoke.ts +73 -20
- package/src/capabilities/server.ts +2 -1
- package/src/cli/admin/app-domains.ts +140 -0
- package/src/cli/admin/app-keys.ts +25 -3
- package/src/cli/admin/index.ts +107 -88
- package/src/config/env.ts +10 -4
- package/src/contracts/app-domains.ts +91 -0
- package/src/contracts/app-status.ts +104 -0
- package/src/contracts/capabilities.ts +37 -2
- package/src/contracts/index.ts +1 -0
- package/src/contracts/outgoing-mail.ts +1 -1
- package/src/contracts/registry.ts +2 -0
- package/src/events/app-event-delivery.ts +1 -1
- package/src/events/live-delivery.ts +2 -1
- package/src/index.ts +1 -0
- package/src/runtime/bootstrap-client.ts +195 -0
- package/src/runtime/bootstrap-protocol.ts +67 -0
- package/src/runtime/clients.ts +6 -2
- package/src/runtime/connections.ts +9 -0
- package/src/server/app-key.ts +14 -0
- package/src/server/index.ts +2 -0
- package/src/server/ratelimit.ts +1 -1
- package/src/server/services/access.ts +2 -1
- package/src/services/account-category-policy.ts +1 -1
- package/src/services/account-lifecycle/audit.ts +1 -1
- package/src/services/account-lifecycle/index.ts +1 -1
- package/src/services/account-lifecycle/ipa-backfill.ts +2 -1
- package/src/services/accounts/app.ts +1 -1
- package/src/services/accounts/avatar.ts +1 -1
- package/src/services/accounts/base-group.ts +1 -1
- package/src/services/accounts/directory-shares.ts +1 -1
- package/src/services/accounts/duplicate-emails.ts +1 -1
- package/src/services/accounts/entities.ts +2 -1
- package/src/services/accounts/group-sql.ts +2 -1
- package/src/services/accounts/groups.ts +1 -1
- package/src/services/accounts/identities.ts +13 -11
- package/src/services/accounts/ipa-data.ts +1 -1
- package/src/services/accounts/local-groups.ts +2 -1
- package/src/services/accounts/machine-group-management.ts +2 -1
- package/src/services/accounts/posix.ts +13 -12
- package/src/services/accounts/request-policy.ts +2 -1
- package/src/services/accounts/switching.ts +1 -1
- package/src/services/accounts/users.ts +2 -1
- package/src/services/announcements/index.ts +1 -1
- package/src/services/app-access-management.ts +3 -9
- package/src/services/app-access.ts +2 -1
- package/src/services/app-admin-facts.ts +160 -0
- package/src/services/app-approval.ts +34 -30
- package/src/services/app-bootstrap.ts +217 -0
- package/src/services/app-details.ts +3 -2
- package/src/services/app-domain-check.ts +152 -0
- package/src/services/app-domains.ts +172 -0
- package/src/services/app-events/accept.ts +5 -5
- package/src/services/app-events/admin.ts +15 -15
- package/src/services/app-events/declarations.ts +6 -3
- package/src/services/app-events/deliver.ts +6 -6
- package/src/services/app-events.ts +3 -2
- package/src/services/app-keys.ts +45 -11
- package/src/services/approvals/content.ts +1 -1
- package/src/services/approvals/index.ts +2 -1
- package/src/services/approvals/list.ts +1 -1
- package/src/services/approvals/migrate.ts +1 -1
- package/src/services/audit/index.ts +2 -1
- package/src/services/auth-flows/ipa.ts +1 -1
- package/src/services/auth-flows/magic-link.ts +1 -1
- package/src/services/auth-flows/password-reset.ts +1 -1
- package/src/services/auth-flows/proxy-return.ts +1 -1
- package/src/services/downstream-budget.ts +2 -2
- package/src/services/geo/admin.ts +13 -12
- package/src/services/geo/identity.ts +46 -0
- package/src/services/geo/ledger.ts +118 -106
- package/src/services/geo/migrate.ts +2 -1
- package/src/services/geo/route-chain.ts +3 -3
- package/src/services/geo/service.ts +4 -24
- package/src/services/geoip/index.ts +5 -5
- package/src/services/geoip/store.ts +24 -21
- package/src/services/help/index.ts +2 -1
- package/src/services/help/store.ts +2 -1
- package/src/services/identity/app-request.ts +44 -0
- package/src/services/identity/constants.ts +1 -0
- package/src/services/identity/index.ts +1 -0
- package/src/services/identity/invocation-actor.ts +2 -1
- package/src/services/identity/invocation-token.ts +112 -0
- package/src/services/identity/key-ring.ts +2 -2
- package/src/services/identity/resource-readers.ts +1 -1
- package/src/services/identity/routes.ts +11 -3
- package/src/services/identity/runtime-config.ts +14 -3
- package/src/services/identity/workload-auth.ts +2 -0
- package/src/services/identity/workload-runtime.ts +3 -3
- package/src/services/index.ts +4 -0
- package/src/services/ipa/groups.ts +1 -1
- package/src/services/ipa/profile.ts +1 -1
- package/src/services/ipa/search.ts +1 -1
- package/src/services/ipa/sync.ts +1 -1
- package/src/services/ipa/users.ts +1 -1
- package/src/services/known-browsers.ts +2 -1
- package/src/services/legal-consent.ts +1 -1
- package/src/services/links/index.ts +16 -0
- package/src/services/links/runtime.ts +44 -0
- package/src/services/links/store.ts +105 -0
- package/src/services/logging/index.ts +13 -8
- package/src/services/logging/redaction.ts +6 -1
- package/src/services/logging/trace.ts +9 -9
- package/src/services/logging/web-vitals.ts +1 -1
- package/src/services/mandates/index.ts +2 -1
- package/src/services/notifications/batches.ts +1 -1
- package/src/services/notifications/browser.ts +1 -1
- package/src/services/notifications/catalog.ts +1 -1
- package/src/services/notifications/dispatcher.ts +1 -1
- package/src/services/notifications/index.ts +1 -1
- package/src/services/notifications/observability.ts +2 -1
- package/src/services/notifications/platform.ts +1 -1
- package/src/services/notifications/quiet.ts +1 -1
- package/src/services/notifications/user.ts +1 -1
- package/src/services/oauth-tokens.ts +1 -1
- package/src/services/outbox.ts +15 -14
- package/src/services/outgoing-mail/admin.ts +1 -1
- package/src/services/outgoing-mail/dispatcher.ts +1 -1
- package/src/services/outgoing-mail/drain.ts +2 -1
- package/src/services/outgoing-mail/enqueue.ts +2 -1
- package/src/services/outgoing-mail/messages.ts +2 -1
- package/src/services/outgoing-mail/retention.ts +1 -1
- package/src/services/outgoing-mail/send.ts +1 -1
- package/src/services/outgoing-mail/store.ts +2 -1
- package/src/services/panel-context-consent.ts +32 -28
- package/src/services/providers/local/auth.ts +1 -1
- package/src/services/providers/local/users.ts +1 -1
- package/src/services/pwa-devices.ts +2 -1
- package/src/services/rail-preferences.ts +16 -12
- package/src/services/rail-shortcuts.ts +12 -10
- package/src/services/request-cache-redis.ts +3 -3
- package/src/services/service-account-credentials.ts +2 -1
- package/src/services/service-accounts.ts +2 -1
- package/src/services/session/index.ts +2 -1
- package/src/services/session/user.ts +2 -1
- package/src/services/settings/core-settings.ts +20 -0
- package/src/services/settings/crypto.ts +2 -2
- package/src/services/settings/index.ts +1 -1
- package/src/services/settings/store.ts +6 -1
- package/src/services/settings/versions.ts +2 -1
- package/src/services/weather/forecast.ts +2 -1
- package/src/services/weather/locations.ts +1 -1
- package/src/services/weather/migrate.ts +2 -1
- package/src/services/weather/short-id.ts +2 -1
- package/src/services/webauthn.ts +1 -1
- package/src/ssr/admin-navigation.ts +0 -1
- package/src/ssr/compact-shell.browser-harness.ts +2 -0
- package/src/ssr/platform-messages.ts +0 -2
- package/src/start.ts +35 -14
- package/src/workflows/ai/store.ts +2 -1
- package/src/workflows/store/budget.ts +2 -1
- package/src/workflows/store/definitions.ts +2 -1
- package/src/workflows/store/events.ts +2 -1
- package/src/workflows/store/observability.ts +2 -1
- package/src/workflows/store/retention.ts +2 -1
- package/src/workflows/store/runs.ts +2 -1
- package/src/workflows/store/transaction.ts +2 -1
- package/src/workflows/store/worker.ts +2 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { err, fail, ok, type Result } from "@k2b/stdlib";
|
|
3
3
|
import type { Worker } from "@k2b/sync";
|
|
4
|
-
import { sql } from "bun";
|
|
5
4
|
import { lazySync } from "../../_internal/process-sync";
|
|
6
5
|
import { type MailMessage, MailMessageSchema, type MailStatus, MailStatusSchema } from "../../contracts/outgoing-mail";
|
|
6
|
+
import { sql } from "../../runtime/clients";
|
|
7
7
|
import { markdown } from "../../shared/markdown";
|
|
8
8
|
import { type AuditActor, audit } from "../audit";
|
|
9
9
|
import { logger, trace } from "../logging";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { sql } from "bun";
|
|
3
2
|
import webpush from "web-push";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { notificationGroupTag } from "../../contracts/notification-group";
|
|
6
5
|
import { isSafeNotificationTargetHref } from "../../contracts/notification-types";
|
|
7
6
|
import { type BrowserPushSubscription, BrowserPushSubscriptionSchema } from "../../contracts/user-notifications";
|
|
7
|
+
import { sql } from "../../runtime/clients";
|
|
8
8
|
import { decryptSecret, encryptSecret } from "../secrets";
|
|
9
9
|
import { coreSettings } from "../settings/api";
|
|
10
10
|
import { SettingUnreadableError } from "../settings/store";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import type { ZodType } from "zod";
|
|
3
2
|
import type { BoundNotificationDefinition, NotificationRecipientKind, NotificationSendInput } from "../../contracts/notification-types";
|
|
4
3
|
import type { PaginationParams } from "../../contracts/shared";
|
|
4
|
+
import { sql } from "../../runtime/clients";
|
|
5
5
|
import { logger } from "../logging";
|
|
6
6
|
import { escapeLikePattern } from "../postgres";
|
|
7
7
|
import { notificationMailOutcome, sendNotificationMail } from "./email-mail";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SQL } from "bun";
|
|
2
2
|
import type { MailStatus } from "../../contracts/outgoing-mail";
|
|
3
3
|
import type { NotificationDeliveryStatus } from "../../contracts/user-notifications";
|
|
4
|
+
import { sql } from "../../runtime/clients";
|
|
4
5
|
import { escapeLikePattern, toPgTextArray } from "../postgres";
|
|
5
6
|
|
|
6
7
|
const DELIVERY_STATUSES = new Set<NotificationDeliveryStatus>(["deferred", "pending", "sending", "delivered", "suppressed", "failed"]);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import type { output, ZodType } from "zod";
|
|
3
2
|
import { isNotificationGroup, notificationGroupTag } from "../../contracts/notification-group";
|
|
4
3
|
import type {
|
|
@@ -10,6 +9,7 @@ import type {
|
|
|
10
9
|
NotificationSendInput,
|
|
11
10
|
} from "../../contracts/notification-types";
|
|
12
11
|
import { validateNotificationTargetHref } from "../../contracts/notification-types";
|
|
12
|
+
import { sql } from "../../runtime/clients";
|
|
13
13
|
import { normalizeLocale } from "../../shared/locale";
|
|
14
14
|
import { logger } from "../logging";
|
|
15
15
|
import { encryptSecret } from "../secrets";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { dates } from "@k2b/stdlib";
|
|
2
|
-
import { sql } from "bun";
|
|
3
2
|
import type { MutationResult } from "../../contracts/shared";
|
|
4
3
|
import {
|
|
5
4
|
NOTIFICATION_DO_NOT_DISTURB_MAX_DAYS,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
type NotificationQuietState,
|
|
10
9
|
type UpdateNotificationQuietSettings,
|
|
11
10
|
} from "../../contracts/user-notifications";
|
|
11
|
+
import { sql } from "../../runtime/clients";
|
|
12
12
|
import { normalizeTimeZone } from "../../shared/time";
|
|
13
13
|
|
|
14
14
|
const DAY_MS = 24 * 60 * 60_000;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import {
|
|
3
2
|
isSafeNotificationTargetHref,
|
|
4
3
|
type NotificationDefinitionPresentationCatalog,
|
|
@@ -11,6 +10,7 @@ import type {
|
|
|
11
10
|
UserNotificationPreference,
|
|
12
11
|
UserNotificationPreferencesResponse,
|
|
13
12
|
} from "../../contracts/user-notifications";
|
|
13
|
+
import { sql } from "../../runtime/clients";
|
|
14
14
|
import { normalizeLocale } from "../../shared/locale";
|
|
15
15
|
import { toPgTextArray } from "../postgres";
|
|
16
16
|
import { listNotificationChannels } from "./channels";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import { createRemoteJWKSet, decodeProtectedHeader, errors, type JWTPayload, type JWTVerifyGetKey, jwtVerify } from "jose";
|
|
3
2
|
import type { User } from "../contracts/shared";
|
|
3
|
+
import { sql } from "../runtime/clients";
|
|
4
4
|
import { isAccountCategoryAllowed } from "./account-category-policy";
|
|
5
5
|
import { isAccountExpired } from "./account-model";
|
|
6
6
|
import { IDENTITY_JWKS_MAX_AGE_SECONDS } from "./identity/constants";
|
package/src/services/outbox.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { sql } from "
|
|
1
|
+
import { sql } from "../runtime/clients";
|
|
2
|
+
|
|
2
3
|
import { logger } from "./logging";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -66,9 +67,9 @@ export type PgOutbox<Row extends OutboxRow> = {
|
|
|
66
67
|
stop(): Promise<void>;
|
|
67
68
|
};
|
|
68
69
|
|
|
69
|
-
const identifier = (name: string, label: string)
|
|
70
|
+
const identifier = (name: string, label: string) => {
|
|
70
71
|
if (!name.split(".").every((part) => IDENTIFIER.test(part))) throw new Error(`Invalid outbox ${label}: ${name}`);
|
|
71
|
-
return
|
|
72
|
+
return name;
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row>): PgOutbox<Row> => {
|
|
@@ -81,7 +82,7 @@ export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row
|
|
|
81
82
|
const log = logger(config.name);
|
|
82
83
|
const filters = Object.entries(config.where ?? {}).map(([column, value]) => ({ column: identifier(column, "filter column"), value }));
|
|
83
84
|
const matches = (alias: "current" | "busy") =>
|
|
84
|
-
filters.reduce((fragment, { column, value }) => sql`${fragment} AND ${sql.unsafe(alias)}.${column} = ${value}`, sql``);
|
|
85
|
+
filters.reduce((fragment, { column, value }) => sql`${fragment} AND ${sql.unsafe(alias)}.${sql.unsafe(column)} = ${value}`, sql``);
|
|
85
86
|
// Sorted, so the order of the `where` entries cannot give one outbox two locks. Descending keeps the
|
|
86
87
|
// lock that live outboxes (`kind`, `app_id`) always took, so replicas of two releases share it.
|
|
87
88
|
const where = Object.entries(config.where ?? {}).sort(([left], [right]) => (left < right ? 1 : -1));
|
|
@@ -115,28 +116,28 @@ export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row
|
|
|
115
116
|
return tx<Row[]>`
|
|
116
117
|
WITH candidates AS MATERIALIZED (
|
|
117
118
|
SELECT current.id
|
|
118
|
-
FROM ${table} current
|
|
119
|
+
FROM ${sql.unsafe(table)} current
|
|
119
120
|
WHERE (current.attempts = 0 OR current.next_attempt_at <= now())
|
|
120
121
|
AND (current.claimed_until IS NULL OR current.claimed_until <= now())
|
|
121
122
|
${matches("current")}
|
|
122
123
|
AND NOT EXISTS (
|
|
123
124
|
SELECT
|
|
124
|
-
FROM ${table} busy
|
|
125
|
-
WHERE busy.${runColumn} = current.${runColumn}
|
|
125
|
+
FROM ${sql.unsafe(table)} busy
|
|
126
|
+
WHERE busy.${sql.unsafe(runColumn)} = current.${sql.unsafe(runColumn)}
|
|
126
127
|
AND (busy.claimed_until > now() OR (busy.attempts > 0 AND busy.next_attempt_at > now()))
|
|
127
128
|
${matches("busy")}
|
|
128
129
|
OFFSET 0
|
|
129
130
|
)
|
|
130
|
-
ORDER BY current.${sequence}
|
|
131
|
+
ORDER BY current.${sql.unsafe(sequence)}
|
|
131
132
|
LIMIT ${cap}
|
|
132
133
|
FOR UPDATE SKIP LOCKED
|
|
133
134
|
), claimed AS (
|
|
134
|
-
UPDATE ${table} outbox
|
|
135
|
+
UPDATE ${sql.unsafe(table)} outbox
|
|
135
136
|
SET claimed_until = now() + (${claimMs} * interval '1 millisecond')
|
|
136
137
|
WHERE outbox.id = ANY(ARRAY(SELECT id FROM candidates))
|
|
137
138
|
RETURNING outbox.*
|
|
138
139
|
)
|
|
139
|
-
SELECT * FROM claimed ORDER BY ${sequence}
|
|
140
|
+
SELECT * FROM claimed ORDER BY ${sql.unsafe(sequence)}
|
|
140
141
|
`;
|
|
141
142
|
});
|
|
142
143
|
|
|
@@ -145,7 +146,7 @@ export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row
|
|
|
145
146
|
const message = error instanceof Error ? error.message : String(error);
|
|
146
147
|
const delaySeconds = Math.min(300, 2 ** Math.min(attempts, 8));
|
|
147
148
|
await sql`
|
|
148
|
-
UPDATE ${table}
|
|
149
|
+
UPDATE ${sql.unsafe(table)}
|
|
149
150
|
SET attempts = ${attempts},
|
|
150
151
|
next_attempt_at = now() + (${delaySeconds} * interval '1 second'),
|
|
151
152
|
claimed_until = NULL,
|
|
@@ -158,7 +159,7 @@ export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row
|
|
|
158
159
|
const dispatch: PgOutbox<Row>["dispatch"] = async (row, publish = config.publish) => {
|
|
159
160
|
try {
|
|
160
161
|
await publish(row);
|
|
161
|
-
await sql`DELETE FROM ${table} WHERE id = ${row.id}::uuid AND attempts = ${row.attempts}`;
|
|
162
|
+
await sql`DELETE FROM ${sql.unsafe(table)} WHERE id = ${row.id}::uuid AND attempts = ${row.attempts}`;
|
|
162
163
|
} catch (error) {
|
|
163
164
|
await retryLater(row, error);
|
|
164
165
|
}
|
|
@@ -182,7 +183,7 @@ export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row
|
|
|
182
183
|
const rest = run.slice(index + 1).map((later) => later.id);
|
|
183
184
|
// Past the claim, the rest is free already, and releasing it could end another replica's claim.
|
|
184
185
|
if (rest.length > 0 && performance.now() < claimEnds) {
|
|
185
|
-
await sql`UPDATE ${table} SET claimed_until = NULL WHERE id = ANY(${sql.array(rest, "uuid")})`;
|
|
186
|
+
await sql`UPDATE ${sql.unsafe(table)} SET claimed_until = NULL WHERE id = ANY(${sql.array(rest, "uuid")})`;
|
|
186
187
|
}
|
|
187
188
|
return;
|
|
188
189
|
}
|
|
@@ -190,7 +191,7 @@ export const createPgOutbox = <Row extends OutboxRow>(config: PgOutboxConfig<Row
|
|
|
190
191
|
}
|
|
191
192
|
}),
|
|
192
193
|
);
|
|
193
|
-
if (delivered.length > 0) await sql`DELETE FROM ${table} WHERE id = ANY(${sql.array(delivered, "uuid")})`;
|
|
194
|
+
if (delivered.length > 0) await sql`DELETE FROM ${sql.unsafe(table)} WHERE id = ANY(${sql.array(delivered, "uuid")})`;
|
|
194
195
|
for (const result of results) if (result.status === "rejected") throw result.reason;
|
|
195
196
|
};
|
|
196
197
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import {
|
|
3
2
|
type AdminMailFilter,
|
|
4
3
|
type AdminMailRecord,
|
|
@@ -6,6 +5,7 @@ import {
|
|
|
6
5
|
type MailPage,
|
|
7
6
|
type MailPageParams,
|
|
8
7
|
} from "../../contracts/outgoing-mail";
|
|
8
|
+
import { sql } from "../../runtime/clients";
|
|
9
9
|
import { audit } from "../audit";
|
|
10
10
|
import { cleanupMailAttachments, publishMailSettled } from "./dispatcher";
|
|
11
11
|
import { adminMessageRecord, type MessageRow, outgoingMailMessages } from "./messages";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Socket } from "node:net";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
|
-
import { sql } from "bun";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
import { listApps } from "../../_internal/registry";
|
|
6
|
+
import { sql } from "../../runtime/clients";
|
|
7
7
|
import { DownstreamBusyError, downstreamBudget } from "../downstream-budget";
|
|
8
8
|
import { parsePgJsonValue } from "../postgres";
|
|
9
9
|
import { get } from "../settings";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ObjectRef } from "@k2b/sync";
|
|
2
|
-
import {
|
|
2
|
+
import type { SQL } from "bun";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import {
|
|
5
5
|
type AdminMailFilter,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
MailRecordSchema,
|
|
15
15
|
type MailStatus,
|
|
16
16
|
} from "../../contracts/outgoing-mail";
|
|
17
|
+
import { sql } from "../../runtime/clients";
|
|
17
18
|
import { sanitizeEmailHtml } from "../../shared/email-html";
|
|
18
19
|
import { audit } from "../audit";
|
|
19
20
|
import { escapeLikePattern, parsePgJsonValue, toPgTextArray, toPgUuidArray } from "../postgres";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import type { MailRetention } from "../../contracts/outgoing-mail";
|
|
2
|
+
import { sql } from "../../runtime/clients";
|
|
3
3
|
import { toPgUuidArray } from "../postgres";
|
|
4
4
|
import { get, read } from "../settings";
|
|
5
5
|
import { deleteMailObjects } from "./attachments";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import type { MailMessage, MailRecord } from "../../contracts/outgoing-mail";
|
|
2
|
+
import { sql } from "../../runtime/clients";
|
|
3
3
|
import { cancelMailStreams, deleteMailObjects, uploadMailAttachments } from "./attachments";
|
|
4
4
|
import {
|
|
5
5
|
allowedMailProfile,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import type { SQL } from "bun";
|
|
3
3
|
import { listApps } from "../../_internal/registry";
|
|
4
4
|
import {
|
|
5
5
|
type AdminMailApp,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
MailProfileKeySchema,
|
|
19
19
|
} from "../../contracts/outgoing-mail";
|
|
20
20
|
import type { AppRegistryEntry } from "../../contracts/registry";
|
|
21
|
+
import { sql } from "../../runtime/clients";
|
|
21
22
|
import { type AuditActor, audit } from "../audit";
|
|
22
23
|
import { toPgTextArray } from "../postgres";
|
|
23
24
|
import { decryptValue, encryptValue } from "../settings/crypto";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SQL } from "bun";
|
|
2
2
|
import { CapabilityAppIdSchema } from "../contracts/capabilities";
|
|
3
|
+
import { sql } from "../runtime/clients";
|
|
3
4
|
|
|
4
5
|
type ConsentAnswer = { allowed: boolean; revision: number };
|
|
5
6
|
export class PanelConsentCancelled extends Error {
|
|
@@ -12,34 +13,36 @@ export class PanelConsentCancelled extends Error {
|
|
|
12
13
|
* Core owns the table; platform requests read without a process-wide cache. Missing is distinct from denied. Reads
|
|
13
14
|
* take a share lock, so a page rendered while a save is in flight sees that save's outcome, not the answer before it.
|
|
14
15
|
*/
|
|
15
|
-
export const createPanelContextConsentService = (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
export const createPanelContextConsentService = (injectedDb?: SQL) => {
|
|
17
|
+
const db = () => injectedDb ?? sql;
|
|
18
|
+
return {
|
|
19
|
+
async get(userId: string, provider: string): Promise<ConsentAnswer | null> {
|
|
20
|
+
CapabilityAppIdSchema.parse(provider);
|
|
21
|
+
const [row] = await db()<ConsentAnswer[]>`
|
|
19
22
|
SELECT allowed, revision FROM auth.panel_context_consent WHERE user_id = ${userId}::uuid AND provider = ${provider}
|
|
20
23
|
FOR SHARE
|
|
21
24
|
`;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return row ?? null;
|
|
26
|
+
},
|
|
27
|
+
async list(userId: string, providers: string[]): Promise<Record<string, ConsentAnswer>> {
|
|
28
|
+
if (!providers.length) return {};
|
|
29
|
+
for (const provider of providers) CapabilityAppIdSchema.parse(provider);
|
|
30
|
+
const rows = await db()<({ provider: string } & ConsentAnswer)[]>`
|
|
28
31
|
SELECT provider, allowed, revision FROM auth.panel_context_consent
|
|
29
|
-
WHERE user_id = ${userId}::uuid AND provider IN ${db(providers)}
|
|
32
|
+
WHERE user_id = ${userId}::uuid AND provider IN ${db()(providers)}
|
|
30
33
|
FOR SHARE
|
|
31
34
|
`;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
return Object.fromEntries(rows.map(({ provider, allowed, revision }) => [provider, { allowed, revision }]));
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Compare-and-set on the revision the client read. A save whose request was cancelled (the browser gave up or
|
|
39
|
+
* left) is rolled back before commit, so no answer lands after its page stopped waiting.
|
|
40
|
+
*/
|
|
41
|
+
async set(userId: string, provider: string, allowed: boolean, revision: number, signal?: AbortSignal): Promise<ConsentAnswer | null> {
|
|
42
|
+
CapabilityAppIdSchema.parse(provider);
|
|
43
|
+
return db().begin(async (tx) => {
|
|
44
|
+
await tx`SET LOCAL statement_timeout = '5s'`;
|
|
45
|
+
const [row] = await tx<ConsentAnswer[]>`
|
|
43
46
|
INSERT INTO auth.panel_context_consent (user_id, provider, allowed, revision)
|
|
44
47
|
SELECT ${userId}::uuid, ${provider}, ${allowed}, 1
|
|
45
48
|
WHERE ${revision} = 0 OR EXISTS (
|
|
@@ -50,9 +53,10 @@ export const createPanelContextConsentService = (db: SQL = sql) => ({
|
|
|
50
53
|
WHERE auth.panel_context_consent.revision = ${revision}
|
|
51
54
|
RETURNING allowed, revision
|
|
52
55
|
`;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
56
|
+
if (signal?.aborted) throw new PanelConsentCancelled();
|
|
57
|
+
return row ?? null;
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
};
|
|
58
62
|
export const panelContextConsent = createPanelContextConsentService();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { redis } from "
|
|
1
|
+
import { redis } from "../../../runtime/clients";
|
|
2
2
|
|
|
3
3
|
/** Requested by email or username on the login page; resolves the account through its email at use. */
|
|
4
4
|
export type EmailLoginTokenPayload = { email: string; category?: "guest" | "login" };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { sql } from "bun";
|
|
2
1
|
import type { MutationResult, UserProfile } from "../../../contracts/shared";
|
|
3
2
|
import { coreUserEventPublisher } from "../../../events/core-user-created";
|
|
3
|
+
import { sql } from "../../../runtime/clients";
|
|
4
4
|
import { isAccountCategoryAllowed } from "../../account-category-policy";
|
|
5
5
|
import { writeDeletedAccountAudit } from "../../account-lifecycle/audit";
|
|
6
6
|
import { emailAlreadyUsed, findAccountsByEmail, lockAccountEmail, normalizeAccountEmail } from "../../accounts/email-write";
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { timingSafeEqual } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import type { SQL } from "bun";
|
|
3
3
|
import type { Context } from "hono";
|
|
4
4
|
import { lazySync } from "../_internal/process-sync";
|
|
5
5
|
import { PWA_LIMITS as limits, type PwaDeviceView, type PwaErrorCode, type PwaPairingState, type PwaPlatform } from "../contracts/pwa";
|
|
6
|
+
import { sql } from "../runtime/clients";
|
|
6
7
|
import { isAccountCategoryAllowed } from "./account-category-policy";
|
|
7
8
|
import { audit } from "./audit";
|
|
8
9
|
import { logger } from "./logging";
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SQL } from "bun";
|
|
2
2
|
import { defaultRailPreferences, type RailPreferences, RailPreferencesSchema } from "../contracts/rail-preferences";
|
|
3
|
+
import { sql } from "../runtime/clients";
|
|
3
4
|
|
|
4
5
|
/** Core-owned data, read by the platform SSR seam without a process-wide cache. */
|
|
5
|
-
export const createRailPreferencesService = (
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export const createRailPreferencesService = (injectedDb?: SQL) => {
|
|
7
|
+
const db = () => injectedDb ?? sql;
|
|
8
|
+
return {
|
|
9
|
+
async get(userId: string): Promise<RailPreferences> {
|
|
10
|
+
const [row] = await db()<{ revision: number; visibility: unknown; shortcuts: unknown }[]>`
|
|
8
11
|
SELECT revision, visibility, shortcuts FROM auth.rail_preferences WHERE user_id = ${userId}::uuid
|
|
9
12
|
`;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
return row ? RailPreferencesSchema.parse(row) : defaultRailPreferences();
|
|
14
|
+
},
|
|
15
|
+
async save(userId: string, input: RailPreferences): Promise<RailPreferences | null> {
|
|
16
|
+
const value = RailPreferencesSchema.parse(input);
|
|
17
|
+
const [row] = await db()<{ revision: number; visibility: unknown; shortcuts: unknown }[]>`
|
|
15
18
|
INSERT INTO auth.rail_preferences (user_id, revision, visibility, shortcuts)
|
|
16
19
|
SELECT ${userId}::uuid, 1, (${JSON.stringify(value.visibility)}::text)::jsonb, (${JSON.stringify(value.shortcuts)}::text)::jsonb
|
|
17
20
|
WHERE ${value.revision} = 0 OR EXISTS (SELECT 1 FROM auth.rail_preferences WHERE user_id = ${userId}::uuid)
|
|
@@ -21,7 +24,8 @@ export const createRailPreferencesService = (db: SQL = sql) => ({
|
|
|
21
24
|
WHERE auth.rail_preferences.revision = ${value.revision}
|
|
22
25
|
RETURNING revision, visibility, shortcuts
|
|
23
26
|
`;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
+
return row ? RailPreferencesSchema.parse(row) : null;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
27
31
|
export const railPreferences = createRailPreferencesService();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SQL } from "bun";
|
|
2
2
|
import { type RailAdminInput, RailAdminInputSchema, RailAdminSchema, type RailAdminState } from "../contracts/rail-admin";
|
|
3
3
|
import { RailShortcutSchema } from "../contracts/rail-preferences";
|
|
4
4
|
import { hasRole, type User } from "../contracts/shared";
|
|
5
|
+
import { sql } from "../runtime/clients";
|
|
5
6
|
import { buildAccessPrincipalCondition, createAccess } from "../server/services/access";
|
|
6
7
|
|
|
7
8
|
export class RailAdminError extends Error {
|
|
@@ -16,8 +17,9 @@ const requireAdmin = (actor: User | undefined) => {
|
|
|
16
17
|
if (!actor || !hasRole(actor, "admin")) throw new RailAdminError(403, "Administrator access required");
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
export const createRailShortcutsService = (
|
|
20
|
-
const
|
|
20
|
+
export const createRailShortcutsService = (injectedDb?: SQL) => {
|
|
21
|
+
const db = () => injectedDb ?? sql;
|
|
22
|
+
const list = async (query: SQL = db()): Promise<RailAdminState> => {
|
|
21
23
|
const [state] = await query<{ revision: number }[]>`SELECT revision FROM auth.rail_state WHERE singleton`;
|
|
22
24
|
const rows = await query<{ shortcut: unknown; access: unknown }[]>`
|
|
23
25
|
SELECT r.shortcut, COALESCE((
|
|
@@ -41,7 +43,7 @@ export const createRailShortcutsService = (db: SQL = sql) => {
|
|
|
41
43
|
requireAdmin(actor);
|
|
42
44
|
// One generation switch invalidates every user, including concurrent old refills.
|
|
43
45
|
// Keep the configuration revision unchanged so open editors remain valid.
|
|
44
|
-
await db`UPDATE auth.rail_state SET cache_version = gen_random_uuid() WHERE singleton`;
|
|
46
|
+
await db()`UPDATE auth.rail_state SET cache_version = gen_random_uuid() WHERE singleton`;
|
|
45
47
|
},
|
|
46
48
|
async list(actor: User | undefined) {
|
|
47
49
|
requireAdmin(actor);
|
|
@@ -50,7 +52,7 @@ export const createRailShortcutsService = (db: SQL = sql) => {
|
|
|
50
52
|
async save(actor: User | undefined, input: RailAdminInput) {
|
|
51
53
|
requireAdmin(actor);
|
|
52
54
|
const value = RailAdminInputSchema.parse(input);
|
|
53
|
-
return db.begin(async (tx) => {
|
|
55
|
+
return db().begin(async (tx) => {
|
|
54
56
|
const [state] = await tx<{ revision: number }[]>`SELECT revision FROM auth.rail_state WHERE singleton FOR UPDATE`;
|
|
55
57
|
if (state?.revision !== value.revision) throw new RailAdminError(409, "App bar configuration changed. Reload before saving.");
|
|
56
58
|
await tx`DELETE FROM auth.access WHERE id IN (SELECT access_id FROM auth.rail_shortcut_access)`;
|
|
@@ -76,13 +78,13 @@ export const createRailShortcutsService = (db: SQL = sql) => {
|
|
|
76
78
|
const match = buildAccessPrincipalCondition({
|
|
77
79
|
subject: { type: "user", userId },
|
|
78
80
|
columns: {
|
|
79
|
-
userId: db`a.user_id`,
|
|
80
|
-
groupId: db`a.group_id`,
|
|
81
|
-
serviceAccountId: db`a.service_account_id`,
|
|
82
|
-
authenticatedOnly: db`a.authenticated_only`,
|
|
81
|
+
userId: db()`a.user_id`,
|
|
82
|
+
groupId: db()`a.group_id`,
|
|
83
|
+
serviceAccountId: db()`a.service_account_id`,
|
|
84
|
+
authenticatedOnly: db()`a.authenticated_only`,
|
|
83
85
|
},
|
|
84
86
|
});
|
|
85
|
-
const rows = await db<{ shortcut: unknown }[]>`
|
|
87
|
+
const rows = await db()<{ shortcut: unknown }[]>`
|
|
86
88
|
SELECT r.shortcut FROM auth.rail_shortcuts r WHERE EXISTS (
|
|
87
89
|
SELECT 1 FROM auth.rail_shortcut_access ra JOIN auth.access a ON a.id = ra.access_id
|
|
88
90
|
WHERE ra.shortcut_id = r.id AND a.permission = 'read' AND ${match}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RedisClient } from "bun";
|
|
2
|
-
import {
|
|
1
|
+
import type { RedisClient } from "bun";
|
|
2
|
+
import { createRedis } from "../runtime/clients";
|
|
3
3
|
|
|
4
4
|
// Cache commands must not queue across a disconnect: delayed fills/invalidations
|
|
5
5
|
// are not useful, and reads must be able to fall back to Postgres. Keep this
|
|
@@ -10,7 +10,7 @@ let reconnecting: Promise<void> | undefined;
|
|
|
10
10
|
|
|
11
11
|
export const requestCacheRedis = async (): Promise<RedisClient> => {
|
|
12
12
|
if (!client) {
|
|
13
|
-
client =
|
|
13
|
+
client = createRedis({ enableOfflineQueue: false });
|
|
14
14
|
// The first operation waits for normal connection establishment (Bun's
|
|
15
15
|
// connection timeout applies). Later disconnected operations fail promptly.
|
|
16
16
|
initialConnection = client.connect().catch(() => {});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { crypto, err, fail, ok, type PageParams, type Paginated, type Result } from "@k2b/stdlib";
|
|
3
|
-
import {
|
|
3
|
+
import type { SQL } from "bun";
|
|
4
4
|
import { AI_INFERENCE_SCOPE, type User } from "../contracts/shared";
|
|
5
|
+
import { sql } from "../runtime/clients";
|
|
5
6
|
import { accounts } from "./accounts";
|
|
6
7
|
import { audit } from "./audit";
|
|
7
8
|
import { isUniqueViolation, toPgTextArray } from "./postgres";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { err, fail, ok, type Result } from "@k2b/stdlib";
|
|
2
|
-
import { sql } from "
|
|
2
|
+
import { sql } from "../runtime/clients";
|
|
3
|
+
|
|
3
4
|
import { isUniqueViolation } from "./postgres";
|
|
4
5
|
|
|
5
6
|
export type ServiceAccountKind = "user_delegated" | "resource_bound" | "standalone" | "agent";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SQL } from "bun";
|
|
2
2
|
import type { Context } from "hono";
|
|
3
3
|
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
|
|
4
4
|
import { HTTPException } from "hono/http-exception";
|
|
5
5
|
import { env } from "../../config/env";
|
|
6
6
|
import { PWA_AUTH_PATH, PWA_COOKIES } from "../../contracts/pwa";
|
|
7
7
|
import type { User } from "../../contracts/shared";
|
|
8
|
+
import { sql } from "../../runtime/clients";
|
|
8
9
|
import { getLocale } from "../../server/locale";
|
|
9
10
|
import { describeRequest } from "../../server/request-context";
|
|
10
11
|
import { isAccountCategoryAllowed } from "../account-category-policy";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SQLQuery } from "bun";
|
|
2
2
|
import type { User } from "../../contracts/shared";
|
|
3
|
+
import { sql } from "../../runtime/clients";
|
|
3
4
|
import { decodeAccountCategoryEnabled } from "../account-category-policy";
|
|
4
5
|
import { buildRoles } from "../accounts/authz";
|
|
5
6
|
import { resolveProviderProfile } from "../accounts/base-user";
|
|
@@ -49,6 +49,26 @@ export const CORE_SETTINGS = {
|
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
|
+
"gateway.ingress_cert_resolver": {
|
|
53
|
+
kind: "string",
|
|
54
|
+
default: "le",
|
|
55
|
+
label: "App domain certificate resolver",
|
|
56
|
+
description:
|
|
57
|
+
"Traefik certificate resolver used by app domain routers. Must name a configured ACME resolver; changes apply within five seconds.",
|
|
58
|
+
validate: (value: unknown) =>
|
|
59
|
+
typeof value === "string" && /^[a-zA-Z0-9_-]+$/.test(value)
|
|
60
|
+
? undefined
|
|
61
|
+
: "Use a Traefik certificate resolver name (letters, digits, underscores or hyphens).",
|
|
62
|
+
presentation: {
|
|
63
|
+
translations: {
|
|
64
|
+
de: {
|
|
65
|
+
label: "Zertifikatsresolver für App-Domains",
|
|
66
|
+
description:
|
|
67
|
+
"Traefik-Zertifikatsresolver der App-Domains. Muss einen konfigurierten ACME-Resolver benennen; Änderungen wirken innerhalb von fünf Sekunden.",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
52
72
|
"gateway.trusted_proxies": {
|
|
53
73
|
kind: "string_list",
|
|
54
74
|
default: DEFAULT_TRUSTED_PROXIES,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { connections } from "../../runtime/connections";
|
|
1
2
|
/**
|
|
2
3
|
* Encryption helpers for settings at-rest.
|
|
3
4
|
*
|
|
@@ -7,7 +8,6 @@
|
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
10
|
-
import { env } from "../../config/env";
|
|
11
11
|
import { crypto } from "../../server/services";
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -34,7 +34,7 @@ import { crypto } from "../../server/services";
|
|
|
34
34
|
* with deployments that have always used hex-format APP_SECRET.
|
|
35
35
|
*/
|
|
36
36
|
export const getAppSecret = (): string => {
|
|
37
|
-
const raw =
|
|
37
|
+
const raw = connections().appSecret.current.trim();
|
|
38
38
|
if (!raw) {
|
|
39
39
|
throw new Error("APP_SECRET is required to read or write encrypted settings");
|
|
40
40
|
}
|