Skip to content

Latest commit

Β 

History

History
799 lines (614 loc) Β· 18.5 KB

File metadata and controls

799 lines (614 loc) Β· 18.5 KB

Electronics-Astra Backend

A full-featured REST API backend for managing coding problems, testcases, and admin authentication. Built with Node.js, Express, PostgreSQL, and MongoDB.

🎯 Overview

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


✨ Features

Authentication & Authorization

  • βœ… Admin signup with email-based OTP verification (2FA)
  • βœ… JWT-based authentication
  • βœ… Secure password hashing (bcrypt)
  • βœ… Protected admin routes
  • βœ… Profile management (view, edit, delete)

Problem Management

  • βœ… 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)

Testcase Management

  • βœ… 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

πŸ›  Tech Stack

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
Email Nodemailer
Dev Tools nodemon, dotenv

πŸ“¦ Prerequisites

Before you begin, ensure you have the following installed: commit the changes

Required Software

  1. Node.js (v18.0.0 or higher)

    node --version  # Should output v18.x.x or higher

    Download Node.js

  2. PostgreSQL (v12 or higher)

    psql --version  # Should output PostgreSQL 12.x or higher

    Download PostgreSQL

  3. MongoDB (v5.0 or higher)

    mongod --version  # Should output v5.x.x or higher

    Download MongoDB Community Server

  4. Git

    git --version

Account Setup

  • Gmail Account: Required for sending OTP emails (you'll need an App Password)
  • MongoDB Atlas (Optional): For cloud MongoDB hosting

πŸš€ Installation

Step 1: Clone the Repository

# Clone the repository
git clone https://github.com/mohith1976/electronics-astra.git

# Navigate to backend directory
cd electronics-astra/backend

Step 2: Install Dependencies

npm install

This installs all required packages:

  • express, cors, dotenv - Server essentials
  • sequelize, pg, pg-hstore - PostgreSQL ORM
  • mongoose - MongoDB ODM
  • jsonwebtoken, bcryptjs - Authentication
  • nodemailer - Email OTP delivery
  • multer, uuid - File uploads
  • nodemon - Development auto-reload

Step 3: Database Setup

PostgreSQL Setup

# Login to PostgreSQL
psql -U postgres

# Create database
CREATE DATABASE electronics_astra;

# Exit psql
\q

MongoDB Setup (Local)

# Start MongoDB service (Windows)
net start MongoDB

# Start MongoDB service (macOS/Linux)
sudo systemctl start mongod

# Verify MongoDB is running
mongosh
# Should connect successfully

MongoDB Setup (Atlas - Cloud)

  1. Create account at MongoDB Atlas
  2. Create a new cluster (free tier available)
  3. Add database user with password
  4. Whitelist your IP address (or use 0.0.0.0/0 for development)
  5. Get connection string (looks like: mongodb+srv://username:password@cluster0.mongodb.net/electronics-astra)

βš™οΈ Configuration

Step 1: Create Environment File

Create a .env file in the backend/ directory:

# In backend/ directory
touch .env  # macOS/Linux
# OR
type nul > .env  # Windows

Step 2: Configure Environment Variables

Add 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

Step 3: Generate Secure JWT Secret

# 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.

Step 4: Get Gmail App Password

  1. Go to Google Account Security
  2. Enable 2-Step Verification
  3. Go to App Passwords
  4. Select "Mail" and "Other (Custom name)"
  5. Generate password (16 characters, no spaces)
  6. Use this as EMAIL_PASS in .env

MongoDB and Images

  • Add MONGO_URI to your .env. Example:
    • Local: mongodb://localhost:27017/electronics-astra
    • Atlas: mongodb+srv://<user>:<password>@cluster0.mongodb.net/electronics-astra?retryWrites=true&w=majority
  • Problems API stores images as URLs in the images array on the Problem document.
  • For local testing we save uploaded images to /uploads and serve them statically at http://localhost:5000/uploads/<filename>.
  • For production, replace the upload handling with S3 or other object storage and save public URLs.

Install dependencies

⚠️ Security Warning: Never commit .env to version control. It's already in .gitignore.


πŸƒ Running the Application

Development Mode (with auto-reload)

npm run dev

You should see:

Server running on port 5000
PostgreSQL connected
MongoDB connected successfully

Production Mode

npm start

Verify Server is Running

Open browser and navigate to:

http://localhost:5000

You should see the Express welcome message or 404 (depending on root route configuration).

Troubleshooting

PostgreSQL connection fails

# Check PostgreSQL is running
# Windows
net start postgresql-x64-14

# macOS/Linux
sudo systemctl status postgresql

MongoDB 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=5000 to another port in .env (e.g., PORT=5001)

πŸ“š API Documentation

Base URL

http://localhost:5000

Authentication Flow

1. Request OTP for Signup

POST /api/otp/request
Content-Type: application/json

{
  "email": "admin@example.com"
}

Response (200):

{
  "message": "OTP sent successfully"
}

2. Verify OTP and Complete Signup

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"
  }
}

