Skip to content

Latest commit

 

History

History
1385 lines (1146 loc) · 41.8 KB

File metadata and controls

1385 lines (1146 loc) · 41.8 KB

Reflect - Comprehensive Architecture Documentation

Table of Contents

  1. System Overview
  2. Complete System Architecture
  3. Database Schema
  4. Backend Services
  5. Frontend Architecture
  6. API Documentation
  7. Data Flow Diagrams
  8. Deployment Architecture
  9. Security & Authentication
  10. Environment Configuration

System Overview

Reflect is a comprehensive mental health counseling platform that combines real-time video therapy sessions with AI-powered analysis, mood tracking, journaling, and personalized questionnaires. The system is built on a distributed microservices architecture with four primary subsystems working in concert.

Technology Stack Summary

Component Technology Purpose
Frontend React 18.3.1, React Router 5.3.4 SPA with client-side routing
Database & BaaS Convex 1.29.3 Real-time database with serverless functions
Signaling Server Node.js, Express 4.17.1, Socket.IO 4.0.0 WebRTC coordination
AI Service Python, FastAPI, Google Gemini Content generation & analysis
Authentication Clerk 5.56.1 User authentication & identity management
Video/Audio WebRTC, SimplePeer 9.10.0, RecordRTC 5.6.2 P2P video calls & recording

Complete System Architecture

