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
Smart Student Hub v2 is a Next.js full-stack application. The API layer lives inside Next.js as App Router API Routes (app/api/*/route.js) served from the same process as the UI — no separate backend server.
erDiagram
USER ||--o{ ACTIVITY : "submits"
USER ||--o{ ACTIVITY : "approves"
USER {
int id PK
string name
string email UK
string role
string studentId UK
boolean isActive
}
ACTIVITY {
int id PK
int studentId FK
string title
string type
string filePath
string status
int approvedBy FK
decimal credits
}
Loading
Sequelize Associations (defined in lib/database.js)
All routes live under app/api/ as Next.js Route Handlers.
🔓 Auth — /api/auth
Method
Route
Access
Description
POST
/auth/register
Public
Register new user
POST
/auth/login
Public
Get JWT token
GET
/auth/profile
🔒 Any role
Current user profile
POST
/auth/admin-password-reset
Public + ADMIN_RESET_CODE
Create or reset admin
👨🎓 Students — /api/students
Method
Route
Roles
Description
GET
/students/profile
student, admin
My profile
PUT
/students/profile
student, admin
Update profile
POST
/students/upload-avatar
student, admin
Upload avatar (multipart/form-data)
POST
/students/activities
student, admin
Submit activity + optional certificate
GET
/students/activities
student, admin
List my activities (paginated, filterable)
PUT
/students/activities/:id
student, admin
Update pending activity
DELETE
/students/activities/:id
student, admin
Delete pending activity
GET
/students/activities/stats
student, admin
My dashboard statistics
GET
/students/browse
student, faculty, admin
Browse all student profiles
👨🏫 Faculty — /api/faculty
Method
Route
Roles
Description
GET
/faculty/stats
faculty, admin
Dashboard statistics
GET
/faculty/activities/pending
faculty, admin
Activities awaiting review
GET
/faculty/activities
faculty, admin
All activities (filterable)
PUT
/faculty/activities/:id
faculty, admin
Approve or reject + set credits
GET
/faculty/students
faculty, admin
All students with search/filter/pagination
👑 Admin — /api/admin
Method
Route
Roles
Description
GET
/admin/stats
admin
System-wide statistics
GET
/admin/users
admin
All users
POST
/admin/users
admin
Create user
PUT
/admin/users/:id
admin
Update user
DELETE
/admin/users/:id
admin
Delete user
POST
/admin/users/:id/toggle-status
admin
Activate / deactivate user
GET
/admin/reports
admin
Export reports
📁 Files — /api/files
Method
Route
Description
GET
/files/view?url=<fileUrl>
Inline view — serves local file from disk or proxies Cloudinary
GET
/files/download?url=<fileUrl>
Download — serves local file with Content-Disposition: attachment or redirects Cloudinary signed URL
Local files:url = /uploads/certificates/filename.pdf — read from public/uploads/ Cloudinary files:url = https://res.cloudinary.com/… — proxied or redirected
Auth & Authorization
JWT Flow
sequenceDiagram
participant C as Client
participant A as /api/auth/login
participant DB as Database
C->>A: POST {email, password}
A->>DB: SELECT user WHERE email
DB-->>A: User row
A->>A: bcrypt.compare(password, hash)
A->>A: jwt.sign({id, role}, JWT_SECRET)
A-->>C: {token, user}
C->>A: GET /api/auth/profile (Authorization: Bearer <token>)
A->>A: jwt.verify(token, JWT_SECRET)
A->>DB: SELECT user WHERE id
DB-->>A: User
A-->>C: User profile
Loading
Authorization Matrix
Endpoint
Student
Faculty
Admin
POST /auth/register
✅
✅
✅
POST /auth/login
✅
✅
✅
GET /students/profile
✅ own
❌
✅
PUT /students/profile
✅ own
❌
✅
POST /students/activities
✅
❌
✅
GET /students/activities
✅ own
❌
✅
GET /faculty/activities/pending
❌
✅
✅
PUT /faculty/activities/:id
❌
✅
✅
GET /faculty/students
❌
✅
✅
GET /admin/users
❌
❌
✅
POST /admin/users
❌
❌
✅
DELETE /admin/users/:id
❌
❌
✅
GET /admin/stats
❌
❌
✅
File Storage
How it works (lib/cloudStorage.js)
NODE_ENV === 'production'
AND Cloudinary credentials are real (not placeholder strings)
→ Upload to Cloudinary CDN → returns https://res.cloudinary.com/… URL
Otherwise (development, or missing creds)
→ Write to public/uploads/<folder>/<timestamp-random.ext>
→ Returns /uploads/<folder>/filename.ext
→ Next.js serves it as a static file from public/
sequenceDiagram
participant S as Student
participant API as /api/students/activities
participant FS as File Storage
participant DB as Database
S->>API: POST multipart/form-data (title, type, date, certificate)
API->>API: Verify JWT & role
API->>FS: uploadFile(buffer, 'certificates')
FS-->>API: /uploads/certificates/file.pdf (dev) OR Cloudinary URL (prod)
API->>DB: INSERT INTO activities (filePath=url, status='pending')
DB-->>API: Activity created
API-->>S: 201 {activity}
Loading
Faculty Review
sequenceDiagram
participant F as Faculty
participant API as /api/faculty/activities/:id
participant DB as Database
F->>API: PUT {status: 'approved', credits: 5, remarks: '...'}
API->>API: Verify JWT & faculty/admin role
API->>DB: UPDATE activities SET status, approvedBy, credits, remarks
DB-->>API: Updated row
API-->>F: 200 {activity}
Loading
File View/Download
sequenceDiagram
participant B as Browser
participant API as /api/files/view or /download
participant Disk as public/uploads
participant CDN as Cloudinary
B->>API: GET ?url=/uploads/certificates/file.pdf
API->>Disk: fs.readFileSync(public/uploads/certificates/file.pdf)
Disk-->>API: Buffer
API-->>B: 200 Content-Type: application/pdf
B->>API: GET ?url=https://res.cloudinary.com/...
API->>CDN: fetch(cloudinaryUrl)
CDN-->>API: Buffer
API-->>B: 200 Content-Type: application/pdf
Loading
Query Patterns
Student Stats
SELECTCOUNT(*) AS total,
SUM(CASE WHEN status='approved' THEN 1 ELSE 0 END) AS approved,
SUM(CASE WHEN status='pending' THEN 1 ELSE 0 END) AS pending,
SUM(CASE WHEN status='rejected' THEN 1 ELSE 0 END) AS rejected,
COALESCE(SUM(CASE WHEN status='approved' THEN credits ELSE 0 END), 0) AS totalCredits
FROM activities
WHERE studentId = ?;
Pending Activities for Faculty
SELECT a.*, u.name, u.email, u.studentId, u.department, u.year, u.programFROM activities a
INNER JOIN users u ONa.studentId=u.idWHEREa.status='pending'ORDER BYa.createdAtDESC;
Top Students by Credits (Admin)
SELECTu.name, u.email, u.department, u.year,
COUNT(a.id) AS activityCount,
SUM(a.credits) AS totalCredits
FROM users u
INNER JOIN activities a ONu.id=a.studentIdWHEREu.role='student'ANDa.status='approved'GROUP BYu.idORDER BY totalCredits DESCLIMIT10;
Error Handling
Standard API Errors
Error
HTTP Status
Cause
Missing/invalid JWT
401
No token or expired
Wrong role
403
Insufficient permissions
Not found
404
Record doesn't exist
Validation failed
400
Missing required fields
Duplicate email/studentId
409
Unique constraint
DB unavailable
503
Connection error
Unhandled exception
500
Internal server error
DB Sync Strategy (lib/database.js)
On startup, initDB() runs once (cached via globalThis):
Tries sync({ alter: true }) — adds missing columns without dropping data
On SQLite: disables FK checks during alter to avoid constraint errors
Falls back to sync() (no-op, just ensures tables exist) if alter fails
Always marks as initialized — never retries on every request
Environment Configuration
# Required for all environmentsJWT_SECRET=64_char_hex_stringJWT_EXPIRES_IN=7dDB_NAME=smart_student_hub.db# SQLite filename (dev)ADMIN_RESET_CODE=secure_random_code# Required for production onlyDATABASE_URL=postgresql://user:pass@host:5432/dbCLOUDINARY_CLOUD_NAME=your_cloud_nameCLOUDINARY_API_KEY=your_api_keyCLOUDINARY_API_SECRET=your_api_secret# Public (available in browser)NEXT_PUBLIC_API_URL=/apiNEXT_PUBLIC_APP_NAME=Smart Student HubNEXT_PUBLIC_APP_VERSION=2.0.0
Last Updated: February 2026 Version: 2.0 Maintained by: Smart Student Hub Development Team