3. Login

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"
  }
}

3.b Admin Reset Password (Admin only)

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"
}

Problem Management

4. Create Problem (Admin Only)

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"
  }
}

5. Get All Problems (Public)

GET /api/problems

6. Get Single Problem (Public)

GET /api/problems/:id

7. Update Problem (Admin Only)

PUT /api/problems/:id
Authorization: Bearer <your_jwt_token>
Content-Type: multipart/form-data

title: "Updated Title"
difficulty: "Medium"

8. Delete Problem (Admin Only)

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
  }
}

Testcase Management

9. Add Testcases to Problem (Admin Only)

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": [...]
}

10. Get All Testcases (Admin Only)

GET /api/problems/:id/testcases
Authorization: Bearer <your_jwt_token>

11. Get Public Testcases (No Auth Required)

GET /api/problems/:id/testcases/public

12. Update Single Testcase (Admin Only)

PUT /api/problems/:id/testcases/:testcase_id
Authorization: Bearer <your_jwt_token>
Content-Type: application/json

{
  "input": "updated input",
  "output": "updated output"
}

13. Delete Single Testcase (Admin Only)

DELETE /api/problems/:id/testcases/:testcase_id
Authorization: Bearer <your_jwt_token>

πŸ§ͺ Testing Guide

Manual Testing with Postman

Setup Postman Environment

  1. Create new environment in Postman
  2. Add variables:
    • base_url: http://localhost:5000
    • admin_token: (will be set after login)

Test Sequence

Test 1: Complete Authentication Flow

  1. 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
  2. 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
  3. 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_token environment variable

Test 2: Problem Lifecycle

  1. 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 _id for next tests
  2. Get All Problems

    • Method: GET
    • URL: {{base_url}}/api/problems
    • βœ… Expect: 200, array containing your problem
  3. Get Single Problem

    • Method: GET
    • URL: {{base_url}}/api/problems/{{problem_id}}
    • βœ… Expect: 200, problem details

Test 3: Testcase Management

  1. 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
  2. Get Public Testcases

    • Method: GET
    • URL: {{base_url}}/api/problems/{{problem_id}}/testcases/public
    • βœ… Expect: 200, only visible testcases returned
  3. 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

  1. Delete Problem

    • Method: DELETE
    • URL: {{base_url}}/api/problems/{{problem_id}}
    • Headers: Authorization: Bearer {{admin_token}}
    • βœ… Expect: 200, deletedTestcases count > 0
  2. Verify Cleanup

    • Try to GET the problem again
    • βœ… Expect: 404, problem not found

Expected Error Responses

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"}

πŸ“ Project Structure

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

⚠️ Known Limitations

πŸ”΄ CRITICAL - Not Production Ready

This project is currently in development/beta stage. Do NOT deploy to production without addressing:

Security Issues

  1. 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
  2. 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
  3. No Rate Limiting

    • APIs can be abused (OTP spam, brute force)
    • Fix: Implement express-rate-limit
  4. Local File Storage

    • Files stored on server filesystem
    • Not scalable for multiple servers
    • Fix: Use S3, Cloudinary, or similar cloud storage

Missing Features

  • 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

Recommendations Before Production

  1. Implement Redis for OTP storage
  2. Add comprehensive input validation
  3. Switch to cloud file storage (S3/GCS)
  4. Add request rate limiting
  5. Implement proper logging (Winston, Morgan)
  6. Add integration tests
  7. Set up CI/CD pipeline
  8. Configure production CORS policies
  9. Add API documentation (Swagger/OpenAPI)
  10. Implement database backup strategy

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Contribution Guidelines

  • Follow existing code style
  • Add tests for new features
  • Update documentation as needed
  • Ensure all tests pass before submitting PR

πŸ‘€ Author

Mohith


Happy Coding! πŸš€


πŸ“ Hints (new feature)

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 hints as an array of subdocuments. Existing documents remain compatible (no migration required). New endpoints are protected with the existing auth middleware for admin actions.