Skip to content

Admin Routes Documentation

This section provides a detailed breakdown of the administrative API routes, outlining their purpose, the flow of requests through middleware, controllers, and services, and the expected responses. All admin routes are protected and require authentication with an ADMIN role.

Global Admin Middleware

All routes defined in src/api/admin.routes.ts are protected by the following middleware:

  • auth: Ensures the request has a valid JWT and authenticates the user.
  • authorize([Role.ADMIN]): Checks if the authenticated user has the ADMIN role. If not, access is denied.

This means that any request to these endpoints must include a valid JWT for an administrator user.


1. Get All Users

Endpoint: GET /v1/admin/users Description: Retrieves a list of all registered users in the system.

Flow Map

  1. Initial Request:
  2. Method: GET
  3. Path: /v1/admin/users
  4. Client Sends: No request body or parameters. A valid JWT for an admin user in the Authorization header.

  5. Route Handler (src/api/admin.routes.ts):

    router.get('/users', adminController.getAllUsers);
    

  6. The request first passes through the global auth and authorize([Role.ADMIN]) middleware, ensuring the user is authenticated and has admin privileges.
  7. If authorized, the request is forwarded to adminController.getAllUsers.

  8. Controller (src/controllers/admin.controller.ts):

    const getAllUsers = async (req: Request, res: Response, next: NextFunction) => {
      try {
        const users = await adminService.getAllUsers();
        res.status(200).json({ success: true, count: users.length, data: users });
      } catch (error) {
        next(error); // Pass error to error handling middleware
      }
    };
    

  9. The controller calls adminService.getAllUsers() to fetch all user data.
  10. Upon successful retrieval, it sends a 200 OK response with a JSON object containing success, count of users, and the data array of users.
  11. Any errors are caught and passed to the Express error handling middleware.

  12. Service (src/services/admin.service.ts):

    const getAllUsers = async (): Promise<User[]> => {
      return prisma.user.findMany();
    };
    

  13. The service directly uses the Prisma client (prisma.user.findMany()) to query the database for all user records.
  14. It returns an array of User objects.

  15. Response to Client:

  16. Status: 200 OK
  17. Body (JSON):
    {
      "success": true,
      "count": 2,
      "data": [
        {
          "id": "uuid-of-user-1",
          "name": "Admin User",
          "email": "admin@example.com",
          "role": "ADMIN",
          "isEmailVerified": true,
          "createdAt": "2023-01-01T12:00:00.000Z",
          "updatedAt": "2023-01-01T12:00:00.000Z"
        },
        {
          "id": "uuid-of-user-2",
          "name": "Regular User",
          "email": "user@example.com",
          "role": "USER",
          "isEmailVerified": true,
          "createdAt": "2023-01-02T12:00:00.000Z",
          "updatedAt": "2023-01-02T12:00:00.000Z"
        }
      ]
    }
    
  18. Error Responses:
  19. 401 Unauthorized: If no valid JWT is provided or the token is expired/invalid.
  20. 403 Forbidden: If the authenticated user does not have the ADMIN role.
  21. 500 Internal Server Error: For unexpected server-side errors.

2. Update User

Endpoint: PUT /v1/admin/users/:userId Description: Updates an existing user's information (including sensitive fields like password) by their ID.

