← back to Dear Bubbe Nextjs

lib/feature-middleware.ts

146 lines

/**
 * Feature Middleware for Bubbe.ai
 * 
 * Intercepts premium feature requests and checks user access
 * Works in parallel with existing Bubbe chat without disrupting it
 */

import { NextRequest, NextResponse } from 'next/server';
import { FeatureKey, detectRequestedFeatures } from './bubbe-pricing';
import { getUserIdFromRequest, canUserAccessFeature } from './user-session';

/**
 * Detect if a chat message is requesting premium features
 * and inject appropriate Bubbe responses if user lacks access
 */
export function checkChatFeatures(message: string, userId: string): {
  hasRestriction: boolean;
  bubbeResponse?: string;
  blockedFeatures?: FeatureKey[];
} {
  // Detect what features the user is trying to use
  const requestedFeatures = detectRequestedFeatures(message);
  
  if (requestedFeatures.length === 0) {
    return { hasRestriction: false };
  }
  
  // Check each requested feature
  const blockedFeatures: FeatureKey[] = [];
  const responses: string[] = [];
  
  for (const feature of requestedFeatures) {
    const access = canUserAccessFeature(userId, feature);
    if (!access.allowed) {
      blockedFeatures.push(feature);
      if (access.bubbeResponse) {
        responses.push(access.bubbeResponse);
      }
    }
  }
  
  if (blockedFeatures.length > 0) {
    return {
      hasRestriction: true,
      bubbeResponse: responses[0], // Use first rejection message
      blockedFeatures
    };
  }
  
  return { hasRestriction: false };
}

/**
 * Middleware wrapper for API routes that require specific features
 * Use this to protect premium endpoints
 */
export function requireFeature(feature: FeatureKey) {
  return (handler: Function) => {
    return async (req: NextRequest) => {
      // Extract user ID from request
      const userId = req.cookies.get('userId')?.value || 
                     req.headers.get('x-user-id') ||
                     `user_${req.headers.get('x-forwarded-for') || 'anonymous'}`;
      
      // Check feature access
      const access = canUserAccessFeature(userId, feature);
      
      if (!access.allowed) {
        return NextResponse.json({
          error: 'feature_locked',
          message: access.bubbeResponse,
          requiredFeature: feature
        }, { status: 402 }); // 402 Payment Required
      }
      
      // Feature allowed, proceed with handler
      return handler(req);
    };
  };
}

/**
 * Check if an API endpoint requires premium features
 * based on the endpoint path
 */
export function getEndpointFeature(pathname: string): FeatureKey | null {
  const featureMap: Record<string, FeatureKey> = {
    '/api/bubbe-voice': 'voice_notes',
    '/api/daily-reminder': 'daily_reminders',
    '/api/holiday-mode': 'holiday_mode',
    '/api/cooking-tips': 'cooking_tips',
    '/api/family-gossip': 'family_gossip',
    '/api/yiddish-full': 'yiddish_dictionary_full',
    '/api/local-news': 'local_news',
    '/api/local-sports': 'local_sports',
    '/api/local-weather': 'local_weather',
    '/api/personalized-insights': 'personalized_insights'
  };
  
  return featureMap[pathname] || null;
}

/**
 * Inject feature check into existing chat response
 * This modifies the response to include feature restrictions
 */
export function enhanceChatResponse(
  originalResponse: string,
  message: string,
  userId: string
): string {
  const featureCheck = checkChatFeatures(message, userId);
  
  if (featureCheck.hasRestriction && featureCheck.bubbeResponse) {
    // Prepend the feature restriction message to the original response
    return featureCheck.bubbeResponse + "\n\n" + 
           "But here's what I CAN tell you for free:\n" + 
           originalResponse;
  }
  
  return originalResponse;
}

/**
 * Log feature request attempts for analytics
 */
export function logFeatureAttempt(
  userId: string,
  feature: FeatureKey,
  allowed: boolean
) {
  const timestamp = new Date().toISOString();
  const logEntry = {
    timestamp,
    userId,
    feature,
    allowed,
    event: allowed ? 'feature_accessed' : 'feature_blocked'
  };
  
  // Log to console (in production, send to analytics)
  console.log('[Feature Access]', JSON.stringify(logEntry));
  
  // You could also write to a file or database here
  return logEntry;
}