107 lines
2.9 KiB
TypeScript
107 lines
2.9 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import { danger } from "./globals.js";
|
|
import { defaultRuntime, type RuntimeEnv } from "./runtime.js";
|
|
|
|
export type AuthMode =
|
|
| { accountSid: string; authToken: string }
|
|
| { accountSid: string; apiKey: string; apiSecret: string };
|
|
|
|
export type EnvConfig = {
|
|
accountSid: string;
|
|
whatsappFrom: string;
|
|
whatsappSenderSid?: string;
|
|
auth: AuthMode;
|
|
};
|
|
|
|
const EnvSchema = z
|
|
.object({
|
|
TWILIO_ACCOUNT_SID: z.string().min(1, "TWILIO_ACCOUNT_SID required"),
|
|
TWILIO_WHATSAPP_FROM: z.string().min(1, "TWILIO_WHATSAPP_FROM required"),
|
|
TWILIO_SENDER_SID: z.string().optional(),
|
|
TWILIO_AUTH_TOKEN: z.string().optional(),
|
|
TWILIO_API_KEY: z.string().optional(),
|
|
TWILIO_API_SECRET: z.string().optional(),
|
|
})
|
|
.superRefine((val, ctx) => {
|
|
if (val.TWILIO_API_KEY && !val.TWILIO_API_SECRET) {
|
|
ctx.addIssue({
|
|
code: "custom",
|
|
message: "TWILIO_API_SECRET required when TWILIO_API_KEY is set",
|
|
});
|
|
}
|
|
if (val.TWILIO_API_SECRET && !val.TWILIO_API_KEY) {
|
|
ctx.addIssue({
|
|
code: "custom",
|
|
message: "TWILIO_API_KEY required when TWILIO_API_SECRET is set",
|
|
});
|
|
}
|
|
if (
|
|
!val.TWILIO_AUTH_TOKEN &&
|
|
!(val.TWILIO_API_KEY && val.TWILIO_API_SECRET)
|
|
) {
|
|
ctx.addIssue({
|
|
code: "custom",
|
|
message:
|
|
"Provide TWILIO_AUTH_TOKEN or both TWILIO_API_KEY and TWILIO_API_SECRET",
|
|
});
|
|
}
|
|
});
|
|
|
|
export function readEnv(runtime: RuntimeEnv = defaultRuntime): EnvConfig {
|
|
// Load and validate Twilio auth + sender configuration from env.
|
|
const parsed = EnvSchema.safeParse(process.env);
|
|
if (!parsed.success) {
|
|
runtime.error("Invalid environment configuration:");
|
|
parsed.error.issues.forEach((iss) => {
|
|
runtime.error(`- ${iss.message}`);
|
|
});
|
|
runtime.exit(1);
|
|
}
|
|
|
|
const {
|
|
TWILIO_ACCOUNT_SID: accountSid,
|
|
TWILIO_WHATSAPP_FROM: whatsappFrom,
|
|
TWILIO_SENDER_SID: whatsappSenderSid,
|
|
TWILIO_AUTH_TOKEN: authToken,
|
|
TWILIO_API_KEY: apiKey,
|
|
TWILIO_API_SECRET: apiSecret,
|
|
} = parsed.data;
|
|
|
|
let auth: AuthMode;
|
|
if (apiKey && apiSecret) {
|
|
auth = { accountSid, apiKey, apiSecret };
|
|
} else if (authToken) {
|
|
auth = { accountSid, authToken };
|
|
} else {
|
|
runtime.error("Missing Twilio auth configuration");
|
|
runtime.exit(1);
|
|
throw new Error("unreachable");
|
|
}
|
|
|
|
return {
|
|
accountSid,
|
|
whatsappFrom,
|
|
whatsappSenderSid,
|
|
auth,
|
|
};
|
|
}
|
|
|
|
export function ensureTwilioEnv(runtime: RuntimeEnv = defaultRuntime) {
|
|
// Guardrails: fail fast when Twilio env vars are missing or incomplete.
|
|
const required = ["TWILIO_ACCOUNT_SID", "TWILIO_WHATSAPP_FROM"];
|
|
const missing = required.filter((k) => !process.env[k]);
|
|
const hasToken = Boolean(process.env.TWILIO_AUTH_TOKEN);
|
|
const hasKey = Boolean(
|
|
process.env.TWILIO_API_KEY && process.env.TWILIO_API_SECRET,
|
|
);
|
|
if (missing.length > 0 || (!hasToken && !hasKey)) {
|
|
runtime.error(
|
|
danger(
|
|
`Missing Twilio env: ${missing.join(", ") || "auth token or api key/secret"}. Set them in .env before using provider=twilio.`,
|
|
),
|
|
);
|
|
runtime.exit(1);
|
|
}
|
|
}
|