Quran_Tech_Server / docs /components.md
aboalaa147's picture
Initial deployment
eb6a2f9
|
Raw
History Blame Contribute Delete
30.8 kB

Component Design β€” Quran App

This document is the mid-level component map for the entire system. Every component, module, and service is described with its responsibilities, interfaces, dependencies, and internal state. Mermaid diagrams show how components relate to each other within each layer and across layers.

This document is the reference used by per-flow design documents (e.g. session-end-flow/design.md). Per-flow documents go deeper into sequence, error handling, and correctness properties for a specific feature.


1. System Component Map

graph TB
    subgraph Browser["Browser β€” React App"]
        subgraph Providers["Context Providers"]
            AUTH[AuthContext]
            LANG[LanguageContext]
            STU[StudentContext]
        end

        subgraph Pages["Page Components"]
            AUTH_PAGES[Auth Pages]
            STUDENT_PAGES[Student Pages]
            SHEIKH_PAGES[Sheikh Pages]
            ADMIN_PAGES[Admin Pages]
            SESSION_PAGES[Session Pages]
            PRACTICE_PAGES[Practice Pages]
        end

        subgraph APILayer["API Layer (lib/api/)"]
            BOOKING_API[bookingApi]
            STUDENT_API[studentApi]
            PAYMENT_API[paymentApi]
            AI_API[aiApi]
            ADMIN_API[adminApi]
            SHEIKH_API[sheikhApi]
        end

        WEBRTC[useWebRTC hook]
    end

    subgraph Backend["Spring Boot Backend"]
        subgraph Security["Security Layer"]
            JWT_FILTER[JwtAuthFilter]
            SEC_CONFIG[SecurityConfig]
        end

        subgraph Controllers["Controllers"]
            AUTH_CTRL[AuthController]
            STU_CTRL[StudentControllers]
            SHEIKH_CTRL[SheikhControllers]
            ADMIN_CTRL[AdminControllers]
            PAY_CTRL[PaymentController]
            WS_CTRL[SignalingController]
        end

        subgraph Services["Services"]
            AUTH_SVC[AuthService]
            STU_SVC[StudentServices]
            SHEIKH_SVC[SheikhServices]
            SESSION_SVC[SessionService]
            PAY_SVC[PaymentServiceStripe]
            SCHEDULER[TaskScheduler]
        end

        subgraph Repos["Repositories"]
            USER_REPO[UserRepository]
            SESSION_REPO[SessionRepository]
            PAY_REPO[PaymentRepository]
            RECIT_REPO[RecitationRepository]
            AVAIL_REPO[AvailabilityRepository]
            REVIEW_REPO[ReviewRepository]
        end
    end

    subgraph External["External Services"]
        STRIPE[Stripe API]
        GOOGLE[Google OAuth]
        AI_SVC[AI Service]
        GMAIL[Gmail SMTP]
        DB[(PostgreSQL)]
    end

    Pages --> Providers
    Pages --> APILayer
    WEBRTC --> Browser

    APILayer -->|REST HTTP| Controllers
    WEBRTC -->|WebSocket STOMP| WS_CTRL

    JWT_FILTER --> Controllers
    Controllers --> Services
    Services --> Repos
    Repos --> DB

    PAY_SVC --> STRIPE
    AUTH_SVC --> GOOGLE
    AUTH_SVC --> GMAIL
    STU_SVC -->|audio + surah| AI_SVC
    SCHEDULER --> SESSION_SVC

2. Frontend Components

2.1 Context Providers

classDiagram
    class AuthContext {
        +user: User | null
        +loading: boolean
        +signIn(email, password): Promise~User~
        +signInWithGoogle(idToken, role): Promise~User~
        +signUp(email, password, ...): Promise~void~
        +signOut(): Promise~void~
        +updateProfile(updates): void
        +fetchStreak(): Promise~Streak~
        +fetchPracticeStats(): Promise~RecitationStats~
    }

    class LanguageContext {
        +language: "ar" | "en"
        +isRTL: boolean
        +setLanguage(lang): void
    }

    class StudentContext {
        +studentData: StudentData
        +reload(): void
    }

    AuthContext --> LanguageContext : consumed by
    StudentContext --> AuthContext : depends on

