package utils import "github.com/gofiber/fiber/v2" // Customer-app (doormile_cx) response envelope. // // Deliberately a separate helper set from OK/List/Fail rather than a reuse of // them. The customer contract fixes three things the miler/console contract // does not: `message` is always present on success (empty string, never // omitted), the machine-readable code is nested under `error.code` rather than // sitting at the top level, and `total`/`nextCursor` ride the list envelope. // Serving one endpoint in one shape and its neighbour in another is exactly // what cost the miler client a release on /miler/verify-pin — so the customer // surface gets its own helpers and every /customer/* response goes through // them, auth included. // CxErr* are the error codes in the customer contract. The client branches on // these; `message` is customer-safe English shown verbatim in the UI. const ( CxErrInvalid = "invalid" CxErrInvalidName = "invalid_name" CxErrInvalidOtp = "invalid_otp" CxErrInvalidPin = "invalid_pin" CxErrPinNotSet = "pin_not_set" CxErrPinAlreadySet = "pin_already_set" CxErrUnauthorized = "unauthorized" CxErrForbidden = "forbidden" CxErrNotFound = "not_found" CxErrConflict = "conflict" CxErrUnserviceable = "unserviceable" CxErrRateLimited = "rate_limited" CxErrServer = "server_error" ) // CxOK is the success envelope: {success, data, message}. func CxOK(c *fiber.Ctx, data interface{}) error { return c.JSON(fiber.Map{"success": true, "data": data, "message": ""}) } // CxCreated is CxOK with a 201. func CxCreated(c *fiber.Ctx, data interface{}) error { return c.Status(fiber.StatusCreated). JSON(fiber.Map{"success": true, "data": data, "message": ""}) } // CxList is the list envelope. data is always an array — never null, because // the client types it as a list and a null throws in the parser. nextCursor is // null when the page is the last one. func CxList(c *fiber.Ctx, data interface{}, total int, nextCursor *string) error { body := fiber.Map{ "success": true, "data": data, "total": total, "nextCursor": nil, "message": "", } if nextCursor != nil && *nextCursor != "" { body["nextCursor"] = *nextCursor } return c.JSON(body) } // CxFail is the error envelope: {success:false, message, error:{code}}. The // app funnels every failure into one retryable error state and renders // `message` verbatim, so callers must pass customer-safe English here — never // an enum key, a driver error or a wrapped stack. func CxFail(c *fiber.Ctx, status int, code, msg string) error { return c.Status(status).JSON(fiber.Map{ "success": false, "message": msg, "error": fiber.Map{"code": code}, }) } // Shorthands for the statuses the contract pins to a specific code. func CxBadRequest(c *fiber.Ctx, msg string) error { return CxFail(c, fiber.StatusBadRequest, CxErrInvalid, msg) } func CxUnauthorized(c *fiber.Ctx, msg string) error { return CxFail(c, fiber.StatusUnauthorized, CxErrUnauthorized, msg) } func CxForbidden(c *fiber.Ctx, msg string) error { return CxFail(c, fiber.StatusForbidden, CxErrForbidden, msg) } func CxNotFound(c *fiber.Ctx, msg string) error { return CxFail(c, fiber.StatusNotFound, CxErrNotFound, msg) } func CxConflict(c *fiber.Ctx, msg string) error { return CxFail(c, fiber.StatusConflict, CxErrConflict, msg) } // CxInternal never leaks the underlying error to the customer. Log the real // one; the app shows this. func CxInternal(c *fiber.Ctx) error { return CxFail(c, fiber.StatusInternalServerError, CxErrServer, "Something went wrong") }