2025-12-05 17:24:16 +01:00
import * as z from 'zod' ;
2025-06-09 01:38:57 +01:00
const envSchema = z . object ({
2026-03-09 11:18:22 +01:00
// Legacy Cloudflare R2 config (used when OBJECT_STORAGE_* vars are absent)
2025-06-09 01:38:57 +01:00
CLOUDFLARE_ACCESS_KEY : z.string (). optional (),
CLOUDFLARE_ACCOUNT_ID : z.string (). optional (),
2025-09-21 19:35:41 +01:00
CLOUDFLARE_BUCKET_DOMAIN : z.string (). optional (),
CLOUDFLARE_RENDERING_API_KEY : z.string (). optional (),
CLOUDFLARE_SECRET_ACCESS_KEY : z.string (). optional (),
2025-12-02 09:54:38 +01:00
CLOUDFLARE_IMAGE_BUCKET_DOMAIN : z.string (). optional (),
2026-05-21 11:29:47 +01:00
APPROXIMATED_API_KEY : z.string (). optional (),
APPROXIMATED_TARGET_ADDRESS : z.string (). optional (),
2026-05-21 14:05:37 +01:00
/** Public IP customers point their A record to (Approximated edge IP). */
APPROXIMATED_DNS_TARGET_IP : z.string (). optional (),
/** Optional CNAME target customers can point to instead of A record. */
APPROXIMATED_DNS_TARGET_CNAME : z.string (). optional (),
2026-03-09 11:18:22 +01:00
// S3-compatible storage (MinIO, AWS S3, or other S3-compatible backends)
OBJECT_STORAGE_ENDPOINT : z.string (). optional (),
OBJECT_STORAGE_REGION : z.string (). optional (),
OBJECT_STORAGE_ACCESS_KEY_ID : z.string (). optional (),
OBJECT_STORAGE_SECRET_ACCESS_KEY : z.string (). optional (),
OBJECT_STORAGE_FORCE_PATH_STYLE : z.string (). optional (),
OBJECT_STORAGE_BUCKET_VIDEOS : z.string (). optional (),
OBJECT_STORAGE_BUCKET_DOCUMENTS : z.string (). optional (),
OBJECT_STORAGE_BUCKET_MEDIA : z.string (). optional (),
/** Public URL base for media bucket (images, thumbnails). Required for image uploads. */
OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL : z.string (). optional (),
/** Optional: endpoint for presigned URLs when different from internal (e.g. browser-reachable) */
OBJECT_STORAGE_PUBLIC_ENDPOINT : z.string (). optional (),
2025-09-21 19:35:41 +01:00
NODE_ENV : z.enum ([ 'development' , 'production' , 'test' ]). default ( 'development' ),
OPENAPI_URL : z.string (). optional (),
PORT : z.string (). optional (),
REDIS_URL : z.string (). optional (),
2026-04-27 11:40:38 +01:00
PUBLIC_EMBED_BASE_URL : z.string (). optional (),
2026-03-09 11:18:22 +01:00
PUBLIC_SERVER_URL : z.string (). optional (),
2025-06-09 01:38:57 +01:00
SMTP_HOST : z.string (). optional (),
SMTP_PASSWORD : z.string (). optional (),
2025-09-21 19:35:41 +01:00
SMTP_PORT : z.string (). optional (),
SMTP_SENDER : z.string (). optional (),
SMTP_USER : z.string (). optional (),
2025-11-24 01:00:50 +01:00
ZOHO_TOKEN : z.string (). optional (),
2025-12-02 09:54:38 +01:00
TRUSTED_ORIGINS : z.string (). optional (),
2026-03-09 11:18:22 +01:00
PRIVATE_SERVER_KEY : z.string (). optional (),
PROJECT_ID_VERCEL : z.string (). optional (),
TEAM_ID_VERCEL : z.string (). optional (),
AUTH_BEARER_TOKEN : z.string (). optional (),
UNSPLASH_API_KEY : z.string (). optional (),
/** When 'true', instance runs in self-hosted mode: single org, auto-enroll as student, no org switching */
PUBLIC_IS_SELFHOSTED : z.string (). optional (),
/** License key for enterprise features (SSO, token-auth, no-tracking, etc.) */
2026-03-11 17:00:44 +01:00
LICENSE_KEY : z.string (). optional (),
/** Dashboard origin for invite/email links (e.g. https://app.yourdomain.com). When set, all email links point here instead of app.classroomio.com. Required for self-hosted. */
2026-05-18 03:47:22 +01:00
DASHBOARD_ORIGIN : z.string (). optional (),
/**
2026-06-07 20:32:37 +01:00
* Shared password for the /admin/queues HTTP Basic Auth prompt. When set, the
* dashboard mounts in production and challenges with `WWW-Authenticate: Basic`,
* accepting any username with this password — keeps the secret out of the
* URL/history. Leave unset to keep the dashboard hidden outside dev.
2026-05-18 03:47:22 +01:00
*/
2026-06-07 20:32:37 +01:00
QUEUE_DASHBOARD_PASSWORD : z.string (). optional (),
2026-05-18 03:47:22 +01:00
/** Optional Bearer token for Jina Reader (higher rate limits). */
JINA_API_KEY : z.string (). optional (),
/** Max completed fetch_documentation_url calls per agent conversation (default 15). */
AGENT_MAX_FETCHES_PER_CONVERSATION : z
. string ()
. optional ()
. transform (( v ) => {
if ( v === undefined || v === '' ) {
return 15 ;
}
const parsed = Number ( v );
return Number . isFinite ( parsed ) && parsed >= 1 ? Math . trunc ( parsed ) : 15 ;
}),
2026-05-21 17:18:08 +01:00
/** Sentry error tracking (server-side). Leave SENTRY_DSN unset to disable. */
SENTRY_DSN : z.string (). optional (),
SENTRY_ENVIRONMENT : z.string (). optional (),
SENTRY_TRACES_SAMPLE_RATE : z.string (). optional (),
2026-05-18 03:47:22 +01:00
/** Max fetch_documentation_url calls per org per UTC day (default 500). */
AGENT_MAX_FETCHES_PER_ORG_PER_DAY : z
. string ()
. optional ()
. transform (( v ) => {
if ( v === undefined || v === '' ) {
return 500 ;
}
const parsed = Number ( v );
return Number . isFinite ( parsed ) && parsed >= 1 ? Math . trunc ( parsed ) : 500 ;
})
2025-06-09 01:38:57 +01:00
});
export const env = envSchema . parse ( process . env );