Detailed setup and configuration instructions for TAP after installation.
- Initial Setup
- Database Initialization
- User Roles & Permissions
- API Configuration
- Security Setup
- Email Configuration
- File Upload Configuration
Terminal 1 - Backend:
cd backend
npm run devTerminal 2 - Frontend:
cd frontend
npm run devAccess Points:
- Frontend: http://localhost:5173
- Backend API: http://localhost:5000
- API Documentation: http://localhost:5000/api-docs
Using API:
curl -X POST http://localhost:5000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "Admin User",
"email": "admin@tap.local",
"password": "SecurePassword123!",
"role": "admin"
}'Response:
{
"success": true,
"data": {
"id": "user_id_here",
"name": "Admin User",
"email": "admin@tap.local",
"role": "admin",
"token": "jwt_token_here"
}
}Faculty Account:
curl -X POST http://localhost:5000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "Dr. John Smith",
"email": "john.smith@tap.local",
"password": "FacultyPass123!",
"role": "faculty"
}'Student Account:
curl -X POST http://localhost:5000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane.doe@tap.local",
"password": "StudentPass123!",
"role": "student"
}'The database collections are automatically created when you first use them. However, you can manually initialize them:
# Connect to MongoDB
mongosh
# Use TAP database
use tap
# Create collections
db.createCollection("users")
db.createCollection("schedules")
db.createCollection("assignments")
db.createCollection("materials")
db.createCollection("attendance")
db.createCollection("grades")
# Verify collections
show collectionsFor better performance, create indexes on frequently queried fields:
# Users collection
db.users.createIndex({ "email": 1 }, { unique: true })
db.users.createIndex({ "role": 1 })
# Schedules collection
db.schedules.createIndex({ "userId": 1 })
db.schedules.createIndex({ "date": 1 })
# Assignments collection
db.assignments.createIndex({ "courseId": 1 })
db.assignments.createIndex({ "dueDate": 1 })
# Materials collection
db.materials.createIndex({ "courseId": 1 })
# Attendance collection
db.attendance.createIndex({ "userId": 1, "date": 1 })
# Grades collection
db.grades.createIndex({ "userId": 1, "courseId": 1 })Create backend/seeds/seed.js:
const mongoose = require('mongoose');
const User = require('../models/User');
const Schedule = require('../models/Schedule');
const seedDatabase = async () => {
try {
// Clear existing data
await User.deleteMany({});
await Schedule.deleteMany({});
// Create sample users
const users = await User.create([
{
name: 'Admin User',
email: 'admin@tap.local',
password: 'admin123',
role: 'admin'
},
{
name: 'Dr. John Smith',
email: 'john@tap.local',
password: 'faculty123',
role: 'faculty'
},
{
name: 'Jane Doe',
email: 'jane@tap.local',
password: 'student123',
role: 'student'
}
]);
console.log('✅ Database seeded successfully');
process.exit(0);
} catch (error) {
console.error('❌ Seeding failed:', error);
process.exit(1);
}
};
seedDatabase();Run seed script:
node backend/seeds/seed.js┌─────────────────────────────────────┐
│ ADMIN │
│ - Full system access │
│ - User management │
│ - System configuration │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ FACULTY │
│ - Create/manage courses │
│ - Create assignments │
│ - Grade students │
│ - View attendance │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ STUDENT │
│ - View schedules │
│ - Submit assignments │
│ - View grades │
│ - Mark attendance │
└─────────────────────────────────────┘
| Feature | Admin | Faculty | Student |
|---|---|---|---|
| User Management | ✅ | ❌ | ❌ |
| Create Courses | ✅ | ✅ | ❌ |
| Create Assignments | ✅ | ✅ | ❌ |
| Submit Assignments | ✅ | ❌ | ✅ |
| Grade Assignments | ✅ | ✅ | ❌ |
| View Grades | ✅ | ✅ | ✅ |
| Manage Attendance | ✅ | ✅ | ❌ |
| View Reports | ✅ | ✅ | ❌ |
Development:
VITE_API_URL=http://localhost:5000Production:
VITE_API_URL=https://api.your-domain.comConfigure in backend/.env:
# Rate limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100Implement in backend/middleware/rateLimiter.js:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: process.env.RATE_LIMIT_WINDOW_MS || 15 * 60 * 1000,
max: process.env.RATE_LIMIT_MAX_REQUESTS || 100,
message: 'Too many requests, please try again later'
});
module.exports = limiter;Structure your API with versioning:
/api/v1/auth/login
/api/v1/auth/register
/api/v1/schedules
/api/v1/assignments
/api/v1/materials
Generate Strong JWT Secret:
# Generate random string
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Add to .env:
JWT_SECRET=your_generated_secret_here
JWT_EXPIRE=7d
JWT_REFRESH_EXPIRE=30dConfigure bcrypt in backend/utils/passwordHash.js:
const bcrypt = require('bcryptjs');
const hashPassword = async (password) => {
const salt = await bcrypt.genSalt(10);
return await bcrypt.hash(password, salt);
};
const comparePassword = async (password, hash) => {
return await bcrypt.compare(password, hash);
};
module.exports = { hashPassword, comparePassword };For Production:
# Install SSL certificate (Let's Encrypt)
sudo certbot certonly --standalone -d your-domain.com
# Configure in backend
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/your-domain.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/your-domain.com/fullchain.pem')
};
https.createServer(options, app).listen(443);Configure in backend/server.js:
const cors = require('cors');
app.use(cors({
origin: process.env.CORS_ORIGIN?.split(',') || ['http://localhost:5173'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization']
}));Enable 2-Factor Authentication:
- Go to Google Account Security
- Enable 2-Step Verification
- Generate App Password
Configure in .env:
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_app_passwordEmail Service in backend/services/emailService.js:
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
secure: false,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
const sendEmail = async (to, subject, html) => {
try {
await transporter.sendMail({
from: process.env.EMAIL_USER,
to,
subject,
html
});
console.log('✅ Email sent successfully');
} catch (error) {
console.error('❌ Email sending failed:', error);
}
};
module.exports = { sendEmail };Password Reset Email:
<h1>Password Reset Request</h1>
<p>Click the link below to reset your password:</p>
<a href="http://localhost:5173/reset-password?token={{token}}">
Reset Password
</a>
<p>This link expires in 1 hour.</p>Create upload directory:
mkdir -p backend/uploads
mkdir -p backend/uploads/assignments
mkdir -p backend/uploads/materials
mkdir -p backend/uploads/profilesbackend/middleware/upload.js:
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
}
});
const upload = multer({
storage,
limits: {
fileSize: parseInt(process.env.MAX_FILE_SIZE) || 10 * 1024 * 1024
},
fileFilter: (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|gif|pdf|doc|docx|xls|xlsx/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb(new Error('Invalid file type'));
}
}
});
module.exports = upload;backend/routes/upload.js:
const express = require('express');
const upload = require('../middleware/upload');
const router = express.Router();
router.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
res.json({
success: true,
file: {
filename: req.file.filename,
path: `/uploads/${req.file.filename}`,
size: req.file.size,
mimetype: req.file.mimetype
}
});
});
module.exports = router;Next Steps: Proceed to API_DOCUMENTATION.md for detailed API endpoints.