Flow Map

  1. Initial Request:
  2. Method: PUT
  3. Path: /v1/admin/users/some-user-id
  4. Client Sends:
  5. Valid JWT for an admin user in the Authorization header.
  6. Path Parameter: userId (e.g., some-user-id).
  7. Request Body (JSON): Partial<User> object containing fields to update (e.g., { "name": "New Name", "email": "new@example.com", "password": "newpassword" }).

  8. Route Handler (src/api/admin.routes.ts):

    router.put('/users/:userId', validate(adminValidation.updateUser), adminController.updateUser);
    

  9. The request first passes through the global auth and authorize([Role.ADMIN]) middleware.
  10. Then, validate(adminValidation.updateUser) middleware validates the userId path parameter and the request body against the adminValidation.updateUser Zod schema.
  11. If validation passes, the request is forwarded to adminController.updateUser.

  12. Controller (src/controllers/admin.controller.ts):

    const updateUser = async (req: Request, res: Response, next: NextFunction) => {
      try {
        const updatedUser = await adminService.updateUserAsAdmin(req.params.userId, req.body);
        const { password, ...userWithoutPassword } = updatedUser; // Exclude password from response
        res.status(200).json({ success: true, data: userWithoutPassword });
      } catch (error) {
        next(error);
      }
    };
    

  13. The controller extracts userId from req.params and the update data from req.body.
  14. It calls adminService.updateUserAsAdmin() with the user ID and update body.
  15. Upon successful update, it destructures the password field from the updatedUser object to prevent sending it in the response.
  16. Sends a 200 OK response with the updated user data (excluding password).
  17. Any errors are caught and passed to the Express error handling middleware.

  18. Service (src/services/admin.service.ts):

    const updateUserAsAdmin = async (userId: string, updateBody: Partial<User>): Promise<User> => {
      const user = await prisma.user.findUnique({ where: { id: userId } });
      if (!user) {
        throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
      }
    
      const updatedUser: Partial<User> = { ...updateBody };
    
      if (updateBody.password) {
        // Password history check
        if (user.passwordHistory) {
          for (const oldHashedPassword of user.passwordHistory) {
            if (await bcrypt.compare(updateBody.password, oldHashedPassword)) {
              throw new ApiError(
                httpStatus.BAD_REQUEST,
                'New password cannot be one of the recently used passwords',
              );
            }
          }
        }
    
        const hashedPassword = await bcrypt.hash(updateBody.password, 10);
        const passwordHistoryLimit = 5;
        const updatedPasswordHistory = [hashedPassword, ...(user.passwordHistory || [])].slice(
          0,
          passwordHistoryLimit,
        );
    
        updatedUser.password = hashedPassword;
        updatedUser.passwordHistory = updatedPasswordHistory;
      }
    
      return prisma.user.update({
        where: { id: userId },
        data: updatedUser,
      });
    };
    

  19. The service first checks if the user exists. If not, it throws an ApiError (404 Not Found).
  20. If a password is provided in updateBody, it performs a password history check (if passwordHistory is enabled for the user model) to prevent reuse of recent passwords.
  21. The new password is then hashed using bcryptjs.
  22. The passwordHistory is updated with the new hashed password, keeping a limit of the last 5 passwords.
  23. Finally, it updates the user record in the database using prisma.user.update().
  24. Returns the updated User object.

  25. Response to Client:

  26. Status: 200 OK
  27. Body (JSON):
    {
      "success": true,
      "data": {
        "id": "uuid-of-user",
        "name": "New Name",
        "email": "new@example.com",
        "role": "ADMIN",
        "isEmailVerified": true,
        "createdAt": "2023-01-01T12:00:00.000Z",
        "updatedAt": "2023-01-03T10:30:00.000Z"
      }
    }
    
  28. Error Responses:
  29. 400 Bad Request: If validation fails, or new password is in history.
  30. 401 Unauthorized: If no valid JWT is provided.
  31. 403 Forbidden: If the authenticated user is not an admin.
  32. 404 Not Found: If the userId does not correspond to an existing user.
  33. 500 Internal Server Error: For unexpected server-side errors.

3. Delete User

Endpoint: DELETE /v1/admin/users/:userId Description: Deletes a user from the system by their ID.

Flow Map

  1. Initial Request:
  2. Method: DELETE
  3. Path: /v1/admin/users/some-user-id
  4. Client Sends:
  5. Valid JWT for an admin user in the Authorization header.
  6. Path Parameter: userId (e.g., some-user-id).
  7. No request body.

  8. Route Handler (src/api/admin.routes.ts):

    router.delete('/users/:userId', validate(adminValidation.deleteUser), adminController.deleteUser);
    

  9. The request first passes through the global auth and authorize([Role.ADMIN]) middleware.
  10. Then, validate(adminValidation.deleteUser) middleware validates the userId path parameter.
  11. If validation passes, the request is forwarded to adminController.deleteUser.

  12. Controller (src/controllers/admin.controller.ts):

    const deleteUser = async (req: Request, res: Response, next: NextFunction) => {
      try {
        await adminService.deleteUser(req.params.userId);
        res.status(204).send(); // No content on successful deletion
      } catch (error) {
        next(error);
      }
    };
    

  13. The controller extracts userId from req.params.
  14. It calls adminService.deleteUser() with the user ID.
  15. Upon successful deletion, it sends a 204 No Content response.
  16. Any errors are caught and passed to the Express error handling middleware.

  17. Service (src/services/admin.service.ts):

    const deleteUser = async (userId: string): Promise<User> => {
      // Optionally, check if user exists before attempting to delete
      const user = await prisma.user.findUnique({ where: { id: userId } });
      if (!user) {
        throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
      }
      return prisma.user.delete({
        where: { id: userId },
      });
    };
    

  18. The service first checks if the user exists. If not, it throws an ApiError (404 Not Found).
  19. It then deletes the user record from the database using prisma.user.delete().
  20. Returns the deleted User object (though the controller sends 204 No Content).

  21. Response to Client:

  22. Status: 204 No Content
  23. Body: Empty
  24. Error Responses:
  25. 400 Bad Request: If validation fails.
  26. 401 Unauthorized: If no valid JWT is provided.
  27. 403 Forbidden: If the authenticated user is not an admin.
  28. 404 Not Found: If the userId does not correspond to an existing user.
  29. 500 Internal Server Error: For unexpected server-side errors.

