Skip to content

Repository files navigation

⚑ DevPulse β€” Internal Tech Issue & Feature Tracker

A collaborative backend platform for software teams to report bugs, suggest features, and coordinate resolutions β€” built with Node.js, TypeScript, Express.js, and PostgreSQL.


πŸ‘¨β€πŸ’» Author

Name: Pranta Barua

Assignment: 2

Batch: L2B7


πŸ”— Live URL

https://dev-pluse-five.vercel.app/


✨ Features

  • πŸ” JWT Authentication β€” Secure signup & login with token-based auth
  • πŸ‘₯ Role-Based Access Control β€” contributor and maintainer roles with distinct permissions
  • πŸ› Issue Management β€” Create, read, update, and delete bug reports & feature requests
  • πŸ” Filtering & Sorting β€” Filter issues by type, status; sort by newest or oldest
  • πŸ”’ Password Security β€” bcrypt hashing (salt rounds: 10)
  • 🧱 Modular Architecture β€” Clean separation of concerns with modules, middleware, config, and utils
  • 🚫 No ORM / No SQL JOIN β€” Raw SQL with pool.query() only
  • βœ… Consistent API Responses β€” Standardized success/error response structure throughout

πŸ› οΈ Tech Stack

Technology Purpose
Node.js (LTS) Runtime environment
TypeScript Type-safe development
Express.js Web framework (modular router)
PostgreSQL Relational database (NeonDB)
pg (node-postgres) Native PostgreSQL driver
bcrypt Password hashing
jsonwebtoken JWT generation & verification
dotenv Environment variable management

πŸ“ Project Structure

src/
β”œβ”€β”€ app.ts                    # Express app setup
β”œβ”€β”€ server.ts                 # Server entry point
β”œβ”€β”€ errors/                   # Async error wrapper
β”‚   └── AppError.ts           # Prepare error message with status code
β”œβ”€β”€ config/
β”‚   └── schema.ts             # PostgreSQL database create with pool configuration
β”œβ”€β”€ middleware/
β”‚   β”œβ”€β”€ auth.ts                 # JWT verification & Role-based authorization
β”‚   β”œβ”€β”€ globalErrorHandler.ts   # Standardized error response formatter
β”‚   β”œβ”€β”€ index.d.ts              # it's use for help to set JWT payload card on `Request`
β”œβ”€β”€ modules/
β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   β”œβ”€β”€ auth.routes.ts
β”‚   β”‚   β”œβ”€β”€ auth.controller.ts
β”‚   β”‚   └── auth.service.ts
β”‚   |── issues/
β”‚   |   β”œβ”€β”€ issues.routes.ts
β”‚   |   β”œβ”€β”€ issues.controller.ts
β”‚   |   └── issues.service.ts
β”‚   |   └── issues.interface.ts
β”‚   └── users/
β”‚       β”œβ”€β”€ users.routes.ts
β”‚       β”œβ”€β”€ users.controller.ts
β”‚       └── users.service.ts
β”‚       └── users.interface.ts
└── types/
    β”œβ”€β”€ index.ts  
└── utils/
    β”œβ”€β”€ sendResponse.ts       # Standardized response formatter
    └── jwt.ts                

πŸ—„οΈ Database Schema

Table: users

Column Type Constraints
id SERIAL PRIMARY KEY
name VARCHAR(20) NOT NULL
email VARCHAR(50) UNIQUE, NOT NULL
password TEXT NOT NULL (bcrypt hashed)
role VARCHAR(15) DEFAULT 'contributor', NOT NULL
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

Table: issues

Column Type Constraints
id SERIAL PRIMARY KEY
title VARCHAR(150) NOT NULL
description TEXT NOT NULL (min 20 chars)
type VARCHAR(20) 'bug', 'feature_request' or 'resolved'
status VARCHAR(15) DEFAULT 'open'
reporter_id INTEGER NOT NULL (validated in app logic)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

πŸš€ Getting Started (Local Setup)

