Files
tower/packages/config/src/index.ts
T
maaz519 530f81416a feat: AI dev stream over HTTPS (SSE read + draft writeback)
Replaces the Redis-over-Tailscale handoff with a far simpler HTTP surface
on the existing public API — no Redis client, Tailscale, or firewall changes
needed on the dev side.

- GET /ai/stream — SSE feed of every ingested message, reads the internal
  Redis stream; resumes from Last-Event-ID on reconnect (no missed messages);
  heartbeats keep the connection warm through proxies
- POST /ai/drafts — writeback: creates a ContentDraft (PENDING_REVIEW) that
  shows up in the admin /admin/drafts review UI
- AiStreamGuard — static bearer token auth (AI_STREAM_TOKEN), accepts header
  or ?token= query for browser EventSource
- Remove Tailscale sidecar + public Redis port; Redis stays internal-only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 13:06:17 +05:30

31 lines
1.2 KiB
TypeScript

import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url(),
API_PORT: z.coerce.number().default(3001),
JWT_SECRET: z.string().min(32),
MEILI_URL: z.string().url().default('http://localhost:7700'),
MEILI_MASTER_KEY: z.string().default('tower_meili_dev_key'),
LOG_LEVEL: z.enum(['trace', 'debug', 'info', 'warn', 'error']).default('info'),
WHATSAPP_SESSION_PATH: z.string().default('./sessions'),
TOWER_PORTAL_BASE_URL: z.string().url().default('http://localhost:3000'),
BCRYPT_ROUNDS: z.coerce.number().int().min(4).max(15).default(10),
JWT_EXPIRES_IN: z.string().default('7d'),
MEMBER_JWT_EXPIRES_IN: z.string().default('30d'),
OPENROUTER_API_KEY: z.string().optional(),
AI_STREAM_TOKEN: z.string().optional(),
});
export type Env = z.infer<typeof envSchema>;
export function validateEnv(env: NodeJS.ProcessEnv = process.env): Env {
const result = envSchema.safeParse(env);
if (!result.success) {
console.error('Invalid environment variables:', result.error.format());
throw new Error('Invalid environment variables');
}
return result.data;
}