const admin = require("firebase-admin"); const client = require("../../../graphql-client/graphql-client").client; const DefaultNewShop = require("../defaultNewShop.json"); const { CHECK_EXTERNAL_SHOP_ID, CREATE_SHOP, DELETE_VENDORS_BY_SHOP, DELETE_SHOP, CREATE_USER } = require("../partsManagement.queries"); const requireFields = (payload, fields) => { for (const field of fields) { if (!payload[field]) { throw { status: 400, message: `${field} is required.` }; } } }; const ensureEmailNotRegistered = async (email) => { try { await admin.auth().getUserByEmail(email); throw { status: 400, message: "userEmail is already registered in Firebase." }; } catch (err) { if (err.code !== "auth/user-not-found") { throw { status: 500, message: "Error validating userEmail uniqueness", detail: err }; } } }; const createFirebaseUser = async (email, password = null) => { const userData = { email }; if (password) userData.password = password; return admin.auth().createUser(userData); }; const deleteFirebaseUser = async (uid) => { return admin.auth().deleteUser(uid); }; const generateResetLink = async (email) => { return admin.auth().generatePasswordResetLink(email); }; const ensureExternalIdUnique = async (externalId) => { const resp = await client.request(CHECK_EXTERNAL_SHOP_ID, { key: externalId }); if (resp.bodyshops.length) { throw { status: 400, message: `external_shop_id '${externalId}' is already in use.` }; } }; const insertBodyshop = async (input) => { const resp = await client.request(CREATE_SHOP, { bs: input }); return resp.insert_bodyshops_one.id; }; const deleteVendorsByShop = async (shopId) => { await client.request(DELETE_VENDORS_BY_SHOP, { shopId }); }; const deleteBodyshop = async (shopId) => { await client.request(DELETE_SHOP, { id: shopId }); }; const insertUserAssociation = async (uid, email, shopId) => { const vars = { u: { email, authid: uid, validemail: true, associations: { data: [{ shopid: shopId, authlevel: 80, active: true }] } } }; const resp = await client.request(CREATE_USER, vars); return resp.insert_users_one; }; const partsManagementProvisioning = async (req, res) => { const { logger } = req; const p = { ...req.body, userEmail: req.body.userEmail?.toLowerCase() }; try { await ensureEmailNotRegistered(p.userEmail); requireFields(p, [ "external_shop_id", "shopname", "address1", "city", "state", "zip_post", "country", "email", "phone", "userEmail" ]); await ensureExternalIdUnique(p.external_shop_id); logger.log("admin-create-shop-user", "debug", p.userEmail, null, { request: req.body, ioadmin: true }); const shopInput = { shopname: p.shopname, address1: p.address1, address2: p.address2 || null, city: p.city, state: p.state, zip_post: p.zip_post, country: p.country, email: p.email, external_shop_id: p.external_shop_id, timezone: p.timezone, phone: p.phone, logo_img_path: { src: p.logoUrl, width: "", height: "", headerMargin: DefaultNewShop.logo_img_path.headerMargin }, features: { partsManagementOnly: true }, md_ro_statuses: DefaultNewShop.md_ro_statuses, vendors: { data: p.vendors.map((v) => ({ name: v.name, street1: v.street1 || null, street2: v.street2 || null, city: v.city || null, state: v.state || null, zip: v.zip || null, country: v.country || null, email: v.email || null, discount: v.discount ?? 0, due_date: v.due_date ?? null, cost_center: v.cost_center || null, favorite: v.favorite ?? [], phone: v.phone || null, active: v.active ?? true, dmsid: v.dmsid || null })) } }; const newShopId = await insertBodyshop(shopInput); const userRecord = await createFirebaseUser(p.userEmail, p.userPassword); let resetLink = null; if (!p.userPassword) resetLink = await generateResetLink(p.userEmail); const createdUser = await insertUserAssociation(userRecord.uid, p.userEmail, newShopId); return res.status(200).json({ shop: { id: newShopId, shopname: p.shopname }, user: { id: createdUser.id, email: createdUser.email, resetLink: resetLink || undefined } }); } catch (err) { logger.log("admin-create-shop-user-error", "error", p.userEmail, null, { message: err.message, detail: err.detail || err }); if (err.userRecord) { await deleteFirebaseUser(err.userRecord.uid).catch(() => { /* empty */ }); } if (err.newShopId) { await deleteVendorsByShop(err.newShopId).catch(() => { /* empty */ }); await deleteBodyshop(err.newShopId).catch(() => { /* empty */ }); } return res.status(err.status || 500).json({ error: err.message || "Internal server error" }); } }; module.exports = partsManagementProvisioning;