graph TB
    subgraph Client["Client Layer (React SPA)"]
        subgraph Auth_Layer["Authentication"]
            Clerk[Clerk Auth Provider]
        end
        
        subgraph UI_Components["UI Components"]
            Dashboard[Dashboard.jsx<br/>- Student: Mood orb, graph, sessions<br/>- Counselor: AI summary, student trends]
            VideoCall[VideoCall.jsx<br/>WebRTC P2P Connection]
            Sessions[Sessions.jsx<br/>Schedule & View Sessions]
            Clients[Clients.jsx<br/>Student Management]
            Questionnaires[Questionnaires.jsx<br/>Create & View Forms]
            StudentQ[StudentQuestionnaires.jsx<br/>Answer Forms]
            Journal[Journal.jsx<br/>Write & Share Entries]
            Tasks[Tasks.jsx<br/>View & Complete Todos]
            Settings[Settings.jsx<br/>Profile Management]
            SessionDetails[SessionDetails.jsx<br/>Session Analytics]
            AnalysisPanel[AnalysisPanel.jsx<br/>AI Insights Display]
        end
        
        subgraph State_Management["State & Routing"]
            Router[React Router]
            ConvexReact[Convex React Client<br/>Reactive Queries & Mutations]
            SocketIOClient[Socket.IO Client]
            WebRTCPeer[SimplePeer<br/>WebRTC Peer Connection]
        end
        
        Clerk --> UI_Components
        UI_Components --> Router
        UI_Components --> ConvexReact
        VideoCall --> SocketIOClient
        VideoCall --> WebRTCPeer
    end

    subgraph Convex_Layer["Convex Backend as a Service"]
        subgraph DB_Schema["Database Tables"]
            Users_Table[(users<br/>- tokenIdentifier<br/>- name, email<br/>- role: student/counselor<br/>- image)]
            
            Sessions_Table[(sessions<br/>- counselorId, studentId<br/>- scheduledAt<br/>- status<br/>- recordingUrl<br/>- analysisId)]
            
            Moods_Table[(moods<br/>- studentId<br/>- value: 1-10<br/>- note<br/>- _creationTime)]
            
            Journals_Table[(journals<br/>- studentId<br/>- title, content<br/>- isShared<br/>- date)]
            
            Questionnaires_Table[(questionnaires<br/>- sessionId<br/>- studentId, counselorId<br/>- type: pre/post_meeting<br/>- questions[]<br/>- status<br/>- isAIGenerated)]
            
            QResponses_Table[(questionnaire_responses<br/>- questionnaireId<br/>- studentId<br/>- responses[]<br/>- completedAt)]
            
            Analyses_Table[(analyses<br/>- sessionId<br/>- summary<br/>- keyTopics[]<br/>- sentiment<br/>- safetyHighlights[])]
            
            Todos_Table[(todos<br/>- studentId, counselorId<br/>- title, description<br/>- status<br/>- type<br/>- isAIGenerated)]
            
            Notes_Table[(counselor_notes<br/>- counselorId, studentId<br/>- content<br/>- type<br/>- sessionId)]
            
            Flags_Table[(client_flags<br/>- studentId, counselorId<br/>- riskLevel<br/>- isActive<br/>- tags[])]
            
            Attachments_Table[(attachments<br/>- uploaderId<br/>- sessionId<br/>- fileUrl, fileType)]
        end
        
        subgraph Convex_Functions["Convex Functions (TypeScript)"]
            subgraph User_Funcs["users.ts"]
                UserQ1[query: currentUser]
                UserQ2[query: getUser]
                UserQ3[query: listStudents]
                UserQ4[query: getStudentsForSummary]
                UserM1[mutation: storeUser]
                UserM2[mutation: updateUser]
                UserM3[mutation: updateRole]
            end
            
            subgraph Session_Funcs["sessions.ts"]
                SessionQ1[query: getMySessions]
                SessionQ2[query: getSession]
                SessionQ3[query: getSessionWithDetails]
                SessionM1[mutation: createSession]
                SessionM2[mutation: updateSessionStatus]
                SessionM3[mutation: updateSessionRecording]
            end
            
            subgraph Mood_Funcs["moods.ts"]
                MoodQ1[query: getMyMoodHistory]
                MoodQ2[query: getAllStudentMoods]
                MoodM1[mutation: logMood]
            end
            
            subgraph Journal_Funcs["journals.ts"]
                JournalQ1[query: getMyEntries]
                JournalQ2[query: getSharedEntries]
                JournalQ3[query: getAllSharedEntries]
                JournalM1[mutation: createEntry]
            end
            
            subgraph Questionnaire_Funcs["questionnaires.ts"]
                QuestQ1[query: getMyQuestionnaires]
                QuestQ2[query: getAllQuestionnaires]
                QuestQ3[query: getQuestionnaireById]
                QuestQ4[query: getQuestionnaireContext]
                QuestM1[mutation: createQuestionnaire]
                QuestM2[mutation: submitResponse]
                QuestM3[mutation: updateQuestionnaireStatus]
            end
            
            subgraph Todo_Funcs["todos.ts"]
                TodoQ1[query: getTodosForStudent]
                TodoM1[mutation: createTodo]
                TodoM2[mutation: updateTodoStatus]
            end
            
            subgraph Analysis_Funcs["analyses.ts"]
                AnalysisQ1[query: getAnalysisBySession]
                AnalysisM1[mutation: createAnalysis]
            end
            
            subgraph Notes_Funcs["counselor_notes.ts"]
                NotesQ1[query: getNotesForStudent]
                NotesQ2[query: getNotesForSession]
                NotesM1[mutation: addNote]
            end
            
            subgraph Flags_Funcs["client_flags.ts"]
                FlagsQ1[query: getClientFlag]
                FlagsM1[mutation: setClientFlag]
            end
        end
        
        Convex_Functions <--> DB_Schema
    end

    subgraph Signaling_Server["WebRTC Signaling Server (Node.js)"]
        Express[Express Server :5000]
        SocketIO[Socket.IO Server]
        
        subgraph Signaling_Logic["Connection Logic"]
            RoomMgr[Room Manager<br/>- session_{id} rooms<br/>- User status tracking]
            Events["Socket Events<br/>- join_session<br/>- callUser<br/>- answerCall<br/>- match_found"]
        end
        
        Express --> SocketIO
        SocketIO --> RoomMgr
        SocketIO --> Events
    end

    subgraph AI_Service["AI Analysis Service (Python/FastAPI)"]
        FastAPI[FastAPI Server :8000]
        
        subgraph AI_Endpoints["REST Endpoints"]
            Analyze[POST /analyze<br/>Video/Audio → AI Analysis]
            Summary[POST /counselor_summary<br/>Student Data → Daily Briefing]
            GenQ[POST /generate_questionnaire<br/>Context → Custom Questions]
        end
        
        subgraph AI_Core["AI Processing"]
            GeminiSDK[Google Generative AI SDK]
            VideoUpload[File Upload & Processing]
            PromptEngine[Advanced Prompt Engineering]
            JSONParser[Structured Output Parser]
        end
        
        FastAPI --> AI_Endpoints
        AI_Endpoints --> AI_Core
        AI_Core --> GeminiSDK
    end

    subgraph External_Services["External Services"]
        Gemini[Google Gemini API<br/>gemini-1.5-flash<br/>gemini-1.5-flash-8b]
        ClerkService[Clerk Authentication Service]
        WebRTC_STUN[STUN/TURN Servers<br/>Public ICE Servers]
    end

    %% Connections
    ConvexReact <-->|WebSocket| Convex_Functions
    SocketIOClient <-->|WebSocket| SocketIO
    WebRTCPeer <-.->|Signaling| SocketIO
    WebRTCPeer <-.->|Direct P2P| WebRTCPeer
    
    UI_Components -.->|HTTP POST| FastAPI
    Clerk <-->|OAuth/JWT| ClerkService
    
    GeminiSDK <-->|REST API| Gemini
    WebRTCPeer <-.->|ICE| WebRTC_STUN
    
    Convex_Functions -.->|Auth Verification| Clerk

    %% Styling
    classDef database fill:#e1f5ff,stroke:#01579b,stroke-width:2px
    classDef service fill:#fff3e0,stroke:#e65100,stroke-width:2px
    classDef external fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
    classDef client fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
    
    class Users_Table,Sessions_Table,Moods_Table,Journals_Table,Questionnaires_Table,QResponses_Table,Analyses_Table,Todos_Table,Notes_Table,Flags_Table,Attachments_Table database
    class FastAPI,Express,SocketIO service
    class Gemini,ClerkService,WebRTC_STUN external
    class Dashboard,VideoCall,Sessions client
