Load backend secrets from Vault
Some checks failed
Build and deploy Backend / build (push) Failing after 27s
Some checks failed
Build and deploy Backend / build (push) Failing after 27s
This commit is contained in:
@@ -5,7 +5,6 @@ RUN npm ci
|
|||||||
|
|
||||||
FROM node:22-alpine AS build
|
FROM node:22-alpine AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV DATABASE_URL="postgresql://mapflow:mapflow@localhost:5432/mapflow"
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN npm run prisma:generate
|
RUN npm run prisma:generate
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
|
|
||||||
|
import { loadVaultEnvironment } from './vault/env.js';
|
||||||
|
|
||||||
|
await loadVaultEnvironment();
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
host: process.env.HOST ?? '0.0.0.0',
|
host: process.env.HOST ?? '0.0.0.0',
|
||||||
port: Number(process.env.PORT ?? '4000'),
|
port: Number(process.env.PORT ?? '4000'),
|
||||||
|
|||||||
65
src/vault/env.ts
Normal file
65
src/vault/env.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user