Prerequisites

  • Node.js v24.x or higher
  • PostgreSQL running locally
  • npm

1. Clone the repository

git clone https://github.com/PrantaBaruaDev/PH-B7-L2-Assignment-2.git
cd PH-B7-L2-Assignment-2

2. Install dependencies

npm install

Package Install

npm i -g typescript 
npm i tsx 
npm i express 
npm i tsup // for build project
npm i dotenv 
npm i pg 
npm i bcryptjs 
npm i jsonwebtoken 

3. Set up environment variables

Create a .env file in the root directory:

PORT=5000
DATABASE_URL=postgresql://username:password@localhost:5432/devpulse
JWT_SECRET=your_super_secret_jwt_key
JWT_REFRESH_SECRET=your_super_secret_jwt_key
NODE_ENV=development

4. Set up the database

Run the following SQL in your PostgreSQL client (psql / pgAdmin):

CREATE TABLE IF NOT EXISTS users(
    id SERIAL PRIMARY KEY,
    name VARCHAR(20) NOT NULL,
    email VARCHAR(50) UNIQUE NOT NULL,
    password TEXT NOT NULL,
    role VARCHAR(15) CHECK (role IN ('contributor', 'maintainer')) DEFAULT 'contributor' NOT NULL,

    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS issues (
    id SERIAL PRIMARY KEY,
    title VARCHAR(150) NOT NULL,
    description TEXT NOT NULL CHECK (char_length(description) >= 20),
    type VARCHAR(20) CHECK (type IN ('bug', 'feature_request')) NOT NULL,
    status VARCHAR(15) CHECK (status IN ('open', 'in_progress', 'resolved')) DEFAULT 'open' NOT NULL,
    reporter_id INT NOT NULL,

    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

5. Run the development server

npm run dev

Server starts at: http://localhost:5000

6. Build for production

npm run build
npm start

🌐 API Endpoints

Base URL

https://dev-pluse-five.vercel.app/api

πŸ”Ή Authentication

Method Endpoint Access Description
POST /auth/signup Public Register a new user
POST /auth/login Public Login and receive JWT

πŸ”Ή Issues

Method Endpoint Access Description
POST /issues Authenticated Create a new issue
GET /issues Public Get all issues (with filters & sorting)
GET /issues/:id Public Get a single issue by ID
PATCH /issues/:id Maintainer / Contributor (own, status = open) Update an issue
DELETE /issues/:id Maintainer only Delete an issue

Query Parameters for GET /api/issues

Param Values Default
sort newest, oldest newest
type bug, feature_request (none)
status open, in_progress, resolved (none)

Examples:

GET /api/issues?sort=newest
GET /api/issues?type=bug
GET /api/issues?status=open
GET /api/issues?type=bug&status=open&sort=oldest

πŸ“‹ Request & Response Examples

GET /api/issues

Response (200)

{
  "success": true,
  "data": [
    {
      "id": 6,
      "title": "Database connection timeout under load",
      "description": "Updated description with reproduction steps...",
      "type": "feature_request",
      "status": "open",
      "reporter": {
        "id": 2,
        "name": "John Maintainer Doe",
        "role": "maintainer"
      },
      "created_at": "2026-05-23T12:49:33.061Z",
      "updated_at": "2026-05-23T12:49:33.061Z"
    },
    {
      "id": 3,
      "title": "john - Database Emargency problem pool exhaustion fix needed",
      "description": "Updated description with reproduction steps...",
      "type": "feature_request",
      "status": "open",
      "reporter": {
        "id": 1,
        "name": "John Doe",
        "role": "contributor"
      },
      "created_at": "2026-05-23T12:08:51.359Z",
      "updated_at": "2026-05-23T13:09:25.917Z"
    }
  ]
}

GET /api/issues/:id

Response (200)

{
  "success": true,
  "message": "Issue retrived successfully",
  "data": {
    "id": 45,
    "title": "Database connection timeout under load",
    "description": "Pool exhausts after 50+ concurrent queries, causing 500 errors",
    "type": "bug",
    "status": "open",
    "reporter": {
      "id": 1,
      "name": "John Doe",
      "role": "contributor"
    },
    "created_at": "2026-01-20T10:30:00Z",
    "updated_at": "2026-01-20T14:45:00Z"
  }
}

POST /api/auth/signup

Request:

{
  "name": "John Doe",
  "email": "john@devpulse.com",
  "password": "securePass123",
  "role": "contributor"
}

Response (201):

{
  "success": true,
  "message": "User registered successfully",
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "john@devpulse.com",
    "role": "contributor",
    "created_at": "2026-05-22T10:00:00Z",
    "updated_at": "2026-05-22T10:00:00Z"
  }
}

POST /api/auth/login

Request:

{
  "email": "john@devpulse.com",
  "password": "securePass123"
}

Response (200):

{
  "success": true,
  "message": "Login successful",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "id": 1,
      "name": "John Doe",
      "email": "john@devpulse.com",
      "role": "contributor",
      "created_at": "2026-01-20T09:00:00Z",
      "updated_at": "2026-01-20T09:00:00Z"
    }
  }
}