Loading

Database Schema

Complete Schema Definitions

1. users Table

{
  name: string,
  email: string,
  role: "student" | "counselor",
  image?: string,
  tokenIdentifier?: string  // Clerk auth token
}

Indexes:

  • by_email: [email]
  • by_token: [tokenIdentifier]

Purpose: Central user registry with role-based access control


2. sessions Table

{
  counselorId: Id<"users">,
  studentId: Id<"users">,
  scheduledAt: number,  // Unix timestamp
  status: "scheduled" | "completed" | "cancelled",
  recordingUrl?: string,
  analysisId?: Id<"analyses">,
  studentReflections?: string,
  studentMoodRating?: number
}

Indexes:

  • by_counselor: [counselorId]
  • by_student: [studentId]
  • by_schedule: [scheduledAt]

Purpose: Manages therapy appointments and links to recordings/analysis


3. moods Table

{
  studentId: Id<"users">,
  value: number,  // 1-10 scale
  note?: string
}

Indexes:

  • by_student: [studentId]

Auto-generated:

  • _creationTime: number (timestamp)

Purpose: Daily mood check-ins with optional context notes


4. journals Table

{
  studentId: Id<"users">,
  title: string,
  content: string,
  isShared: boolean,  // Visible to counselor?
  date: string  // YYYY-MM-DD format
}

Indexes:

  • by_student: [studentId]
  • by_student_date: [studentId, date]

Purpose: Student journaling with privacy controls


5. questionnaires Table

{
  sessionId: Id<"sessions">,
  studentId: Id<"users">,
  counselorId: Id<"users">,
  type: "pre_meeting" | "post_meeting",
  questions: Array<{
    id: string,
    question: string,
    type: "text" | "scale" | "multiple_choice",
    options?: string[],
    scaleMin?: number,
    scaleMax?: number
  }>,
  isAIGenerated: boolean,
  counselorNotes?: string,
  status: "pending" | "completed" | "skipped",
  generatedAt: number
}

Indexes:

  • by_session: [sessionId]
  • by_student: [studentId]
  • by_student_status: [studentId, status]

Purpose: Pre/post-session questionnaires, optionally AI-generated


6. questionnaire_responses Table

{
  questionnaireId: Id<"questionnaires">,
  studentId: Id<"users">,
  responses: Array<{
    questionId: string,
    answer: string  // Stored as string, parsed by type
  }>,
  completedAt: number
}

Indexes:

  • by_questionnaire: [questionnaireId]
  • by_student: [studentId]

Purpose: Student answers to questionnaires


7. analyses Table

{
  sessionId: Id<"sessions">,
  summary: string,
  keyTopics: string[],
  sentiment: string,
  talkTimeRatio?: string,
  safetyHighlights?: string[],
  rawJson?: string  // Full AI response
}

Indexes:

  • by_session: [sessionId]

Purpose: AI-generated session analysis from video/audio


8. counselor_notes Table

{
  counselorId: Id<"users">,
  studentId: Id<"users">,
  content: string,
  type: "general" | "session_specific",
  sessionId?: Id<"sessions">
}

Indexes:

  • by_counselor_student: [counselorId, studentId]
  • by_session: [sessionId]

Purpose: Private counselor observations and case notes


9. client_flags Table

{
  studentId: Id<"users">,
  counselorId: Id<"users">,
  isActive: boolean,
  riskLevel: "none" | "low" | "high",
  tags: string[]  // e.g., ["anxiety", "crisis-support"]
}

Indexes:

  • by_student: [studentId]
  • by_counselor: [counselorId]

Purpose: Risk assessment and case management tags


10. todos Table

{
  studentId: Id<"users">,
  counselorId?: Id<"users">,
  title: string,
  description?: string,
  status: "pending" | "completed",
  dueDate?: number,
  type: string,  // e.g., "journaling", "breathing"
  isAIGenerated: boolean,
  studentWork?: string  // For writing assignments
}

Indexes:

  • by_student: [studentId]
  • by_student_status: [studentId, status]

Purpose: Therapeutic homework and self-care tasks


11. attachments Table

{
  uploaderId: Id<"users">,
  sessionId?: Id<"sessions">,
  fileUrl: string,
  fileType: string,
  fileName: string
}

Indexes:

  • by_session: [sessionId]

Purpose: File uploads (currently for session recordings)


Backend Services

1. Convex Backend Service

Base URL: Configured via VITE_CONVEX_URL environment variable

Complete Function Inventory

