const admin = require("firebase-admin"); const client = require("../../../graphql-client/graphql-client").client; const { DELETE_SHOP } = require("../partsManagement.queries"); // Define corrected DELETE_VENDORS_BY_SHOP locally const DELETE_VENDORS_BY_SHOP = ` mutation DELETE_VENDORS_BY_SHOP($shopId: uuid!) { delete_vendors(where: {bodyshopid: {_eq: $shopId}}) { affected_rows } } `; // New queries for deprovisioning const GET_BODYSHOP = ` query GetBodyshop($id: uuid!) { bodyshops_by_pk(id: $id) { external_shop_id shopname } } `; const GET_ASSOCIATED_USERS = ` query GetAssociatedUsers($shopId: uuid!) { associations(where: {shopid: {_eq: $shopId}}) { user { authid email } } } `; const DELETE_ASSOCIATIONS_BY_SHOP = ` mutation DeleteAssociationsByShop($shopId: uuid!) { delete_associations(where: {shopid: {_eq: $shopId}}) { affected_rows } } `; const GET_USER_ASSOCIATIONS_COUNT = ` query GetUserAssociationsCount($userEmail: String!) { associations_aggregate(where: {useremail: {_eq: $userEmail}}) { aggregate { count } } } `; const DELETE_USER = ` mutation DeleteUser($email: String!) { delete_users(where: {email: {_eq: $email}}) { affected_rows } } `; const GET_VENDORS = ` query GetVendors($shopId: uuid!) { vendors(where: {bodyshopid: {_eq: $shopId}}) { name } } `; /** * Deletes a Firebase user by UID. * @param uid * @returns {Promise} */ const deleteFirebaseUser = async (uid) => { return admin.auth().deleteUser(uid); }; /** * Deletes all vendors associated with a shop. * @param shopId * @returns {Promise} */ const deleteVendorsByShop = async (shopId) => { await client.request(DELETE_VENDORS_BY_SHOP, { shopId }); }; /** * Deletes a bodyshop from the database. * @param shopId * @returns {Promise} */ const deleteBodyshop = async (shopId) => { await client.request(DELETE_SHOP, { id: shopId }); }; /** * Handles deprovisioning a shop for parts management. * @param req * @param res * @returns {Promise<*>} */ const partsManagementDeprovisioning = async (req, res) => { const { logger } = req; const p = req.body; if (process.env.NODE_ENV === "production") { return res.status(403).json({ error: "Deprovisioning not allowed in production environment." }); } try { if (!p.shopId) { throw { status: 400, message: "shopId is required." }; } // Fetch bodyshop and check external_shop_id const shopResp = await client.request(GET_BODYSHOP, { id: p.shopId }); const shop = shopResp.bodyshops_by_pk; if (!shop) { throw { status: 404, message: `Bodyshop with id ${p.shopId} not found.` }; } if (!shop.external_shop_id) { throw { status: 400, message: "Cannot delete bodyshop without external_shop_id." }; } logger.log("admin-delete-shop", "debug", null, null, { shopId: p.shopId, shopname: shop.shopname, ioadmin: true }); // Get vendors const vendorsResp = await client.request(GET_VENDORS, { shopId: p.shopId }); const deletedVendors = vendorsResp.vendors.map((v) => v.name); // Get associated users const assocResp = await client.request(GET_ASSOCIATED_USERS, { shopId: p.shopId }); const associatedUsers = assocResp.associations.map((assoc) => ({ authId: assoc.user.authid, email: assoc.user.email })); // Delete associations for the shop const assocDeleteResp = await client.request(DELETE_ASSOCIATIONS_BY_SHOP, { shopId: p.shopId }); const associationsDeleted = assocDeleteResp.delete_associations.affected_rows; // For each user, check if they have remaining associations; if not, delete user and Firebase account const deletedUsers = []; for (const user of associatedUsers) { const countResp = await client.request(GET_USER_ASSOCIATIONS_COUNT, { userEmail: user.email }); const assocCount = countResp.associations_aggregate.aggregate.count; if (assocCount === 0) { await client.request(DELETE_USER, { email: user.email }); await deleteFirebaseUser(user.authId); deletedUsers.push(user.email); } } // Delete vendors await deleteVendorsByShop(p.shopId); // Delete shop await deleteBodyshop(p.shopId); // Summary log console.log( `Deleted bodyshop ${p.shopId} (${shop.shopname}), ${associationsDeleted} associations, ${deletedUsers.length} users (${deletedUsers.join(", ") || "none"}), ${deletedVendors.length} vendors (${deletedVendors.join(", ") || "none"}).` ); return res.status(200).json({ message: `Bodyshop ${p.shopId} and associated resources deleted successfully.`, deletedShop: { id: p.shopId, name: shop.shopname }, deletedAssociationsCount: associationsDeleted, deletedUsers: deletedUsers, deletedVendors: deletedVendors }); } catch (err) { logger.log("admin-delete-shop-error", "error", null, null, { message: err.message, detail: err.detail || err }); return res.status(err.status || 500).json({ error: err.message || "Internal server error" }); } }; module.exports = partsManagementDeprovisioning;