Skip to content

Latest commit

 

History

History
1444 lines (1135 loc) · 34.9 KB

File metadata and controls

1444 lines (1135 loc) · 34.9 KB

MLera Backend API Documentation

Base URL

Production: http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com
Local: http://localhost

Table of Contents

  1. Authentication
  2. User Management
  3. Course Management
  4. Module Management
  5. Practice Quiz
  6. Notifications
  7. Health Checks

Authentication

All protected endpoints require a JWT token in the Authorization header:

Authorization: Bearer <your_jwt_token>

How to Get Token

  1. Register a user via /user/register
  2. Login via /user/login to receive JWT token
  3. Use token in subsequent requests

User Management

1. Register User

Endpoint: POST /user/register

Description: Creates a new user account with hashed password, sets up notification preferences, and sends welcome email.

Authentication: None (Public)

Request Body:

{
  "Name": "John Doe",
  "Phone": "9876543210",
  "Email": "john.doe@example.com",
  "Profession": "Software Engineer",
  "Password": "securePassword123"
}

Validation Rules:

  • Name: 3-25 characters
  • Phone: Minimum 10 characters
  • Email: Valid email format
  • Profession: Minimum 3 characters
  • Password: Minimum 6 characters

Response: 201 Created

{
  "message": "User registered successfully",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

What Happens:

  1. Generates unique UUID for user
  2. Hashes password with bcrypt
  3. Stores user in PostgreSQL User and Auth tables
  4. Creates user profile in DynamoDB (Notification service)
  5. Sends registration confirmation email via SNS → SQS → Lambda

Error Responses:

  • 500: Database error or email already exists

2. Login User

Endpoint: POST /user/login

Description: Authenticates user credentials and returns JWT token valid for 1 hour.

Authentication: None (Public)

Request Body:

{
  "Email": "john.doe@example.com",
  "Password": "securePassword123"
}

Response: 200 OK

{
  "message": "Login successful",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

What Happens:

  1. Queries Auth table for email
  2. Verifies password hash using bcrypt
  3. Generates JWT token with userId and 1-hour expiration
  4. Returns token for subsequent authenticated requests

Error Responses:

  • 401: Invalid email or password
  • 500: Internal server error

3. Get User Profile

Endpoint: GET /user/profile

Description: Retrieves authenticated user's profile information including enrolled courses.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Response: 200 OK

{
  "Name": "John Doe",
  "Profession": "Software Engineer",
  "Phone": "9876543210",
  "Email": "john.doe@example.com"
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for profile data
  3. If cache miss, queries PostgreSQL User table
  4. Caches result in Redis for faster subsequent requests
  5. Returns user profile

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: User not found
  • 500: Internal server error

4. Delete User Account

Endpoint: POST /user/delete

Description: Permanently deletes authenticated user's account and all associated data, then sends a confirmation email.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Response: 200 OK

{
  "message": "Account deleted successfully"
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Sends delete event to DB API
  3. DB API sends event to SQS queue and clears Redis cache
  4. Lambda consumer processes event:
    • Deletes user from User table
    • CASCADE automatically deletes from:
      • Auth (login credentials)
      • UserCourse (course enrollments)
      • UserModuleProgress (module progress)
      • Quiz (quiz attempts)
      • PracticeQuiz (practice quiz statistics)
  5. Sends notification request to Notification API
  6. Notification API:
    • Retrieves user data from DynamoDB
    • Fetches "AccountDeletion" email template
    • Formats confirmation message
    • Publishes to SNS → SQS → Lambda sends email

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 500: Internal server error

Note: This action is irreversible. All user data will be permanently deleted.


Course Management

5. Purchase Course

Endpoint: POST /course/purchase

Description: Enrolls authenticated user in a course, granting access to all modules, and sends a course purchase notification email.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Request Body:

{
  "courseName": "Python Fundamentals"
}

Response: 201 Created

{
  "message": "Course 'Python Fundamentals' purchased successfully by user 123e4567-e89b-12d3-a456-426614174000"
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Sends purchase event to DB API
  3. DB API processes event:
    • Looks up courseId from Course table
    • Inserts record into UserCourse table
  4. Sends notification request to Notification API with CourseName
  5. Notification API sends "CoursePurchase" email to user
  6. Caches purchase in Redis

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Course not found
  • 500: Internal server error

6. Get Course Progress

Endpoint: GET /course/progress?courseName=<course_name>

Description: Fetches course progress statistics showing completed, in-progress, and pending modules.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Query Parameters:

  • courseName (required): Name of the course

Example:

GET /course/progress?courseName=Python%20Fundamentals

Response: 200 OK

{
  "inProgress": {
    "Introduction to Python": ["intro", "basics", "functions"],
    "Variables and Data Types": ["variables", "types"]
  },
  "completed": [
    "Control Flow",
    "Loops",
    "Functions Advanced"
  ]
}

Response Fields:

  • inProgress: Dictionary mapping module names to arrays of completed pages (TEXT[])
  • completed: Array of completed module names

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for progress data
  3. If cache miss, sends request to DB API
  4. DB API queries:
    • Gets courseId from Course table
    • Queries UserModuleProgress joined with Module table:
      • For completed modules: Returns module names where Completed = TRUE
      • For in-progress modules: Returns module names and CompletedPage arrays where Completed = FALSE
  5. Returns dictionary with in-progress modules (with page arrays) and completed module names
  6. Caches result in Redis

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Course not found
  • 500: Internal server error

7. Get Enrolled Courses

Endpoint: GET /course/enrolled

Description: Retrieves list of all courses the authenticated user has enrolled in.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Response: 200 OK

{
  "courses": [
    "Python Fundamentals",
    "Advanced JavaScript",
    "Data Structures"
  ]
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for enrolled courses
  3. If cache miss, queries DB API
  4. DB API joins UserCourse and Course tables to get course names
  5. Caches result in Redis for faster subsequent requests

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: No enrolled courses found
  • 500: Internal server error

Module Management

8. Update Module Progress

Endpoint: POST /module/update

Description: Records the current page/section user is studying to enable resume functionality.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Request Body:

{
  "ModuleName": "Introduction to Python",
  "CompletedPageName": "Variables",
  "LastseenPageName": "Data Types"
}

Response: 201 Created

{
  "message": "Progress for module 'Introduction to Python' updated successfully for user 123e4567-e89b-12d3-a456-426614174000"
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Sends update event to SQS queue with CompletedPageName and LastseenPageName
  3. Lambda consumer processes event:
    • Looks up moduleId from Module table
    • Upserts record in UserModuleProgress table:
      • CompletedPage: TEXT[] array - appends new page if not already present
      • LastSeenPage: TEXT field - updates to most recent page visited
      • Completed: Set to false
  4. Invalidates Redis cache for resume data and in-progress modules

Database Schema:

  • CompletedPage (TEXT[]): Array of all unique pages user has completed
  • LastSeenPage (TEXT): Most recent page user visited
  • Logic prevents duplicate pages in CompletedPage array

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Module not found
  • 500: Internal server error

9. Complete Module

Endpoint: POST /module/complete

Description: Marks module as completed with quiz score and triggers completion notification email.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Request Body:

{
  "ModuleName": "Introduction to Python",
  "QuizPercentage": 85.5
}

Validation Rules:

  • QuizPercentage: 0-100, required
  • ModuleName: Required

Response: 201 Created

{
  "message": "Module 'Introduction to Python' completed successfully for user 123e4567-e89b-12d3-a456-426614174000"
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Sends completion event to DB API
  3. DB API processes event:
    • Looks up moduleId from Module table
    • Updates UserModuleProgress: Completed = true, CompletedOn = timestamp
    • Inserts quiz record into Quiz table with score
  4. Sends notification request to Notification API with ModuleName and QuizPercentage
  5. Notification API:
    • Retrieves user data from DynamoDB
    • Fetches "ModuleCompletion" email template
    • Formats message with user name, module name, and quiz score
    • Publishes to SNS → SQS → Lambda sends email

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Module not found
  • 500: Internal server error

10. Resume Module

Endpoint: GET /module/resume?moduleName=<module_name>

Description: Retrieves the last page user accessed in a module to continue from where they left off.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Query Parameters:

  • moduleName (required): Name of the module

Example:

GET /module/resume?moduleName=Introduction%20to%20Python

Response: 200 OK

{
  "lastPage": "Data Types"
}

Note: Returns the LastSeenPage value, which is the most recent page the user visited, not the CompletedPage array.

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for resume data
  3. If cache miss, sends request to DB API
  4. DB API queries UserModuleProgress table for last saved page
  5. Caches result in Redis

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Module not found or no progress saved
  • 500: Internal server error

11. Get In-Progress Modules

Endpoint: GET /module/inProgress

Description: Retrieves all modules the authenticated user is currently studying (started but not completed).

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Response: 200 OK

{
  "modules": [
    "Introduction to Python",
    "Variables and Data Types",
    "Control Flow"
  ],
  "courseNames": [
    "Python Fundamentals",
    "Python Fundamentals",
    "Python Fundamentals"
  ],
  "lastPages": [
    ["intro", "basics", "functions"],
    ["variables", "types"],
    ["if-statements"]
  ]
}

Note: lastPages returns TEXT[] arrays containing all unique pages completed for each module.

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for in-progress modules
  3. If cache miss, queries DB API
  4. DB API joins Module, UserModuleProgress, and Course tables
  5. Filters for modules where Completed = FALSE
  6. Returns module names, associated course names, and last accessed pages
  7. Caches result in Redis

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: No in-progress modules found
  • 500: Internal server error

12. Get Completed Modules

Endpoint: GET /module/completed

Description: Retrieves the 3 most recently completed modules for the authenticated user.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Response: 200 OK

{
  "modules": [
    "Introduction to Python",
    "Variables and Data Types",
    "Functions"
  ],
  "completionTimes": [
    "2024-01-15T10:30:00",
    "2024-01-14T15:45:00",
    "2024-01-13T09:20:00"
  ]
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for completed modules
  3. If cache miss, queries DB API
  4. DB API joins Module and UserModuleProgress tables
  5. Filters for modules where Completed = TRUE
  6. Orders by CompletedOn DESC and limits to 3 most recent
  7. Returns module names and completion timestamps in ISO format
  8. Caches result in Redis

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: No completed modules found
  • 500: Internal server error

Practice Quiz

13. Submit Practice Quiz

Endpoint: POST /practicequiz/submit

Description: Submits practice quiz score and updates user's quiz statistics (highest, lowest, attempts).

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Request Body:

{
  "moduleName": "Introduction to Python",
  "score": 85
}

Response: 201 Created

{
  "message": "Quiz submitted successfully"
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Sends submission event to SQS queue
  3. Lambda consumer processes event:
    • Looks up moduleId from Module table
    • Upserts record in PracticeQuiz table:
      • Updates HighestScore if current score is higher
      • Updates LowestScore if current score is lower
      • Increments Attempts counter
  4. Updates Redis cache with new statistics

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Module not found
  • 500: Internal server error

14. Get Practice Quiz Report

Endpoint: GET /practicequiz/report?moduleName=<module_name>

Description: Retrieves comprehensive quiz performance statistics for a specific module.

Authentication: Required (JWT Bearer Token)

Request Headers:

Authorization: Bearer <jwt_token>

Query Parameters:

  • moduleName (required): Name of the module

Example:

GET /practicequiz/report?moduleName=Introduction%20to%20Python

Response: 200 OK

{
  "HighestScore": 95,
  "LowestScore": 65,
  "Attempts": 5
}

What Happens:

  1. Validates JWT token and extracts userId
  2. Checks Redis cache for report data
  3. If cache miss, queries DB API
  4. DB API queries PracticeQuiz table for user's statistics
  5. Caches result in Redis

Error Responses:

  • 401: Invalid or expired token
  • 403: Forbidden - Insufficient permissions
  • 404: Module not found or no quiz attempts
  • 500: Internal server error

Notifications

15. Create User in Notification System

Endpoint: POST /api/v1/user/create

Description: Creates user profile in DynamoDB for notification preferences (called internally by Main API during registration).

Authentication: None (Internal API)

Request Body:

{
  "userId": "123e4567-e89b-12d3-a456-426614174000",
  "name": "John Doe",
  "email": "john.doe@example.com"
}

Response: 200 OK

{
  "user_id": "123e4567-e89b-12d3-a456-426614174000"
}

What Happens:

  1. Stores user data in DynamoDB Users table
  2. Sets default notification channel to "email"
  3. Prepares user for receiving notifications

Error Responses:

  • 500: DynamoDB write error

16. Send Notification

Endpoint: POST /notify/

Description: Sends event-based notification to user (registration, module completion, etc.).

Authentication: None (Internal API)

Request Body:

For Registration:

{
  "userId": "123e4567-e89b-12d3-a456-426614174000",
  "TemplateType": "Registration"
}

For Module Completion:

{
  "userId": "123e4567-e89b-12d3-a456-426614174000",
  "TemplateType": "ModuleCompletion",
  "ModuleName": "Introduction to Python",
  "QuizPercentage": 85
}

For Course Purchase:

{
  "userId": "123e4567-e89b-12d3-a456-426614174000",
  "TemplateType": "CoursePurchase",
  "CourseName": "Python Fundamentals"
}

For Account Deletion:

{
  "userId": "123e4567-e89b-12d3-a456-426614174000",
  "TemplateType": "AccountDeletion"
}

Response: 200 OK

{
  "message": "Notification sent successfully"
}

What Happens:

  1. Retrieves user data from DynamoDB Users table
  2. Validates required fields based on TemplateType:
    • ModuleCompletion: Requires ModuleName and QuizPercentage
    • CoursePurchase: Requires CourseName
    • AccountDeletion: No additional fields required
  3. Fetches email template from DynamoDB Templates table
  4. Formats message by replacing placeholders:
    • {Name} → User's name
    • {ModuleName} → Module name (for ModuleCompletion)
    • {QuizPercentage} → Quiz score (for ModuleCompletion)
    • {CourseName} → Course name (for CoursePurchase)
  5. Publishes message to SNS topic with channel filter
  6. SNS routes to appropriate SQS queue (email, apn, fcm)
  7. Lambda consumer processes queue:
    • Email Lambda sends via Gmail SMTP
    • Other channels handled by respective consumers

Error Responses:

  • 400: Missing required fields (e.g., QuizPercentage and ModuleName for ModuleCompletion, CourseName for CoursePurchase)
  • 404: User or template not found
  • 500: SNS publish error or message formatting failure

Health Checks

17. Nginx Health Check

Endpoint: GET /health

Description: Checks if Nginx reverse proxy is running.

Authentication: None (Public)

Response: 200 OK

{
  "status": "healthy"
}

18. Main API Health Check

Endpoint: GET /health (on Main API container)

Description: Checks if Main API service is running.

Authentication: None (Public)

Response: 200 OK

{
  "status": "healthy"
}

19. DB API Health Check

Endpoint: GET /health (on DB API container)

Description: Checks if DB API service is running.

Authentication: None (Public)

Response: 200 OK

{
  "status": "healthy"
}

20. Notification API Health Check

Endpoint: GET /health (on Notification API container)

Description: Checks if Notification API service is running.

Authentication: None (Public)

Response: 200 OK

{
  "status": "healthy"
}

Error Response Format

All endpoints return errors in this format:

{
  "detail": "Error message describing what went wrong"
}

Common HTTP Status Codes:

  • 200: Success
  • 201: Created (for POST endpoints that create resources)
  • 400: Bad request (validation error)
  • 401: Unauthorized (invalid/expired token)
  • 403: Forbidden (authenticated but insufficient permissions)
  • 404: Resource not found
  • 500: Internal server error

Nginx Reverse Proxy Configuration

Overview

Nginx acts as a reverse proxy and API gateway sitting between the AWS Load Balancer and your backend services. It's the single entry point that routes incoming requests to the appropriate microservice.

Request Flow

Client → AWS ALB (Port 80) → EC2 (Port 80) → Nginx Container (Port 80) → Backend Services
                                                      ↓
                                    ┌─────────────────┼─────────────────┐
                                    ↓                 ↓                 ↓
                            Main API (8000)   DB API (8080)   Notification API (8001)

Nginx Configuration Breakdown

1. Events Block

events {
    worker_connections 1024;
}

Purpose: Configures how Nginx handles connections

  • worker_connections 1024: Each Nginx worker process can handle up to 1024 simultaneous connections
  • With default 1 worker, this means 1024 concurrent connections total

2. Upstream Blocks

upstream main_api {
    server main-api:8000;
}

upstream db_api {
    server db-api:8080;
}

upstream notification_api {
    server notification-api:8001;
}

Purpose: Defines backend service groups for load balancing

  • main-api:8000: Docker service name and port (Docker DNS resolves this)
  • db-api:8080: Database service endpoint
  • notification-api:8001: Notification service endpoint

Why use upstreams?

  • Enables load balancing if you scale to multiple containers
  • Provides health checking capabilities
  • Cleaner configuration with reusable names

3. Server Block

server {
    listen 80;
    ...
}

Purpose: Nginx listens on port 80 for incoming HTTP requests from the Load Balancer

4. Health Check Endpoint

location /health {
    return 200 "healthy\n";
    add_header Content-Type text/plain;
}

Purpose: ALB Target Group health checks

  • ALB periodically sends GET /health requests
  • Nginx responds with 200 OK if running
  • If Nginx is down, ALB marks the EC2 instance as unhealthy

Request: http://your-alb.amazonaws.com/health Response: 200 OK with body "healthy"

5. Location Blocks (Routing Rules)

Main API Routes:

location /api/main/ {
    proxy_pass http://main_api/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

What happens:

  1. Client requests: http://alb.com/api/main/user/login
  2. Nginx matches /api/main/ prefix
  3. Strips /api/main and forwards /user/login to main-api:8000
  4. Main API receives: GET /user/login

Headers explained:

  • Host: Original hostname from client request
  • X-Real-IP: Client's actual IP address (not ALB's IP)
  • X-Forwarded-For: Chain of proxy IPs (for tracking request path)

DB API Routes:

location /api/db/ {
    proxy_pass http://db_api/;
    ...
}
  • Client: http://alb.com/api/db/user/profile/123
  • Forwards to: db-api:8080/user/profile/123

Notification API Routes:

location /api/notification/ {
    proxy_pass http://notification_api/;
    ...
}
  • Client: http://alb.com/api/notification/notify/
  • Forwards to: notification-api:8001/notify/

Why Use Nginx?

1. Single Entry Point

  • One public endpoint for all services
  • Simplifies client configuration
  • Easier to manage SSL/TLS certificates

2. Service Isolation

  • Backend services don't need to be publicly accessible
  • Only Nginx container exposes port 80
  • Services communicate via Docker internal network

3. Load Balancing

# If you scale to multiple containers:
upstream main_api {
    server main-api-1:8000;
    server main-api-2:8000;
    server main-api-3:8000;
}

Nginx automatically distributes requests across instances

4. Request Routing

  • Routes by URL path prefix
  • No need for clients to know individual service ports
  • Easy to add/remove services without client changes

5. Performance

  • Nginx is extremely fast and lightweight
  • Handles static content efficiently
  • Connection pooling to backend services
  • Request buffering

6. Security

  • Hides internal service architecture
  • Can add rate limiting
  • Can add authentication at gateway level
  • Protects against slow HTTP attacks

Current Architecture

┌─────────────────────────────────────────────────────────────┐
│                    AWS Load Balancer                        │
│                  (Port 80, Public IP)                       │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ↓
┌─────────────────────────────────────────────────────────────┐
│                      EC2 Instance                           │
│  ┌───────────────────────────────────────────────────────┐ │
│  │              Nginx Container (Port 80)                │ │
│  │                                                       │ │
│  │  Routes:                                             │ │
│  │  /health          → 200 OK                          │ │
│  │  /api/main/*      → main-api:8000                   │ │
│  │  /api/db/*        → db-api:8080                     │ │
│  │  /api/notification/* → notification-api:8001        │ │
│  └───────────┬───────────────┬───────────────┬──────────┘ │
│              │               │               │            │
│  ┌───────────▼─────┐ ┌──────▼──────┐ ┌─────▼──────────┐ │
│  │   Main API      │ │   DB API    │ │ Notification   │ │
│  │   (Port 8000)   │ │ (Port 8080) │ │   API (8001)   │ │
│  └─────────────────┘ └─────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Example Request Flow

User Login Request:

  1. Client sends:

    POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/api/main/user/login
    
  2. AWS ALB receives:

    • Checks target group health
    • Forwards to EC2 instance on port 80
  3. Nginx receives:

    POST /api/main/user/login
    
    • Matches location /api/main/
    • Strips /api/main prefix
    • Adds proxy headers
  4. Main API receives:

    POST /user/login
    Headers:
      Host: mleraalb-1595386243.ap-south-1.elb.amazonaws.com
      X-Real-IP: 203.0.113.45 (client's actual IP)
      X-Forwarded-For: 203.0.113.45
    
  5. Response flows back:

    Main API → Nginx → ALB → Client
    

Docker Compose Integration

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"  # Expose to host
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    networks:
      - app-network

  main-api:
    # No ports exposed to host!
    # Only accessible via Docker network
    networks:
      - app-network

Key points:

  • Only Nginx exposes port 80 to the host
  • Backend services are isolated on app-network
  • Services communicate using Docker DNS (service names)

Benefits of This Architecture

  1. Microservices Pattern: Each service is independent
  2. Scalability: Can scale services independently
  3. Security: Backend services not directly accessible
  4. Flexibility: Easy to add new services or change routing
  5. Monitoring: Single point to add logging/metrics
  6. SSL Termination: Can add HTTPS at Nginx level

Potential Enhancements

# Rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

location /api/main/ {
    limit_req zone=api_limit burst=20;
    proxy_pass http://main_api/;
}

# Caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m;

location /api/main/static/ {
    proxy_cache api_cache;
    proxy_cache_valid 200 1h;
    proxy_pass http://main_api/static/;
}

# CORS headers
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";

# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

Data Flow Architecture

User Registration Flow

Client → Main API → DB API → PostgreSQL
                  ↓
            Notification API → DynamoDB
                  ↓
                 SNS → SQS → Lambda → Gmail SMTP

Course Purchase Flow

Client → Main API → DB API → SQS Queue
                              ↓
                         Lambda Consumer → PostgreSQL
                              ↓
                         Redis Cache

Module Completion Flow

Client → Main API → DB API → PostgreSQL
                  ↓
            Notification API (ModuleName, QuizPercentage)
                  ↓
            DynamoDB (User + Template)
                  ↓
                 SNS → SQS → Lambda → Email

Course Purchase Flow

Client → Main API → DB API → PostgreSQL
                  ↓
            Notification API (CourseName)
                  ↓
            DynamoDB (User + Template)
                  ↓
                 SNS → SQS → Lambda → Email

Account Deletion Flow

Client → Main API → DB API → SQS Queue
                              ↓
                         Lambda Consumer → PostgreSQL (CASCADE DELETE)
                              ↓
                         Clears Redis Cache
                              ↓
                    Notification API (AccountDeletion)
                              ↓
                    DynamoDB (User + Template)
                              ↓
                         SNS → SQS → Lambda → Email

Database Schema

PostgreSQL Tables

User

  • UserId (UUID, PK)
  • Name (TEXT)
  • Profession (TEXT)
  • Phone (TEXT)
  • Email (TEXT, UNIQUE)
  • CreatedOn (TIMESTAMP)

Auth

  • Email (TEXT, PK)
  • PasswordHash (TEXT)
  • UserId (UUID, FK → User)

Course

  • CourseId (UUID, PK)
  • Type (TEXT)
  • CourseName (TEXT, UNIQUE)

Module

  • ModuleId (UUID, PK)
  • CourseId (UUID, FK → Course)
  • ModuleName (TEXT)

UserCourse

  • UserId (UUID, PK, FK → User)
  • CourseId (UUID, PK, FK → Course)

UserModuleProgress

  • UserId (UUID, PK, FK → User)
  • ModuleId (UUID, PK, FK → Module)
  • CompletedPage (TEXT)
  • Completed (BOOLEAN)
  • CompletedOn (TIMESTAMP)

Quiz

  • QuizId (UUID, PK)
  • UserId (UUID, FK → User)
  • ModuleId (UUID, FK → Module)
  • Percent (INTEGER)
  • Pass (BOOLEAN)
  • AttemptedOn (TIMESTAMP)

PracticeQuiz

  • UserId (UUID, PK, FK → User)
  • ModuleId (UUID, PK, FK → Module)
  • HighestScore (INTEGER)
  • LowestScore (INTEGER)
  • Attempts (INTEGER)

DynamoDB Tables

Users

  • userId (String, PK)
  • channel (String, SK)
  • id (String) - email address
  • Name (String)

Templates

  • TemplateType (String, PK)
  • Channel (String, SK)
  • TemplateId (String)
  • Subject (String)
  • Body (String)
  • Version (Number)
  • CreatedAt (String)

Rate Limiting & Performance

  • Connection Pooling:

    • PostgreSQL: 1-10 connections
    • Redis: 10 max connections
  • Caching Strategy:

    • User profiles cached in Redis
    • Course progress cached in Redis
    • Module resume data cached in Redis
    • Quiz reports cached in Redis
  • Async Processing:

    • Course purchases processed via SQS
    • Module updates processed via SQS
    • Notifications sent via SNS → SQS → Lambda

Security Considerations

  1. Password Security: Bcrypt hashing with salt
  2. JWT Tokens: 1-hour expiration, HS256 algorithm
  3. CORS: Configured for specific origins (update in production)
  4. Environment Variables: Sensitive data stored in .env
  5. Database: Connection pooling prevents exhaustion
  6. API Gateway: Nginx reverse proxy for routing

Testing Examples

Using cURL

Register User:

curl -X POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/user/register \
  -H "Content-Type: application/json" \
  -d '{
    "Name": "John Doe",
    "Phone": "9876543210",
    "Email": "john.doe@example.com",
    "Profession": "Software Engineer",
    "Password": "securePassword123"
  }'

Login:

curl -X POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/user/login \
  -H "Content-Type: application/json" \
  -d '{
    "Email": "john.doe@example.com",
    "Password": "securePassword123"
  }'

Get Profile (with token):

curl -X GET http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/user/profile \
  -H "Authorization: Bearer <your_jwt_token>"

Purchase Course:

curl -X POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/course/purchase \
  -H "Authorization: Bearer <your_jwt_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "courseName": "Python Fundamentals"
  }'

Support & Troubleshooting

Common Issues:

  1. 401 Unauthorized: Token expired or invalid

    • Solution: Login again to get new token
  2. 404 Not Found: Resource doesn't exist

    • Solution: Verify course/module names are correct
  3. 500 Internal Server Error: Server-side issue

    • Solution: Check logs with docker-compose logs -f
  4. Connection Timeout: Network/security group issue

    • Solution: Verify ALB security group allows port 80

Logs:

# View all logs
docker-compose logs -f

# View specific service
docker-compose logs -f main-api
docker-compose logs -f db-api
docker-compose logs -f notification-api

Version History

  • v1.0.0 (2024): Initial release with core functionality
    • User management
    • Course enrollment
    • Module progress tracking
    • Practice quizzes
    • Email notifications

Contact & Support

For issues or questions, check:

  • GitHub Issues: [Repository URL]
  • Documentation: This file
  • Deployment Guide: DEPLOYMENT.md