users.ts (7 functions)
Function Type Auth Required Purpose
currentUser() Query Yes Get authenticated user profile
getUser(id) Query No Fetch user by ID
listStudents() Query No Get all student users
getStudentsForSummary() Query Yes (Counselor) Fetch students with moods, journals, questionnaire responses for AI
storeUser() Mutation Yes Create/update user from auth token
updateUser(id, name?, image?) Mutation Yes (Self) Update profile
updateRole(id, role) Mutation Yes (Self) Change user role
sessions.ts (6 functions)
Function Type Auth Required Purpose
getMySessions() Query Yes Get sessions for current user (role-aware)
getSession(sessionId) Query No Fetch session by ID
getSessionWithDetails(sessionId) Query No Session + populated student & counselor
createSession(studentId, scheduledAt) Mutation Yes (Counselor) Schedule new session
updateSessionStatus(sessionId, status) Mutation No Change session status
updateSessionRecording(sessionId, recordingUrl) Mutation No Save recording URL and mark complete
moods.ts (3 functions)
Function Type Auth Required Purpose
getMyMoodHistory() Query Yes (Student) Last 30 mood entries
getAllStudentMoods() Query Yes (Counselor) Last 500 moods globally (for counselor dashboard)
logMood(value, note?) Mutation Yes (Student) Record mood check-in
journals.ts (4 functions)
Function Type Auth Required Purpose
getMyEntries() Query Yes All journal entries for current user
getSharedEntries(studentId) Query No Shared entries for specific student
getAllSharedEntries() Query Yes (Counselor) All shared entries across students
createEntry(title, content, isShared, date) Mutation Yes Create journal entry
questionnaires.ts (6 functions)
Function Type Auth Required Purpose
getMyQuestionnaires() Query Yes Questionnaires for current user (role-aware)
getAllQuestionnaires() Query Yes (Counselor) All questionnaires with responses
getQuestionnaireById(id) Query No Single questionnaire details
getQuestionnaireContext(studentId, sessionId) Query Yes (Counselor) Context for AI generation (past sessions, responses, notes)
createQuestionnaire(...) Mutation Yes (Counselor) Save new questionnaire
submitResponse(questionnaireId, responses) Mutation Yes (Student) Submit answers
updateQuestionnaireStatus(id, status) Mutation No Mark questionnaire status
todos.ts (3 functions)
Function Type Auth Required Purpose
getTodosForStudent(studentId?) Query Yes Get tasks (counselor specifies student, student gets own)
createTodo(...) Mutation Yes Create new task
updateTodoStatus(todoId, status, studentWork?) Mutation Yes Mark complete/submit work
analyses.ts (2 functions)
Function Type Auth Required Purpose
getAnalysisBySession(sessionId) Query No Get AI analysis for session
createAnalysis(...) Mutation No Store AI analysis results
counselor_notes.ts (3 functions)
Function Type Auth Required Purpose
getNotesForStudent(studentId) Query Yes (Counselor) All notes for specific student
getNotesForSession(sessionId) Query No Notes for specific session
addNote(studentId, content, type, sessionId?) Mutation Yes (Counselor) Add private note
client_flags.ts (2 functions)
Function Type Auth Required Purpose
getClientFlag(studentId) Query Yes (Counselor) Get flag status for student
setClientFlag(studentId, riskLevel, isActive, tags) Mutation Yes (Counselor) Create/update risk flag

2. WebRTC Signaling Server (Node.js)

Base URL: localhost:5000 (configurable via PORT env var)

Technology: Express 4.17.1 + Socket.IO 4.0.0

Socket.IO Events

Event Direction Payload Purpose
me Server → Client socketId: string Client receives their socket ID
join_session Client → Server { sessionId: string } Join video call room
session_full Server → Client (none) Room at capacity
match_found Server → Client { partnerId: string, initiator: boolean } Partner connected, start WebRTC
callUser Client → Server { userToCall, signalData, from, name } Forward WebRTC offer
callUser Server → Client { signal, from, name } Receive WebRTC offer
answerCall Client → Server { to, signal } Send WebRTC answer
callAccepted Server → Client signal Receive WebRTC answer
disconnect Auto (none) Clean up on disconnect
callEnded Server → Broadcast (none) Notify partner of disconnect

Room Management Logic

  1. Client calls join_session with sessionId
  2. Server creates/joins room session_{sessionId}
  3. If 2 users in room → emit match_found to both
  4. Clients exchange WebRTC signals via callUser/answerCall
  5. Direct P2P video connection established (server no longer involved in media)

3. AI Analysis Service (Python/FastAPI)

Base URL: localhost:8000 (configurable via PORT env var)

Technology: FastAPI, Google Generative AI SDK

Environment Variables

  • GEMINI_API_KEY: Google API key
  • GEMINI_MODEL: Model for video analysis (default: gemini-1.5-flash)
  • GEMINI_MODEL_SUMMARY: Model for summaries (default: gemini-1.5-flash-8b)

Complete API Endpoints

POST /analyze

Purpose: Analyze video/audio of therapy session

Request:

  • Content-Type: multipart/form-data
  • Body: file (video/audio file)

Response:

{
  "analysis": {
    "summary": "string",
    "verbal_cues": ["string"],
    "non_verbal_cues": ["string"],
    "key_topics": ["string"],
    "emotional_tone": "string",
    "moments_of_significance": [
      {
        "timestamp": "string",
        "description": "string",
        "emotional_context": "string"
      }
    ],
    "counselor_student_dynamics": "string",
    "safety_and_ethical_flags": ["string"],
    "recommendations_for_followup": ["string"]
  }
}