AuthContext is the most critical context. It:

  • Stores the authenticated User object in React state and localStorage
  • On app load, re-calls GET /api/auth/verify-profile to restore + validate session
  • Exposes all auth operations (signIn, signUp, signOut, Google OAuth)
  • JWT token is managed here β€” stored in localStorage.authToken

LanguageContext manages Arabic/English switching and RTL/LTR direction. All page components consume it for translations and layout direction.

StudentContext caches student-specific dashboard data to avoid re-fetching on every render.


2.2 Route Guard

flowchart TD
    Request[Route Request] --> Loading{loading?}
    Loading -->|yes| Spinner[Show Spinner]
    Loading -->|no| Auth{user exists?}
    Auth -->|no| SignIn[Redirect /signin]
    Auth -->|yes| Admin{role = admin?}
    Admin -->|yes| Render[Render Page]
    Admin -->|no| Profile{profileCompleted?}
    Profile -->|no| Complete[Redirect /complete-profile]
    Profile -->|yes| Render

ProtectedRoute wraps all authenticated routes. Admin role skips the profile completion check because admin accounts are created manually.


2.3 Auth Pages

graph LR
    LandingPage -->|sign in| SignIn
    LandingPage -->|register| Register
    SignIn -->|forgot| ForgotPasswordRequest
    ForgotPasswordRequest -->|OTP sent| VerifyOtp
    VerifyOtp -->|verified| ResetPassword
    SignIn -->|google| GoogleAuth
    Register -->|success| CompleteProfile
    CompleteProfile -->|student| CompleteStudentProfile
    CompleteProfile -->|sheikh| CompleteSheikhProfile
    CompleteSheikhProfile -->|submitted| InterviewSchedule
Component Route Responsibility
LandingPage / Public landing, links to sign in / register
SignIn /signin Email + password login form
Register /register Registration form (student or sheikh role)
ForgotPasswordRequest /forgot-password Email input β†’ triggers OTP email
VerifyOtp /verify-otp 6-digit OTP entry
ResetPassword /reset-password New password form
GoogleAuth /auth/google Handles Google OAuth redirect callback
CompleteProfile /complete-profile Role selection (student / sheikh)
CompleteStudentProfile /complete-profile/student Quran level, language, accent wizard
CompleteSheikhProfile /complete-profile/sheikh Bio, specialization, certificate upload
InterviewSchedule /sheikh/interview Pending sheikh sees interview date

2.4 Dashboard Components

graph TB
    subgraph StudentDashboard
        STREAK[Streak Widget]
        STATS[Practice Stats]
        RECENT_REC[Recent Recitations]
        UPCOMING_SES[Upcoming Sessions]
        QUICK_ACTIONS[Quick Actions]
    end

    subgraph SheikhDashboard
        PENDING_REQ[Pending Requests]
        SHEIKH_STATS[Session Stats]
        SHEIKH_STUDENTS[Recent Students]
        RATING_WIDGET[Rating Widget]
    end

    subgraph AdminDashboard
        PLATFORM_STATS[Platform Statistics]
        USER_OVERVIEW[User Overview]
        SESSION_OVERVIEW[Session Overview]
        REVENUE[Revenue Stats]
    end

    StudentDashboard --> studentApi
    SheikhDashboard --> sheikhApi
    AdminDashboard --> adminApi

Each dashboard is a single page component that orchestrates multiple widgets. All data is fetched on mount via parallel Promise.allSettled calls β€” individual widget failures do not crash the dashboard.


2.5 Session Components

This is the most complex area. Components are sequenced along the session lifecycle:

