- System Overview
- Complete System Architecture
- Database Schema
- Backend Services
- Frontend Architecture
- API Documentation
- Data Flow Diagrams
- Deployment Architecture
- Security & Authentication
- Environment Configuration
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.
| 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 |
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
{
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
{
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
{
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
{
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
{
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
{
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
{
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
{
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
{
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
{
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
{
uploaderId: Id<"users">,
sessionId?: Id<"sessions">,
fileUrl: string,
fileType: string,
fileName: string
}Indexes:
by_session: [sessionId]
Purpose: File uploads (currently for session recordings)
Base URL: Configured via VITE_CONVEX_URL environment variable
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Function | Type | Auth Required | Purpose |
|---|---|---|---|
getAnalysisBySession(sessionId) |
Query | No | Get AI analysis for session |
createAnalysis(...) |
Mutation | No | Store AI analysis results |
| 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 |
| 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 |
Base URL: localhost:5000 (configurable via PORT env var)
Technology: Express 4.17.1 + Socket.IO 4.0.0
| 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 |
- Client calls
join_sessionwithsessionId - Server creates/joins room
session_{sessionId} - If 2 users in room → emit
match_foundto both - Clients exchange WebRTC signals via
callUser/answerCall - Direct P2P video connection established (server no longer involved in media)
Base URL: localhost:8000 (configurable via PORT env var)
Technology: FastAPI, Google Generative AI SDK
GEMINI_API_KEY: Google API keyGEMINI_MODEL: Model for video analysis (default:gemini-1.5-flash)GEMINI_MODEL_SUMMARY: Model for summaries (default:gemini-1.5-flash-8b)
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:
- Upload file to Gemini
- Wait for processing
- Send comprehensive prompt analyzing verbal + non-verbal cues
- Parse structured JSON response
- 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
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:
- Mood trend analysis
- Journal sentiment analysis
- Questionnaire response patterns
- Red flag detection
- Positive highlights
- Masking behavior detection (recent addition)
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
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
Reactive Data (Convex Queries):
- All database reads use Convex
useQueryhooks - Auto-updates on database changes (WebSocket subscriptions)
- Example:
const moods = useQuery(api.moods.getMyMoodHistory);
Mutations:
- Use Convex
useMutationhooks - 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
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
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
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
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
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#!/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
waitRace Condition Fix: 10-second sleep ensures Convex generates _generated/api.ts before React tries to import it.
| 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 |
Start Service:
sudo systemctl start reflect-app.serviceStop Service:
sudo systemctl stop reflect-app.serviceRestart Service:
sudo systemctl restart reflect-app.service
# Or use the helper script
./restart.shView Logs:
sudo journalctl -u reflect-app.service -f
# Or use the helper script
./checkLogs.shCheck Status:
sudo systemctl status reflect-app.servicesequenceDiagram
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
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.rolebefore operations - React components conditionally render based on role
- Clerk tokens verified on every Convex request
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)
# 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# 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# Install dependencies
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -r requirements.txtrequirements.txt:
fastapi
uvicorn
google-generativeai
python-dotenv
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"
}
}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
});const recorder = new RecordRTC(stream, {
type: 'video',
mimeType: 'video/webm',
timeSlice: 1000,
ondataavailable: (blob) => {
// Handle recorded chunks
}
});Development:
cd client
npx convex devProduction:
cd client
npx convex deployksu-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
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)
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
Gemini API:
- Uses
gemini-1.5-flash-8bfor summaries (faster, cheaper) - Uses
gemini-1.5-flashfor complex analysis - File uploads cached server-side until processing complete
Rate Limiting:
- Not currently implemented (future improvement)
- Recommended: Queue system for analysis requests
- Pagination for large datasets (sessions, journals)
- Real-time Notifications (new questionnaire, upcoming session)
- Video Transcription (Gemini supports this, not yet implemented)
- Analytics Dashboard (trends over time, cohort analysis)
- Mobile App (React Native using same Convex backend)
- Multi-language Support (i18n for questionnaires & UI)
- Advanced Risk Scoring (ML model for crisis prediction)
- Calendar Integration (Google Calendar sync for sessions)
- Convex Login Prompt: systemd service prompts for device name (currently fails silently, but doesn't block operation)
- No Production Build: Currently running React dev server in production
- No CDN: Static assets served directly from dev server
- Single Deployment: No load balancing or horizontal scaling
- No Backup Strategy: Convex handles backups, but no independent backup
- Limited Error Handling: AI service doesn't handle quota exhaustion gracefully
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
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.