Process Flow:

  1. Upload file to Gemini
  2. Wait for processing
  3. Send comprehensive prompt analyzing verbal + non-verbal cues
  4. Parse structured JSON response
  5. Return to client

Prompt Features:

  • Analyzes vocal tone, pitch, pace, hesitations
  • Evaluates facial expressions, gestures, posture
  • Identifies interaction patterns
  • Detects safety/ethical concerns
  • Provides actionable recommendations

POST /counselor_summary

Purpose: Generate daily briefing for counselor dashboard

Request:

{
  "students": [
    {
      "id": "string",
      "name": "string",
      "recent_moods": [number],
      "journal_entries": ["string"],
      "questionnaire_responses": [
        {
          "type": "pre_meeting" | "post_meeting",
          "date": "string",
          "questions_and_answers": [
            {
              "question": "string",
              "answer": "string",
              "type": "text" | "scale" | "multiple_choice"
            }
          ]
        }
      ]
    }
  ]
}

Response:

{
  "summary": {
    "overall_alert_level": "Low" | "Medium" | "High",
    "critical_alerts": ["string"],
    "student_summaries": [
      {
        "name": "string",
        "status": "string",
        "summary": "string"
      }
    ]
  }
}

AI Analysis Includes:

  1. Mood trend analysis
  2. Journal sentiment analysis
  3. Questionnaire response patterns
  4. Red flag detection
  5. Positive highlights
  6. Masking behavior detection (recent addition)

POST /generate_questionnaire

Purpose: AI-generated personalized questions for students

Request:

{
  "student_id": "string",
  "session_id": "string",
  "type": "pre_meeting" | "post_meeting",
  "counselor_notes": "string (optional)",
  "context": {
    "recent_sessions": [
      {
        "date": "string",
        "status": "string"
      }
    ],
    "analyses": [
      {
        "summary": "string",
        "key_topics": ["string"],
        "sentiment": "string"
      }
    ],
    "previous_responses": [
      {
        "question": "string",
        "answer": "string",
        "date": "string"
      }
    ],
    "counselor_notes_list": [
      {
        "content": "string",
        "date": "string"
      }
    ]
  }
}

Response:

{
  "questions": [
    {
      "id": "string (uuid)",
      "question": "string",
      "type": "text" | "scale" | "multiple_choice",
      "options": ["string"] (if multiple_choice),
      "scaleMin": number (if scale),
      "scaleMax": number (if scale)
    }
  ]
}

Generation Logic:

  • Analyzes previous session topics
  • Considers counselor priorities from notes
  • Reviews past questionnaire patterns
  • Creates 5-7 contextually relevant questions
  • Mixes question types for engagement

Frontend Architecture

Component Tree

App.jsx
  ├── ClerkProvider (Authentication Wrapper)
  │   └── ConvexProviderWithClerk (Data Layer)
  │       └── Router (react-router-dom v5)
  │           ├── /login → Login.jsx
  │           ├── /dashboard → Dashboard.jsx
  │           │   ├── Student View
  │           │   │   ├── Mood Orb (Interactive Slider)
  │           │   │   ├── MoodLineGraph (SVG + HTML markers)
  │           │   │   └── Next Session Card
  │           │   └── Counselor View
  │           │       ├── AI Daily Briefing
  │           │       ├── Next Session Card
  │           │       └── Student Mood Trends Grid
  │           ├── /sessions → Sessions.jsx
  │           │   ├── Schedule New Session (Counselor)
  │           │   ├── Session List with Status
  │           │   └── Recording Upload
  │           ├── /session/:id → SessionDetails.jsx
  │           │   ├── Session Info
  │           │   ├── AnalysisPanel.jsx (If analysis exists)
  │           │   └── Recording Playback
  │           ├── /clients → Clients.jsx (Counselor Only)
  │           │   ├── Student Cards
  │           │   ├── Risk Flags Management
  │           │   ├── Notes Section
  │           │   └── Shared Journals View
  │           ├── /questionnaires → Questionnaires.jsx (Counselor)
  │           │   ├── Create Tab
  │           │   │   ├── Student Selector
  │           │   │   ├── Manual Questions Builder
  │           │   │   └── AI Generation Button
  │           │   └── View All Tab
  │           │       ├── Student Filter
  │           │       └── Response Cards
  │           ├── /student-questionnaires → StudentQuestionnaires.jsx
  │           │   ├── Pending Questionnaires
  │           │   └── Completed History
  │           ├── /journal → Journal.jsx
  │           │   ├── Calendar View
  │           │   ├── Entry Editor
  │           │   └── Share Toggle
  │           ├── /tasks → Tasks.jsx
  │           │   ├── Pending Tasks
  │           │   ├── Completed Tasks
  │           │   └── Task Submission
  │           ├── /video-call/:sessionId → VideoCall.jsx
  │           │   ├── VideoPlayer.jsx (Local + Remote Streams)
  │           │   ├── Controls.jsx (Mute, Camera, Hang Up)
  │           │   └── RecordRTC Integration
  │           └── /settings → Settings.jsx
  │               └── Profile Management