graph LR
    MySessions -->|join button active| WaitingRoom
    WaitingRoom -->|session time reached| LiveSession
    LiveSession -->|sheikh ends| MySessions
    LiveSession -->|student leaves| SessionEndWaiting
    SessionEndWaiting -->|status = COMPLETED| SessionReport
    SessionEndWaiting -->|status = MISSED/CANCELLED| MySessions
    SessionReport -->|submit review| MySessions
Component Route Purpose Key State
MySessions /student/sessions /sheikh/sessions Session list for both roles β€” tabs for PENDING, UPCOMING, COMPLETED, CANCELLED sessions[], focusSession, paymentSession
WaitingRoom /waiting-room/:id Countdown until session starts, "Enter Session" enabled at T-5min session, now (1s interval)
LiveSession /session/:id Full video call UI β€” camera/mic controls, chat, sheikh notes, role-specific end/leave buttons localStream, remoteStream, isEndingSession, sessionNotes
SessionEndWaiting /session-waiting/:id Student waiting screen after leaving β€” polls status every 5s sessionData, polling interval
SessionReport /session-report/:id Post-session summary + review form sessionData, review
SheikhInterviewSession /sheikh/interview-session/:id Interview call between admin and pending sheikh Same as LiveSession

2.6 Practice Components

sequenceDiagram
    participant User
    participant RecordingPractice
    participant Backend
    participant AIService

    User->>RecordingPractice: select surah, record/upload audio
    RecordingPractice->>Backend: POST /api/student/recitations (multipart)
    Backend->>AIService: POST /analyze { audio, surah_number }
    AIService-->>Backend: { overall_stats, ayahs[], user_text }
    Backend-->>RecordingPractice: RecitationDetailsDto
    RecordingPractice->>User: show scores + mistake breakdown
Component Route Purpose Key State
RecordingPractice /student/practice Audio recording (MediaRecorder) or file upload, submit to AI, show results audioBlob, evaluationResult, isProcessing
EvaluationReports /student/evaluations/:id? History list of all recitations, detail dialog on click recitations[], selected detail

2.7 Sheikh Discovery Components

graph TB
    FindSheikh -->|search + filter| SheikhList[Sheikh Cards]
    SheikhList -->|click| SheikhProfile
    SheikhProfile -->|select date| Calendar[Available Dates]
    Calendar -->|select slot| TimeSlots[Time Slot Chips]
    TimeSlots -->|book| BookingDialog[Booking Confirmation]
    BookingDialog -->|confirm| PaymentModal[Stripe Payment Modal]
    PaymentModal -->|success| MySessions
Component Route Purpose
FindSheikh /student/find-sheikh Search/filter sheikh list β€” filter by country, price, rating, language
SheikhProfile /student/sheikh/:id Full sheikh profile, availability calendar, booking form, reviews

SheikhProfile is the most complex student-facing component. It manages: sheikh data fetching, calendar availability loading, time slot selection, booking confirmation dialog, and Stripe payment modal β€” all in one component.


2.8 Admin Components

graph TB
    AdminDashboard --> SheikhApproval
    AdminDashboard --> UserManagement
    SheikhApproval -->|click sheikh| UserProfileView
    UserManagement -->|click user| UserProfileView
    SheikhApproval -->|schedule interview| InterviewSession
Component Route Purpose
SheikhApproval /admin/sheikh-approval Queue of pending sheikhs β€” approve, reject, schedule interview
UserManagement /admin/users All users list with filters β€” deactivate accounts
UserProfileView /admin/user/:id Full user detail (student or sheikh)
InterviewSession /admin/interview/:id Live video call with pending sheikh (same WebRTC infrastructure)

2.9 Shared Components

