Files
GRH/water-api/src/controllers/user.controller.ts

353 lines
7.8 KiB
TypeScript

import { Response } from 'express';
import { AuthenticatedRequest } from '../middleware/auth.middleware';
import * as userService from '../services/user.service';
import {
CreateUserInput,
UpdateUserInput,
ChangePasswordInput,
} from '../validators/user.validator';
/**
* GET /users
* List all users (admin only)
* Supports filtering by role_id, is_active, and search
* Supports pagination with page, limit, sortBy, sortOrder
*/
export async function getAllUsers(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
// Parse query parameters for filters
const filters: userService.UserFilter = {};
if (req.query.role_id) {
filters.role_id = parseInt(req.query.role_id as string, 10);
}
if (req.query.is_active !== undefined) {
filters.is_active = req.query.is_active === 'true';
}
if (req.query.search) {
filters.search = req.query.search as string;
}
// Parse pagination parameters
const pagination = {
page: parseInt(req.query.page as string, 10) || 1,
limit: parseInt(req.query.limit as string, 10) || 10,
sortBy: (req.query.sortBy as string) || 'created_at',
sortOrder: (req.query.sortOrder as 'asc' | 'desc') || 'desc',
};
const result = await userService.getAll(filters, pagination);
res.status(200).json({
success: true,
message: 'Users retrieved successfully',
data: result.users,
pagination: result.pagination,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to retrieve users';
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* GET /users/:id
* Get a single user by ID (admin or self)
*/
export async function getUserById(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.params.id;
if (!userId) {
res.status(400).json({
success: false,
error: 'Invalid user ID',
});
return;
}
// Check if user is admin or requesting their own data
const requestingUser = req.user;
const isAdmin = requestingUser?.roleName === 'ADMIN';
const isSelf = requestingUser?.userId === userId;
if (!isAdmin && !isSelf) {
res.status(403).json({
success: false,
error: 'Insufficient permissions',
});
return;
}
const user = await userService.getById(userId);
if (!user) {
res.status(404).json({
success: false,
error: 'User not found',
});
return;
}
res.status(200).json({
success: true,
message: 'User retrieved successfully',
data: user,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to retrieve user';
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* POST /users
* Create a new user (admin only)
*/
export async function createUser(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const data = req.body as CreateUserInput;
const user = await userService.create({
email: data.email,
password: data.password,
name: data.name,
avatar_url: data.avatar_url,
role_id: data.role_id,
is_active: data.is_active,
});
res.status(201).json({
success: true,
message: 'User created successfully',
data: user,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to create user';
if (message === 'Email already in use') {
res.status(409).json({
success: false,
error: message,
});
return;
}
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* PUT /users/:id
* Update a user (admin can update all fields, regular users can only update limited fields on self)
*/
export async function updateUser(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.params.id;
if (!userId) {
res.status(400).json({
success: false,
error: 'Invalid user ID',
});
return;
}
const requestingUser = req.user;
const isAdmin = requestingUser?.roleName === 'ADMIN';
const isSelf = requestingUser?.userId === userId;
if (!isAdmin && !isSelf) {
res.status(403).json({
success: false,
error: 'Insufficient permissions',
});
return;
}
const data = req.body as UpdateUserInput;
// Non-admin users can only update their own profile fields (not role_id or is_active)
if (!isAdmin) {
if (data.role_id !== undefined || data.is_active !== undefined) {
res.status(403).json({
success: false,
error: 'You can only update your profile information',
});
return;
}
}
const user = await userService.update(userId, data);
if (!user) {
res.status(404).json({
success: false,
error: 'User not found',
});
return;
}
res.status(200).json({
success: true,
message: 'User updated successfully',
data: user,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to update user';
if (message === 'Email already in use') {
res.status(409).json({
success: false,
error: message,
});
return;
}
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* DELETE /users/:id
* Deactivate a user (soft delete, admin only)
*/
export async function deleteUser(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.params.id;
if (!userId) {
res.status(400).json({
success: false,
error: 'Invalid user ID',
});
return;
}
// Prevent admin from deleting themselves
if (req.user?.userId === userId) {
res.status(400).json({
success: false,
error: 'Cannot deactivate your own account',
});
return;
}
const deleted = await userService.deleteUser(userId);
if (!deleted) {
res.status(404).json({
success: false,
error: 'User not found',
});
return;
}
res.status(200).json({
success: true,
message: 'User deactivated successfully',
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to deactivate user';
res.status(500).json({
success: false,
error: message,
});
}
}
/**
* PUT /users/:id/password
* Change user password (self only)
*/
export async function changePassword(
req: AuthenticatedRequest,
res: Response
): Promise<void> {
try {
const userId = req.params.id;
if (!userId) {
res.status(400).json({
success: false,
error: 'Invalid user ID',
});
return;
}
// Only allow users to change their own password
if (req.user?.userId !== userId) {
res.status(403).json({
success: false,
error: 'You can only change your own password',
});
return;
}
const data = req.body as ChangePasswordInput;
await userService.changePassword(
userId,
data.current_password,
data.new_password
);
res.status(200).json({
success: true,
message: 'Password changed successfully',
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to change password';
if (message === 'Current password is incorrect') {
res.status(401).json({
success: false,
error: message,
});
return;
}
if (message === 'User not found') {
res.status(404).json({
success: false,
error: message,
});
return;
}
res.status(500).json({
success: false,
error: message,
});
}
}