Spaces:
Sleeping
Sleeping
| # Quran App β Full System Architecture | |
| --- | |
| ## 1. System Overview | |
| Quran App is a full-stack web platform that connects students with certified Quran teachers (Sheikhs) for live online recitation sessions. It also provides AI-powered solo practice where students record their recitation and receive automated evaluation. | |
| The system has three user roles: | |
| - **Student** β finds a sheikh, books sessions, practices solo, tracks progress | |
| - **Sheikh** β manages availability, accepts session requests, conducts live sessions, reviews students | |
| - **Admin** β approves sheikh applications, manages users, monitors the platform | |
| ### High-Level System Diagram | |
| ``` | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β Browser (Client) β | |
| β β | |
| β React 19 + TypeScript + Vite β | |
| β Tailwind CSS + Radix UI (shadcn) β | |
| β React Router v7 Β· i18next (AR/EN) Β· Stripe.js β | |
| β STOMP/SockJS Client Β· WebRTC (RTCPeerConnection) β | |
| βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββ | |
| β HTTPS REST + WebSocket (WS) | |
| βΌ | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β Spring Boot 4 Backend β | |
| β (Java 21) β | |
| β β | |
| β REST API (port 8080) β | |
| β Spring Security + JWT + Google OAuth2 β | |
| β Spring Data JPA Β· WebSocket (STOMP broker) β | |
| β JavaMail Β· Stripe SDK Β· JAVE2 (audio) β | |
| βββββββββββββββ¬βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ | |
| β β | |
| βΌ βΌ | |
| βββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββ | |
| β PostgreSQL DB β β External Services β | |
| β β β β | |
| β Users, Sessions, β β β AI Service (ngrok) β recitation eval β | |
| β Payments, β β β Stripe β payments & payouts β | |
| β Recitations, β β β Google OAuth2 β social login β | |
| β Reviews, β β β Gmail SMTP β email / OTP β | |
| β Availability, β β β | |
| β Streaks β ββββββββββββββββββββββββββββββββββββββββββββ | |
| βββββββββββββββββββββββ | |
| ``` | |
| --- | |
| ## 2. Technology Stack | |
| ### Frontend | |
| | Concern | Technology | | |
| |---|---| | |
| | Framework | React 19 + TypeScript | | |
| | Build tool | Vite 7 | | |
| | Styling | Tailwind CSS + Radix UI (shadcn components) | | |
| | Routing | React Router v7 | | |
| | Internationalization | i18next + react-i18next (Arabic / English) | | |
| | Real-time | STOMP/SockJS (@stomp/stompjs + sockjs-client) | | |
| | Video calls | WebRTC (RTCPeerConnection, browser native) | | |
| | Payments | @stripe/react-stripe-js + @stripe/stripe-js | | |
| | Notifications | Sonner + react-hot-toast | | |
| | Icons | Lucide React | | |
| | Date picker | react-day-picker | | |
| ### Backend | |
| | Concern | Technology | | |
| |---|---| | |
| | Framework | Spring Boot 4.0 (Java 21) | | |
| | Security | Spring Security + JWT (jjwt 0.12.5) + OAuth2 Client | | |
| | Database ORM | Spring Data JPA + Hibernate | | |
| | Database | PostgreSQL | | |
| | WebSocket | Spring WebSocket + STOMP (in-memory broker) | | |
| | Email | Spring Mail (Gmail SMTP, STARTTLS) | | |
| | Payments | Stripe Java SDK 25.3 | | |
| | Audio processing | JAVE2 3.5 (ffmpeg wrapper for Linux) | | |
| | Google Auth | Google API Client 2.6 | | |
| | Monitoring | Spring Boot Actuator | | |
| | Boilerplate | Lombok | | |
| --- | |
| ## 3. User Roles & Capabilities | |
| ### Student | |
| - Register/login (email or Google) | |
| - Complete profile (Quran level, language preference, accent) | |
| - Browse and search Sheikhs by rating, price, availability | |
| - Book 1-hour sessions with a Sheikh | |
| - Pay for sessions via Stripe (credit card) | |
| - Join live video sessions (WebRTC) | |
| - Record solo recitations and get AI-powered evaluation | |
| - View evaluation reports (word accuracy, tashkeel, mistakes) | |
| - Track daily streak and practice statistics | |
| - Write reviews after completed sessions | |
| ### Sheikh | |
| - Register/login (email or Google) | |
| - Complete profile (bio, specialization, years of experience, hourly rate) | |
| - Submit to admin approval process (including interview scheduling) | |
| - Set weekly availability (recurring time slots per day) | |
| - Receive and manage session requests (accept / reject) | |
| - Conduct live video sessions | |
| - Write session notes for students | |
| - Receive payouts via Stripe Connect after session completion | |
| - View their students and session history | |
| ### Admin | |
| - Approve or reject sheikh applications | |
| - Schedule interview sessions with pending sheikhs | |
| - Conduct video interview sessions | |
| - Manage all users (activate / deactivate accounts) | |
| - View full user profiles and session history | |
| - Access admin dashboard with platform statistics | |
| --- | |
| ## 4. Frontend Architecture | |
| ### Application Entry Point | |
| The app boots from `main.tsx` β `App.tsx`. The provider stack wraps the entire app: | |
| ``` | |
| BrowserRouter | |
| βββ LanguageProvider (i18next context) | |
| βββ AuthProvider (user state, JWT management) | |
| βββ StudentProvider (student-specific data) | |
| βββ Routes | |
| ``` | |
| ### Route Structure | |
| Routes are split into three groups: | |
| **Public (no auth required)** | |
| ``` | |
| / Landing page | |
| /signin Email login | |
| /register Registration | |
| /forgot-password OTP request | |
| /verify-otp OTP verification | |
| /reset-password New password | |
| /auth/google Google OAuth callback | |
| /complete-profile Role selection after register | |
| /complete-profile/student Student profile wizard | |
| /complete-profile/sheikh Sheikh profile wizard | |
| /sheikh/interview Interview scheduling (pending sheikh) | |
| ``` | |
| **Protected β Student** | |
| ``` | |
| /student/dashboard Dashboard with stats, streak, recent activity | |
| /student/practice AI-powered recitation practice | |
| /student/find-sheikh Sheikh search/browse | |
| /student/sheikh/:id Sheikh profile + booking | |
| /student/sessions Session list | |
| /student/evaluations/:id AI evaluation reports | |
| /waiting-room/:id Pre-session waiting room | |
| /session/:sessionId Live video session | |
| /session-report/:id Post-session report + review | |
| /session-waiting/:id Post-session waiting (before report) | |
| ``` | |
| **Protected β Sheikh** | |
| ``` | |
| /sheikh/dashboard Dashboard with stats, pending requests | |
| /sheikh/sessions Session management | |
| /sheikh/students Student list | |
| ``` | |
| **Protected β Admin** | |
| ``` | |
| /admin/dashboard Platform statistics | |
| /admin/sheikh-approval Sheikh approval queue | |
| /admin/users All users management | |
| /admin/user/:userId User detail view | |
| /admin/interview/:id Live interview session | |
| ``` | |
| **Common** | |
| ``` | |
| /profile Edit own profile | |
| ``` | |
| ### Route Guard Logic | |
| `ProtectedRoute` checks three conditions in order: | |
| 1. Loading state β show spinner | |
| 2. No user β redirect to `/signin` | |
| 3. Profile not completed β redirect to `/complete-profile` (except admin, who skips this) | |
| ### State Management | |
| No Redux. State lives in three React contexts: | |
| | Context | Responsibility | | |
| |---|---| | |
| | `AuthContext` | User identity, JWT token, login/logout/register, session restore on load | | |
| | `LanguageContext` | Current language (AR/EN), direction (RTL/LTR) | | |
| | `StudentContext` | Student-specific data (streak, stats, etc.) | | |
| Auth token is stored in `localStorage` under key `authToken`. User data is stored under `currentUser`. On app load, `AuthContext` re-validates the token by calling `GET /api/auth/verify-profile`. | |
| ### Frontend Module Map | |
| ``` | |
| src/ | |
| βββ App.tsx Route definitions + providers | |
| βββ main.tsx App entry + Google OAuth provider | |
| βββ components/ | |
| β βββ auth/ Login, Register, OTP, Profile wizards | |
| β βββ dashboards/ StudentDashboard, SheikhDashboard, AdminDashboard | |
| β βββ sessions/ LiveSession, WaitingRoom, MySessions, SessionReport | |
| β βββ sheikh/ FindSheikh, SheikhProfile, MyStudents, UpdatePricing | |
| β βββ practice/ RecordingPractice, EvaluationReports | |
| β βββ profile/ ProfilePage | |
| β βββ admin/ SheikhApproval, UserManagement, InterviewSession | |
| β βββ ui/ Reusable shadcn/Radix UI components (50+ components) | |
| β βββ Navbar.tsx Top navigation bar | |
| β βββ RoleSwitcher.tsx Dev tool for switching roles | |
| βββ hooks/ | |
| β βββ useWebRTC.ts WebRTC + STOMP signaling hook | |
| βββ lib/ | |
| β βββ authContext.tsx Auth state and API calls | |
| β βββ languageContext.tsx i18n context | |
| β βββ StudentContext.tsx Student data context | |
| β βββ api/ All REST API modules (one file per domain) | |
| β βββ aiApi.ts AI analysis service calls | |
| β βββ bookingApi.ts Availability, booking, sessions | |
| β βββ paymentApi.ts Stripe payment flow | |
| β βββ studentApi.ts Student data | |
| β βββ sheikhApi.ts Sheikh data | |
| β βββ adminApi.ts Admin operations | |
| β βββ ... | |
| βββ types/ | |
| β βββ index.ts Domain types (User, Session, Payment, etc.) | |
| β βββ api.ts API helper types (PaginatedResponse, AppError, etc.) | |
| βββ styles/ | |
| βββ globals.css | |
| ``` | |
| --- | |
| ## 5. Backend Architecture | |
| ### Package Structure | |
| The backend is organized by domain slice, not by layer. Each domain has its own controller, service, repository, DTO, and mapper. | |
| ``` | |
| com.yourcompany.quranapp.quran_app_backend/ | |
| βββ config/ | |
| β βββ SecurityConfig.java CORS, JWT filter chain, role rules | |
| β βββ GlobalExceptionHandler.java Centralized error responses | |
| βββ security/ | |
| β βββ JwtAuthenticationFilter.java Validates Bearer token on every request | |
| β βββ CustomUserDetails.java Wraps User entity, exposes userId + role | |
| β βββ UserDetailsServiceImpl.java Loads user by email from DB | |
| βββ entity/ JPA entities (data model) | |
| βββ dashboard/ | |
| β βββ common/ Auth (login, register, OTP, Google OAuth) | |
| β βββ student/ Student APIs (sessions, recitations, profile) | |
| β βββ sheikh/ Sheikh APIs (sessions, availability, notes) | |
| β βββ admin/ Admin APIs (approvals, user management) | |
| βββ payment/ Stripe payment controller + service | |
| βββ websocket/ WebSocket config + signaling controller | |
| βββ google/ Google token verification | |
| βββ setup/ | |
| βββ DataLoader.java Seed data on startup | |
| ``` | |
| ### Security Configuration | |
| Spring Security is configured as stateless (no HTTP session). Every request goes through: | |
| ``` | |
| HTTP Request | |
| β JwtAuthenticationFilter | |
| β Extract Bearer token from Authorization header | |
| β Validate token via JwtService | |
| β Load user from DB | |
| β Set SecurityContext | |
| β SecurityFilterChain | |
| β Check role-based access rules | |
| β Controller | |
| ``` | |
| **Endpoint access rules:** | |
| | Path pattern | Access | | |
| |---|---| | |
| | `/api/auth/**` | Public | | |
| | `/api/public/**` | Public | | |
| | `/ws/**` | Public (WebSocket needs its own auth) | | |
| | `/api/admin/**` | ADMIN only | | |
| | `/api/sheikhs/**` | STUDENT only (browse sheikhs) | | |
| | `/api/sheikh/**` | SHEIKH, ADMIN, STUDENT | | |
| | `/api/student/**` | STUDENT, SHEIKH, ADMIN | | |
| | `/api/reviews/**` | STUDENT, SHEIKH, ADMIN | | |
| | `/api/payments/**` | STUDENT, SHEIKH, ADMIN | | |
| | Everything else | Any authenticated user | | |
| ### Domain Controllers | |
| #### Common β `/api/auth/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `POST /register/student` | Register new student | | |
| | `POST /register/sheikh` | Register new sheikh | | |
| | `POST /login` | Email/password login β returns JWT | | |
| | `POST /google` | Google ID token login/register | | |
| | `POST /forgot-password` | Send OTP email | | |
| | `POST /verify-otp` | Validate OTP | | |
| | `POST /reset-password` | Set new password with OTP | | |
| | `POST /logout` | Invalidate session (stateless β client discards token) | | |
| | `GET /verify-profile` | Check profile completion status | | |
| | `POST /complete-google-profile` | Complete profile for Google-registered student | | |
| | `POST /complete-google-profile/sheikh` | Complete profile for Google-registered sheikh | | |
| #### Student β `/api/student/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `GET /sessions` | Get all student sessions (filterable by status) | | |
| | `GET /sessions/{id}` | Get single session | | |
| | `POST /sessions` | Book a session with a sheikh | | |
| | `POST /sessions/{id}/join` | Record student join timestamp | | |
| | `POST /sessions/{id}/leave` | Record student leave timestamp | | |
| | `GET /recitations` | Get all recitations | | |
| | `GET /recitations/recent` | Get top 5 recent recitations | | |
| | `GET /recitations/stats` | Get recitation statistics | | |
| | `POST /recitations` | Upload audio + surah number β AI eval + save | | |
| | `GET /recitations/{id}` | Get single recitation detail | | |
| | `GET /profile/complete` | (also via auth) Complete student profile | | |
| #### Sheikh β `/api/sheikh/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `GET /sessions` | Get sheikh sessions (filterable by status) | | |
| | `GET /sessions/{id}` | Get single session | | |
| | `POST /sessions/{id}/accept` | Accept session β status = TOPAY | | |
| | `POST /sessions/{id}/reject` | Reject/cancel session | | |
| | `POST /sessions/{id}/start` | Start session β status = ON_GOING | | |
| | `POST /sessions/{id}/end` | End session + save notes β status = COMPLETED | | |
| | `POST /sessions/{id}/join` | Record sheikh join timestamp | | |
| | `POST /sessions/{id}/leave` | Record sheikh leave timestamp | | |
| | `POST /sessions/{id}/notes` | Update session notes | | |
| | `GET /availability` | Get sheikh's weekly availability slots | | |
| | `PUT /availability` | Set sheikh's weekly availability slots | | |
| | `GET /students` | Get sheikh's student list | | |
| | `GET /dashboard` | Dashboard statistics | | |
| #### Student browsing Sheikhs β `/api/sheikhs/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `GET /` | Browse sheikhs (filters: country, price, rating, language) | | |
| | `GET /{id}` | Get sheikh public profile | | |
| | `GET /{id}/available-dates` | Get available dates in next N days | | |
| | `GET /{id}/available-slots/day` | Get time slots for a specific date | | |
| | `GET /{id}/available-slots` | Get all slots in a date range | | |
| | `GET /{id}/Top-Reviews` | Get top reviews for a sheikh | | |
| #### Admin β `/api/admin/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `GET /dashboard` | Platform-wide statistics | | |
| | `GET /sheikh-approvals` | Pending sheikh applications | | |
| | `POST /sheikh-approvals/{id}/approve` | Approve sheikh | | |
| | `POST /sheikh-approvals/{id}/reject` | Reject sheikh with reason | | |
| | `POST /sheikh-approvals/{id}/schedule-interview` | Schedule interview session | | |
| | `GET /users` | All users with filters | | |
| | `GET /users/{id}` | Full user profile | | |
| | `POST /users/{id}/deactivate` | Deactivate user account | | |
| #### Reviews β `/api/reviews/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `POST /session/{id}` | Student submits review after session | | |
| | `GET /student/session/{id}/details` | Student view of session + review | | |
| | `GET /sheikh/session/{id}/details` | Sheikh view of session + review | | |
| #### Payments β `/api/payments/**` | |
| | Endpoint | Description | | |
| |---|---| | |
| | `POST /initiate/card` | Create Stripe PaymentIntent, return clientSecret | | |
| | `POST /confirm-payment/{intentId}` | Confirm payment with backend after Stripe.js success | | |
| | `POST /confirm-session/{sessionId}` | Verify payment is PAID when sheikh accepts | | |
| | `POST /payout/{sessionId}` | Transfer funds to sheikh via Stripe Connect | | |
| | `POST /refund/{sessionId}` | Refund student via Stripe Refunds API | | |
| | `POST /connect/sheikh/{sheikhId}` | Create Stripe Connect account for sheikh | | |
| #### WebSocket β `/ws` | |
| | Destination | Description | | |
| |---|---| | |
| | `/app/signal` | Send WebRTC/chat signaling message | | |
| | `/topic/session/{id}` | Subscribe to receive signals for a session | | |
| --- | |
| ## 6. Data Model | |
| ### Entity Relationship Summary | |
| ``` | |
| User (1) βββββββββββ (0..1) StudentProfile | |
| User (1) βββββββββββ (0..1) SheikhProfile | |
| User (1) βββββββββββ (0..1) Streak | |
| User (1) βββββββββββ (n) Availability [sheikh side] | |
| User (1) βββββββββββ (n) Session [as student] | |
| User (1) βββββββββββ (n) Session [as sheikh] | |
| User (1) βββββββββββ (n) Recitation [as student] | |
| User (1) βββββββββββ (n) Payment [as student] | |
| User (1) βββββββββββ (n) Payment [as sheikh] | |
| User (1) βββββββββββ (n) Review [as student] | |
| Session (1) βββββββ (0..1) Payment | |
| Session (1) βββββββ (0..1) Review | |
| ``` | |
| ### Entity Details | |
| #### User | |
| ``` | |
| id BIGINT PK | |
| email VARCHAR UNIQUE | |
| passwordHash VARCHAR | |
| fullName VARCHAR | |
| phone VARCHAR | |
| gender VARCHAR | |
| country VARCHAR | |
| birthDateTime TIMESTAMP | |
| role ENUM(STUDENT, SHEIKH, ADMIN) | |
| status ENUM(ALIVE, DEAD) [soft delete] | |
| isActive BOOLEAN | |
| authProvider VARCHAR [LOCAL or GOOGLE] | |
| resetPasswordOtp VARCHAR | |
| resetPasswordOtpExpiresAt TIMESTAMP | |
| createdAt TIMESTAMP | |
| Indexes: role, phone, country, is_active, created_at | |
| ``` | |
| #### StudentProfile | |
| ``` | |
| id (= user_id) BIGINT PK FK β users.id | |
| quranLevel VARCHAR [beginner / intermediate / advanced] | |
| preferredLanguage VARCHAR | |
| preferredAccent VARCHAR | |
| profilePhotoUrl VARCHAR | |
| ``` | |
| #### SheikhProfile | |
| ``` | |
| id (= user_id) BIGINT PK FK β users.id | |
| bio TEXT | |
| specialization VARCHAR | |
| yearsOfExperience INT | |
| hourlyRate DECIMAL(10,2) | |
| ratingAvg DOUBLE | |
| ratingCount INT | |
| certificateUrl VARCHAR | |
| profilePhotoUrl VARCHAR | |
| status ENUM(PENDING, APPROVED, REJECTED, INTERVIEW_SCHEDULED) | |
| rejectionReason VARCHAR | |
| stripeAccountId VARCHAR [Stripe Connect account ID] | |
| interviewDateTime TIMESTAMP | |
| messageToPendingSheikh VARCHAR | |
| Indexes: status, rating_avg, hourly_rate, status+rating_avg | |
| ``` | |
| #### Session | |
| ``` | |
| id BIGINT PK | |
| scheduledStart TIMESTAMP | |
| scheduledEnd TIMESTAMP | |
| actualStart TIMESTAMP | |
| actualEnd TIMESTAMP | |
| studentJoinedAt TIMESTAMP | |
| sheikhJoinedAt TIMESTAMP | |
| studentLeftAt TIMESTAMP | |
| sheikhLeftAt TIMESTAMP | |
| status ENUM(REQUESTED, TOPAY, ON_GOING, COMPLETED, CANCELLED, MISSED) | |
| price DECIMAL(10,2) | |
| sessionDescription VARCHAR(255) | |
| sessionNotes TEXT | |
| student_id FK β users.id | |
| sheikh_id FK β users.id | |
| createdAt TIMESTAMP | |
| updatedAt TIMESTAMP | |
| Indexes: student_id, sheikh_id, status, scheduled_start, | |
| sheikh+status+start, student+status, sheikh+student | |
| ``` | |
| #### Payment | |
| ``` | |
| id BIGINT PK | |
| amount DECIMAL(10,2) | |
| currency VARCHAR(10) [Always "EGP" currently, Stripe handles conversion] | |
| paymentMethod ENUM(CREDIT_CARD, VODAFONE_CASH) | |
| paymentStatus ENUM(PENDING, PAID, REFUNDED, FAILED) | |
| transactionId VARCHAR UNIQUE [Stripe PaymentIntent ID] | |
| orderId VARCHAR UNIQUE [Stripe Order ID] | |
| transferId VARCHAR UNIQUE [Stripe Transfer/Refund ID] | |
| platformCommission DECIMAL(10,2) [15% of amount] | |
| sheikhPayout DECIMAL(10,2) [85% of amount] | |
| student_id FK β users.id | |
| sheikh_id FK β users.id | |
| session_id FK β sessions.id UNIQUE | |
| paymentDate TIMESTAMP | |
| capturedAt TIMESTAMP | |
| transferredAt TIMESTAMP | |
| refundedAt TIMESTAMP | |
| Indexes: student_id, sheikh_id, payment_status | |
| ``` | |
| #### Recitation | |
| ``` | |
| id BIGINT PK | |
| surahName VARCHAR(25) | |
| ayahStart INT | |
| ayahEnd INT | |
| wordScore DECIMAL(4,2) | |
| tashkeelScore DECIMAL(4,2) | |
| totalScore DECIMAL(4,2) [tajweed score from AI] | |
| durationInMinutes DECIMAL(5,2) | |
| mistakesReportJson JSONB [full AI response stored as JSON] | |
| student_id FK β users.id | |
| createdAt TIMESTAMP | |
| Index: student_id | |
| ``` | |
| #### Review | |
| ``` | |
| id BIGINT PK | |
| comment TEXT | |
| rate INT (1-5) | |
| session_id FK β sessions.id UNIQUE | |
| student_id FK β users.id | |
| createdAt TIMESTAMP | |
| Index: student_id | |
| ``` | |
| #### Availability | |
| ``` | |
| id BIGINT PK | |
| dayOfWeek ENUM(MONDAY..SUNDAY) | |
| startTime TIME | |
| endTime TIME | |
| sheikh_id FK β users.id | |
| Indexes: sheikh_id, day_of_week | |
| ``` | |
| #### Streak | |
| ``` | |
| id (= user_id) BIGINT PK FK β users.id | |
| currentStreak INT | |
| longestStreak INT | |
| lastActivityDate DATE | |
| ``` | |
| --- | |
| ## 7. Authentication & Authorization Flow | |
| ### Email/Password Registration | |
| ``` | |
| Frontend Backend | |
| βββββββββ βββββββ | |
| POST /api/auth/register/student | |
| { fullName, email, password, | |
| phone, gender, country, | |
| birthDate } | |
| ββββΊ Validate fields | |
| Hash password (BCrypt) | |
| Save User (role=STUDENT) | |
| Generate JWT | |
| ββββ { token, user } | |
| POST /api/auth/verify-profile | |
| Authorization: Bearer <token> | |
| ββββΊ Decode token β userId | |
| Check StudentProfile exists | |
| ββββ { profileCompleted, profileStatus } | |
| β if profileCompleted = false β redirect to /complete-profile/student | |
| POST /api/student/profile/complete | |
| { quranLevel, language, accent } | |
| ββββΊ Save StudentProfile | |
| ββββ 200 OK | |
| β redirect to /student/dashboard | |
| ``` | |
| ### Google OAuth Flow | |
| ``` | |
| Frontend Backend Google | |
| βββββββββ βββββββ ββββββ | |
| User clicks "Sign in with Google" | |
| β Google Identity Services | |
| returns idToken | |
| ββββββββΊ Verify idToken | |
| POST /api/auth/google | |
| { idToken, role: "STUDENT" } | |
| ββββΊ Verify idToken with | |
| Google API Client | |
| Find/create User | |
| Generate JWT | |
| ββββ { token, user } | |
| (same profile completion flow as above) | |
| ``` | |
| ### JWT Token Lifecycle | |
| - Token expiry: 24 hours (86400000 ms, configurable via `app.jwt.expiration-ms`) | |
| - Token storage: `localStorage` key `authToken` | |
| - Token format: Standard JWT signed with HMAC-SHA key (`app.jwt.secret-key`) | |
| - Every protected API request sends: `Authorization: Bearer <token>` | |
| - `JwtAuthenticationFilter` validates token on every request before it reaches the controller | |
| - On app load, `AuthContext` restores session by re-calling `GET /api/auth/verify-profile` | |
| ### Password Reset (OTP flow) | |
| ``` | |
| POST /api/auth/forgot-password { email } | |
| β Generate 6-digit OTP | |
| β Store OTP + expiry in users table | |
| β Send OTP via Gmail SMTP | |
| POST /api/auth/verify-otp { email, otp } | |
| β Validate OTP matches + not expired | |
| β Return success | |
| POST /api/auth/reset-password { email, otp, newPassword } | |
| β Re-validate OTP | |
| β BCrypt hash new password | |
| β Clear OTP fields | |
| ``` | |
| --- | |
| ## 8. Session Lifecycle | |
| ### Complete Flow: Booking to Completion | |
| ``` | |
| 1. REQUESTED | |
| Student books a session β POST /api/student/sessions | |
| Session created with status = REQUESTED | |
| 2. TOPAY (payment pending) | |
| Sheikh accepts β POST /api/sheikh/sessions/{id}/accept | |
| Status changes to TOPAY | |
| 3. Payment | |
| Student sees payment prompt | |
| Frontend creates PaymentIntent β POST /api/payments/initiate/card | |
| Backend returns { clientSecret, publishableKey } | |
| Stripe.js handles card entry on frontend | |
| On Stripe success β POST /api/payments/confirm-payment/{intentId} | |
| Status changes to SCHEDULED (payment confirmed) | |
| 4. ON_GOING | |
| At session time, both parties join waiting room | |
| Sheikh starts β POST /api/sheikh/sessions/{id}/start | |
| Status = ON_GOING | |
| Both parties join live WebRTC session | |
| 5. COMPLETED | |
| Sheikh ends session + adds notes β POST /api/sheikh/sessions/{id}/end | |
| Status = COMPLETED | |
| Payout triggered β POST /api/payments/payout/{sessionId} | |
| Platform keeps 15%, sheikh receives 85% via Stripe Connect | |
| 6. Review | |
| Student writes review β POST /api/reviews/session/{id} | |
| Sheikh rating updated (rolling average) | |
| Exception paths: | |
| β Sheikh rejects β CANCELLED | |
| β Session time passes without start β MISSED β student refunded | |
| ``` | |
| ### Session Statuses | |
| ``` | |
| REQUESTED β TOPAY β (SCHEDULED) β ON_GOING β COMPLETED | |
| ββββΊ CANCELLED (sheikh rejects) | |
| ββββββββββββββββΊ MISSED (no-show, auto-refund) | |
| ``` | |
| --- | |
| ## 9. Real-Time Communication (WebRTC + WebSocket) | |
| ### Architecture | |
| The backend acts as a **signaling server** only. It does not relay audio/video. Actual media flows peer-to-peer between browser clients via WebRTC. | |
| ``` | |
| Student Browser Backend (Signaling) Sheikh Browser | |
| βββββββββββββββ βββββββββββββββββ ββββββββββββββ | |
| Connect SockJS /ws βββββββΊ STOMP broker Connect SockJS /ws | |
| Subscribe /topic/ /topic/session/{id} Subscribe /topic/ | |
| session/{id} session/{id} | |
| Send join signal βββββββΊ Broadcast to topic βββββββββΊ Receives join | |
| /topic/session/{id} | |
| Creates RTCPeerConnection Creates RTCPeerConnection | |
| Creates Offer βββββββΊ Broadcast βββββββββΊ Receives Offer | |
| Creates Answer | |
| Receives Answer βββββββ Broadcast βββββββββ Sends Answer | |
| ICE exchange ββββββββΊ Broadcast ββββββββββΊ ICE exchange | |
| Direct P2P stream βββββββββββββββββββββββββββββββββββββββββΊ Direct P2P stream | |
| (audio + video, no server relay) | |
| ``` | |
| ### Signaling Message Types | |
| | Type | Direction | Purpose | | |
| |---|---|---| | |
| | `join` | Any β Topic | Notify that a user entered the session room | | |
| | `offer` | Caller β Topic | WebRTC SDP offer | | |
| | `answer` | Callee β Topic | WebRTC SDP answer | | |
| | `ice-candidate` | Any β Topic | ICE connectivity candidates | | |
| | `chat` | Any β Topic | In-session text chat message | | |
| ### ICE Servers (STUN) | |
| The frontend uses Google's public STUN servers for NAT traversal: | |
| - `stun:stun.l.google.com:19302` | |
| - `stun:stun1.l.google.com:19302` | |
| - `stun:stun2.l.google.com:19302` | |
| No TURN server is currently configured. This means sessions behind symmetric NAT may fail to connect. | |
| ### WebSocket Config (Backend) | |
| - Endpoint: `/ws` with SockJS fallback | |
| - Application destination prefix: `/app` (send to controller) | |
| - Broker topics: `/topic` (broadcast), `/queue` (point-to-point) | |
| - User destination prefix: `/user` | |
| --- | |
| ## 10. Payment Flow (Stripe) | |
| ### Architecture | |
| The system uses **Stripe Payment Intents** for collecting student payments and **Stripe Connect** (Express accounts) for paying out sheikhs. | |
| ### Student Payment Flow | |
| ``` | |
| Frontend Backend Stripe | |
| βββββββββ βββββββ ββββββ | |
| POST /api/payments/initiate/card | |
| { sessionId, studentId, | |
| sheikhId, amount } | |
| ββββΊ Create PaymentIntent | |
| (amount in smallest unit) | |
| βββββ { clientSecret, | |
| publishableKey, | |
| paymentIntentId } | |
| Stripe.js loads card form | |
| User enters card details | |
| stripe.confirmCardPayment(clientSecret) | |
| βββββββΊ Charge card | |
| βββββββ Payment confirmed | |
| POST /api/payments/confirm-payment/{intentId} | |
| ββββΊ Retrieve PaymentIntent from Stripe | |
| Verify status = succeeded | |
| Save Payment record (PAID) | |
| Update Session status β SCHEDULED | |
| βββββ { success: true } | |
| ``` | |
| ### Sheikh Payout Flow (after session completion) | |
| ``` | |
| Backend Stripe | |
| βββββββ ββββββ | |
| POST /api/payments/payout/{sessionId} | |
| Verify session is COMPLETED | |
| Calculate: sheikhPayout = amount Γ 0.85 | |
| commission = amount Γ 0.15 | |
| Create Stripe Transfer to sheikh's | |
| connected account (stripeAccountId) βββββββΊ Transfer funds | |
| Update Payment: transferredAt, transferId βββββββ Transfer confirmed | |
| ``` | |
| ### Refund Flow (missed/cancelled sessions) | |
| ``` | |
| Backend Stripe | |
| βββββββ ββββββ | |
| POST /api/payments/refund/{sessionId} | |
| Verify payment is PAID | |
| Create Stripe Refund for full amount βββββββΊ Refund issued | |
| Update Payment: refundedAt, paymentStatus=REFUNDED | |
| ``` | |
| ### Platform Commission | |
| Commission percentage is configurable: `platform.commission.percentage = 15` | |
| --- | |
| ## 11. AI Recitation Evaluation Flow | |
| ### Flow | |
| ``` | |
| Frontend Backend AI Service | |
| βββββββββ βββββββ ββββββββββ | |
| User records audio in browser | |
| (MediaRecorder API) | |
| POST /api/student/recitations | |
| multipart/form-data | |
| { surahNumber, audio file } | |
| ββββΊ Receive audio file | |
| Convert to WAV (JAVE2) | |
| Forward to AI service: | |
| POST {AI_BASE_URL}/analyze | |
| { audio, surah_number } | |
| βββββββΊ Speech recognition | |
| Text alignment with | |
| Quran reference | |
| Error detection: | |
| - Extra words | |
| - Missed words | |
| - Tashkeel errors | |
| βββββββ { surah_info, | |
| overall_stats, | |
| ayahs[] } | |
| Parse AI response | |
| Calculate scores: | |
| - wordScore | |
| - tashkeelScore | |
| - totalScore | |
| Store Recitation | |
| (mistakesReportJson as JSONB) | |
| βββββ RecitationDetailsDto | |
| Display scores + mistake report | |
| ``` | |
| ### AI Response Structure | |
| The AI service returns per-ayah analysis: | |
| - `words_stage`: extra words and missed words per ayah | |
| - `tashkeel_stage`: per-word tashkeel comparison with reasons | |
| - `overall_stats`: word accuracy % and tashkeel accuracy % | |
| Scores are stored as JSONB in `mistakesReportJson` for full replay capability on the frontend. | |
| ### Direct AI Call (frontend also calls AI directly) | |
| The frontend `aiApi.ts` can also call the AI service directly (bypassing the backend) for immediate feedback before saving. This is used in the `RecordingPractice` component. | |
| --- | |
| ## 12. Sheikh Approval Flow | |
| ### Steps | |
| ``` | |
| 1. Register | |
| Sheikh registers β role = SHEIKH | |
| Status = PENDING (no SheikhProfile yet) | |
| 2. Complete Profile | |
| POST /api/sheikh/profile/complete | |
| { bio, specialization, yearsOfExperience, | |
| hourlyRate, certificate } | |
| SheikhProfile created β status = PENDING | |
| 3. Admin Review | |
| Admin sees sheikh in approval queue | |
| GET /api/admin/sheikh-approvals | |
| 4. Interview Scheduling | |
| Admin schedules interview: | |
| POST /api/admin/sheikh-approvals/{id}/schedule-interview | |
| { dateTime, message } | |
| Status β INTERVIEW_SCHEDULED | |
| Sheikh notified (email or in-app) | |
| 5. Interview Session | |
| Admin and sheikh join live video call | |
| (same WebRTC infrastructure as student sessions) | |
| Route: /admin/interview/:sheikhId | |
| Route: /sheikh/interview-session/:sheikhId | |
| 6. Decision | |
| Approve: status β APPROVED, sheikh can accept students | |
| Reject: status β REJECTED, rejectionReason saved | |
| 7. Post-Approval | |
| Sheikh sets hourly rate: PUT /api/sheikh/update-pricing | |
| Sheikh sets availability: PUT /api/sheikh/availability | |
| Sheikh appears in student search: GET /api/sheikhs/ | |
| ``` | |
| --- | |
| ## 13. Internationalization (i18n) | |
| The frontend supports Arabic and English using i18next. | |
| - Language detection: browser preference (`i18next-browser-languagedetector`) | |
| - Translations: `src/lib/translations.ts` | |
| - Language context: `src/lib/languageContext.tsx` | |
| - Direction: Automatically switches between LTR (English) and RTL (Arabic) | |
| - Language switcher: `src/components/LanguageSwitcher.tsx` | |
| --- | |
| ## 14. Database Optimization | |
| ### JDBC Batching (Hibernate) | |
| Enabled globally for bulk insert/update performance: | |
| ```yaml | |
| hibernate: | |
| jdbc: | |
| batch_size: 20 | |
| order_inserts: true | |
| order_updates: true | |
| ``` | |
| ### Indexes | |
| Key composite and single-column indexes defined at entity level: | |
| | Table | Indexes | | |
| |---|---| | |
| | users | role, phone, country, is_active, created_at | | |
| | sessions | student_id, sheikh_id, status, scheduled_start, sheikh+status+start, student+status, sheikh+student | | |
| | sheikh_profile | status, rating_avg, hourly_rate, status+rating_avg | | |
| | payment | student_id, sheikh_id, payment_status | | |
| | recitations | student_id | | |
| | review | student_id | | |
| | availability | sheikh_id, day_of_week | | |
| --- | |
| ## 15. Configuration & Environment | |
| ### Backend Environment Variables | |
| | Variable | Purpose | | |
| |---|---| | |
| | `DB_URL` | PostgreSQL JDBC URL | | |
| | `PGUSER` | Database username | | |
| | `PGPASSWORD` | Database password | | |
| | `JWT_SECRET` | JWT HMAC signing key | | |
| | `MAIL_USERNAME` | Gmail address for OTP emails | | |
| | `MAIL_PASSWORD` | Gmail app password | | |
| | `STRIPE_SECRET_KEY` | Stripe secret key | | |
| | `STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | | |
| | `GOOGLE_CLIENT_ID` | Google OAuth client ID | | |
| ### Frontend Environment Variables | |
| | Variable | Purpose | | |
| |---|---| | |
| | `VITE_API_BASE_URL` | Backend base URL (defaults to `http://localhost:8080`) | | |
| ### AI Service | |
| External Python service exposed via ngrok: | |
| - Base URL: configured in `app.ai.base-url` | |
| - Connect timeout: 5s | |
| - Read timeout: 600s (10 min, for long audio files) | |
| - Endpoint: `POST /analyze` with `{ audio, surah_number }` | |
| --- | |
| ## 16. Cross-Cutting Concerns | |
| ### Error Handling | |
| - Backend: `GlobalExceptionHandler` catches all exceptions and returns consistent JSON error responses | |
| - Frontend: `AppError` class standardizes error handling across all API modules with `status` + `path` context | |
| - Frontend: `ErrorBoundary` component wraps dashboards to prevent full-page crashes | |
| ### CORS | |
| Backend allows requests from `http://localhost:*` with credentials. For production this needs to be restricted to the actual frontend domain. | |
| ### Monitoring | |
| Spring Boot Actuator is enabled with all endpoints exposed (`management.endpoints.web.exposure.include: "*"`). For production this should be restricted to health + metrics only. | |
| ### Logging | |
| ```yaml | |
| org.hibernate.SQL: DEBUG # All SQL statements | |
| org.hibernate.stat: DEBUG # Query counts per session | |
| ``` | |
| Hibernate statistics are enabled (`generate_statistics: true`) for N+1 query detection during development. | |
| ### Schema Management | |
| JPA `ddl-auto: update` β Hibernate auto-updates the schema on startup. This is acceptable for development but should be replaced with Flyway or Liquibase for production. | |
| ### Audio Processing | |
| JAVE2 wraps ffmpeg. The Linux 64-bit native binary is included as a Maven dependency (`jave-nativebin-linux64`). Audio files uploaded by students are converted to WAV before being sent to the AI service. | |