State Management Strategy

Reactive Data (Convex Queries):

  • All database reads use Convex useQuery hooks
  • Auto-updates on database changes (WebSocket subscriptions)
  • Example: const moods = useQuery(api.moods.getMyMoodHistory);

Mutations:

  • Use Convex useMutation hooks
  • Example: const logMood = useMutation(api.moods.logMood);

Local State (React useState):

  • Form inputs
  • UI toggles
  • Temporary selections

No Global State Library:

  • Clerk handles auth state
  • Convex handles data state
  • React Router handles routing state
  • Component-level state for UI

Data Flow Diagrams

Flow 1: Mood Logging

sequenceDiagram
    participant Student
    participant Dashboard
    participant ConvexClient
    participant ConvexBackend
    participant DB

    Student->>Dashboard: Drags Empathy Orb
    Dashboard->>Dashboard: Update local state (1-10)
    Student->>Dashboard: Clicks "Add Context" (Optional)
    Dashboard->>Dashboard: Show textarea
    Student->>Dashboard: Enters note & submits
    Dashboard->>ConvexClient: logMood(value, note)
    ConvexClient->>ConvexBackend: mutation: moods.logMood
    ConvexBackend->>ConvexBackend: Verify auth
    ConvexBackend->>DB: Insert mood entry
    DB-->>ConvexBackend: Confirm
    ConvexBackend-->>ConvexClient: Success
    ConvexClient-->>Dashboard: Re-fetch mood history (reactive)
    Dashboard->>Dashboard: Update MoodLineGraph
Loading

Flow 2: AI Questionnaire Generation

sequenceDiagram
    participant Counselor
    participant Questionnaires
    participant ConvexClient
    participant ConvexBackend
    participant AIService
    participant Gemini

    Counselor->>Questionnaires: Select student & session
    Counselor->>Questionnaires: Add counselor notes
    Counselor->>Questionnaires: Click "Generate Questions"
    Questionnaires->>ConvexClient: getQuestionnaireContext(studentId, sessionId)
    ConvexClient->>ConvexBackend: query
    ConvexBackend->>ConvexBackend: Fetch sessions, analyses, responses, notes
    ConvexBackend-->>ConvexClient: Return context object
    Questionnaires->>AIService: POST /generate_questionnaire
    AIService->>Gemini: Send prompt with context
    Gemini-->>AIService: Return questions JSON
    AIService-->>Questionnaires: Return parsed questions
    Questionnaires->>Questionnaires: Display questions for review
    Counselor->>Questionnaires: Click "Save Questionnaire"
    Questionnaires->>ConvexClient: createQuestionnaire(...)
    ConvexClient->>ConvexBackend: mutation
    ConvexBackend->>ConvexBackend: Insert questionnaire
    ConvexBackend-->>Questionnaires: Success
Loading

Flow 3: Video Session Analysis

sequenceDiagram
    participant Student
    participant Counselor
    participant VideoCall
    participant SignalingServer
    participant WebRTC
    participant Sessions
    participant AIService
    participant Gemini
    participant ConvexBackend

    Student->>VideoCall: Join session
    Counselor->>VideoCall: Join session
    VideoCall->>SignalingServer: join_session(sessionId)
    SignalingServer-->>VideoCall: match_found
    VideoCall->>WebRTC: Exchange SDP/ICE
    WebRTC-->>VideoCall: P2P connection established
    Note over Student,Counselor: Session in progress
    VideoCall->>VideoCall: RecordRTC recording
    Counselor->>VideoCall: End call
    VideoCall->>VideoCall: Stop recording
    VideoCall->>Sessions: Upload recording blob
    Sessions->>ConvexBackend: updateSessionRecording(url)
    Sessions->>AIService: POST /analyze (video file)
    AIService->>Gemini: Upload & analyze
    Gemini-->>AIService: Analysis JSON
    AIService-->>Sessions: Return analysis
    Sessions->>ConvexBackend: createAnalysis(...)
    ConvexBackend->>ConvexBackend: Link analysis to session
Loading

Flow 4: Counselor Daily Summary

sequenceDiagram
    participant Counselor
    participant Dashboard
    participant ConvexClient
    participant ConvexBackend
    participant AIService
    participant Gemini

    Counselor->>Dashboard: Open dashboard
    Dashboard->>ConvexClient: getStudentsForSummary()
    ConvexClient->>ConvexBackend: query
    ConvexBackend->>ConvexBackend: Fetch all students
    loop For each student
        ConvexBackend->>ConvexBackend: Get recent moods (5)
        ConvexBackend->>ConvexBackend: Get shared journals (3)
        ConvexBackend->>ConvexBackend: Get completed questionnaires (3)
        ConvexBackend->>ConvexBackend: Get questionnaire responses
    end
    ConvexBackend-->>ConvexClient: Return aggregated data
    Dashboard->>AIService: POST /counselor_summary (students data)
    AIService->>Gemini: Send comprehensive prompt
    Gemini-->>AIService: Summary JSON
    AIService-->>Dashboard: Return parsed summary
    Dashboard->>Dashboard: Display alert level, critical alerts, student summaries