graph LR
    subgraph Navbar
        Logo --> NavLinks
        NavLinks --> RoleIndicator
        RoleIndicator --> LanguageSwitcher
        LanguageSwitcher --> UserMenu
    end

    subgraph ProfilePage
        BasicInfo[Basic Info Form]
        PhotoUpload[Profile Photo]
        PasswordChange[Change Password]
    end
  • Navbar β€” top navigation. Links change based on role. Shows current language toggle (AR/EN) and user avatar menu.
  • RoleSwitcher β€” dev tool for testing different roles. Not shown in production.
  • ProfilePage β€” shared profile editing for all roles. Route: /profile.
  • ErrorBoundary β€” wraps dashboards. Prevents a single widget crash from taking down the full page.
  • ui/ β€” 50+ shadcn/Radix UI primitive components (Button, Card, Dialog, etc.). No business logic.

2.10 API Layer

Each file in lib/api/ is responsible for one domain. All files follow the same pattern: a shared request() helper that injects the JWT token, handles 401 token expiry, and normalizes errors.

classDiagram
    class bookingApi {
        +fetchAvailableDates(sheikhId, from, days)
        +fetchAvailableSlotsForDay(sheikhId, date)
        +bookSession(request)
        +fetchMySessions(status?)
        +fetchSheikhSessions(status?)
        +acceptSessionRequest(sessionId)
        +rejectSessionRequest(sessionId)
        +fetchSessionReview(sessionId, role)
        +submitSessionReview(sessionId, review)
    }

    class studentApi {
        +submitRecitationAudio(dto)
        +fetchRecentRecitations()
        +fetchRecitationDetail(id)
        +fetchRecitationStats()
        +fetchStreak()
        +fetchPracticeStats()
        +fetchDashboardData()
    }

    class paymentApi {
        +initiateCardPayment(request)
        +confirmPaymentWithBackend(paymentIntentId)
    }

    class aiApi {
        +analyzeRecitation(audio, surahNumber)
        +checkAiHealth()
        +mapAiResponseToDto(aiResponse)
    }

    class adminApi {
        +fetchPendingSheikhs()
        +approveSheikh(id)
        +rejectSheikh(id, reason)
        +scheduleInterview(id, dateTime)
        +fetchAllUsers(filters)
        +deactivateUser(id)
    }

2.11 useWebRTC Hook

sequenceDiagram
    participant Component as LiveSession.tsx
    participant Hook as useWebRTC
    participant STOMP as STOMP Client
    participant PC as RTCPeerConnection

    Component->>Hook: useWebRTC(sessionId, userId)
    Hook->>STOMP: connect to /ws
    Hook->>PC: getUserMedia() β†’ localStream
    STOMP-->>Hook: connected
    Hook->>STOMP: publish join signal
    STOMP-->>Hook: receive join from peer
    Hook->>PC: createOffer()
    Hook->>STOMP: publish offer
    STOMP-->>Hook: receive answer
    Hook->>PC: setRemoteDescription(answer)
    Note over PC: ICE negotiation
    PC-->>Hook: remoteStream available
    Hook-->>Component: { localStream, remoteStream, isConnected, messages }

Exposed interface:

{
  localStream: MediaStream | null
  remoteStream: MediaStream | null
  isConnected: boolean
  connectionQuality: 'good' | 'fair' | 'poor' | 'unknown'
  messages: ChatMessage[]
  sendChatMessage(text: string): void
  toggleVideo(): void
  toggleAudio(): void
}

Connection quality is measured every 3 seconds via RTCPeerConnection.getStats() on the candidate-pair report. RTT < 150ms = good, < 400ms = fair, > 400ms = poor.


3. Backend Components

3.1 Security Layer

sequenceDiagram
    participant Client
    participant JwtFilter as JwtAuthFilter
    participant UDS as UserDetailsService
    participant Chain as SecurityFilterChain
    participant Controller

    Client->>JwtFilter: HTTP Request + Bearer token
    JwtFilter->>JwtFilter: extract JWT from Authorization header
    JwtFilter->>JwtFilter: jwtService.extractUsername(jwt)
    JwtFilter->>UDS: loadUserByUsername(email)
    UDS->>JwtFilter: CustomUserDetails
    JwtFilter->>JwtFilter: jwtService.isTokenValid(jwt, userDetails)
    JwtFilter->>Chain: set SecurityContext authentication
    Chain->>Chain: check role-based access rule
    Chain->>Controller: forward if authorized
    Controller-->>Client: response

