A secure Node.js/Express/TypeScript backend for the AU Chapel Workers management system with role-based authentication for Workers and Executives.
- Worker-Only Registration: Only workers can self-register. Executives are pre-seeded.
- Gender-Specific Hostels: Different hostel options for male and female workers.
- Pre-seeded Executives: All executive accounts are created via seed script with default credentials.
- JWT Authentication: Access tokens (15min) + Refresh tokens (7 days) for desktop persistence.
- Role-Based Access Control: Separate permissions for Workers and Executives.
src/
βββ config/
β βββ index.ts # Environment configuration
β βββ database.ts # MongoDB connection
β βββ executives.seed.ts # Hardcoded executive data
β βββ seed.ts # Database seeder script
βββ controllers/
β βββ index.ts
β βββ auth.controller.ts
βββ middlewares/
β βββ index.ts
β βββ auth.middleware.ts # JWT authentication
β βββ role.middleware.ts # RBAC middleware
β βββ error.middleware.ts # Global error handler
βββ models/
β βββ index.ts
β βββ user.model.ts # User schema with conditional fields
βββ routes/
β βββ index.ts
β βββ auth.routes.ts
β βββ user.routes.ts
β βββ enum.routes.ts # Dropdown values for Flutter
βββ services/
β βββ index.ts
β βββ auth.service.ts # Business logic
βββ types/
β βββ index.ts # TypeScript interfaces
β βββ enums.ts # Enum definitions (gender-specific hostels)
βββ utils/
β βββ index.ts
β βββ errors.ts # Custom error classes
β βββ response.ts # API response helper
βββ validations/
β βββ index.ts
β βββ auth.validation.ts # Zod schemas (worker registration only)
βββ app.ts # Express app factory
βββ server.ts # Entry point
- Node.js >= 18.0.0
- MongoDB (local or Atlas)
- npm or yarn
-
Clone the repository and navigate to the project:
cd workers-accountable -
Install dependencies:
npm install
-
Create environment file:
cp .env.example .env
-
Update
.envwith your configuration:NODE_ENV=development PORT=5000 MONGODB_URI=mongodb://localhost:27017/au_chapel_workers JWT_SECRET=your-super-secret-jwt-key JWT_REFRESH_SECRET=your-super-secret-refresh-key
-
Seed the database with executives (required before first run):
npm run seed
This creates all executive accounts with default password:
AUChapel@2026 -
Start the development server:
npm run dev
| Script | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm run build |
Compile TypeScript to JavaScript |
npm start |
Run compiled production server |
npm run seed |
Seed database with executive accounts |
npm run lint |
Run ESLint |
Workers select their gender first, which determines the available hostel options. Workers can also select their assigned executive from a predefined list.
POST /api/auth/register
{
"fullName": "John Doe",
"email": "john@example.com",
"phoneNumber": "+234 801 234 5678",
"gender": "male",
"password": "SecurePass123",
"confirmPassword": "SecurePass123",
"hostel": "peter_hall", // Must match gender (male hostels only)
"workforceDepartment": "choir",
"assignedExecutive": "65abc123..." // Executive ObjectId from /api/enums/executives
}Executives cannot self-register. They use the seeded accounts with default credentials.
Default Executive Credentials:
- Email:
generalcoord@auchapel.org,sisterwelfarecoord@auchapel.org, etc. - Password:
AUChapel@2026
After first login, executives will see mustChangePassword: true in the response
and should be prompted to change their password.
POST /api/auth/login
{
"email": "president@auchapel.org",
"password": "AUChapel@2026"
}Response (First Login):
{
"success": true,
"message": "Login successful",
"data": {
"user": {
"_id": "65abc...",
"fullName": "Chapel President",
"email": "president@auchapel.org",
"role": "executive",
"excoPosition": "president",
"mustChangePassword": true
},
"tokens": { ... }
}
}POST /api/auth/refresh
{
"refreshToken": "eyJhbG..."
}PATCH /api/auth/change-password
{
"currentPassword": "AUChapel@2026",
"newPassword": "MyNewSecurePass123",
"confirmPassword": "MyNewSecurePass123"
}| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | /api/auth/register |
Public | Register new worker (only workers) |
| POST | /api/auth/login |
Public | Login user |
| POST | /api/auth/refresh |
Public | Refresh access token |
| POST | /api/auth/logout |
Private | Logout user |
| GET | /api/auth/me |
Private | Get current user profile |
| PATCH | /api/auth/change-password |
Private | Change password |
| POST | /api/auth/deactivate |
Private | Deactivate own account |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /api/users |
Executive | Get all users (paginated) |
| GET | /api/users/executives |
Private | Get all executives |
| GET | /api/users/departments |
Executive | Get workers by department |
| GET | /api/users/:userId |
Self/Executive | Get user by ID |
| PATCH | /api/users/:userId/reactivate |
Executive | Reactivate user |
| POST | /api/users/assign-executive |
Executive | Assign executive to worker |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /api/enums |
Public | Get all enum values |
| GET | /api/enums/hostels?gender=male |
Public | Get gender-specific hostel options |
| GET | /api/enums/departments |
Public | Get department options |
| GET | /api/enums/positions |
Public | Get executive positions |
| GET | /api/enums/executives |
Public | Get list of executives for selection |
worker- Chapel worker (can self-register)executive- Executive member (pre-seeded only)
malefemale
peter_hall,paul_hall,joseph_hall,john_halldaniel_hall,moses_hall,elijah_hall,david_halloff_campus_male
mary_hall,esther_hall,ruth_hall,deborah_halllydia_hall,priscilla_hall,hannah_hall,abigail_halloff_campus_female
choir,ushering,technical,media,prayerevangelism,welfare,sanctuary,protocolchildren,drama,instrumentals
president,vice_president,general_secretaryassistant_general_secretary,financial_secretary,treasurerwelfare_director,prayer_director,evangelism_directorchoir_director,technical_director,media_directorprotocol_director,ushering_coordinator,sanctuary_coordinatorchildren_coordinator,drama_coordinator,instrumentals_coordinator
The following executives are created when you run npm run seed:
| Position | Default Password | |
|---|---|---|
| President | president@auchapel.org | AUChapel@2026 |
| Vice President | vice.president@auchapel.org | AUChapel@2026 |
| General Secretary | general.secretary@auchapel.org | AUChapel@2026 |
| Assistant General Secretary | assistant.secretary@auchapel.org | AUChapel@2026 |
| Financial Secretary | financial.secretary@auchapel.org | AUChapel@2026 |
| Treasurer | treasurer@auchapel.org | AUChapel@2026 |
| Welfare Director | welfare.director@auchapel.org | AUChapel@2026 |
| Prayer Director | prayer.director@auchapel.org | AUChapel@2026 |
| Evangelism Director | evangelism.director@auchapel.org | AUChapel@2026 |
| Choir Director | choir.director@auchapel.org | AUChapel@2026 |
| Technical Director | technical.director@auchapel.org | AUChapel@2026 |
| Media Director | media.director@auchapel.org | AUChapel@2026 |
| Protocol Director | protocol.director@auchapel.org | AUChapel@2026 |
| Ushering Coordinator | ushering.coordinator@auchapel.org | AUChapel@2026 |
| Sanctuary Coordinator | sanctuary.coordinator@auchapel.org | AUChapel@2026 |
| Children Coordinator | children.coordinator@auchapel.org | AUChapel@2026 |
| Drama Coordinator | drama.coordinator@auchapel.org | AUChapel@2026 |
| Instrumentals Coordinator | instrumentals.coordinator@auchapel.org | AUChapel@2026 |
- Password Hashing: bcryptjs with configurable salt rounds (default: 12)
- JWT Authentication: Access tokens (15min) + Refresh tokens (7 days)
- Refresh Token Rotation: New refresh token issued on each refresh
- Role-Based Access Control: Worker and Executive permissions
- Input Validation: Zod schemas with detailed error messages
- Security Headers: Helmet.js middleware
- CORS Configuration: Configurable for Flutter Desktop
All API responses follow this structure for easy Dart serialization:
{
"success": true|false,
"message": "Human readable message",
"data": { ... },
"error": {
"code": "ERROR_CODE",
"details": { ... }
},
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
}
}
}class ApiResponse<T> {
final bool success;
final String message;
final T? data;
final ApiError? error;
final Meta? meta;
ApiResponse({
required this.success,
required this.message,
this.data,
this.error,
this.meta,
});
factory ApiResponse.fromJson(
Map<String, dynamic> json,
T Function(Map<String, dynamic>) fromJsonT,
) {
return ApiResponse(
success: json['success'],
message: json['message'],
data: json['data'] != null ? fromJsonT(json['data']) : null,
error: json['error'] != null ? ApiError.fromJson(json['error']) : null,
meta: json['meta'] != null ? Meta.fromJson(json['meta']) : null,
);
}
}| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR |
422 | Input validation failed |
UNAUTHORIZED |
401 | Not authenticated |
FORBIDDEN |
403 | Not authorized |
NOT_FOUND |
404 | Resource not found |
CONFLICT |
409 | Resource already exists |
TOKEN_ERROR |
401 | Invalid/expired token |
TOKEN_EXPIRED |
401 | Token has expired |
INTERNAL_ERROR |
500 | Server error |
ISC License
AU Chapel Tech Team