150 lines
6.8 KiB
JavaScript
150 lines
6.8 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
import {
|
|
loadManagedProvisionerPublicKeyFile,
|
|
validateManagedProvisionerAudience,
|
|
validateManagedProvisionerIdentity,
|
|
} from "./managed-provisioner-auth.mjs";
|
|
|
|
export function readConfig(env = process.env) {
|
|
const provisionerApiEnabled = boolean(env.EXTERNAL_DATA_PLANE_PROVISIONING_ENABLED, false);
|
|
const managedProvisionerApiEnabled = boolean(env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONING_ENABLED, false);
|
|
const foundryProvisionerApiEnabled = boolean(env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONING_ENABLED, false);
|
|
const managedProvisionerMaxSkewSeconds = integer(
|
|
env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_MAX_SKEW_SECONDS,
|
|
60,
|
|
5,
|
|
300,
|
|
);
|
|
const config = {
|
|
port: integer(env.PORT, 18106, 1, 65535),
|
|
databaseUrl: required(env.EXTERNAL_DATA_PLANE_DATABASE_URL, "EXTERNAL_DATA_PLANE_DATABASE_URL"),
|
|
databasePoolSize: integer(env.EXTERNAL_DATA_PLANE_DATABASE_POOL_SIZE, 10, 1, 50),
|
|
internalAccessToken: optional(env.NODEDC_INTERNAL_ACCESS_TOKEN),
|
|
provisionerApiEnabled,
|
|
managedProvisionerApiEnabled,
|
|
foundryProvisionerApiEnabled,
|
|
provisionerAccessToken: provisionerApiEnabled ? secretFile(env.EXTERNAL_DATA_PLANE_PROVISIONER_TOKEN_FILE) : "",
|
|
managedProvisionerPublicKey: managedProvisionerApiEnabled
|
|
? loadManagedProvisionerPublicKeyFile(env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_PUBLIC_KEY_FILE)
|
|
: null,
|
|
managedProvisionerServiceId: managedProvisionerApiEnabled
|
|
? validateManagedProvisionerIdentity(
|
|
required(env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_SERVICE_ID, "EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_SERVICE_ID"),
|
|
"service_id",
|
|
)
|
|
: "",
|
|
managedProvisionerKeyId: managedProvisionerApiEnabled
|
|
? validateManagedProvisionerIdentity(
|
|
required(env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_KEY_ID, "EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_KEY_ID"),
|
|
"key_id",
|
|
)
|
|
: "",
|
|
managedProvisionerAudience: managedProvisionerApiEnabled
|
|
? validateManagedProvisionerAudience(
|
|
required(env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_AUDIENCE, "EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_AUDIENCE"),
|
|
)
|
|
: "",
|
|
managedProvisionerMaxSkewMs: managedProvisionerMaxSkewSeconds * 1000,
|
|
managedProvisionerReplayCacheMaxEntries: integer(
|
|
env.EXTERNAL_DATA_PLANE_MANAGED_PROVISIONER_REPLAY_CACHE_MAX_ENTRIES,
|
|
10_000,
|
|
100,
|
|
100_000,
|
|
),
|
|
foundryProvisionerPublicKey: foundryProvisionerApiEnabled
|
|
? loadManagedProvisionerPublicKeyFile(env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_PUBLIC_KEY_FILE)
|
|
: null,
|
|
foundryProvisionerServiceId: foundryProvisionerApiEnabled
|
|
? validateManagedProvisionerIdentity(
|
|
required(env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_SERVICE_ID, "EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_SERVICE_ID"),
|
|
"service_id",
|
|
)
|
|
: "",
|
|
foundryProvisionerKeyId: foundryProvisionerApiEnabled
|
|
? validateManagedProvisionerIdentity(
|
|
required(env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_KEY_ID, "EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_KEY_ID"),
|
|
"key_id",
|
|
)
|
|
: "",
|
|
foundryProvisionerAudience: foundryProvisionerApiEnabled
|
|
? validateManagedProvisionerAudience(
|
|
required(env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_AUDIENCE, "EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_AUDIENCE"),
|
|
)
|
|
: "",
|
|
foundryProvisionerMaxSkewMs: integer(
|
|
env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_MAX_SKEW_SECONDS,
|
|
60,
|
|
5,
|
|
300,
|
|
) * 1000,
|
|
foundryProvisionerReplayCacheMaxEntries: integer(
|
|
env.EXTERNAL_DATA_PLANE_FOUNDRY_PROVISIONER_REPLAY_CACHE_MAX_ENTRIES,
|
|
10_000,
|
|
100,
|
|
100_000,
|
|
),
|
|
rawRetentionDays: integer(env.EXTERNAL_DATA_PLANE_RAW_RETENTION_DAYS, 14, 1, 3650),
|
|
maxBatchBytes: integer(env.EXTERNAL_DATA_PLANE_MAX_BATCH_BYTES, 5 * 1024 * 1024, 1024, 50 * 1024 * 1024),
|
|
maxFactsPerPublish: integer(env.EXTERNAL_DATA_PLANE_MAX_FACTS_PER_PUBLISH, 5000, 1, 100_000),
|
|
maxAttributesBytesPerFact: integer(env.EXTERNAL_DATA_PLANE_MAX_ATTRIBUTES_BYTES_PER_FACT, 64 * 1024, 256, 1024 * 1024),
|
|
maxPatchOperations: integer(env.EXTERNAL_DATA_PLANE_MAX_PATCH_OPERATIONS, 500, 1, 5000),
|
|
maxPatchBytes: integer(env.EXTERNAL_DATA_PLANE_MAX_PATCH_BYTES, 256 * 1024, 16 * 1024, 5 * 1024 * 1024),
|
|
patchRetentionMs: integer(env.EXTERNAL_DATA_PLANE_PATCH_RETENTION_MS, 60 * 60 * 1000, 60 * 1000, 7 * 24 * 60 * 60 * 1000),
|
|
receiptRetentionMs: integer(env.EXTERNAL_DATA_PLANE_RECEIPT_RETENTION_MS, 7 * 24 * 60 * 60 * 1000, 24 * 60 * 60 * 1000, 90 * 24 * 60 * 60 * 1000),
|
|
retentionDeleteLimit: integer(env.EXTERNAL_DATA_PLANE_RETENTION_DELETE_LIMIT, 10_000, 100, 100_000),
|
|
streamHeartbeatMs: integer(env.EXTERNAL_DATA_PLANE_STREAM_HEARTBEAT_MS, 20_000, 5_000, 60_000),
|
|
streamPollMs: integer(env.EXTERNAL_DATA_PLANE_STREAM_POLL_MS, 1_000, 250, 10_000),
|
|
maxReaderStreams: integer(env.EXTERNAL_DATA_PLANE_MAX_READER_STREAMS, 10, 1, 1000),
|
|
writerBindingMaxTtlDays: integer(env.EXTERNAL_DATA_PLANE_WRITER_BINDING_MAX_TTL_DAYS, 90, 1, 365),
|
|
maxFutureSkewSeconds: integer(env.EXTERNAL_DATA_PLANE_MAX_FUTURE_SKEW_SECONDS, 300, 0, 86400),
|
|
retentionSweepMs: integer(env.EXTERNAL_DATA_PLANE_RETENTION_SWEEP_MS, 60 * 60 * 1000, 60 * 1000, 24 * 60 * 60 * 1000),
|
|
legacyIntakeEnabled: boolean(env.EXTERNAL_DATA_PLANE_LEGACY_INTAKE_ENABLED, false),
|
|
};
|
|
if (config.internalAccessToken && config.provisionerAccessToken && config.internalAccessToken === config.provisionerAccessToken) {
|
|
throw new Error("provisioner_token_must_differ_from_internal_token");
|
|
}
|
|
return config;
|
|
}
|
|
|
|
function required(value, name) {
|
|
const normalized = optional(value);
|
|
if (!normalized) throw new Error(`${name}_required`);
|
|
return normalized;
|
|
}
|
|
|
|
function optional(value) {
|
|
const normalized = String(value ?? "").trim();
|
|
return normalized || "";
|
|
}
|
|
|
|
function integer(value, fallback, min, max) {
|
|
const candidate = optional(value);
|
|
if (!candidate) return fallback;
|
|
const number = Number.parseInt(candidate, 10);
|
|
if (!Number.isInteger(number) || number < min || number > max) throw new Error(`invalid_integer:${candidate}`);
|
|
return number;
|
|
}
|
|
|
|
function boolean(value, fallback) {
|
|
const normalized = optional(value).toLowerCase();
|
|
if (!normalized) return fallback;
|
|
if (normalized === "true") return true;
|
|
if (normalized === "false") return false;
|
|
throw new Error(`invalid_boolean:${normalized}`);
|
|
}
|
|
|
|
function secretFile(value) {
|
|
const path = optional(value);
|
|
if (!path) return "";
|
|
let secret = "";
|
|
try {
|
|
secret = readFileSync(path, "utf8").trim();
|
|
} catch {
|
|
throw new Error("external_data_plane_provisioner_token_file_unreadable");
|
|
}
|
|
if (!/^[A-Za-z0-9_-]{48,256}$/.test(secret)) {
|
|
throw new Error("external_data_plane_provisioner_token_file_invalid");
|
|
}
|
|
return secret;
|
|
}
|