Spaces:
Sleeping
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
Userobject in React state andlocalStorage - On app load, re-calls
GET /api/auth/verify-profileto 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
Bearertoken fromAuthorizationheader - Validates expiry + signature via
JwtService - Sets
SecurityContextHolderso downstream code can callauthentication.getPrincipal()
CustomUserDetails
- Wraps the
UserJPA entity - Exposes
getUserId()β used by every controller to identify the caller isEnabled()returnsuser.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:
- Receives audio
MultipartFile+surahNumber - Converts audio to WAV using JAVE2 (ffmpeg wrapper)
- Calls external AI service via
RestTemplatewithmultipart/form-data - Parses AI JSON response into
AIReportDTO - Calculates
wordScore,tashkeelScore,totalScorefrom the AI report - Saves
Recitationentity withmistakesReportJson(full AI response as JSONB)
StudentSessionService handles booking:
- Validates
startDateTimeis in the future - Calls
SheikhAvailabilityService.validateSessionSlot()β checks slot is within availability and not already booked by REQUESTED/TOPAY/SCHEDULED sessions - Creates
Sessionwith 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 atscheduledStart + 1 min. If neither party joined βMISSED+ refundscheduleSheikhNoShowCheckβ fires atscheduledStart + 10 min. If sheikh didn't join βMISSED+ refundscheduleCancelIfUnpaidβ fires atscheduledStart - 5 min. If stillTOPAYβ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 onesgetSlotsForDate()β generates 1-hour slots within the availability window, minus already-booked sessionsvalidateSessionSlot()β 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: trueheader - 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 |