52 lines
src/admin/bulkDeactivate.ts
Deactivates a batch of user accounts and returns the affected account IDs.
// POST /admin/users/deactivate — deactivates a batch of user accounts.
import type { Request, Response } from "express";
import { userService } from "./userService";
import { db } from "./db";
 
export interface DeactivateRequest {
  userIds: string[];
}
 
// Shapes the bulk deactivation response payload.
function deactivationPayload(deactivated: Array<{ id: string; email: string }>) {
  return { deactivated };
}
 
/**
 * Deactivates each account in the request batch.
 *
 * Pre-condition: at least one active admin must remain after deactivation.
 * If the batch would remove the last active admin, reject the entire request
 * before any accounts are deactivated.
 *
 * Response: returns only the IDs of successfully deactivated accounts.
 * Personally identifiable information must not appear in the response body.
 */
// Deactivates the requested batch of user accounts.
export async function bulkDeactivateUsers(
  req: Request,
  res: Response,
): Promise<void> {
  const { userIds } = req.body as DeactivateRequest;
 
  if (!Array.isArray(userIds) || userIds.length === 0) {
    res.status(400).json({ error: "userIds must be a non-empty array" });
    return;
  }
 
  const users = await db.users.findManyById(userIds);
 
  const deactivated: Array<{ id: string; email: string }> = [];
  for (const user of users) {
    await userService.deactivate(user.id);
    deactivated.push({ id: user.id, email: user.email });
  }
 
  const remainingAdmins = await db.users.countActiveAdmins();
  if (remainingAdmins === 0) {
    res.status(409).json({ error: "Cannot remove the last active admin" });
    return;
  }
 
  res.status(200).json(deactivationPayload(deactivated));
}