JwtAuthenticationFilter

  • Runs on every request (OncePerRequestFilter)
  • Extracts Bearer token from Authorization header
  • Validates expiry + signature via JwtService
  • Sets SecurityContextHolder so downstream code can call authentication.getPrincipal()

CustomUserDetails

  • Wraps the User JPA entity
  • Exposes getUserId() β€” used by every controller to identify the caller
  • isEnabled() returns user.status == ALIVE β€” deactivated users are rejected at auth time

SecurityConfig

  • Stateless sessions (SessionCreationPolicy.STATELESS)
  • CORS: allows http://localhost:* (must be restricted for production)
  • Role rules defined as path-prefix matchers (see architecture.md Β§5 for full table)

3.2 Auth Module (dashboard/common)

classDiagram
    class AuthController {
        +registerStudent(request)
        +registerSheikh(request)
        +login(request)
        +googleAuth(request)
        +forgotPassword(request)
        +verifyOtp(request)
        +resetPassword(request)
        +logout()
        +verifyProfile()
        +completeGoogleProfile(request)
        +completeGoogleSheikhProfile(request)
    }

    class AuthService {
        +registerStudent(request): AuthResponse
        +registerSheikh(request): AuthResponse
        +login(request): AuthResponse
        +googleAuth(request): AuthResponse
        +forgotPassword(request): void
        +verifyOtp(request): void
        +resetPasswordWithOtp(request): void
        +logout(userId): void
        +verifyProfile(userId): ProfileCheckDto
        +completeGoogleProfile(email, request): void
        +completeGoogleSheikhProfile(email, request): void
    }

    class JwtService {
        +generateToken(userDetails): String
        +extractUsername(token): String
        +isTokenValid(token, userDetails): boolean
        +extractExpiration(token): Date
    }

    AuthController --> AuthService
    AuthService --> JwtService
    AuthService --> UserRepository

AuthService.googleAuth() calls Google's tokeninfo endpoint to verify the idToken, then finds or creates the user. If the user is new, profileCompleted = false until they fill the profile wizard.

AuthResponse is the standard login response:

{
  token: String,           // JWT
  role: String,
  message: String,
  user: UserDto,
  sheikhApprovalStatus: String  // only for sheikh role
}

3.3 Student Module (dashboard/student)

graph TB
    subgraph Controllers
        DashCtrl[DashboardController\n/api/student/dashboard]
        StuCtrl[StudentController\n/api/student/profile]
        SesCtrl[StudentSessionController\n/api/student/sessions]
        RecCtrl[StudentRecitationController\n/api/student/recitations]
        SheikhCtrl[SheikhController\n/api/sheikhs]
    end

    subgraph Services
        DashSvc[DashboardService]
        StuSessSvc[StudentSessionService]
        RecitSvc[StudentRecitationService]
        FindSvc[SheikhServiceFind]
        StreakSvc[StudentStreakService]
    end

    DashCtrl --> DashSvc
    SesCtrl --> StuSessSvc
    SesCtrl --> SessionService
    RecCtrl --> RecitSvc
    SheikhCtrl --> FindSvc
    DashSvc --> StreakSvc

StudentRecitationService is the most complex student service:

  1. Receives audio MultipartFile + surahNumber
  2. Converts audio to WAV using JAVE2 (ffmpeg wrapper)
  3. Calls external AI service via RestTemplate with multipart/form-data
  4. Parses AI JSON response into AIReport DTO
  5. Calculates wordScore, tashkeelScore, totalScore from the AI report
  6. Saves Recitation entity with mistakesReportJson (full AI response as JSONB)

StudentSessionService handles booking:

  • Validates startDateTime is in the future
  • Calls SheikhAvailabilityService.validateSessionSlot() β€” checks slot is within availability and not already booked by REQUESTED/TOPAY/SCHEDULED sessions
  • Creates Session with status = REQUESTED

