import bcrypt from "bcryptjs"; import { eq, sql } from "drizzle-orm"; import { Router } from "express"; import z from "zod"; import { db } from "../db/db.controller.js"; import { type NewScanUser, type ScanUser, scanUser, } from "../db/schema/scanUsers.js"; import { requireAuth } from "../middleware/auth.middleware.js"; import { apiReturn, returnFunc } from "../utils/returnHelper.utils.js"; import { tryCatch } from "../utils/trycatch.utils.js"; const r = Router(); export async function hashPin(pin: string) { // if (!/^\d{6}$/.test(pin)) { // throw new Error("PIN must be exactly 6 digits"); // } return bcrypt.hashSync(pin, 12); } const registerSchema = z.object({ name: z.string().min(2).max(100), pinNumber: z.string(), scannerId: z .string() .min(1) .max(500) .optional() .describe("if you leave blank it will be the same as your username"), role: z .enum(["user", "lead", "manager", "admin"]) .optional() .describe("What roles are available to use."), pinHash: z.string().optional(), }); r.post("/pin", async (req, res) => { const { pin } = req.body; if (!pin || pin.length !== 6) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `Pin number must be a min of 6 digits`, data: [], status: 401, }); } // const user = await db // .select() // .from(scanUser) // .where(eq(scanUser.pinNumber, parseInt(pin, 10))); const user = await db.query.scanUser.findFirst({ where: (u, { eq }) => eq(u.pinNumber, pin), }); if (!user) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `Invalid login please try again.`, data: [], status: 401, }); } const validPin = bcrypt.compareSync(pin, user.pinHash); if (!validPin) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `Invalid pin please try again.`, data: [], status: 401, }); } return apiReturn(res, { success: true, level: "info", module: "mobile", subModule: "auth", message: `Welcome back ${user.name}`, data: user as ScanUser | any, status: 200, }); }); r.post("/user", async (req, res) => { try { // validate the body is correct before accepting it let validated = registerSchema.parse(req.body); validated = { ...validated, pinHash: await hashPin(validated.pinNumber.toString()), }; const values: NewScanUser = { name: validated.name, pinNumber: validated.pinNumber, pinHash: validated.pinHash ?? "", scannerId: validated.scannerId ?? "", }; const newUser = await db.insert(scanUser).values(values).returning(); apiReturn(res, { success: true, level: "info", //connect.success ? "info" : "error", module: "mobile", subModule: "auth", message: `${validated.name} was just created`, data: newUser as any, status: 200, //connect.success ? 200 : 400, }); } catch (err) { if (err instanceof z.ZodError) { const flattened = z.flattenError(err); // return res.status(400).json({ // error: "Validation failed", // details: flattened, // }); return apiReturn(res, { success: false, level: "error", //connect.success ? "info" : "error", module: "mobile", subModule: "auth", message: "Validation failed", data: [flattened.fieldErrors], status: 400, //connect.success ? 200 : 400, }); } return apiReturn(res, { success: false, level: "error", //connect.success ? "info" : "error", module: "mobile", subModule: "auth", message: "This User already exist with this pin or scanner id please try again", data: [err], status: 400, //connect.success ? 200 : 400, }); } }); r.get("/user", requireAuth, async (_, res) => { const { data, error } = await tryCatch(db.select().from(scanUser)); if (error) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `There was an error getting the user`, data: error as any, status: 400, }); } if (!data) { return apiReturn(res, { success: true, level: "info", module: "mobile", subModule: "auth", message: `There are no users you should add one . `, data: [], status: 200, }); } return apiReturn(res, { success: true, level: "info", module: "mobile", subModule: "auth", message: `All users. `, data, status: 200, }); }); r.patch("/user/:id", requireAuth, async (req, res) => { const updates: Record = {}; const { id } = req.params; const { data, error } = await tryCatch( db.query.scanUser.findFirst({ where: (u, { eq }) => eq(u.id, `${id}`), }), ); if (error) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `There was an error getting the user`, data: error as any, status: 400, }); } if (!data) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `Invalid user id was passed over. `, data: [], status: 400, }); } if (req.body?.name !== undefined) { updates.name = req.body.name; } if (req.body?.pinNumber !== undefined) { const existing = await db.query.scanUser.findFirst({ where: (u, { eq }) => eq(u.pinHash, req.body.pinNumber), }); if (existing) return returnFunc({ success: false, level: "error", module: "mobile", subModule: "auth", message: `${req.body.pinNumber} already exists please try again`, data: [], notify: false, room: "", }); updates.pinNumber = req.body.pinNumber; updates.pinHash = await hashPin(req.body.pinNumber); } if (req.body?.scannerId !== undefined) { updates.scannerId = req.body.scannerId; } if (req.body?.active !== undefined) { updates.active = req.body.active; } if (req.body?.excludedCommand !== undefined) { updates.excludedCommand = req.body.excludedCommand; } if (req.body?.role !== undefined) { updates.role = req.body.role; } updates.upd_date = sql`NOW()`; const updatedSetting = await db .update(scanUser) .set(updates) .where(eq(scanUser.id, `${id}`)) .returning(); return apiReturn(res, { success: true, level: "info", module: "mobile", subModule: "user", message: `User ${data.name} was updated. `, data: updatedSetting, status: 200, }); }); r.delete("/user/:id", requireAuth, async (req, res) => { const { id } = req.params; const { data, error } = await tryCatch( db.delete(scanUser).where(eq(scanUser.id, `${id}`)), ); if (error) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `There was an error deleting the user`, data: error as any, status: 400, }); } if (!data) { return apiReturn(res, { success: false, level: "error", module: "mobile", subModule: "auth", message: `There was no user to delete. `, data: [], status: 400, }); } return apiReturn(res, { success: true, level: "info", module: "mobile", subModule: "user", message: `User was deleted. `, data: data ?? [], status: 200, }); }); export default r;