97 lines
3.8 KiB
TypeScript
97 lines
3.8 KiB
TypeScript
import { mkdirSync } from "node:fs";
|
|
import { join, resolve } from "node:path";
|
|
import { withStorage } from "@/lib/storage";
|
|
import { openFileBucket } from "./file-bucket";
|
|
import { openMailer, type Mailer, type MailerConfig } from "./mailer";
|
|
import { openAssets, openRateLimiter } from "./misc";
|
|
import { openQueue, type InProcessQueue } from "./queue";
|
|
import { RealtimeHubRegistry } from "./realtime";
|
|
import { openSqliteDatabase, type SqliteDatabase } from "./sqlite-database";
|
|
import type { NodeRuntime } from "./types";
|
|
|
|
function optional(name: string): string | undefined {
|
|
const value = process.env[name]?.trim();
|
|
return value || undefined;
|
|
}
|
|
|
|
function mailerConfig(): MailerConfig {
|
|
const smtp = optional("SMTP_URL");
|
|
if (smtp) return { kind: "smtp", url: smtp };
|
|
const accountId = optional("CF_ACCOUNT_ID");
|
|
const token = optional("CF_TOKEN");
|
|
if (accountId && token) return { kind: "cloudflare", accountId, token };
|
|
return { kind: "none" };
|
|
}
|
|
|
|
/**
|
|
* Assemble a `CloudflareEnv` for the Node runtime from environment variables
|
|
* and local resources. Everything the app reaches through `getEnv()` is here,
|
|
* so application code does not know which platform it runs on.
|
|
*/
|
|
export function createNodeRuntime(): NodeRuntime {
|
|
const dataDir = resolve(optional("DATA_DIR") ?? "./data");
|
|
mkdirSync(join(dataDir, "blobs"), { recursive: true });
|
|
|
|
const database = openSqliteDatabase(join(dataDir, "mailflare.sqlite"));
|
|
const bucket = openFileBucket(join(dataDir, "blobs"));
|
|
const mailer = openMailer(mailerConfig());
|
|
const inboundQueue = openQueue("mailflare-inbound");
|
|
const outboundQueue = openQueue("mailflare-outbound");
|
|
const agentQueue = openQueue("mailflare-agent");
|
|
const realtime = new RealtimeHubRegistry();
|
|
const publicDir = resolve(optional("PUBLIC_DIR") ?? "./public");
|
|
|
|
const env = {
|
|
DB: database,
|
|
BUCKET: bucket,
|
|
EMAIL: mailer,
|
|
INBOUND_QUEUE: inboundQueue,
|
|
OUTBOUND_QUEUE: outboundQueue,
|
|
AGENT_QUEUE: agentQueue,
|
|
AI_MODEL: optional("AI_MODEL"),
|
|
AI_BASE_URL: optional("AI_BASE_URL"),
|
|
AI_API_KEY: optional("AI_API_KEY"),
|
|
REALTIME: realtime.namespace(),
|
|
ASSETS: openAssets(publicDir),
|
|
IMAGES: undefined as unknown as CloudflareEnv["IMAGES"],
|
|
WORKER_SELF_REFERENCE: undefined as unknown as CloudflareEnv["WORKER_SELF_REFERENCE"],
|
|
LOGIN_RATE_LIMIT: openRateLimiter(20, 60),
|
|
AGENT_RATE_LIMIT: openRateLimiter(120, 60),
|
|
B2_KEY_ID: optional("B2_KEY_ID"),
|
|
B2_APPLICATION_KEY: optional("B2_APPLICATION_KEY"),
|
|
B2_BUCKET: optional("B2_BUCKET"),
|
|
B2_ENDPOINT: optional("B2_ENDPOINT"),
|
|
S3_BUCKET: optional("S3_BUCKET"),
|
|
S3_REGION: optional("S3_REGION") ?? optional("AWS_REGION"),
|
|
S3_ACCESS_KEY_ID: optional("S3_ACCESS_KEY_ID") ?? optional("AWS_ACCESS_KEY_ID"),
|
|
S3_SECRET_ACCESS_KEY: optional("S3_SECRET_ACCESS_KEY") ?? optional("AWS_SECRET_ACCESS_KEY"),
|
|
CF_TOKEN: optional("CF_TOKEN"),
|
|
CF_API_KEY: optional("CF_API_KEY"),
|
|
CF_EMAIL: optional("CF_EMAIL"),
|
|
TURNSTILE_SECRET_KEY: optional("TURNSTILE_SECRET_KEY"),
|
|
GITHUB_UPDATE_TOKEN: optional("GITHUB_UPDATE_TOKEN"),
|
|
GITHUB_UPDATE_REF: optional("GITHUB_UPDATE_REF"),
|
|
GITHUB_UPDATE_REPO: optional("GITHUB_UPDATE_REPO"),
|
|
// Marks the runtime for the few places that must behave differently.
|
|
MAILFLARE_RUNTIME: "node",
|
|
APP_URL: optional("APP_URL")?.replace(/\/$/, ""),
|
|
INBOUND_WEBHOOK_SECRET: optional("INBOUND_WEBHOOK_SECRET"),
|
|
VAPID_PUBLIC_KEY: optional("VAPID_PUBLIC_KEY"),
|
|
VAPID_PRIVATE_KEY: optional("VAPID_PRIVATE_KEY"),
|
|
VAPID_SUBJECT: optional("VAPID_SUBJECT"),
|
|
} as unknown as CloudflareEnv;
|
|
const storageEnv = withStorage(env);
|
|
realtime.bindEnv(storageEnv);
|
|
|
|
return {
|
|
env: storageEnv,
|
|
dataDir,
|
|
database: database as unknown as SqliteDatabase,
|
|
mailer: mailer as unknown as Mailer,
|
|
inboundQueue: inboundQueue as unknown as InProcessQueue,
|
|
outboundQueue: outboundQueue as unknown as InProcessQueue,
|
|
agentQueue: agentQueue as unknown as InProcessQueue,
|
|
realtime,
|
|
};
|
|
}
|