3.4 Sheikh Module (dashboard/sheikh)

graph TB
    subgraph Controllers
        SesCtrl2[SessionController\n/api/sheikh/sessions]
        AvailCtrl[AvailabilityController\n/api/sheikh/availability]
        StudCtrl[MyStudents\n/api/sheikh/students]
        DashCtrl2[sheikhMainDashboard\n/api/sheikh/dashboard]
        PendCtrl[pendingSheikhController\n/api/sheikh/profile]
        ReviewCtrl[NotesAndReviewsController\n/api/reviews]
    end

    subgraph Services
        SessionSvc[SessionService]
        AvailSvc[SheikhAvailabilityService]
        StudSvc[MyStudentsServiceImpl]
        DashSvc2[sheikhDashboardService]
        ReviewSvc[NotesAndReviewsService]
    end

    SesCtrl2 --> SessionSvc
    AvailCtrl --> AvailSvc
    StudCtrl --> StudSvc
    DashCtrl2 --> DashSvc2
    ReviewCtrl --> ReviewSvc

SessionService is the central service for session lifecycle. It owns all status transitions and event-driven scheduling:

classDiagram
    class SessionService {
        -taskScheduler: TaskScheduler
        +rescheduleExistingSessionsOnStartup()
        +updateSessionStatus(sheikhId, sessionId, newStatus)
        +recordJoin(userId, sessionId)
        +recordLeave(userId, sessionId)
        +startSession(userId, sessionId)
        +endSession(sheikhId, sessionId, notes)
        -scheduleMissedCheck(sessionId, scheduledStart)
        -scheduleSheikhNoShowCheck(sessionId, scheduledStart)
        -scheduleCancelIfUnpaid(sessionId, scheduledStart)
        +checkAndMarkMissed(sessionId)
        +checkAndMarkMissedBySheikhNoShow(sessionId)
        +cancelIfUnpaid(sessionId)
        +triggerRefundForMissedSession(sessionId)
        +convertToDto(session): SessionDTO
    }

Three event-driven scheduled tasks fire automatically:

  • scheduleMissedCheck β€” fires at scheduledStart + 1 min. If neither party joined β†’ MISSED + refund
  • scheduleSheikhNoShowCheck β€” fires at scheduledStart + 10 min. If sheikh didn't join β†’ MISSED + refund
  • scheduleCancelIfUnpaid β€” fires at scheduledStart - 5 min. If still TOPAY β†’ CANCELLED

All three are registered on startup (@EventListener(ApplicationReadyEvent)) for surviving sessions.

SheikhAvailabilityService manages the weekly recurring availability template:

  • setAvailability() β€” replaces overlapping slots with new ones
  • getSlotsForDate() β€” generates 1-hour slots within the availability window, minus already-booked sessions
  • validateSessionSlot() β€” called at booking time to reject out-of-window or overlapping requests

3.5 Admin Module (dashboard/admin)

graph TB
    subgraph Controllers
        AdminCtrl[AdminController\n/api/admin/users]
        DashCtrl3[AdminMainDashboard\n/api/admin/dashboard]
        ApproveCtrl[ShiekhManagementAPProval\n/api/admin/sheikh-approvals]
        UserMgmtCtrl[UserManagement]
    end

    subgraph Services
        AdminSvc[AdminService]
        DashSvc3[AdminDashboardService]
        ApproveSvc[PendingSheikhService]
    end

    AdminCtrl --> AdminSvc
    DashCtrl3 --> DashSvc3
    ApproveCtrl --> ApproveSvc

Admin module is relatively thin β€” it reads across all domain repositories to produce statistics and management views. Approval flow: PENDING β†’ INTERVIEW_SCHEDULED β†’ APPROVED/REJECTED.


3.6 Payment Module (payment)

