66 lines
1.6 KiB
TypeScript
66 lines
1.6 KiB
TypeScript
type VaultConfig = {
|
|
address: string;
|
|
token: string;
|
|
mount: string;
|
|
sharedPath: string;
|
|
projectPath: string;
|
|
};
|
|
|
|
function requireEnv(name: string) {
|
|
const value = process.env[name];
|
|
if (!value) {
|
|
throw new Error(`${name} is required when VAULT_ENABLED=true.`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function vaultConfig(): VaultConfig {
|
|
return {
|
|
address: requireEnv('VAULT_ADDR').replace(/\/$/, ''),
|
|
token: requireEnv('VAULT_TOKEN'),
|
|
mount: requireEnv('VAULT_KV_MOUNT'),
|
|
sharedPath: requireEnv('VAULT_SHARED_PATH'),
|
|
projectPath: requireEnv('VAULT_PROJECT_PATH'),
|
|
};
|
|
}
|
|
|
|
async function readVaultPath(config: VaultConfig, path: string) {
|
|
const response = await fetch(
|
|
`${config.address}/v1/${config.mount}/data/${path}`,
|
|
{ headers: { 'X-Vault-Token': config.token } },
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Vault read failed for ${path}: ${response.status}.`);
|
|
}
|
|
|
|
const payload = (await response.json()) as {
|
|
data?: { data?: Record<string, unknown> };
|
|
};
|
|
const data = payload.data?.data;
|
|
if (!data) {
|
|
throw new Error(`Vault path ${path} has no KV v2 data.`);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
function applyEnvironment(values: Record<string, unknown>) {
|
|
for (const [key, value] of Object.entries(values)) {
|
|
if (typeof value !== 'string') {
|
|
throw new Error(`Vault value ${key} must be a string.`);
|
|
}
|
|
process.env[key] = value;
|
|
}
|
|
}
|
|
|
|
export async function loadVaultEnvironment() {
|
|
if (process.env.VAULT_ENABLED !== 'true') {
|
|
return;
|
|
}
|
|
|
|
const config = vaultConfig();
|
|
applyEnvironment(await readVaultPath(config, config.sharedPath));
|
|
applyEnvironment(await readVaultPath(config, config.projectPath));
|
|
}
|