diff --git a/.npmrc b/.npmrc index f2e2eba2..8c90ae71 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -package-manager=pnpm@latest +package-manager=pnpm@10.18.0 diff --git a/AICHATINTERFACE_EXPLANATION.md b/AICHATINTERFACE_EXPLANATION.md new file mode 100644 index 00000000..25097a19 --- /dev/null +++ b/AICHATINTERFACE_EXPLANATION.md @@ -0,0 +1,469 @@ +# AIChatInterface.tsx - Code Explanation + +## Overview +`AIChatInterface.tsx` is a React component that provides an AI-powered trading coach chatbot interface. It enables users to interact with an AI assistant that analyzes their trading performance, provides personalized coaching, and offers strategic insights. + +**Location**: `src/components/ai/AIChatInterface.tsx` +**Size**: 678 lines +**Type**: Client-side React component ("use client") + +--- + +## Main Features + +### 1. **AI Chat Interface** +- Real-time conversation with an AI trading coach +- Context-aware responses based on trading history +- Message history with conversation context (keeps last 5 messages) +- Typing indicators for better UX + +### 2. **Voice Features** +- **Speech Recognition**: Convert voice to text for user input +- **Text-to-Speech**: AI responses can be read aloud +- **Voice Settings Panel**: + - Toggle voice responses on/off + - Auto-speak setting for automatic response reading + - Adjustable speech speed (0.5x to 2x) + - Voice pitch control + - Test voice functionality + +### 3. **File Upload & Image Analysis** +- Upload trading screenshots for AI analysis +- Multiple file support +- Preview uploaded files in chat +- Pro/Plus/Elite tier feature (locked for Free/Starter users) + +### 4. **Subscription Tier Integration** +- Displays user's current plan (Free, Starter, Pro, Plus, Elite) +- Feature restrictions based on subscription level: + - Free/Starter: Basic chat, no image analysis + - Pro+: Advanced AI, screenshot analysis, personalized strategies +- Visual tier indicators with crown icons and color coding + +### 5. **Intelligent Response System** +- Fallback responses when API fails +- Pattern-matching for common queries: + - Greetings → Personalized welcome + trading snapshot + - Performance queries → Advanced performance analysis + - Strategy questions → Strategy recommendations + - Risk management → Risk analysis and tips + - Timing/entry → Market timing recommendations + - Emotional support → Motivational insights + - Winning streaks → Celebration with growth tips + - Mindset questions → Personalized motivation + - Screenshots → Advanced visual analysis + +--- + +## Technical Architecture + +### State Management + +```typescript +// Message State +const [messages, setMessages] = useState() // Chat history +const [inputMessage, setInputMessage] = useState('') // Current input +const [isTyping, setIsTyping] = useState(false) // Loading indicator + +// File Upload State +const [uploadedFiles, setUploadedFiles] = useState([]) + +// Voice State +const [isRecording, setIsRecording] = useState(false) // Mic active +const [isSpeaking, setIsSpeaking] = useState(false) // TTS active +const [voiceSettings, setVoiceSettings] = useState({ + voiceEnabled: boolean, + autoSpeak: boolean, + voiceSpeed: number, + voicePitch: number, + selectedVoice: SpeechSynthesisVoice | null +}) + +// UI State +const [showVoiceSettings, setShowVoiceSettings] = useState(false) + +// User Context +const { trades } = useContext(TradeContext) // Trading data +const { plan } = useUser() // Subscription tier +const [userTier, setUserTier] = useState<'free' | 'starter' | 'pro' | 'plus' | 'elite'>() +``` + +### Key Interfaces + +#### Message Interface +```typescript +interface Message { + id: string; // Unique identifier (timestamp) + type: 'user' | 'assistant'; // Message sender + content: string; // Message text + timestamp: Date; // When sent + attachments?: File[]; // Optional uploaded files + isTyping?: boolean; // Loading state + isVoice?: boolean; // Voice input indicator +} +``` + +#### VoiceSettings Interface +```typescript +interface VoiceSettings { + voiceEnabled: boolean; // Enable/disable TTS + autoSpeak: boolean; // Auto-read AI responses + voiceSpeed: number; // 0.5-2.0x speed + voicePitch: number; // Voice pitch + selectedVoice: SpeechSynthesisVoice | null; // Voice selection +} +``` + +--- + +## Core Functions + +### 1. **handleSendMessage()** +**Purpose**: Processes and sends user messages to the AI backend + +**Flow**: +1. Creates user message object with content and attachments +2. Adds message to chat history +3. Checks tier restrictions (blocks image uploads for Free/Starter) +4. Sends POST request to `/api/ai/chat` with: + - User message + - Trade history (for context) + - File metadata (not raw files) + - Conversation history (last 5 messages) +5. Receives AI response from API +6. Displays AI response in chat +7. Auto-speaks response if voice settings enabled +8. Falls back to local intelligent response if API fails +9. Clears input and uploaded files + +**Error Handling**: Displays error message in chat if API fails + +### 2. **Voice Recognition Functions** + +#### startVoiceRecording() +- Activates Web Speech API speech recognition +- Sets recording state to true +- Converts voice to text and populates input field + +#### stopVoiceRecording() +- Stops active speech recognition +- Updates recording state + +#### cleanForSpeech(text: string) +- Sanitizes markdown and HTML from text +- Removes formatting characters (**bold**, *italic*, `code`) +- Removes HTML tags +- Trims whitespace +- Prepares text for natural speech output + +#### speakText(text: string) +- Uses Web Speech API SpeechSynthesis +- Cancels any ongoing speech +- Creates utterance with cleaned text +- Applies voice settings (speed, pitch, volume, voice) +- Sets speaking state indicators +- Handles start/end/error events + +#### stopSpeaking() +- Cancels ongoing speech synthesis +- Resets speaking state + +### 3. **File Upload Functions** + +#### handleFileUpload(event) +- Extracts files from input event +- Adds files to uploadedFiles state array +- Multiple files supported + +#### removeFile(index) +- Removes file at specified index from array +- Updates uploadedFiles state + +### 4. **Utility Functions** + +#### scrollToBottom() +- Auto-scrolls chat to show latest message +- Smooth scroll behavior + +#### handleKeyPress(event) +- Sends message on Enter key (without Shift) +- Allows Shift+Enter for multi-line input + +#### normalizeTier(plan) +- Converts plan string to standardized tier enum +- Returns 'free' as default for invalid values + +### 5. **generateIntelligentCoachingResponse()** +**Purpose**: Fallback response generator when API is unavailable + +**Pattern Matching**: +- Analyzes user message keywords +- Routes to appropriate response generator from `advancedAnalysis` library +- Provides contextual coaching based on: + - Greetings → Welcome + snapshot + - Performance queries → Analysis + - Strategy questions → Recommendations + - Risk queries → Risk analysis + - Timing questions → Market timing tips + - Emotional keywords → Support/motivation + - Screenshots → Visual analysis + +**Returns**: Formatted markdown string with personalized insights + +--- + +## UI Components + +### Header Section +- **AI Status Indicator**: Green pulsing dot showing AI is online +- **Subscription Tier Badge**: Visual indicator with crown icon +- **Voice Settings Button**: Opens settings panel +- **Speaking Indicator**: Shows when TTS is active + +### Voice Settings Panel (Collapsible) +- **Voice Responses Toggle**: Enable/disable text-to-speech +- **Auto Speak Toggle**: Automatically read AI responses +- **Speed Slider**: Adjust speech speed (0.5x - 2.0x) +- **Test Voice Button**: Preview voice settings +- **Stop Button**: Cancel ongoing speech + +### Messages Area +- **Scrollable Container**: Shows conversation history +- **User Messages**: Right-aligned, blue background +- **AI Messages**: Left-aligned, dark background with bot icon +- **Typing Indicator**: Animated dots when AI is processing +- **Timestamps**: Shows when each message was sent +- **Attachments Display**: Shows uploaded files in messages + +### Input Composer +- **Multiline Textarea**: Message input field +- **Voice Recording Button**: Toggle mic for voice input (red when active) +- **Image Upload Button**: Upload screenshots (locked for Free/Starter) + - Shows lock icon for restricted tiers + - Shows paperclip icon for Pro+ tiers +- **Send Button**: Submit message (disabled when input empty) +- **Suggestion Pills**: Quick action hints and stats + - Voice suggestion example + - Strategy advice hint + - Trade count display + - AI online status + +### File Preview +- Shows uploaded files before sending +- Individual remove buttons for each file +- Truncates long filenames + +--- + +## API Integration + +### Endpoint: `/api/ai/chat` +**Method**: POST + +**Request Body**: +```json +{ + "message": "User's question", + "tradeHistory": [/* array of trade objects */], + "attachments": [/* metadata only: { name, type, size } */], + "conversationHistory": [ + { "role": "user", "content": "..." }, + { "role": "assistant", "content": "..." } + ] +} +``` + +**Response**: +```json +{ + "response": "AI-generated response text" +} +``` + +**Error Handling**: Falls back to `generateIntelligentCoachingResponse()` + +--- + +## Subscription Tier Features + +| Feature | Free/Starter | Pro | Plus | Elite | +|---------|-------------|-----|------|-------| +| Basic Chat | ✅ | ✅ | ✅ | ✅ | +| Voice Input | ✅ | ✅ | ✅ | ✅ | +| Voice Output | ✅ | ✅ | ✅ | ✅ | +| Image Upload | ❌ | ✅ | ✅ | ✅ | +| Screenshot Analysis | ❌ | ✅ | ✅ | ✅ | +| Advanced AI | ❌ | ✅ | ✅ | ✅ | +| Personalized Strategies | ❌ | ✅ | ✅ | ✅ | + +--- + +## Browser API Usage + +### Web Speech API +1. **SpeechRecognition**: Voice-to-text conversion + - Supports Chrome, Edge, Safari (with webkit prefix) + - Continuous: false (single utterance) + - Language: en-US + +2. **SpeechSynthesis**: Text-to-speech output + - Available in all modern browsers + - Supports multiple voices (system-dependent) + - Adjustable rate, pitch, volume + +--- + +## Dependencies + +### External Libraries +- **React**: Component framework +- **lucide-react**: Icon library (Bot, Send, Mic, Volume2, etc.) +- **TradeContext**: Provides trade data +- **UserContext**: Provides user plan/subscription info + +### Custom Modules +- **@/lib/ai/advancedAnalysis**: AI response generation helpers + - `analyzeTradingPerformance()` + - `generatePersonalizedGreeting()` + - `generateTradingSnapshot()` + - `generateAdvancedPerformanceAnalysis()` + - `generateStrategyRecommendations()` + - `generateRiskManagementAnalysis()` + - `generateMarketTimingRecommendations()` + - `generateEmotionalSupportWithInsights()` + - `generateWinningCelebrationWithGrowth()` + - `generatePersonalizedMotivation()` + - `generateAdvancedScreenshotAnalysis()` + - `generateDefaultIntelligentResponse()` + +### Plan Access +- **@/lib/planAccess**: `PLAN_LIMITS` and `PlanType` for feature restrictions + +--- + +## Styling + +### Design System +- **Color Scheme**: Dark theme with blue accents + - Background: `#161B22`, `#0D1117`, `#1a1f2e` + - Borders: `#2a2f3a` + - Primary: Blue (`blue-400`, `blue-600`) + - Success: Green + - Warning: Yellow + - Danger: Red + +- **Responsive**: Mobile-first with md: breakpoints +- **Animations**: + - Bounce for typing indicator + - Pulse for active states + - Smooth transitions for interactions + +### Accessibility +- Touch-friendly buttons with `touch-manipulation` class +- Keyboard navigation (Enter to send) +- Screen reader labels via `title` attributes +- Disabled states for restricted features + +--- + +## Performance Optimizations + +1. **Client-Side Only**: `"use client"` directive for React 18+ Server Components +2. **Refs for DOM Elements**: Prevents re-renders + - `messagesEndRef`: Auto-scroll target + - `fileInputRef`: File input trigger + - `recognitionRef`: Speech recognition instance + - `synthRef`: Speech synthesis instance +3. **Cleanup**: Aborts speech recognition and cancels synthesis on unmount +4. **Conversation Context**: Only sends last 5 messages to API (reduces payload) +5. **Metadata Only**: Sends file metadata, not raw File objects to API + +--- + +## Use Cases + +### 1. Performance Review +**User**: "How's my trading this week?" +**AI**: Analyzes recent trades, calculates win rate, profit factor, identifies patterns + +### 2. Strategy Optimization +**User**: "What's my best trading pattern?" +**AI**: Reviews historical data, identifies winning strategies, provides recommendations + +### 3. Risk Management +**User**: "How can I improve my stop losses?" +**AI**: Analyzes risk metrics, suggests position sizing, reviews drawdown patterns + +### 4. Screenshot Analysis (Pro+) +**User**: Uploads chart screenshot +**AI**: Analyzes technical indicators, identifies patterns, suggests entry/exit points + +### 5. Emotional Support +**User**: "I'm stuck in a losing streak" +**AI**: Provides motivational support, analyzes what went wrong, offers recovery strategy + +### 6. Voice Interaction +**User**: Clicks mic, speaks "What should I trade next?" +**AI**: Converts speech to text, processes query, responds with voice output + +--- + +## Edge Cases Handled + +1. **Missing Token**: Graceful degradation when speech APIs unavailable +2. **API Failure**: Falls back to local intelligent response generator +3. **Tier Restrictions**: Shows upgrade prompts instead of errors +4. **Empty Messages**: Disables send button when no content +5. **Browser Compatibility**: Checks for webkit prefix on SpeechRecognition +6. **Storage Access**: Defensive try-catch for localStorage operations +7. **File Type Validation**: Accepts only image files +8. **Concurrent Speech**: Cancels previous utterances before starting new ones + +--- + +## Integration Points + +### 1. Dashboard Integration +- Embedded in main dashboard as "AI Coach" tab +- Access via dashboard navigation + +### 2. Trade Context +- Automatically receives trade data from TradeContext +- Uses real-time trading performance for analysis + +### 3. User Context +- Syncs with user subscription plan +- Enforces feature restrictions based on tier + +### 4. API Backend +- Connects to `/api/ai/chat` for advanced AI processing +- Falls back to local analysis if backend unavailable + +--- + +## Future Enhancement Opportunities + +1. **Conversation History Persistence**: Save chat history to database +2. **Custom Voice Selection**: Allow users to choose preferred voice +3. **Multi-language Support**: Translate interface and responses +4. **Export Chat**: Download conversation as PDF/text +5. **Suggested Prompts**: Dynamic quick actions based on trading data +6. **Real-time Notifications**: Push alerts for important insights +7. **Video Upload**: Analyze trading session recordings +8. **Group Chat**: Connect with other traders (community feature) +9. **Calendar Integration**: Schedule trading reviews +10. **Performance Graphs**: Inline chart rendering in chat + +--- + +## Summary + +`AIChatInterface.tsx` is a comprehensive AI chatbot component that serves as a personal trading coach. It combines: +- **Natural conversation** with context awareness +- **Voice interaction** for hands-free trading analysis +- **Visual analysis** of trading screenshots (Pro+ feature) +- **Intelligent fallbacks** when API is unavailable +- **Tier-based access** to premium features +- **Responsive design** for mobile and desktop + +The component is highly interactive, accessible, and provides real value to traders by analyzing their performance and offering actionable insights. diff --git a/ROUTES.md b/ROUTES.md new file mode 100644 index 00000000..653acd7a --- /dev/null +++ b/ROUTES.md @@ -0,0 +1,506 @@ +# Tradia Routes Documentation + +This document explains all the routes and paths available in the Tradia application. Tradia is built using Next.js 13+ with the App Router pattern. + +## Table of Contents +- [Public Pages](#public-pages) +- [Authentication Pages](#authentication-pages) +- [Dashboard Pages](#dashboard-pages) +- [API Endpoints](#api-endpoints) + - [Authentication APIs](#authentication-apis) + - [Trade APIs](#trade-apis) + - [MT5 Integration APIs](#mt5-integration-apis) + - [Payment APIs](#payment-apis) + - [AI & Coach APIs](#ai--coach-apis) + - [User Management APIs](#user-management-apis) + - [Analytics APIs](#analytics-apis) + +--- + +## Public Pages + +These pages are accessible to anyone without authentication: + +### `/` (Home/Landing Page) +- **Purpose**: Main landing page for Tradia +- **Features**: + - Overview of Tradia's AI-powered trading performance assistant + - Feature highlights (Smart Performance Tracking, Secure & Private, Lightning-Fast Feedback, Trade Anywhere) + - Call-to-action buttons to sign up or login + - Responsive design for mobile and desktop + +### `/about` +- **Purpose**: Information about Tradia +- **Features**: Company background, mission, and team information + +### `/contact` +- **Purpose**: Contact page for user inquiries +- **Features**: Form to reach out to Tradia support team + +### `/pricing` +- **Purpose**: Display subscription plans and pricing +- **Features**: + - Free plan ($0/month) + - Pro plan ($9/month or $90/year) + - Plus plan ($19/month or $190/year) + - Elite plan ($39/month or $390/year) + - Feature comparison between plans + +### `/privacy` +- **Purpose**: Privacy policy page +- **Features**: Details on how user data is collected, used, and protected + +### `/terms` +- **Purpose**: Terms of service page +- **Features**: Legal terms and conditions for using Tradia + +--- + +## Authentication Pages + +These pages handle user authentication and account management: + +### `/login` +- **Purpose**: User login page +- **Features**: + - Email and password login + - Google OAuth login option + - "Remember me" functionality + - Link to forgot password + - Link to signup page + +### `/signup` +- **Purpose**: New user registration +- **Features**: + - Email and password registration + - Google OAuth signup option + - Email verification required + - Password strength validation + +### `/forgot-password` +- **Purpose**: Password reset request +- **Features**: + - Email input to request password reset + - Sends reset link to user's email + +### `/reset-password` +- **Purpose**: Set new password after reset request +- **Features**: + - Token-based password reset + - New password confirmation + +### `/verify-email` +- **Purpose**: Email verification landing page +- **Features**: + - Verifies user email with token from email link + - Sub-routes: + - `/verify-email/success` - Successful verification confirmation + - `/verify-email/failed` - Failed verification message + +### `/resend-verification` +- **Purpose**: Resend email verification link +- **Features**: + - For users who didn't receive or lost verification email + +### `/check-email` +- **Purpose**: Confirmation page after signup +- **Features**: + - Informs user to check email for verification link + +--- + +## Dashboard Pages + +Protected pages requiring authentication. All dashboard pages are under the `/dashboard` route: + +### `/dashboard` (Main Dashboard) +- **Purpose**: Central hub for trading performance +- **Features**: + - Overview cards with key metrics (total trades, win rate, profit/loss, etc.) + - Trade history table + - Mental coach section with AI insights + - Weekly coach recap + - Risk guard alerts + - Risk metrics visualization + - Position sizing calculator + - Trade journal + - Trade analytics with charts + - Trade planner + - AI chat interface + - MT5 integration wizard + - Multiple tabs for different views: + - Overview + - Analytics + - Journal + - Planner + - Risk Management + - Education + - MT5 Integration + - AI Coach + +### `/dashboard/analytics` +- **Purpose**: Detailed trading analytics page +- **Features**: + - Performance charts (line, bar, pie, donut) + - Time-based insights + - Pair-level performance breakdown + - Win rate, profit factor, drawdown analysis + - Powered by Plotly for interactive visualizations + +### `/dashboard/profile` +- **Purpose**: User profile management +- **Features**: + - View and edit personal information + - Avatar upload + - Account details + +### `/dashboard/settings` +- **Purpose**: User preferences and settings +- **Features**: + - Account settings + - Notification preferences + - Password change + - Theme settings (dark/light mode) + +### `/dashboard/billing` +- **Purpose**: Subscription and billing management +- **Features**: + - Current plan information + - Upgrade/downgrade options + - Payment history + - Invoice downloads + +### `/dashboard/mt5/connect` +- **Purpose**: MT5 (MetaTrader 5) account connection +- **Features**: + - Connect MT5 trading account + - Input MT5 credentials + - Account validation + - Multiple account management + +### `/dashboard/mt5/sync` +- **Purpose**: Synchronize MT5 trade data +- **Features**: + - Manual and automatic sync options + - View sync status + - Import trades from MT5 account + +--- + +## API Endpoints + +All API endpoints are under the `/api` route and return JSON responses. + +### Authentication APIs + +#### `POST /api/auth/signup` +- **Purpose**: Create new user account +- **Body**: `{ email, password, name }` +- **Returns**: User object or error + +#### `POST /api/auth/login` +- **Purpose**: Authenticate user +- **Body**: `{ email, password }` +- **Returns**: JWT token and user session + +#### `POST /api/auth/forgot-password` +- **Purpose**: Request password reset +- **Body**: `{ email }` +- **Returns**: Success message + +#### `POST /api/auth/reset-password` +- **Purpose**: Reset password with token +- **Body**: `{ token, newPassword }` +- **Returns**: Success message + +#### `GET /api/auth/verify-email` +- **Purpose**: Verify user email address via token (from email link) +- **Query Parameters**: `token` - Verification token from email +- **Returns**: Redirects to `/verify-email/success` or `/verify-email/failed` + +#### `POST /api/auth/verify-email` +- **Purpose**: Programmatic email verification (for server-to-server calls) +- **Body**: `{ token }` +- **Returns**: JSON response `{ ok: true }` or error + +#### `POST /api/auth/resend-verification` +- **Purpose**: Resend verification email +- **Body**: `{ email }` +- **Returns**: Success message + +#### `POST /api/auth/change-password` +- **Purpose**: Change password for authenticated user +- **Body**: `{ currentPassword, newPassword }` +- **Returns**: Success message + +#### `POST /api/auth/refresh` +- **Purpose**: Refresh authentication token +- **Returns**: New JWT token + +#### `* /api/auth/[...nextauth]` (supports GET and POST) +- **Purpose**: NextAuth.js authentication handler +- **Features**: Handles OAuth and session management +- **Note**: This is a catch-all route that handles multiple HTTP methods for NextAuth.js flows + +--- + +### Trade APIs + +#### `GET /api/trades` +- **Purpose**: Fetch user's trade history +- **Query Parameters**: + - `limit` - Number of trades to return (default: 100) + - `offset` - Pagination offset (default: 0) + - `symbol` - Filter by trading symbol + - `outcome` - Filter by win/loss + - `fromDate` - Start date filter + - `toDate` - End date filter +- **Returns**: Array of trade objects + +#### `POST /api/trades` +- **Purpose**: Create new trade manually +- **Body**: Trade details (symbol, entry, exit, profit, etc.) +- **Returns**: Created trade object + +#### `POST /api/trades/import` +- **Purpose**: Import trades from CSV/XLSX file +- **Body**: File upload with trade data +- **Returns**: Import summary (success count, errors) + +--- + +### MT5 Integration APIs + +#### `GET /api/mt5/accounts` +- **Purpose**: Get user's connected MT5 accounts +- **Returns**: Array of MT5 account objects + +#### `POST /api/mt5/accounts` +- **Purpose**: Add new MT5 account +- **Body**: MT5 account credentials +- **Returns**: Created account object + +#### `GET /api/mt5/accounts/[id]` +- **Purpose**: Get specific MT5 account details +- **Returns**: MT5 account object + +#### `PUT /api/mt5/accounts/[id]` +- **Purpose**: Update MT5 account +- **Body**: Updated account details +- **Returns**: Updated account object + +#### `DELETE /api/mt5/accounts/[id]` +- **Purpose**: Remove MT5 account +- **Returns**: Success message + +#### `POST /api/mt5/connect` +- **Purpose**: Establish connection to MT5 account +- **Body**: MT5 credentials and server details +- **Returns**: Connection status + +#### `POST /api/mt5/validate` +- **Purpose**: Validate MT5 credentials +- **Body**: MT5 credentials +- **Returns**: Validation result + +#### `GET /api/mt5/account-info` +- **Purpose**: Get MT5 account information +- **Query Parameters**: `accountId` +- **Returns**: Account balance, equity, margin info + +#### `POST /api/mt5/sync` +- **Purpose**: Synchronize trades from MT5 account +- **Body**: `{ accountId }` +- **Returns**: Sync status and imported trade count + +#### `POST /api/mt5/import` +- **Purpose**: Import specific trades from MT5 +- **Body**: Trade selection criteria +- **Returns**: Imported trades + +#### `GET /api/mt5/orders` +- **Purpose**: Get open orders from MT5 +- **Query Parameters**: `accountId` +- **Returns**: Array of open orders + +#### `GET /api/mt5/positions` +- **Purpose**: Get open positions from MT5 +- **Query Parameters**: `accountId` +- **Returns**: Array of open positions + +#### `GET /api/mt5/monitoring` +- **Purpose**: Get MT5 account monitoring status +- **Returns**: Connection status, last sync time + +#### `GET /api/mt5/credentials` +- **Purpose**: Get stored MT5 credentials (encrypted) +- **Returns**: Array of credential objects + +#### `POST /api/mt5/credentials` +- **Purpose**: Store new MT5 credentials +- **Body**: Credential details +- **Returns**: Created credential object + +#### `DELETE /api/mt5/credentials/[id]` +- **Purpose**: Delete stored credentials +- **Returns**: Success message + +--- + +### Payment APIs + +#### `POST /api/payments/create-checkout` +- **Purpose**: Create payment checkout session +- **Body**: `{ planId, billingPeriod }` +- **Returns**: Checkout URL or payment intent + +#### `POST /api/payments/webhook` +- **Purpose**: Handle payment provider webhooks +- **Body**: Webhook payload from payment provider +- **Returns**: Acknowledgment + +#### `POST /api/payments/webhook/flutterwave` +- **Purpose**: Specific webhook for Flutterwave payments +- **Body**: Flutterwave webhook payload +- **Returns**: Acknowledgment + +#### `GET /api/payments/subscriptions` +- **Purpose**: Get user's active subscriptions +- **Returns**: Array of subscription objects + +#### `POST /api/payments/verify` +- **Purpose**: Verify payment transaction +- **Body**: `{ transactionId }` +- **Returns**: Payment verification status + +--- + +### AI & Coach APIs + +#### `POST /api/ai/chat` +- **Purpose**: AI chatbot for trading insights +- **Body**: + ```json + { + "message": "User question (string, required if no attachments)", + "tradeHistory": "Array of trade objects (optional, for context)", + "attachments": "Array of file metadata objects (optional)", + "conversationHistory": "Array of previous messages (optional, for context)" + } + ``` + - `message` (string, required when attachments not provided): The user's question or prompt + - `tradeHistory` (array, optional): User's trade data for personalized insights + - `attachments` (array, optional): File metadata for analysis `[{ name, type, size }]` + - `conversationHistory` (array, optional): Previous chat messages for context `[{ role: 'user'|'assistant', content: string }]` +- **Returns**: AI-generated response with trading insights + +#### `POST /api/ai/voice` +- **Purpose**: AI voice coach for spoken feedback +- **Body**: Voice input or text for voice synthesis +- **Returns**: Audio response or voice feedback + +#### `GET /api/coach/points` +- **Purpose**: Get coaching points for user +- **Returns**: Actionable coaching recommendations + +#### `POST /api/coach/weekly-email` +- **Purpose**: Send weekly coaching recap email +- **Returns**: Email send status + +--- + +### User Management APIs + +#### `GET /api/user/profile` +- **Purpose**: Get user profile information +- **Returns**: User profile object + +#### `PUT /api/user/profile` +- **Purpose**: Update user profile +- **Body**: Updated profile fields +- **Returns**: Updated profile object + +#### `POST /api/user/update` +- **Purpose**: Update user account information +- **Body**: User details to update +- **Returns**: Updated user object + +#### `GET /api/user/settings` +- **Purpose**: Get user settings +- **Returns**: Settings object + +#### `PUT /api/user/settings` +- **Purpose**: Update user settings +- **Body**: Settings to update +- **Returns**: Updated settings + +#### `GET /api/user/plan` +- **Purpose**: Get user's subscription plan +- **Returns**: Plan details (Free, Pro, Plus, Elite) + +#### `GET /api/user/trial-status` +- **Purpose**: Check if user is in trial period +- **Returns**: Trial status and expiry date + +#### `POST /api/user/upload-avatar` +- **Purpose**: Upload user profile picture +- **Body**: Form data with image file +- **Returns**: Avatar URL + +--- + +### Analytics APIs + +#### `POST /api/analytics/track` +- **Purpose**: Track user analytics events +- **Body**: `{ event, properties }` +- **Returns**: Tracking confirmation + +#### `GET /api/analytics/user-stats` +- **Purpose**: Get user trading statistics +- **Returns**: Comprehensive trading stats (win rate, profit factor, etc.) + +#### `GET /api/analytics/activity/recent` +- **Purpose**: Get recent user activity +- **Returns**: Array of recent actions/trades + +--- + +### Additional API + +#### `GET /api/verify-email` +- **Purpose**: Alternative email verification endpoint with JWT re-issuance +- **Query Parameters**: `token` - Verification token +- **Returns**: Redirects to success/failed page and sets new JWT cookie with updated `email_verified` claim +- **Note**: This endpoint uses a different implementation than `/api/auth/verify-email`. It calls a SQL function `verify_email` and re-issues the JWT token with updated verification status. Use this when you need the JWT token to be automatically refreshed after verification. + +--- + +## Route Organization + +The application follows Next.js 13+ App Router conventions: + +- **Page Routes**: Defined by `page.tsx` files in route directories +- **API Routes**: Defined by `route.ts` files in `/api` directories +- **Layout**: Each route section has a `layout.tsx` for shared UI +- **Dynamic Routes**: Use `[param]` syntax for dynamic segments (e.g., `/api/mt5/accounts/[id]`) + +## Authentication & Authorization + +- **Public routes**: `/`, `/about`, `/contact`, `/pricing`, `/privacy`, `/terms`, `/login`, `/signup` +- **Protected routes**: All `/dashboard/*` routes require authentication +- **API authentication**: Most API endpoints require valid JWT token or session cookie +- **Role-based access**: Some features are restricted by subscription plan (Free, Pro, Plus, Elite) + +## Key Features by Route + +- **Trade Management**: Import CSV/XLSX, manual entry, MT5 sync +- **Analytics**: Interactive charts, performance metrics, risk analysis +- **AI Coach**: Text chat and voice feedback on trading performance +- **Journal**: Tag trades, add notes, export reports +- **Subscription**: Upgrade plans, payment via Flutterwave +- **Security**: HTTPS, encrypted data storage in Supabase, secure authentication + +--- + +*This documentation is current as of the application's latest version. For development details, see the main README.md.* diff --git a/build.log b/build.log new file mode 100644 index 00000000..7504fd98 Binary files /dev/null and b/build.log differ diff --git a/package.json b/package.json index c48a3106..db5dc077 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "@tailwindcss/postcss": "^4", "@types/bcrypt": "^6.0.0", "@types/file-saver": "^2.0.7", + "@types/aria-query": "^5.0.1", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^20", "@types/nodemailer": "^7.0.1", @@ -103,7 +104,7 @@ "eslint": "^9", "eslint-config-next": "15.4.2", "postcss": "^8.5.6", - "sharp": " ^0.33.5 ", + "sharp": "^0.33.5", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/pnpm-debug.ndjson b/pnpm-debug.ndjson new file mode 100644 index 00000000..2e9f5b2d Binary files /dev/null and b/pnpm-debug.ndjson differ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7254dec5..031e9337 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -231,6 +231,9 @@ importers: '@tailwindcss/postcss': specifier: ^4 version: 4.1.12 + '@types/aria-query': + specifier: ^5.0.1 + version: 5.0.4 '@types/bcrypt': specifier: ^6.0.0 version: 6.0.0 @@ -271,7 +274,7 @@ importers: specifier: ^8.5.6 version: 8.5.6 sharp: - specifier: ' ^0.33.5 ' + specifier: ^0.33.5 version: 0.33.5 tailwindcss: specifier: ^3.4.1 @@ -2465,6 +2468,9 @@ packages: '@tybys/wasm-util@0.10.0': resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/bcrypt@6.0.0': resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} @@ -10143,6 +10149,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + '@types/bcrypt@6.0.0': dependencies: '@types/node': 20.19.11 diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index be73a7e5..45061274 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -5,8 +5,21 @@ import crypto from "crypto"; import { v4 as uuidv4 } from "uuid"; import { createAdminSupabase } from "@/utils/supabase/admin"; -const JWT_SECRET = process.env.JWT_SECRET!; -if (!JWT_SECRET) console.warn("JWT_SECRET not set"); +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +let loggedMissingSecret = false; +const getJwtSecret = (): string | null => { + const secret = process.env.JWT_SECRET; + if (!secret) { + if (!loggedMissingSecret) { + console.warn("JWT_SECRET not set; login route is disabled."); + loggedMissingSecret = true; + } + return null; + } + return secret; +}; /** * POST /api/auth/login @@ -23,6 +36,11 @@ export async function POST(req: Request) { return NextResponse.json({ error: "Email and password required." }, { status: 400 }); } + const jwtSecret = getJwtSecret(); + if (!jwtSecret) { + return NextResponse.json({ error: "Server misconfigured." }, { status: 500 }); + } + const supabase = createAdminSupabase(); // --- Fetch user record --- @@ -77,7 +95,7 @@ export async function POST(req: Request) { plan, role, }, - JWT_SECRET, + jwtSecret, { expiresIn: "12h" } ); diff --git a/src/app/api/auth/refresh/route.ts b/src/app/api/auth/refresh/route.ts index 9a27e257..b9fbf6d7 100644 --- a/src/app/api/auth/refresh/route.ts +++ b/src/app/api/auth/refresh/route.ts @@ -6,8 +6,21 @@ import crypto from "crypto"; import { v4 as uuidv4 } from "uuid"; import { createAdminSupabase } from "@/utils/supabase/admin"; -const JWT_SECRET = process.env.JWT_SECRET!; -if (!JWT_SECRET) console.warn("JWT_SECRET not set"); +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +let loggedMissingSecret = false; +const getJwtSecret = (): string | null => { + const secret = process.env.JWT_SECRET; + if (!secret) { + if (!loggedMissingSecret) { + console.warn("JWT_SECRET not set; refresh route is disabled."); + loggedMissingSecret = true; + } + return null; + } + return secret; +}; export async function POST() { try { @@ -19,6 +32,11 @@ export async function POST() { return NextResponse.json({ error: "Missing refresh credentials" }, { status: 401 }); } + const jwtSecret = getJwtSecret(); + if (!jwtSecret) { + return NextResponse.json({ error: "Server misconfigured." }, { status: 500 }); + } + const supabase = createAdminSupabase(); // fetch session by id @@ -65,7 +83,7 @@ export async function POST() { name: user.name, email_verified: Boolean(user.email_verified), }, - JWT_SECRET, + jwtSecret, { expiresIn: "12h" } ); diff --git a/src/app/api/mt5/credentials/[id]/route.ts b/src/app/api/mt5/credentials/[id]/route.ts index 9731152f..43467bf9 100644 --- a/src/app/api/mt5/credentials/[id]/route.ts +++ b/src/app/api/mt5/credentials/[id]/route.ts @@ -2,10 +2,12 @@ import { NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/authOptions"; -import { credentialStorage } from "@/lib/credential-storage"; +import { getCredentialStorage } from "@/lib/credential-storage"; import { createClient } from "@/utils/supabase/server"; import { requireActiveTrialOrPaid } from "@/lib/trial"; +export const runtime = "nodejs"; + function asString(value: unknown): string { return typeof value === "string" ? value.trim() : ""; } @@ -54,10 +56,11 @@ export async function GET( ); } + const storage = getCredentialStorage(); const credentialId = params.id; // Get the specific credential - const credential = await credentialStorage.getCredentials(user.id, credentialId); + const credential = await storage.getCredentials(user.id, credentialId); if (!credential) { return NextResponse.json( @@ -67,7 +70,7 @@ export async function GET( } // Get credential metadata (without password) - const credentials = await credentialStorage.getUserCredentials(user.id); + const credentials = await storage.getUserCredentials(user.id); const metadata = credentials.find(c => c.id === credentialId); if (!metadata) { @@ -147,6 +150,7 @@ export async function PUT( ); } + const storage = getCredentialStorage(); const credentialId = params.id; const body = await req.json() as { name?: string; @@ -154,7 +158,7 @@ export async function PUT( }; // Get existing credential for comparison - const existingCredential = await credentialStorage.getCredentials(user.id, credentialId); + const existingCredential = await storage.getCredentials(user.id, credentialId); if (!existingCredential) { return NextResponse.json( { error: "CREDENTIAL_NOT_FOUND", message: "Credential not found" }, @@ -170,7 +174,7 @@ export async function PUT( }; // Store updated credential - const storedCredential = await credentialStorage.storeCredentials(user.id, updatedCredential); + const storedCredential = await storage.storeCredentials(user.id, updatedCredential); // Log security audit await supabase.from("mt5_security_audit").insert({ @@ -256,10 +260,11 @@ export async function DELETE( ); } + const storage = getCredentialStorage(); const credentialId = params.id; // Get credential info before deletion for audit - const credential = await credentialStorage.getCredentials(user.id, credentialId); + const credential = await storage.getCredentials(user.id, credentialId); if (!credential) { return NextResponse.json( { error: "CREDENTIAL_NOT_FOUND", message: "Credential not found" }, @@ -268,7 +273,7 @@ export async function DELETE( } // Delete the credential - await credentialStorage.deleteCredentials(user.id, credentialId); + await storage.deleteCredentials(user.id, credentialId); // Log security audit await supabase.from("mt5_security_audit").insert({ diff --git a/src/app/api/mt5/credentials/route.ts b/src/app/api/mt5/credentials/route.ts index 3b69b1e4..68391dbb 100644 --- a/src/app/api/mt5/credentials/route.ts +++ b/src/app/api/mt5/credentials/route.ts @@ -2,10 +2,12 @@ import { NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/authOptions"; -import { credentialStorage } from "@/lib/credential-storage"; +import { getCredentialStorage } from "@/lib/credential-storage"; import { MT5Credentials } from "@/types/mt5"; import { requireActiveTrialOrPaid } from "@/lib/trial"; +export const runtime = "nodejs"; + function asString(value: unknown): string { return typeof value === "string" ? value.trim() : ""; } @@ -54,7 +56,8 @@ export async function GET() { } // Get user's credentials - const credentials = await credentialStorage.getUserCredentials(user.id); + const storage = getCredentialStorage(); + const credentials = await storage.getUserCredentials(user.id); // Return credentials without sensitive data const safeCredentials = credentials.map(cred => ({ @@ -164,7 +167,8 @@ export async function POST(req: Request) { console.log('Credentials object before storeCredentials:', JSON.stringify(credentials, null, 2)); // Store credentials securely - const storedCredential = await credentialStorage.storeCredentials(user.id, credentials); + const storage = getCredentialStorage(); + const storedCredential = await storage.storeCredentials(user.id, credentials); // Log security audit await supabase.from("mt5_security_audit").insert({ diff --git a/src/app/api/mt5/sync/route.ts b/src/app/api/mt5/sync/route.ts index b8b220a3..754bf87d 100644 --- a/src/app/api/mt5/sync/route.ts +++ b/src/app/api/mt5/sync/route.ts @@ -2,10 +2,12 @@ import { NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/authOptions"; -import { credentialStorage } from "@/lib/credential-storage"; +import { getCredentialStorage } from "@/lib/credential-storage"; import { fetchDeals } from "@/lib/mtapi"; import { requireActiveTrialOrPaid } from "@/lib/trial"; +export const runtime = "nodejs"; + type SyncBody = { credentialId?: string; from?: string; @@ -32,6 +34,7 @@ export async function POST(req: Request) { return NextResponse.json({ error: "UPGRADE_REQUIRED" }, { status: 403 }); } + const storage = getCredentialStorage(); const body: SyncBody = await req.json().catch(() => ({})); const { credentialId, from, to } = body; @@ -47,7 +50,7 @@ export async function POST(req: Request) { { status: 400 } ); } - const creds = await credentialStorage.getCredentials(userId, credentialId); + const creds = await storage.getCredentials(userId, credentialId); if (!creds) { return NextResponse.json( { error: "CREDENTIALS_NOT_FOUND", message: "MT5 credentials not found or inactive" }, diff --git a/src/app/api/user/trial-status/route.ts b/src/app/api/user/trial-status/route.ts index 775078fc..54d441f8 100644 --- a/src/app/api/user/trial-status/route.ts +++ b/src/app/api/user/trial-status/route.ts @@ -6,6 +6,10 @@ import { createClient } from "@/utils/supabase/server"; import { getTrialInfoByEmail } from "@/lib/trial"; import { sendTrialExpiredEmail } from "@/lib/mailer"; +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; +export const revalidate = 0; + export async function GET() { try { const session = await getServerSession(authOptions); @@ -44,6 +48,9 @@ export async function GET() { return NextResponse.json({ ok: true, info }); } catch (err) { + if (err && typeof err === "object" && "digest" in err && (err as { digest?: unknown }).digest === "DYNAMIC_SERVER_USAGE") { + throw err; + } console.error("trial-status error:", err); return NextResponse.json({ error: "INTERNAL_ERROR" }, { status: 500 }); } diff --git a/src/app/api/verify-email/route.ts b/src/app/api/verify-email/route.ts index f4443a8b..8e97cfa6 100644 --- a/src/app/api/verify-email/route.ts +++ b/src/app/api/verify-email/route.ts @@ -2,7 +2,9 @@ import { NextResponse } from "next/server"; import { createAdminClient } from "@/utils/supabase/admin"; import jwt from "jsonwebtoken"; -const JWT_SECRET = process.env.JWT_SECRET!; +export const runtime = "nodejs"; + +const JWT_SECRET = process.env.JWT_SECRET; export async function GET(req: Request) { const url = new URL(req.url); @@ -39,6 +41,11 @@ export async function GET(req: Request) { } // ✅ Re-issue JWT with updated email_verified + if (!JWT_SECRET) { + failUrl.searchParams.set("reason", "missing_secret"); + return NextResponse.redirect(failUrl); + } + const newToken = jwt.sign( { sub: user.id, diff --git a/src/components/mt5/CredentialManager.tsx b/src/components/mt5/CredentialManager.tsx index c2bc7377..03330433 100644 --- a/src/components/mt5/CredentialManager.tsx +++ b/src/components/mt5/CredentialManager.tsx @@ -3,7 +3,6 @@ import React, { useState, useEffect } from "react"; import { StoredCredential, MT5Credentials } from "@/types/mt5"; -import { credentialStorage } from "@/lib/credential-storage"; import { encryptionService } from "@/lib/encryption"; import { Plus, diff --git a/src/lib/connection-monitor.ts b/src/lib/connection-monitor.ts index 159c64d8..c6e1137e 100644 --- a/src/lib/connection-monitor.ts +++ b/src/lib/connection-monitor.ts @@ -1,6 +1,6 @@ // src/lib/connection-monitor.ts import { MT5Credentials, ConnectionStatus } from '@/types/mt5'; -import { credentialStorage } from '@/lib/credential-storage'; +import { getCredentialStorage } from '@/lib/credential-storage'; import { mt5ConnectionManager } from '@/lib/mt5-connection-manager'; import { createClient } from '@/utils/supabase/server'; @@ -68,7 +68,8 @@ export class ConnectionMonitor { this.monitoringConfigs.set(userId, monitoringConfig); // Load existing credentials - const credentials = await credentialStorage.getUserCredentials(userId); + const storage = getCredentialStorage(); + const credentials = await storage.getUserCredentials(userId); // Initialize health status for each credential for (const credential of credentials) { @@ -222,7 +223,8 @@ export class ConnectionMonitor { * Perform health check for all user credentials */ private async performHealthCheck(userId: string): Promise { - const credentials = await credentialStorage.getUserCredentials(userId); + const storage = getCredentialStorage(); + const credentials = await storage.getUserCredentials(userId); const config = this.monitoringConfigs.get(userId); if (!config) return; @@ -245,7 +247,8 @@ export class ConnectionMonitor { try { // Get credentials for health check - const credentials: MT5Credentials | null = await credentialStorage.getCredentials(userId, credentialId); + const storage = getCredentialStorage(); + const credentials: MT5Credentials | null = await storage.getCredentials(userId, credentialId); if (!credentials) { await this.updateHealthStatus(userId, credentialId, { status: 'error', diff --git a/src/lib/credential-storage.ts b/src/lib/credential-storage.ts index be75814c..a886bf36 100644 --- a/src/lib/credential-storage.ts +++ b/src/lib/credential-storage.ts @@ -388,4 +388,6 @@ export class CredentialStorageService { } // Export singleton instance -export const credentialStorage = CredentialStorageService.getInstance(); \ No newline at end of file +export function getCredentialStorage(): CredentialStorageService { + return CredentialStorageService.getInstance(); +} \ No newline at end of file