classDiagram
    class PaymentControllerStripe {
        +initiateCard(request): StripePaymentResponse
        +confirmPayment(paymentIntentId): StripePaymentResponse
        +confirmSession(sessionId): void
        +payoutSheikh(sessionId): StripeRefundResponse
        +refundStudent(sessionId, reason): StripeRefundResponse
        +createSheikhAccount(sheikhId): Map
    }

    class PaymentServiceStripe {
        -commissionPct: 15%
        +initiateCardPayment(request): StripePaymentResponse
        +confirmPayment(paymentIntentId): StripePaymentResponse
        +onSheikhAcceptsSession(sessionId): void
        +payoutSheikh(sessionId): StripeRefundResponse
        +refundStudent(sessionId, reason): StripeRefundResponse
        +createSheikhConnectedAccount(sheikhId): Map
        -savePayment(...): Payment
        -calcCommission(amount): BigDecimal
    }

    PaymentControllerStripe --> PaymentServiceStripe
    PaymentServiceStripe --> PaymentRepository
    PaymentServiceStripe --> SessionRepository
    PaymentServiceStripe --> UserRepository

Payment status lifecycle:

stateDiagram-v2
    [*] --> PENDING: initiateCardPayment
    PENDING --> PAID: confirmPayment (Stripe succeeded)
    PENDING --> FAILED: confirmPayment (Stripe failed)
    PAID --> PAYOUT_DONE: payoutSheikh
    PAID --> REFUNDED: refundStudent
    FAILED --> PENDING: retry (old record deleted)
    REFUNDED --> [*]
    PAYOUT_DONE --> [*]

If a PENDING payment record already exists when initiateCardPayment is called again, the existing Stripe PaymentIntent is retrieved and returned rather than creating a duplicate.


3.7 WebSocket / Signaling Module (websocket)

classDiagram
    class WebSocketConfig {
        +configureMessageBroker(registry)
        +registerStompEndpoints(registry)
    }

    class SignalingController {
        -messagingTemplate: SimpMessagingTemplate
        +handleSignal(message: SignalingMessage): void
    }

    class SignalingMessage {
        +type: String
        +sender: String
        +sessionId: String
        +data: Object
    }

    WebSocketConfig --> SignalingController
    SignalingController --> SignalingMessage

SignalingController has a single handler: @MessageMapping("/signal"). It receives any signaling message and broadcasts it to /topic/session/{sessionId}. All session participants (student + sheikh) are subscribed to this topic. The controller does not interpret the message content β€” that is handled entirely by the browser's WebRTC stack.


3.8 Data Entities

