A full-featured REST API backend for managing coding problems, testcases, and admin authentication. Built with Node.js, Express, PostgreSQL, and MongoDB.
Electronics-Astra Backend is a RESTful API service designed for managing coding problem platforms. It provides secure admin authentication with 2FA, problem management with image uploads, and comprehensive testcase handling for problem evaluation.
Current Status: Development/Beta - Not production-ready
- β Admin signup with email-based OTP verification (2FA)
- β JWT-based authentication
- β Secure password hashing (bcrypt)
- β Protected admin routes
- β Profile management (view, edit, delete)
- β CRUD operations for coding problems
- β Multi-image upload support
- β Rich problem metadata (title, difficulty, tags, description, constraints)
- β Public and admin-only endpoints
- β Cascade deletion (problems β testcases β files)
- β Create visible and hidden testcases
- β Link testcases to specific problems
- β Public vs. admin-only testcase visibility
- β Individual testcase CRUD operations
- β Automatic cleanup on problem deletion
| Category | Technology |
|---|---|
| Runtime | Node.js (v18+) |
| Framework | Express.js |
| Databases | PostgreSQL (admin data), MongoDB (problem data) |
| ORMs | Sequelize (PostgreSQL), Mongoose (MongoDB) |
| Authentication | JWT, bcryptjs |
| File Upload | Multer |
| Nodemailer | |
| Dev Tools | nodemon, dotenv |
Before you begin, ensure you have the following installed: commit the changes
-
Node.js (v18.0.0 or higher)
node --version # Should output v18.x.x or higher -
PostgreSQL (v12 or higher)
psql --version # Should output PostgreSQL 12.x or higher -
MongoDB (v5.0 or higher)
mongod --version # Should output v5.x.x or higher -
Git
git --version
- Gmail Account: Required for sending OTP emails (you'll need an App Password)
- MongoDB Atlas (Optional): For cloud MongoDB hosting
# Clone the repository
git clone https://github.com/mohith1976/electronics-astra.git
# Navigate to backend directory
cd electronics-astra/backendnpm installThis installs all required packages:
express,cors,dotenv- Server essentialssequelize,pg,pg-hstore- PostgreSQL ORMmongoose- MongoDB ODMjsonwebtoken,bcryptjs- Authenticationnodemailer- Email OTP deliverymulter,uuid- File uploadsnodemon- Development auto-reload
# Login to PostgreSQL
psql -U postgres
# Create database
CREATE DATABASE electronics_astra;
# Exit psql
\q# Start MongoDB service (Windows)
net start MongoDB
# Start MongoDB service (macOS/Linux)
sudo systemctl start mongod
# Verify MongoDB is running
mongosh
# Should connect successfully- Create account at MongoDB Atlas
- Create a new cluster (free tier available)
- Add database user with password
- Whitelist your IP address (or use 0.0.0.0/0 for development)
- Get connection string (looks like:
mongodb+srv://username:password@cluster0.mongodb.net/electronics-astra)
Create a .env file in the backend/ directory:
# In backend/ directory
touch .env # macOS/Linux
# OR
type nul > .env # WindowsAdd the following to your .env file:
# PostgreSQL Configuration
PG_HOST=localhost
PG_PORT=5432
PG_DATABASE=electronics_astra
PG_USER=postgres
PG_PASSWORD=your_postgres_password
# MongoDB Configuration (choose one)
# Local MongoDB
MONGO_URI=mongodb://localhost:27017/electronics-astra
# OR Atlas MongoDB
# MONGO_URI=mongodb+srv://<username>:<password>@cluster0.mongodb.net/electronics-astra?retryWrites=true&w=majority
# Server Configuration
PORT=5000
JWT_SECRET=your_very_long_random_secret_key_here_min_32_characters
# Email Configuration (Gmail)
EMAIL_USER=your-email@gmail.com
EMAIL_PASS=your_16_character_app_password# Generate a secure random string (Node.js)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Copy the output and use it as your JWT_SECRET.
- Go to Google Account Security
- Enable 2-Step Verification
- Go to App Passwords
- Select "Mail" and "Other (Custom name)"
- Generate password (16 characters, no spaces)
- Use this as
EMAIL_PASSin.env
- Add
MONGO_URIto your.env. Example:- Local:
mongodb://localhost:27017/electronics-astra - Atlas:
mongodb+srv://<user>:<password>@cluster0.mongodb.net/electronics-astra?retryWrites=true&w=majority
- Local:
- Problems API stores images as URLs in the
imagesarray on the Problem document. - For local testing we save uploaded images to
/uploadsand serve them statically athttp://localhost:5000/uploads/<filename>. - For production, replace the upload handling with S3 or other object storage and save public URLs.
.env to version control. It's already in .gitignore.
npm run devYou should see:
Server running on port 5000
PostgreSQL connected
MongoDB connected successfully
npm startOpen browser and navigate to:
http://localhost:5000
You should see the Express welcome message or 404 (depending on root route configuration).
PostgreSQL connection fails
# Check PostgreSQL is running
# Windows
net start postgresql-x64-14
# macOS/Linux
sudo systemctl status postgresqlMongoDB connection fails
# Check MongoDB is running
# Windows
net start MongoDB
# macOS/Linux
sudo systemctl status mongod
# Check connection string
mongosh "your_MONGO_URI_here"Port 5000 already in use
- Change
PORT=5000to another port in.env(e.g.,PORT=5001)
http://localhost:5000
POST /api/otp/request
Content-Type: application/json
{
"email": "admin@example.com"
}Response (200):
{
"message": "OTP sent successfully"
}POST /api/admin/signup/verify
Content-Type: application/json
{
"name": "Admin Name",
"email": "admin@example.com",
"password": "SecurePassword123!",
"otp": "123456"
}Response (201):
{
"message": "Admin registered successfully",
"admin": {
"id": "uuid-here",
"name": "Admin Name",
"email": "admin@example.com"
}
}POST /api/admin/login
Content-Type: application/json
{
"email": "admin@example.com",
"password": "SecurePassword123!"
}Response (200):
{
"message": "Login successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"admin": {
"id": "uuid-here",
"name": "Admin Name",
"email": "admin@example.com"
}
}POST /api/admin/reset-password
Authorization: Bearer <your_jwt_token>
Content-Type: application/json
{
"email": "admin@example.com",
"newPassword": "NewSecurePassword123!"
}Response (200):
{
"message": "Password updated successfully"
}POST /api/problems
Authorization: Bearer <your_jwt_token>
Content-Type: multipart/form-data
title: "Two Sum"
difficulty: "Easy"
tags: ["Array", "Hash Table"]
description: "Given an array of integers..."
constraints: "1 <= nums.length <= 10^4"
images: [File1, File2]Response (201):
{
"problem": {
"_id": "problem_id_here",
"title": "Two Sum",
"difficulty": "Easy",
"tags": ["Array", "Hash Table"],
"description": "Given an array of integers...",
"constraints": "1 <= nums.length <= 10^4",
"images": ["/uploads/image1.jpg", "/uploads/image2.jpg"],
"createdBy": "admin_id",
"createdAt": "2025-10-20T10:30:00.000Z"
}
}GET /api/problemsGET /api/problems/:idPUT /api/problems/:id
Authorization: Bearer <your_jwt_token>
Content-Type: multipart/form-data
title: "Updated Title"
difficulty: "Medium"DELETE /api/problems/:id
Authorization: Bearer <your_jwt_token>Response (200):
{
"message": "Problem and all related data deleted successfully",
"deletedTestcases": 5,
"deletedFiles": {
"success": 3,
"failed": 0
}
}POST /api/problems/:id/testcases
Authorization: Bearer <your_jwt_token>
Content-Type: application/json
{
"visible": [
{
"input": "[2,7,11,15]\n9",
"output": "[0,1]"
}
],
"hidden": [
{
"input": "[3,2,4]\n6",
"output": "[1,2]"
}
]
}Response (201):
{
"message": "Testcases added successfully",
"visible": [...],
"hidden": [...]
}GET /api/problems/:id/testcases
Authorization: Bearer <your_jwt_token>GET /api/problems/:id/testcases/publicPUT /api/problems/:id/testcases/:testcase_id
Authorization: Bearer <your_jwt_token>
Content-Type: application/json
{
"input": "updated input",
"output": "updated output"
}DELETE /api/problems/:id/testcases/:testcase_id
Authorization: Bearer <your_jwt_token>- Create new environment in Postman
- Add variables:
base_url:http://localhost:5000admin_token: (will be set after login)
Test 1: Complete Authentication Flow
-
Request OTP
- Method: POST
- URL:
{{base_url}}/api/otp/request - Body:
{"email": "test@example.com"} - β Expect: 200, "OTP sent successfully"
- Check your email for OTP
-
Complete Signup
- Method: POST
- URL:
{{base_url}}/api/admin/signup/verify - Body: Include name, email, password, and OTP from email
- β Expect: 201, admin object returned
-
Login
- Method: POST
- URL:
{{base_url}}/api/admin/login - Body:
{"email": "test@example.com", "password": "your_password"} - β Expect: 200, JWT token returned
- Save the token to
admin_tokenenvironment variable
Test 2: Problem Lifecycle
-
Create Problem
- Method: POST
- URL:
{{base_url}}/api/problems - Headers:
Authorization: Bearer {{admin_token}} - Body: form-data with title, difficulty, description
- β
Expect: 201, problem object with
_id - Save problem
_idfor next tests
-
Get All Problems
- Method: GET
- URL:
{{base_url}}/api/problems - β Expect: 200, array containing your problem
-
Get Single Problem
- Method: GET
- URL:
{{base_url}}/api/problems/{{problem_id}} - β Expect: 200, problem details
Test 3: Testcase Management
-
Add Testcases
- Method: POST
- URL:
{{base_url}}/api/problems/{{problem_id}}/testcases - Headers:
Authorization: Bearer {{admin_token}} - Body: JSON with visible and hidden arrays
- β Expect: 201, testcases created
- Save a testcase
_id
-
Get Public Testcases
- Method: GET
- URL:
{{base_url}}/api/problems/{{problem_id}}/testcases/public - β Expect: 200, only visible testcases returned
-
Get All Testcases (Admin)
- Method: GET
- URL:
{{base_url}}/api/problems/{{problem_id}}/testcases - Headers:
Authorization: Bearer {{admin_token}} - β Expect: 200, both visible and hidden testcases
Test 4: Cleanup & Cascade Deletion
-
Delete Problem
- Method: DELETE
- URL:
{{base_url}}/api/problems/{{problem_id}} - Headers:
Authorization: Bearer {{admin_token}} - β Expect: 200, deletedTestcases count > 0
-
Verify Cleanup
- Try to GET the problem again
- β Expect: 404, problem not found
| Scenario | Status | Response |
|---|---|---|
| Invalid OTP | 400 | {"error": "Invalid OTP"} |
| Expired OTP | 400 | {"error": "OTP expired"} |
| Missing JWT | 401 | {"error": "Unauthorized"} |
| Invalid JWT | 403 | {"error": "Invalid token"} |
| Problem not found | 404 | {"error": "Problem not found"} |
| Duplicate email | 409 | {"error": "Email already exists"} |
backend/
βββ src/
β βββ config/
β β βββ db.js # Sequelize/PostgreSQL configuration
β β βββ mongo.js # Mongoose/MongoDB connection
β β
β βββ controllers/
β β βββ adminController.js # Admin auth logic
β β βββ otpController.js # OTP generation/verification
β β βββ problemController.js # Problem & testcase CRUD
β β
β βββ middlewares/
β β βββ auth.js # JWT verification middleware
β β βββ upload.js # Multer file upload config
β β
β βββ models/
β β βββ User.js # Sequelize User model (PostgreSQL)
β β βββ Problem.js # Mongoose Problem model
β β βββ Testcase.js # Mongoose Testcase model
β β
β βββ routes/
β β βββ admin.js # /api/admin/* routes
β β βββ otp.js # /api/otp/* routes
β β βββ problems.js # /api/problems/* routes
β β
β βββ services/
β β βββ otpService.js # OTP email sending logic
β β βββ pendingSignupService.js # Temporary signup data store
β β
β βββ server.js # Express app entry point
β
βββ uploads/ # Local file storage (dev only)
βββ .env # Environment variables (DO NOT COMMIT)
βββ .env.example # Example environment file
βββ .gitignore
βββ package.json
βββ package-lock.json
βββ README.md
π΄ CRITICAL - Not Production Ready
This project is currently in development/beta stage. Do NOT deploy to production without addressing:
-
In-Memory OTP Storage
- OTPs are stored in application memory
- Lost on server restart
- Not suitable for distributed systems
- Fix: Use Redis or database with TTL
-
File Deletion Vulnerability
- Current implementation allows deletion of any file server has access to
- No path traversal protection
- Fix: Restrict deletions to
uploads/directory only
-
No Rate Limiting
- APIs can be abused (OTP spam, brute force)
- Fix: Implement express-rate-limit
-
Local File Storage
- Files stored on server filesystem
- Not scalable for multiple servers
- Fix: Use S3, Cloudinary, or similar cloud storage
- Input validation (no express-validator or Joi)
- API request logging
- Automated tests (unit, integration)
- Database migrations system
- API versioning
- CORS configuration for production
- Error monitoring (Sentry, etc.)
- Health check endpoints
- Implement Redis for OTP storage
- Add comprehensive input validation
- Switch to cloud file storage (S3/GCS)
- Add request rate limiting
- Implement proper logging (Winston, Morgan)
- Add integration tests
- Set up CI/CD pipeline
- Configure production CORS policies
- Add API documentation (Swagger/OpenAPI)
- Implement database backup strategy
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Follow existing code style
- Add tests for new features
- Update documentation as needed
- Ensure all tests pass before submitting PR
Mohith
- GitHub: @mohith1976
Happy Coding! π
Admins can attach hints to problems. Hints are rich objects with: text, order, and visibleToStudents (boolean). Admins can add, update and delete hints; students/public callers will only see hints where visibleToStudents: true.
Endpoints added:
-
Add hint (admin only):
POST /api/problems/:id/hints Authorization: Bearer <your_jwt_token> Content-Type: application/json
Body example: { "text": "Consider using a hash map to reduce complexity.", "order": 1, "visibleToStudents": true }
-
List visible hints (public):
GET /api/problems/:id/hints
-
List all hints (admin):
GET /api/problems/:id/hints/admin Authorization: Bearer <your_jwt_token>
-
Update a hint (admin):
PUT /api/problems/:id/hints/:hintId Authorization: Bearer <your_jwt_token> Content-Type: application/json
Body example: { "text": "Updated hint text", "visibleToStudents": false }
-
Delete a hint (admin):
DELETE /api/problems/:id/hints/:hintId Authorization: Bearer <your_jwt_token>
Notes:
- The Problem model stores
hintsas an array of subdocuments. Existing documents remain compatible (no migration required). New endpoints are protected with the existingauthmiddleware for admin actions.