Loading

Deployment Architecture

systemd Service Configuration

Service File: /etc/systemd/system/reflect-app.service

[Unit]
Description=Reflect KSU Hackathon App
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/ksu-hackathon
ExecStart=/home/ubuntu/ksu-hackathon/run_all.sh
Restart=on-failure
Environment="PATH=/usr/bin:/usr/local/bin"

[Install]
WantedBy=multi-user.target

Startup Script (run_all.sh)

#!/bin/bash

# -------------------------------------------------
# This script starts all 4 services required:
# 1. Convex dev server (WebSocket + DB)
# 2. Node.js signaling server (WebRTC)
# 3. Python AI server (FastAPI)
# 4. React dev server (Frontend)
# -------------------------------------------------

# Start Convex dev server
cd /home/ubuntu/ksu-hackathon/client && npx convex dev &
# Wait for Convex to generate API files
sleep 10

# Start Node backend
cd /home/ubuntu/ksu-hackathon && node index.js &

# Start Python AI server
cd /home/ubuntu/ksu-hackathon && source venv/bin/activate && python ai_server.py &

# Start React client
cd /home/ubuntu/ksu-hackathon/client && npm start &

# Wait for all background processes
wait

Race Condition Fix: 10-second sleep ensures Convex generates _generated/api.ts before React tries to import it.

Port Allocation

Service Port Protocol Purpose
React Dev Server 3000 HTTP Frontend SPA
Node Signaling Server 5000 HTTP/WebSocket Socket.IO
Python AI Service 8000 HTTP FastAPI REST
Convex Dynamic WebSocket Managed by Convex CLI

Process Management

Start Service:

sudo systemctl start reflect-app.service

Stop Service:

sudo systemctl stop reflect-app.service

Restart Service:

sudo systemctl restart reflect-app.service
# Or use the helper script
./restart.sh

View Logs:

sudo journalctl -u reflect-app.service -f
# Or use the helper script
./checkLogs.sh

Check Status:

sudo systemctl status reflect-app.service

Security & Authentication

Authentication Flow (Clerk)

sequenceDiagram
    participant User
    participant Browser
    participant Clerk
    participant React
    participant Convex

    User->>Browser: Navigate to app
    Browser->>React: Load SPA
    React->>Clerk: Check auth state
    alt Not authenticated
        Clerk-->>React: Redirect to login
        React->>User: Show Login.jsx
        User->>Clerk: Sign in (OAuth/Password)
        Clerk-->>User: JWT token
    end
    Clerk-->>React: User object + token
    React->>Convex: Connect with JWT
    Convex->>Convex: Verify token signature
    Convex->>Convex: Query users by tokenIdentifier
    alt User not in DB
        Convex->>Convex: Auto-create via storeUser()
    end
    Convex-->>React: User authenticated
    React->>User: Show Dashboard
Loading

Authorization Patterns

Role-Based Access Control (RBAC):

Feature Student Counselor
Log Moods
View Own Mood History
View All Student Moods
Create Journal Entry
Share Journal
View Shared Journals
Create Session
Answer Questionnaire
Create Questionnaire
View AI Summary
Add Counselor Notes
Set Client Flags
Assign Tasks
Complete Tasks

Implementation:

  • Convex functions check user.role before operations
  • React components conditionally render based on role
  • Clerk tokens verified on every Convex request

Data Privacy

Private Data:

  • Counselor notes (never shown to students)
  • Non-shared journal entries
  • Client flags and risk assessments
  • Session analyses (counselor-only access)

Shared Data:

  • Journal entries with isShared: true
  • Completed questionnaire responses
  • Session metadata (schedule, status)

Encryption:

  • HTTPS in production (see HTTPS_SETUP.md)
  • Clerk handles auth token encryption
  • Database encryption at rest (managed by Convex)

Environment Configuration

Root .env

# Gemini API Configuration
GEMINI_API_KEY=your_google_api_key_here
GEMINI_MODEL=gemini-1.5-flash
GEMINI_MODEL_SUMMARY=gemini-1.5-flash-8b

Client .env.local (gitignored)

# Convex Backend
VITE_CONVEX_URL=https://your-deployment.convex.cloud

# Clerk Authentication
VITE_CLERK_PUBLISHABLE_KEY=pk_test_...

# Node.js Signaling Server
REACT_APP_SIGNALING_SERVER=http://localhost:5000

Python Virtual Environment

# Install dependencies
python -m venv venv
source venv/bin/activate  # or venv\Scripts\activate on Windows
pip install -r requirements.txt

requirements.txt:

fastapi
uvicorn
google-generativeai
python-dotenv

Node.js Dependencies

Root package.json:

{
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "socket.io": "^4.0.0"
  }
}

Client package.json:

{
  "dependencies": {
    "@clerk/clerk-react": "^5.56.1",
    "convex": "^1.29.3",
    "react": "^18.3.1",
    "react-router-dom": "^5.3.4",
    "simple-peer": "^9.10.0",
    "socket.io-client": "^4.0.0",
    "recordrtc": "^5.6.2"
  }
}