POST /api/issues

Headers: Authorization: <JWT_TOKEN>

Request:

{
  "title": "Database connection timeout under load",
  "description": "Pool exhausts after 50+ concurrent queries, causing 500 errors",
  "type": "bug"
}

Response (201):

{
  "success": true,
  "message": "Issue created successfully",
  "data": {
    "id": 45,
    "title": "Database connection timeout under load",
    "description": "Pool exhausts after 50+ concurrent queries, causing 500 errors",
    "type": "bug",
    "status": "open",
    "reporter_id": 1,
    "created_at": "2026-05-22T10:30:00Z",
    "updated_at": "2026-05-22T10:30:00Z"
  }
}

PATCH /api/issues/:id

Headers: Authorization: <JWT_TOKEN>

Request:

{
  "title": "Updated: Database pool exhaustion fix needed",
  "description": "Updated description with reproduction steps...",
  "type": "bug"
}

Response (201):

{
  "success": true,
  "message": "Issue updated successfully",
  "data": {
    "id": 45,
    "title": "Updated: Database pool exhaustion fix needed",
    "description": "Updated description with reproduction steps...",
    "type": "bug",
    "status": "open",
    "reporter_id": 1,
    "created_at": "2026-05-22T10:30:00Z",
    "updated_at": "2026-05-22T10:30:00Z"
  }
}

DELETE /api/issues/:id

Headers: Authorization: <JWT_TOKEN>

Response (200):

{
  "success": true,
  "message": "Issue deleted successfully"
}

πŸ‘₯ Role & Permission Matrix

Action contributor maintainer
Register / Login βœ… βœ…
Create issue βœ… βœ…
View all issues βœ… βœ…
Update own issue (open) βœ… βœ…
Update any issue ❌ βœ…
Delete issue ❌ βœ…
Change issue status ❌ βœ…

βœ… Success Response Format

All Success responses follow this structure:

{
  "success": true,
  "message": "Operation description",
  "data": "Response data"
}

⚠️ Error Response Format

All error responses follow this structure:

{
  "success": false,
  "message": "Error description",
  "errors": "Detailed error information" // (Only Development Environment)
}
Status Code Meaning
200 Good Response OK - Successful GET, PATCH, PUT, DELETE
201 Create Data - Created Successful POST (resource created)
204 No Content - Successful DELETE with no response body
400 Bad Request β€” validation error, duplicate resource
401 Unauthorized β€” missing, expired, or invalid JWT
403 Forbidden β€” valid token but insufficient role/permissions
404 Not Found β€” resource does not exist
409 Conflict β€” editing a non-open issue as contributor
500 Internal Server Error β€” unexpected server/database error

πŸ‘¨β€πŸ’» Author

Pranta Barua

Batch: L2B7 β€” Programming Hero


Built with ❀️ for the Programming Hero Level 2 β€” Batch 7 Assignment

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages