Load worker secrets from Vault
Some checks failed
Build and deploy Worker / build (push) Failing after 23s
Some checks failed
Build and deploy Worker / build (push) Failing after 23s
This commit is contained in:
@@ -5,7 +5,6 @@ RUN npm ci
|
||||
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
ENV DATABASE_URL="postgresql://mapflow:mapflow@localhost:5432/mapflow"
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
RUN npm run prisma:generate
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import 'dotenv/config';
|
||||
|
||||
import { loadVaultEnvironment } from './vault/env.js';
|
||||
|
||||
await loadVaultEnvironment();
|
||||
|
||||
export const config = {
|
||||
databaseUrl: process.env.DATABASE_URL ?? '',
|
||||
workerName: process.env.HATCHET_WORKER_NAME ?? 'mapflow-hatchet-worker',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dotenv/config';
|
||||
|
||||
import { config } from '../config.js';
|
||||
import { hatchet } from './hatchet-client.js';
|
||||
import { processVoiceExperienceWorkflow } from './workflows/process-voice-experience.js';
|
||||
|
||||
function resolveWorkerSlots(): number {
|
||||
@@ -14,6 +13,7 @@ async function main() {
|
||||
throw new Error('HATCHET_CLIENT_TOKEN is required for hatchet worker');
|
||||
}
|
||||
|
||||
const { hatchet } = await import('./hatchet-client.js');
|
||||
const worker = await hatchet.worker(config.workerName, {
|
||||
workflows: [processVoiceExperienceWorkflow],
|
||||
slots: resolveWorkerSlots(),
|
||||
|
||||
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