bookwiz.io / lib / env-validation.ts
env-validation.ts
Raw
// Environment variable validation for all services
export function validateEnvironment() {
  const isServer = typeof window === 'undefined';
  const isProduction = process.env.NODE_ENV === 'production';
  
  // Server-side validation
  if (isServer) {
    const requiredServerVars = {
      // Stripe
      STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
      STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
      // Supabase
      SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
      // AI Services (optional in development)
      ...(isProduction && {
        OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY,
      })
    };

    const missingVars = Object.entries(requiredServerVars)
      .filter(([key, value]) => !value)
      .map(([key]) => key);

    if (missingVars.length > 0) {
      console.warn(
        `Missing server environment variables: ${missingVars.join(', ')}\n` +
        'Some features may not work correctly.'
      );
    }

    // Validate format (only if variables exist)
    if (requiredServerVars.STRIPE_SECRET_KEY && !requiredServerVars.STRIPE_SECRET_KEY?.startsWith('sk_')) {
      throw new Error('STRIPE_SECRET_KEY must start with "sk_"');
    }

    if (requiredServerVars.STRIPE_WEBHOOK_SECRET && !requiredServerVars.STRIPE_WEBHOOK_SECRET?.startsWith('whsec_')) {
      throw new Error('STRIPE_WEBHOOK_SECRET must start with "whsec_"');
    }
  }

  // Client-side validation
  const clientEnvVars = {
    NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
    NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
    NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
    NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
  };

  const missingClientVars = Object.entries(clientEnvVars)
    .filter(([key, value]) => !value)
    .map(([key]) => key);

  if (missingClientVars.length > 0 && isProduction) {
    throw new Error(
      `Missing required client environment variables: ${missingClientVars.join(', ')}\n` +
      'Please configure these in your Vercel dashboard.'
    );
  }

  // Validate client-side format (only if variables exist)
  if (clientEnvVars.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY && !clientEnvVars.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY?.startsWith('pk_')) {
    throw new Error('NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY must start with "pk_"');
  }

  return {
    ...clientEnvVars,
    ...(isServer ? {
      STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
      STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
      SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
    } : {})
  };
}

// Legacy function for backward compatibility
export function validateStripeEnv() {
  return validateEnvironment();
}

// Check if we're in development mode
export const isDevelopment = process.env.NODE_ENV === 'development';

// Check if Stripe is in test mode
export const isStripeTestMode = process.env.STRIPE_SECRET_KEY?.includes('test') || false;