4. Send Notification to All Users

Endpoint: POST /v1/admin/notifications Description: Sends a notification message to all users. Online users receive it via WebSockets, while offline users have it persisted in the database.

Flow Map

  1. Initial Request:
  2. Method: POST
  3. Path: /v1/admin/notifications
  4. Client Sends:
  5. Valid JWT for an admin user in the Authorization header.
  6. Request Body (JSON): { "message": "Your notification message here" }

  7. Route Handler (src/api/admin.routes.ts):

    router.post(
      '/notifications',
      validate(notificationValidation.sendNotification),
      adminController.sendNotificationToAll,
    );
    

  8. The request first passes through the global auth and authorize([Role.ADMIN]) middleware.
  9. Then, validate(notificationValidation.sendNotification) middleware validates the request body.
  10. If validation passes, the request is forwarded to adminController.sendNotificationToAll.

  11. Controller (src/controllers/admin.controller.ts):

    const sendNotificationToAll = async (req: Request, res: Response, next: NextFunction) => {
      try {
        const { message } = req.body;
        const users = await adminService.getAllUsers();
        const userIds = users.map((user) => user.id).filter((userId) => req.user?.id !== userId); // Exclude sender
    
        await notificationService.createNotificationsForUserIds(userIds, message, 'new_notification');
    
        res.status(httpStatus.OK).json({ success: true, message: 'Notification sent to all users.' });
      } catch (error) {
        next(error);
      }
    };
    

  12. The controller extracts the message from req.body.
  13. It calls adminService.getAllUsers() to get all user IDs.
  14. It filters out the sender's own ID from the list of recipients.
  15. It then calls notificationService.createNotificationsForUserIds() to handle sending/persisting notifications.
  16. Sends a 200 OK response with a success message.
  17. Any errors are caught and passed to the Express error handling middleware.

  18. Service (src/services/admin.service.ts and src/services/notification.service.ts):

  19. adminService.getAllUsers(): (As described in "Get All Users" flow) Fetches all users from the database.
  20. notificationService.createNotificationsForUserIds():
    const createNotificationsForUserIds = async (
      userIds: string[],
      message: string,
      event: string = 'new_notification',
    ): Promise<void> => {
      const notificationsToPersist: { userId: string; message: string }[] = [];
    
      for (const userId of userIds) {
        const isOnline = await socketService.isUserOnline(userId); // Checks if user is connected via WebSocket
        if (isOnline) {
          socketService.emitToUser(userId, event, { message }); // Emits notification via WebSocket
        } else {
          notificationsToPersist.push({ userId, message }); // Prepares for database persistence
        }
      }
    
      if (notificationsToPersist.length > 0) {
        await prisma.notification.createMany({
          data: notificationsToPersist, // Persists notifications for offline users
        });
      }
    };
    
  21. This service iterates through the provided userIds.
  22. For each user, it checks if they are currently online using socketService.isUserOnline().
  23. If online, the notification is immediately sent via WebSocket using socketService.emitToUser().
  24. If offline, the notification is added to a list to be persisted in the database.
  25. Finally, any notifications for offline users are saved to the Notification table in the database using prisma.notification.createMany().
  26. When an offline user reconnects, all their saved notifications are sent to them and then removed from the database. This ensures that all users receive every notification.

  27. Response to Client:

  28. Status: 200 OK
  29. Body (JSON):
    {
      "success": true,
      "message": "Notification sent to all users."
    }
    
  30. Error Responses:
  31. 400 Bad Request: If validation fails.
  32. 401 Unauthorized: If no valid JWT is provided.
  33. 403 Forbidden: If the authenticated user is not an admin.
  34. 500 Internal Server Error: For unexpected server-side errors.