Skip to content

unrealtim-tech/taskmanager-assessment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Multi-Tenant Task Manager

A minimal multi-tenant task management system built with React (Vite), Node.js (Express), PostgreSQL (Sequelize), and Bull (Redis).

Architecture

  • Schema-based multitenancy: Each organization gets its own PostgreSQL schema. Tenant data is fully isolated — no row-level filtering needed.
  • Background exports: Bull + Redis queue processes CSV exports asynchronously.
  • Auth simulation: Pass x-tenant-id and x-user-id headers per request.

Prerequisites

  • Node.js 18+
  • PostgreSQL 14+
  • Redis 6+

Setup

1. Clone & Install

git clone <repo-url>
cd task-manager

# Backend
cd backend
cp .env.example .env   # Edit DB/Redis credentials
npm install

# Frontend
cd ../frontend
npm install

2. Configure Environment

Edit backend/.env:

PORT=3001
DB_HOST=localhost
DB_PORT=5432
DB_NAME=taskmanager
DB_USER=postgres
DB_PASSWORD=yourpassword
REDIS_URL=redis://localhost:6379
EXPORTS_DIR=./exports

3. Create the Database

-- In psql:
CREATE DATABASE taskmanager;

The public.organizations table is auto-created on server start.


Running Redis

macOS (Homebrew)

brew install redis
brew services start redis

Ubuntu/Debian

sudo apt-get install redis-server
sudo systemctl start redis

Docker

docker run -d -p 6379:6379 redis:7-alpine

Running the App

Backend

cd backend
npm run dev      # or: npm start
# Server: http://localhost:3001

Frontend

cd frontend
npm run dev
# UI: http://localhost:5173

Creating a Tenant

Use the REST API to create an organization (tenant). This also creates its PostgreSQL schema and syncs models.

curl -X POST http://localhost:3001/organizations \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp", "schemaName": "acme"}'

Response includes the organization id — save it as your TENANT_ID.

Add Users to the Tenant

# Create an admin user
curl -X POST http://localhost:3001/organizations/<TENANT_ID>/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "email": "alice@acme.com", "role": "admin"}'

# Create a member user
curl -X POST http://localhost:3001/organizations/<TENANT_ID>/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Bob", "email": "bob@acme.com", "role": "member"}'

Save the user id values as ADMIN_ID and MEMBER_ID.


API Reference

All tenant-scoped routes require:

  • x-tenant-id header — Organization UUID
  • x-user-id header — User UUID (required for export, optional for task listing)

Tasks

Method Endpoint Description
GET /tasks List all tasks for tenant
POST /tasks Create a task
PATCH /tasks/:id Mark task as completed
POST /tasks/export Trigger CSV export (admin only)

Exports

Method Endpoint Description
GET /exports/:id Get export status + file URL
GET /exports/:id/download Download the CSV file

Organizations (public, no tenant headers needed)

Method Endpoint Description
GET /organizations List all organizations
POST /organizations Create organization + schema
GET /organizations/:id/users List users in tenant
POST /organizations/:id/users Add user to tenant

Testing the Export Feature

# 1. Set your IDs
TENANT_ID="<your-org-uuid>"
ADMIN_ID="<your-admin-uuid>"

# 2. Create some tasks first
curl -X POST http://localhost:3001/tasks \
  -H "x-tenant-id: $TENANT_ID" \
  -H "x-user-id: $ADMIN_ID" \
  -H "Content-Type: application/json" \
  -d '{"title": "Write tests", "description": "Unit + integration"}'

# 3. Trigger an export (admin only)
curl -X POST http://localhost:3001/tasks/export \
  -H "x-tenant-id: $TENANT_ID" \
  -H "x-user-id: $ADMIN_ID"

# Response: {"exportId": "<uuid>", "jobId": "1"}
EXPORT_ID="<export-uuid>"

# 4. Poll export status
curl http://localhost:3001/exports/$EXPORT_ID \
  -H "x-tenant-id: $TENANT_ID" \
  -H "x-user-id: $ADMIN_ID"

# When status = "completed", response includes fileUrl

# 5. Download the CSV
curl -O http://localhost:3001/exports/$EXPORT_ID/download \
  -H "x-tenant-id: $TENANT_ID" \
  -H "x-user-id: $ADMIN_ID"

Frontend Usage

  1. Open http://localhost:5173
  2. The top bar shows dropdowns for Tenant and User — the UI auto-loads available orgs/users from the API
  3. Tasks tab: view, create, and complete tasks
  4. Export tab (admins only): trigger an export and watch it update in real time; download the CSV when ready

Project Structure

task-manager/
├── backend/
│   ├── src/
│   │   ├── config/database.js       # Sequelize connection
│   │   ├── middleware/tenant.js     # Tenant resolution + user attach
│   │   ├── models/
│   │   │   ├── Organization.js      # Public schema model
│   │   │   └── tenantModels.js      # Per-schema User/Task/Export factories
│   │   ├── routes/
│   │   │   ├── tasks.js
│   │   │   ├── exports.js
│   │   │   └── organizations.js
│   │   ├── jobs/
│   │   │   ├── queue.js             # Bull queue setup
│   │   │   └── exportWorker.js      # Background CSV generation
│   │   └── index.js
│   ├── .env.example
│   └── package.json
└── frontend/
    ├── src/
    │   ├── api/index.js             # API helper functions
    │   ├── pages/
    │   │   ├── TasksPage.jsx
    │   │   └── ExportPage.jsx
    │   ├── App.jsx
    │   └── main.jsx
    ├── index.html
    ├── vite.config.js
    └── package.json

Key Design Decisions

  • Schema isolation: getTenantModels(sequelize, schemaName) caches Sequelize model definitions per schema — no global model pollution
  • Dynamic schema creation: POST /organizations runs CREATE SCHEMA and syncs models immediately
  • Bull + Redis: Export jobs are fire-and-forget; the worker updates Export.status and saves the CSV path on completion
  • No auth library: Auth is simulated via request headers as specified

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published