Additional Technical Details

WebRTC Configuration

STUN Servers Used:

const configuration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'stun:stun1.l.google.com:19302' }
  ]
};

SimplePeer Options:

const peer = new SimplePeer({
  initiator: isInitiator,
  stream: localStream,
  config: configuration,
  trickle: true
});

Recording Configuration (RecordRTC)

const recorder = new RecordRTC(stream, {
  type: 'video',
  mimeType: 'video/webm',
  timeSlice: 1000,
  ondataavailable: (blob) => {
    // Handle recorded chunks
  }
});

Convex Deployment

Development:

cd client
npx convex dev

Production:

cd client
npx convex deploy

Git Repository Structure

ksu-hackathon/
├── client/                 # React frontend
│   ├── src/
│   │   ├── components/     # React components
│   │   ├── convex/         # Convex schema & functions
│   │   ├── App.jsx
│   │   └── index.jsx
│   ├── public/
│   └── package.json
├── analyses/               # Uploaded analysis files
├── Documentation/          # Additional docs
├── .env                    # Server env vars (gitignored)
├── ai_server.py           # Python FastAPI service
├── index.js               # Node.js signaling server
├── run_all.sh             # Startup script
├── restart.sh             # Helper script
├── checkLogs.sh           # Helper script
├── requirements.txt       # Python dependencies
├── package.json           # Node.js dependencies
└── README.md

Performance Considerations

Database Query Optimization

Indexes Usage:

  • All queries use appropriate indexes
  • Compound indexes for multi-field queries (e.g., by_student_status)
  • Convex auto-optimizes query plans

Query Limits:

  • Mood history: Last 30 entries (student), Last 500 (counselor)
  • Journal entries: Last 3 shared entries for AI summary
  • Sessions: Ordered by date, no pagination (future improvement)

Real-time Subscriptions

Convex WebSocket:

  • Queries are reactive by default
  • Client auto-updates on any database change
  • Minimal network overhead (delta updates)

Socket.IO:

  • Event-driven, not polling
  • Only active during video calls
  • Automatic reconnection handling

AI Service Optimization

Gemini API:

  • Uses gemini-1.5-flash-8b for summaries (faster, cheaper)
  • Uses gemini-1.5-flash for complex analysis
  • File uploads cached server-side until processing complete

Rate Limiting:

  • Not currently implemented (future improvement)
  • Recommended: Queue system for analysis requests

Future Enhancements

Planned Features

  1. Pagination for large datasets (sessions, journals)
  2. Real-time Notifications (new questionnaire, upcoming session)
  3. Video Transcription (Gemini supports this, not yet implemented)
  4. Analytics Dashboard (trends over time, cohort analysis)
  5. Mobile App (React Native using same Convex backend)
  6. Multi-language Support (i18n for questionnaires & UI)
  7. Advanced Risk Scoring (ML model for crisis prediction)
  8. Calendar Integration (Google Calendar sync for sessions)

Known Limitations

  1. Convex Login Prompt: systemd service prompts for device name (currently fails silently, but doesn't block operation)
  2. No Production Build: Currently running React dev server in production
  3. No CDN: Static assets served directly from dev server
  4. Single Deployment: No load balancing or horizontal scaling
  5. No Backup Strategy: Convex handles backups, but no independent backup
  6. Limited Error Handling: AI service doesn't handle quota exhaustion gracefully

Troubleshooting Guide

Common Issues

Issue: Convex queries return undefined

  • Cause: Convex dev server not running or API not generated
  • Solution: Check client/src/convex/_generated/ exists, restart service

Issue: Video call doesn't connect

  • Cause: STUN server unreachable or firewall blocking WebRTC
  • Solution: Check browser console for ICE errors, test STUN connectivity

Issue: AI analysis fails with 429 error

  • Cause: Gemini API quota exceeded
  • Solution: Check API key quota in Google Cloud Console, upgrade plan

Issue: Permission denied errors in logs

  • Cause: File ownership incorrect
  • Solution: sudo chown -R ubuntu:ubuntu /home/ubuntu/ksu-hackathon

Issue: React app shows "Module not found: convex/react"

  • Cause: Dependencies not installed or wrong directory
  • Solution: cd client && npm install

Summary

This architecture provides a robust, scalable foundation for a mental health counseling platform. The separation of concerns between database (Convex), signaling (Node.js), AI (Python), and UI (React) allows each service to be developed, tested, and scaled independently.

Key strengths:

  • Real-time Everything: Convex's reactive queries provide instant UI updates
  • AI-First: Gemini integration for insights, not just data storage
  • Privacy-Focused: Fine-grained control over data sharing
  • Extensible: Adding new features is straightforward due to modular design

Total Lines of Code (Estimated):

  • Frontend: ~15,000 lines
  • Convex Functions: ~2,500 lines
  • Backend Services: ~1,000 lines
  • Total: ~18,500 lines

For questions or contributions, refer to the development team or GitHub repository.