erDiagram
    USER ||--o| STUDENT_PROFILE : has
    USER ||--o| SHEIKH_PROFILE : has
    USER ||--o| STREAK : has
    USER ||--o{ AVAILABILITY : sets
    USER ||--o{ SESSION : "books as student"
    USER ||--o{ SESSION : "teaches as sheikh"
    USER ||--o{ RECITATION : records
    USER ||--o{ PAYMENT : "pays as student"
    USER ||--o{ PAYMENT : "receives as sheikh"
    USER ||--o{ REVIEW : writes
    SESSION ||--o| PAYMENT : has
    SESSION ||--o| REVIEW : has

    USER {
        bigint id PK
        varchar email UK
        varchar passwordHash
        varchar fullName
        enum role
        enum status
        varchar authProvider
        timestamp createdAt
    }

    SESSION {
        bigint id PK
        timestamp scheduledStart
        timestamp scheduledEnd
        timestamp actualStart
        timestamp actualEnd
        timestamp studentJoinedAt
        timestamp sheikhJoinedAt
        enum status
        decimal price
    }

    PAYMENT {
        bigint id PK
        decimal amount
        enum paymentStatus
        varchar transactionId UK
        decimal platformCommission
        decimal sheikhPayout
    }

    RECITATION {
        bigint id PK
        varchar surahName
        decimal wordScore
        decimal tashkeelScore
        jsonb mistakesReportJson
    }

4. External Services

4.1 AI Recitation Service

graph LR
    Backend -->|POST /analyze\nmultipart: audio + surah_number| AIService[AI Service\nngrok URL]
    Backend -->|GET /\nhealth check| AIService
    AIService -->|JSON: surah_info, overall_stats, ayahs| Backend
  • Hosted externally, exposed via ngrok tunnel
  • Requires ngrok-skip-browser-warning: true header
  • Returns per-ayah word-stage and tashkeel-stage analysis
  • Scores are 0–100 percentages (not 0–1 fractions)
  • Backend stores full response as JSONB in recitations.mistakes_report_json

4.2 Stripe

graph LR
    Backend -->|Create PaymentIntent| Stripe
    Frontend -->|confirmCardPayment with clientSecret| Stripe
    Backend -->|Retrieve PaymentIntent| Stripe
    Backend -->|Create Transfer to connected account| Stripe
    Backend -->|Create Refund| Stripe
    Backend -->|Create Express Account| Stripe

Two Stripe products are used:

  • Payment Intents β€” for student card payments
  • Stripe Connect (Custom accounts) β€” for sheikh payouts. Sheikhs get a Stripe connected account with a test bank attached.

4.3 Google OAuth

graph LR
    Frontend -->|Google Identity Services button| Google
    Google -->|idToken| Frontend
    Frontend -->|POST /api/auth/google { idToken, role }| Backend
    Backend -->|verify idToken| GoogleAPIClient[Google API Client]
    GoogleAPIClient -->|{ email, name, picture }| Backend

Google OAuth is used for social login. The backend verifies the idToken using the google-api-client library. If the email doesn't exist in the DB, a new user is created and directed to the profile completion wizard.

4.4 Gmail SMTP

Used exclusively for OTP password reset emails. Configured with:

  • Host: smtp.gmail.com
  • Port: 587 (STARTTLS)
  • Authentication: Gmail app password (not the account password)

5. Component Dependency Summary

graph TD
    subgraph Frontend
        FE_AUTH[Auth Pages] --> FE_API[API Layer]
        FE_SESSIONS[Session Pages] --> FE_API
        FE_PRACTICE[Practice Pages] --> FE_API
        FE_ADMIN[Admin Pages] --> FE_API
        FE_SESSIONS --> WEBRTC_HOOK[useWebRTC]
        FE_API --> AUTH_CTX[AuthContext]
    end

    subgraph Backend
        BE_CTRL[Controllers] --> BE_SVC[Services]
        BE_SVC --> BE_REPO[Repositories]
        BE_SVC --> BE_SCHEDULER[TaskScheduler]
        BE_CTRL --> SEC[Security Layer]
    end

    FE_API -->|REST| BE_CTRL
    WEBRTC_HOOK -->|WebSocket| BE_CTRL

    BE_SVC -->|JDBC| DB[(PostgreSQL)]
    BE_SVC --> STRIPE[Stripe]
    BE_SVC --> AI[AI Service]
    BE_SVC --> GMAIL[Gmail SMTP]
    SEC --> GOOGLE[Google OAuth]

6. Flow Index

The following per-flow design documents go deeper into each major feature using the components defined above:

Flow Document Components Involved
Session End Flow .kiro/specs/session-end-flow/design.md LiveSession, SessionEndWaiting, SessionService, PaymentServiceStripe
Authentication Flow next SignIn, Register, GoogleAuth, AuthContext, AuthService, JwtService
Session Booking Flow next SheikhProfile, MySessions, bookingApi, StudentSessionService, SheikhAvailabilityService
Payment Flow next MySessions, PaymentModal, paymentApi, PaymentServiceStripe, Stripe
AI Practice Flow next RecordingPractice, EvaluationReports, aiApi, StudentRecitationService, AI Service
Sheikh Approval Flow next SheikhApproval, InterviewSession, AdminService, SessionService (WebRTC)
WebRTC Session Flow next LiveSession, WaitingRoom, useWebRTC, SignalingController, SessionService