You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
VideoTube is a YouTube-like video-sharing platform enhanced with an AI-powered learning assistant. Users can upload, watch, like, comment on, and subscribe to video channels. A unique differentiator is the AI doubt-resolution system that lets students ask questions — either general or based on a specific video's transcript — and receive AI-generated answers.
sequenceDiagram
participant C as Client
participant RL as Rate Limiter
participant H as Helmet + CORS
participant R as Router
participant JWT as JWT Auth Middleware
participant Ctrl as Controller
participant SVC as Service
participant DB as MongoDB
participant CDN as Cloudinary
C->>RL: HTTP Request
RL->>H: Pass (if under limit)
H->>R: Route matching
R->>JWT: Protected route check
JWT->>JWT: Verify access token
JWT->>DB: Fetch user by token._id
JWT->>Ctrl: req.user attached
Ctrl->>SVC: Business logic
Ctrl->>DB: CRUD operations
Ctrl->>CDN: Upload/delete files
Ctrl->>C: ApiResponse JSON
Note: Live chat messages are sent via Socket.io (see Section 7). This REST endpoint only retrieves message history.
3.7 AI — /api/v1/ai
Method
Endpoint
Auth
Description
POST
/ask
✅
Ask a general AI question
GET
/history
✅
Get user's doubt history
GET
/notes-pdf
✅
Download doubt history as PDF
POST
/video-doubt
✅
Ask AI about a specific video
Ask AI — Request Body
{
"question": "What is a closure in JavaScript?"
}
Ask AI — Response
{
"statuscode": 200,
"data": {
"_id": "...",
"user": "...",
"question": "What is a closure in JavaScript?",
"answer": "A closure is a function that has access to...",
"topic": "general",
"createdAt": "..."
},
"message": "AI response generated successfully",
"success": true
}
Video Doubt — Request Body
{
"videoId": "60d21b4667d0d8992e610c85",
"question": "What did the speaker say about React hooks?"
}
4. Database Schema (MongoDB)
4.1 Entity-Relationship Diagram
erDiagram
USER ||--o{ VIDEO : owns
USER ||--o{ COMMENT : writes
USER ||--o{ LIKE : gives
USER ||--o{ SUBSCRIPTION : subscribes
USER ||--o{ SUBSCRIPTION : "is subscribed to"
USER ||--o{ CHATMESSAGE : sends
USER ||--o{ DOUBT : asks
VIDEO ||--o{ COMMENT : has
VIDEO ||--o{ LIKE : receives
VIDEO ||--o{ CHATMESSAGE : has
VIDEO ||--o{ TRANSCRIPTCHUNK : has
USER {
ObjectId _id PK
String username UK
String email UK
String password
String fullname
Object avtar "url + public_id"
Object coverImage "url + public_id"
ObjectId[] watchHistory "ref Video"
String refreshToken
}
VIDEO {
ObjectId _id PK
Object videoFile "url + public_id"
Object thumbnail "url + public_id"
String title
String description
Number duration
Number views
ObjectId owner FK "ref User"
String transcript
Boolean isPublished
}
COMMENT {
ObjectId _id PK
ObjectId video FK "ref Video"
ObjectId owner FK "ref User"
String content
}
LIKE {
ObjectId _id PK
ObjectId video FK "ref Video"
ObjectId likes FK "ref User"
}
SUBSCRIPTION {
ObjectId _id PK
ObjectId subscriber FK "ref User"
ObjectId channel FK "ref User"
}
CHATMESSAGE {
ObjectId _id PK
ObjectId video FK "ref Video"
ObjectId user FK "ref User"
String message
}
DOUBT {
ObjectId _id PK
ObjectId user FK "ref User"
String question
String answer
String topic "default: general"
}
TRANSCRIPTCHUNK {
ObjectId _id PK
ObjectId video FK "ref Video"
String text
Number[] embedding
}
Automatically hashes password with bcrypt (salt rounds = 10) before saving, only when password field is modified
isPasswordCorrect(password)
Compares plaintext password against stored hash using bcrypt.compare()
generateAccessToken()
Creates JWT with { _id, email, username, fullname }, signed with ACCESS_TOKEN_SECRET, expires in 1d
generateRefreshToken()
Creates JWT with { _id }, signed with REFRESH_TOKEN_SECRET, expires in 10d
5. Authentication Flow
sequenceDiagram
participant C as Client
participant S as Server
participant DB as MongoDB
Note over C,S: Registration
C->>S: POST /api/v1/users/register (form-data)
S->>S: Validate fields, check duplicates
S->>S: Upload avtar + coverImage to Cloudinary
S->>DB: Create User (password auto-hashed via pre-save hook)
S->>C: 201 { user } (no password/refreshToken)
Note over C,S: Login
C->>S: POST /api/v1/users/login { email, password }
S->>DB: Find user by email/username
S->>S: bcrypt.compare(password)
S->>S: Generate accessToken (1d) + refreshToken (10d)
S->>DB: Save refreshToken on User document
S->>C: 200 { user, accessToken, refreshToken } + httpOnly cookies
Note over C,S: Authenticated Request
C->>S: GET /api/v1/users/current-user (Bearer token)
S->>S: jwtverify middleware decodes token
S->>DB: Find user by decoded._id
S->>S: Attach user to req.user
S->>C: 200 { user }
Note over C,S: Token Refresh
C->>S: POST /api/v1/users/refresh-token
S->>S: Verify refreshToken from cookie/body
S->>DB: Match against stored refreshToken
S->>S: Generate new accessToken + refreshToken
S->>C: 200 { newAccessToken, newRefreshToken }
Note over C,S: Logout
C->>S: POST /api/v1/users/logout (Bearer token)
S->>DB: Unset refreshToken from User document
S->>S: Clear accessToken & refreshToken cookies
S->>C: 200 { "user log out" }
Loading
Token Config:
Access Token: signed with ACCESS_TOKEN_SECRET, expires in 1d
Refresh Token: signed with REFRESH_TOKEN_SECRET, expires in 10d
Both set as httpOnly, secure cookies
6. AI Pipeline
6.1 General Doubt Resolution (Working ✅)
graph LR
Q["Student Question"] --> Groq["Groq API (LLaMA 3.3 70B)"]
Groq --> A["AI Answer"]
A --> DB["Save to Doubt collection"]
DB --> Res["Return to student"]
Loading
Uses Groq SDK with llama-3.3-70b-versatile model
System prompt: "You are a helpful learning assistant that explains programming concepts clearly."
Each Q&A is persisted in the Doubt collection for history & PDF export
6.2 Video-Specific RAG Pipeline (Planned, not fully wired)
src/utils/splitTranscript.js — Chunk transcript into 200-word blocks
7. Real-time Chat (Socket.io)
File: src/Socket.io/socket.js
Events
Event
Direction
Payload
Description
connection
Server ← Client
—
New client connected
join-video-room
Server ← Client
videoId
Join a video's chat room
recived-message
Server → Room
{ user, message }
Broadcast message to room
disconnect
Server ← Client
—
Client disconnected
Client Usage Example
import{io}from"socket.io-client";constsocket=io("http://localhost:4000");// Join a video roomsocket.emit("join-video-room","VIDEO_ID_HERE");// Listen for messagessocket.on("recived-message",(data)=>{console.log(`${data.user}: ${data.message}`);});// Send a message (when implemented)socket.emit("send-message",{videoId: "VIDEO_ID_HERE",user: "username",message: "Hello!"});
⚠️Warning: The Socket.io implementation is incomplete. The send-message listener is missing — the io.to(videoId).emit(...) call is outside any event handler and references undefined variables. Messages are not being persisted to the ChatMessage model.
Higher-order function that wraps async route handlers, catches Promise rejections and forwards to next(err)
uploadOnCloudinary
src/utils/cloudinary.js
Uploads local file to Cloudinary with resource_type: "auto", deletes local copy after upload, returns Cloudinary response
deletefilefromCloudinary
src/utils/deletefilefromCloudinary.js
Delete file from Cloudinary by public_id
cosineSimilarity
src/utils/cosineSimilarity.js
Compute cosine similarity between two embedding vectors (for RAG search)
splitTranscript
src/utils/splitTranscript.js
Split transcript text into 200-word chunks for embedding generation
10. Environment Variables
Create a .env file in the project root with the following variables:
Variable
Purpose
Example
PORT
Server port
4000
MONGO_URI
MongoDB Atlas connection string
mongodb+srv://user:pass@cluster.mongodb.net
CROSS_ORI
CORS allowed origins
*
ACCESS_TOKEN_SECRET
JWT access token signing key
your-secret-key
ACCESS_TOKEN_EXPIRY
Access token TTL
1d
REFRESH_TOKEN_SECRET
JWT refresh token signing key
your-secret-key
REFRESH_TOKEN_EXPIRY
Refresh token TTL
10d
CLOUDINARY_API_KEY
Cloudinary API key
582355621587992
CLOUDINARY_API_SECRET
Cloudinary API secret
your-cloudinary-secret
CLOUDINARY_URL
Cloudinary full URL
cloudinary://key:secret@cloud-name
GROQ_API_KEY
Groq API key for LLaMA 3.3
gsk_...
OPENAI_API_KEY
OpenAI API key (for Whisper & Embeddings)
sk-...
⚠️Caution:OPENAI_API_KEY is referenced in embedding.service.js and transcript.service.js but is not defined in the current .env. The RAG pipeline will fail without it.
11. Known Bugs & Issues
⚠️Important: The following issues were identified during static code analysis. They should be addressed before production deployment.
Critical Bugs
#
File
Issue
1
src/middleware/auth.middleware.js (L7)
jwtverify uses asynchandler but signature is (req, res) — missing next parameter. next() is called on line 26 but it's undefined.
2
src/controller/ai.controller.js (L83)
generateNotes has (res, req) — parameters are swapped.
3
src/controller/ai.controller.js (L90)
if(doubts) should be if(!doubts.length) — condition is inverted.
4
src/controller/ai.controller.js (L107)
Video.find(ById(videoId)) — syntax error, should be Video.findById(videoId).
5
src/controller/ai.controller.js (L124)
bestChunk is referenced but never defined.
6
src/controller/like.controller.js (L28)
res.stauts(200),json(...) — typo (stauts → status) and comma instead of dot.
7
src/controller/like.controller.js (L18)
Querying likedBy field but model uses likes.
8
src/controller/subscription.controller.js (L46)
Returns newSubscription but variable is named newSubscriber.
9
src/controller/video.controller.js (L98)
Like is used but never imported.
10
src/controller/video.controller.js (L101)
console(...) — missing .log.
11
src/Socket.io/socket.js (L13)
io.to(videoId).emmit(...) — typo (emmit → emit) and placed outside event handler; videoId, user, message are undefined.
12
src/utils/splitTranscript.js (L2)
Splits on empty string "" instead of space " ".
13
src/utils/splitTranscript.js (L7)
Infinite loop: i+chunkSize doesn't mutate i — should be i+=chunkSize.
14
src/service/pdf.service.js (L17-22)
Uses iteam as parameter but item in the body — ReferenceError.
15
src/models/user.model.js (L29)
reuired typo — should be required.
16
src/middleware/ratelimmiter/ratelimmiter.js
windowsMs — should be windowMs (express-rate-limit API).
Architectural Issues
#
Issue
Recommendation
1
Video upload route field names (videoupload, thumbnailupload) don't match controller expectations (video, thumbnail)
Align multer field names with req.files access patterns
2
No validation library (Joi, Zod) — all validation is manual
Add schema validation for request bodies
3
User registration stores avtar.url as a string but model defines it as { url, public_id } object
Fix registration controller to save the full object
4
DB_NAME is extracted but never used in the connection string
Append /${DB_NAME} to MONGO_URI
5
No OPENAI_API_KEY in .env — transcript & embedding services will crash
Add the key or disable unused services
6
The ApiRatelimiter (general) is defined but never mounted
Mount it globally or remove it
7
Comment route path is /commnets (typo)
Fix to /comments
12. Dependencies
Package
Version
Purpose
express
5.2.1
Web framework
mongoose
9.2.0
MongoDB ODM
socket.io
4.8.3
Real-time WebSocket communication
jsonwebtoken
9.0.3
JWT generation & verification
bcrypt
6.0.0
Password hashing
cloudinary
2.9.0
Cloud file storage (images, videos)
multer
2.0.2
Multipart form-data file uploads
cors
2.8.6
Cross-Origin Resource Sharing
helmet
8.1.0
HTTP security headers
cookie-parser
1.4.7
Parse cookies from requests
express-rate-limit
8.2.1
API rate limiting
groq-sdk
1.1.1
Groq AI (LLaMA) integration
@google/generative-ai
0.24.1
Google Gemini (commented out, unused)
openai
6.31.0
OpenAI Whisper + Embeddings
fluent-ffmpeg
2.1.3
Audio extraction from video
ffmpeg-static
5.3.0
Static FFmpeg binary
pdfkit
0.18.0
PDF generation
axios
1.13.6
HTTP client (unused currently)
dotenv
17.2.4
Environment variable loader
nodemon
3.1.11
Dev: auto-restart on file changes
13. How to Run
# 1. Clone the repository
git clone <repo-url>cd Backend
# 2. Install dependencies
npm install
# 3. Create .env with required variables (see Section 10)
cp .env.example .env # then fill in your values# 4. Start development server
npm run dev
# → Server runs on http://localhost:4000
The dev script: nodemon -r dotenv/config --experimental-json-modules src/index.js
Testing with Postman
Register → POST http://localhost:4000/api/v1/users/register (form-data with avatar & cover image files)
Login → POST http://localhost:4000/api/v1/users/login (get access token from response)
Set Auth Header → Authorization: Bearer <accessToken>
Upload Video → POST http://localhost:4000/api/v1/video/upload (form-data with video & thumbnail files)
Ask AI → POST http://localhost:4000/api/v1/ai/ask (JSON body with question)