@accounter/server
Version:
Accounter GraphQL server
268 lines • 9.95 kB
JavaScript
import { config as dotenv } from 'dotenv';
import zod from 'zod';
// Prefer isolated test env file when provided, otherwise fall back to repo-level .env
dotenv({
path: process.env.TEST_ENV_FILE && process.env.TEST_ENV_FILE.trim() !== ''
? process.env.TEST_ENV_FILE
: ['.env', '../../.env'],
debug: process.env.RELEASE ? false : true,
});
const isNumberString = (input) => zod.string().regex(/^\d+$/).safeParse(input).success;
const numberFromNumberOrNumberString = (input) => {
if (typeof input === 'number')
return input;
if (isNumberString(input))
return Number(input);
return undefined;
};
const NumberFromString = zod.preprocess(numberFromNumberOrNumberString, zod.number().min(1));
// treat an empty string (`''`) as undefined
const emptyString = (input) => {
return zod.preprocess((value) => {
if (value === '')
return undefined;
return value;
}, input);
};
const PostgresModel = zod.object({
POSTGRES_SSL: emptyString(zod.union([zod.literal('1'), zod.literal('0')]).optional()),
POSTGRES_HOST: zod.string(),
POSTGRES_PORT: NumberFromString,
POSTGRES_DB: zod.string(),
POSTGRES_USER: zod.string(),
POSTGRES_PASSWORD: zod.string(),
POSTGRES_MAX_CLIENTS: emptyString(NumberFromString).optional().default(20),
});
const CloudinaryModel = zod.union([
zod
.object({
CLOUDINARY_NAME: zod.string().optional(),
CLOUDINARY_API_KEY: zod.string().optional(),
CLOUDINARY_API_SECRET: zod.string().optional(),
})
.superRefine((data, ctx) => {
if (!!data.CLOUDINARY_NAME !== !!data.CLOUDINARY_API_KEY ||
!!data.CLOUDINARY_NAME !== !!data.CLOUDINARY_API_SECRET) {
ctx.addIssue({
code: 'custom',
message: 'CLOUDINARY_NAME, CLOUDINARY_API_KEY, and CLOUDINARY_API_SECRET must be provided together.',
});
}
}),
zod.void(),
]);
const HiveModel = zod.union([
zod.object({
HIVE_TOKEN: zod.string().optional(),
}),
zod.void(),
]);
const GoogleDriveModel = zod.union([
zod.object({
GOOGLE_DRIVE_API_KEY: zod.string().optional(),
}),
zod.void(),
]);
const CredentialsModel = zod.object({
CREDENTIALS_ENCRYPTION_KEY: zod
.string()
.trim()
.regex(/^[a-f0-9]{64}$/i),
});
const GeneralModel = zod.object({
FRONTEND_URL: zod.url().optional(),
});
const OtelModel = zod
.object({
OTEL_ENABLED: emptyString(zod
.union([zod.literal('1'), zod.literal('0')])
.optional()
.default('0')),
OTEL_SERVICE_NAME: emptyString(zod.string().optional().default('accounter-server')),
OTEL_SERVICE_NAMESPACE: emptyString(zod.string().optional().default('accounter')),
OTEL_DEPLOYMENT_ENV: emptyString(zod
.string()
.optional()
.default(process.env.NODE_ENV ?? 'development')),
OTEL_EXPORTER_OTLP_ENDPOINT: emptyString(zod.string().optional()),
OTEL_EXPORTER_OTLP_HEADERS: emptyString(zod.string().optional()),
OTEL_TRACES_SAMPLER: emptyString(zod
.enum([
'parentbased_traceidratio',
'always_on',
'always_off',
'traceidratio',
'parentbased_always_on',
'parentbased_always_off',
])
.optional()
.default('always_on')),
OTEL_TRACES_SAMPLER_ARG: emptyString(zod.string().optional()),
OTEL_STARTUP_STRICT: emptyString(zod.union([zod.literal('true'), zod.literal('false')]).optional()),
})
.superRefine((data, ctx) => {
if (data.OTEL_ENABLED === '1' && !data.OTEL_EXPORTER_OTLP_ENDPOINT) {
ctx.addIssue({
code: 'custom',
path: ['OTEL_EXPORTER_OTLP_ENDPOINT'],
message: 'OTEL_EXPORTER_OTLP_ENDPOINT is required when OTEL_ENABLED is "1".',
});
}
const usesRatioSampler = data.OTEL_TRACES_SAMPLER === 'parentbased_traceidratio' ||
data.OTEL_TRACES_SAMPLER === 'traceidratio';
if (usesRatioSampler) {
if (data.OTEL_TRACES_SAMPLER_ARG === undefined) {
ctx.addIssue({
code: 'custom',
path: ['OTEL_TRACES_SAMPLER_ARG'],
message: 'OTEL_TRACES_SAMPLER_ARG is required when OTEL_TRACES_SAMPLER is a ratio sampler ("traceidratio" or "parentbased_traceidratio").',
});
}
else {
const num = Number(data.OTEL_TRACES_SAMPLER_ARG);
if (!Number.isFinite(num) || num < 0 || num > 1) {
ctx.addIssue({
code: 'custom',
path: ['OTEL_TRACES_SAMPLER_ARG'],
message: 'OTEL_TRACES_SAMPLER_ARG must be a numeric string between 0 and 1.',
});
}
}
}
});
const GatewayControlPlaneModel = zod.object({
GATEWAY_CP_TOKEN: emptyString(zod.string().optional()),
});
const EmailIngestionModel = zod.object({
EMAIL_INGESTION_V2_ENABLED: emptyString(zod
.union([zod.literal('1'), zod.literal('0')])
.optional()
.default('0')),
EMAIL_INGESTION_SHADOW_MODE: emptyString(zod
.union([zod.literal('1'), zod.literal('0')])
.optional()
.default('0')),
});
const Auth0Model = zod.union([
zod.object({
AUTH0_DOMAIN: zod.string().min(1),
AUTH0_AUDIENCE: zod.string().min(1),
AUTH0_CLIENT_ID: zod.string().min(1),
AUTH0_CLIENT_SECRET: zod.string().min(1),
AUTH0_MANAGEMENT_AUDIENCE: zod.string().min(1),
}),
// If no Auth0 variables are provided, validation passes (optional configuration)
// We use a looser object check here because process.env is always an object
zod
.object({
AUTH0_DOMAIN: zod.literal('').optional(),
AUTH0_AUDIENCE: zod.literal('').optional(),
AUTH0_CLIENT_ID: zod.literal('').optional(),
AUTH0_CLIENT_SECRET: zod.literal('').optional(),
AUTH0_MANAGEMENT_AUDIENCE: zod.literal('').optional(),
})
.transform(() => undefined),
]);
const configs = {
postgres: PostgresModel.safeParse(process.env),
cloudinary: CloudinaryModel.safeParse(process.env),
hive: HiveModel.safeParse(process.env),
googleDrive: GoogleDriveModel.safeParse(process.env),
auth0: Auth0Model.safeParse(process.env),
credentials: CredentialsModel.safeParse(process.env),
general: GeneralModel.safeParse(process.env),
otel: OtelModel.safeParse(process.env),
emailIngestion: EmailIngestionModel.safeParse(process.env),
gatewayControlPlane: GatewayControlPlaneModel.safeParse(process.env),
};
const environmentErrors = [];
for (const config of Object.values(configs)) {
if (config.success === false) {
environmentErrors.push(JSON.stringify(config.error.format(), null, 4));
}
}
if (environmentErrors.length) {
const fullError = environmentErrors.join(`\n`);
console.error('❌ Invalid environment variables:', fullError);
process.exit(1);
}
function extractConfig(config) {
if (!config.success) {
throw new Error('Something went wrong.');
}
return config.data;
}
const postgres = extractConfig(configs.postgres);
const cloudinary = extractConfig(configs.cloudinary);
const hive = extractConfig(configs.hive);
const googleDrive = extractConfig(configs.googleDrive);
const auth0 = extractConfig(configs.auth0);
const credentials = extractConfig(configs.credentials);
const general = extractConfig(configs.general);
const otel = extractConfig(configs.otel);
const emailIngestion = extractConfig(configs.emailIngestion);
const gatewayControlPlane = extractConfig(configs.gatewayControlPlane);
export const env = {
postgres: {
host: postgres.POSTGRES_HOST,
port: postgres.POSTGRES_PORT,
db: postgres.POSTGRES_DB,
user: postgres.POSTGRES_USER,
password: postgres.POSTGRES_PASSWORD,
ssl: postgres.POSTGRES_SSL === '1',
max: postgres.POSTGRES_MAX_CLIENTS,
},
cloudinary: cloudinary?.CLOUDINARY_API_KEY
? {
name: cloudinary.CLOUDINARY_NAME,
apiKey: cloudinary.CLOUDINARY_API_KEY,
apiSecret: cloudinary.CLOUDINARY_API_SECRET,
}
: undefined,
hive: hive?.HIVE_TOKEN
? {
hiveToken: hive.HIVE_TOKEN,
}
: undefined,
googleDrive: googleDrive?.GOOGLE_DRIVE_API_KEY
? {
driveApiKey: googleDrive.GOOGLE_DRIVE_API_KEY,
}
: undefined,
credentialsEncryptionKey: credentials.CREDENTIALS_ENCRYPTION_KEY,
auth0: auth0
? {
domain: auth0.AUTH0_DOMAIN,
audience: auth0.AUTH0_AUDIENCE,
clientId: auth0.AUTH0_CLIENT_ID,
clientSecret: auth0.AUTH0_CLIENT_SECRET,
managementAudience: auth0.AUTH0_MANAGEMENT_AUDIENCE,
}
: undefined,
general: {
frontendUrl: general?.FRONTEND_URL,
},
otel: {
enabled: otel.OTEL_ENABLED === '1',
serviceName: otel.OTEL_SERVICE_NAME,
serviceNamespace: otel.OTEL_SERVICE_NAMESPACE,
deploymentEnv: otel.OTEL_DEPLOYMENT_ENV,
exporterEndpoint: otel.OTEL_EXPORTER_OTLP_ENDPOINT,
exporterHeaders: otel.OTEL_EXPORTER_OTLP_HEADERS,
tracesSampler: otel.OTEL_TRACES_SAMPLER,
tracesSamplerArg: (otel.OTEL_TRACES_SAMPLER === 'parentbased_traceidratio' ||
otel.OTEL_TRACES_SAMPLER === 'traceidratio') &&
otel.OTEL_TRACES_SAMPLER_ARG !== undefined
? Number(otel.OTEL_TRACES_SAMPLER_ARG)
: undefined,
startupStrict: otel.OTEL_STARTUP_STRICT === 'true',
},
emailIngestion: {
v2Enabled: emailIngestion.EMAIL_INGESTION_V2_ENABLED === '1',
shadowMode: emailIngestion.EMAIL_INGESTION_SHADOW_MODE === '1',
},
gatewayControlPlane: {
token: gatewayControlPlane.GATEWAY_CP_TOKEN,
},
};
//# sourceMappingURL=environment.js.map