function apiAbort(payload) { writeOutput(serializeJSON(payload)); abort; } function headerValue(name) { // Use servlet request object to get headers (CGI scope doesn't expose custom HTTP headers in Lucee) try { req = getPageContext().getRequest(); val = req.getHeader(arguments.name); if (!isNull(val)) return trim(val); } catch (any e) { // Fall back to CGI scope k = "HTTP_" & ucase(reReplace(arguments.name, "[^A-Za-z0-9]", "_", "all")); if (structKeyExists(cgi, k)) return trim(cgi[k]); } return ""; } // Determine request path request._api_scriptName = ""; if (structKeyExists(cgi, "SCRIPT_NAME")) { request._api_scriptName = cgi.SCRIPT_NAME; } else if (structKeyExists(cgi, "PATH_INFO")) { request._api_scriptName = cgi.PATH_INFO; } request._api_path = lcase(request._api_scriptName); // Public allowlist request._api_isPublic = false; if (len(request._api_path)) { if (findNoCase("/api/login.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/logout.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/login.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/logout.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/avatar.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/sendOTP.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/verifyOTP.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/loginOTP.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/verifyLoginOTP.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/auth/completeProfile.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/businesses/list.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/businesses/get.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/servicepoints/list.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/beacons/list_all.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/beacons/getBusinessFromBeacon.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/menu/items.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/addresses/states.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/debug/", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/getOrCreateCart.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/getCart.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/setLineItem.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/submit.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/listForKDS.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/updateStatus.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/checkStatusUpdate.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/orders/getDetail.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/users/search.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/listPending.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/accept.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/listMine.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/complete.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/completeChat.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/getDetails.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/callserver", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/createChat.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/expirestalechats.cfm", request._api_path)) request._api_isPublic = true; // Chat endpoints if (findNoCase("/api/chat/getMessages.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/chat/sendMessage.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/chat/markRead.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/chat/getActiveChat.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/chat/closechat.cfm", request._api_path)) request._api_isPublic = true; // Token validation (for WebSocket server) if (findNoCase("/api/auth/validateToken.cfm", request._api_path)) request._api_isPublic = true; // Worker app endpoints if (findNoCase("/api/workers/myBusinesses.cfm", request._api_path)) request._api_isPublic = true; // Portal endpoints if (findNoCase("/api/portal/stats.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/portal/myBusinesses.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/portal/team.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/businesses/setHiring.cfm", request._api_path)) request._api_isPublic = true; // Order history (auth handled in endpoint) if (findNoCase("/api/orders/history.cfm", request._api_path)) request._api_isPublic = true; // User profile (auth handled in endpoint) if (findNoCase("/api/auth/profile.cfm", request._api_path)) request._api_isPublic = true; // Menu builder endpoints if (findNoCase("/api/menu/getForBuilder.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/menu/saveFromBuilder.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/menu/updateStations.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/tasks/create.cfm", request._api_path)) request._api_isPublic = true; // Debug endpoints if (findNoCase("/api/debug/checkToken.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/debug/headers.cfm", request._api_path)) request._api_isPublic = true; // Admin endpoints (protected by localhost check in each file) if (findNoCase("/api/admin/resetTestData.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugTasks.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/testTaskType.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/testTaskInsert.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugBusinesses.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/setupStations.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/setupModifierTemplates.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/migrateModifierTemplates.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/deleteOrphanModifiers.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/fixShakeFlavors.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/eliminateCategories.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/cleanupCategories.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/deleteOrphans.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/switchBeacons.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/randomizePrices.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugTemplateLinks.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/fixBigDeansCategories.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/migrateToCategories.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/addItemCategoryColumn.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/checkBigDeans.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/updateBigDeans.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugBigDeansMenu.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/listTables.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/describeTable.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/setupBigDeansInfo.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/beaconStatus.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/updateBeaconMapping.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/setupBigDeansStations.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/copyDrinksToBigDeans.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugDrinkStructure.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/addDrinkModifiers.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/add_task_columns.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/add_service_category.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/createChatMessagesTable.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/addTaskSourceColumns.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugChatMessages.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/cleanupDuplicateEmployees.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugEmployees.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/debugUserByPhone.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/admin/setEmployeeActive.cfm", request._api_path)) request._api_isPublic = true; // Setup/Import endpoints if (findNoCase("/api/setup/importBusiness.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/setup/analyzeMenu.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/setup/analyzeMenuImages.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/setup/saveWizard.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/setup/downloadImages.cfm", request._api_path)) request._api_isPublic = true; // Stations endpoints if (findNoCase("/api/stations/list.cfm", request._api_path)) request._api_isPublic = true; // Stripe endpoints if (findNoCase("/api/stripe/onboard.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/stripe/status.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/stripe/createPaymentIntent.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/stripe/webhook.cfm", request._api_path)) request._api_isPublic = true; } // Carry session values into request (if present) if (!structKeyExists(request, "UserID") && structKeyExists(session, "UserID")) { request.UserID = Duplicate(session.UserID); } if (!structKeyExists(request, "BusinessID") && structKeyExists(session, "BusinessID")) { request.BusinessID = Duplicate(session.BusinessID); } // Token auth: X-User-Token -> request.UserID request._api_userToken = headerValue("X-User-Token"); if (len(request._api_userToken)) { try { request._api_qTok = queryExecute( "SELECT UserID FROM UserTokens WHERE Token = ? LIMIT 1", [ { value = request._api_userToken, cfsqltype = "cf_sql_varchar" } ], { datasource = "payfrit" } ); if (request._api_qTok.recordCount EQ 1) { request.UserID = request._api_qTok.UserID; session.UserID = request._api_qTok.UserID; } } catch (any e) { // ignore; treated as unauthenticated } } // Business header: X-Business-ID -> request.BusinessID request._api_hdrBiz = headerValue("X-Business-ID"); if (len(request._api_hdrBiz) && isNumeric(request._api_hdrBiz)) { request.BusinessID = int(request._api_hdrBiz); session.BusinessID = request.BusinessID; } // Enforce auth (except public) if (!request._api_isPublic) { if (!structKeyExists(request, "UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) { apiAbort({ "OK": false, "ERROR": "not_logged_in", "DEBUG_PATH": request._api_path, "DEBUG_IS_PUBLIC": request._api_isPublic, "DEBUG_SCRIPT_NAME": structKeyExists(cgi, "SCRIPT_NAME") ? cgi.SCRIPT_NAME : "N/A", "DEBUG_PATH_INFO": structKeyExists(cgi, "PATH_INFO") ? cgi.PATH_INFO : "N/A", "DEBUG_HAS_TOKEN": len(request._api_userToken) > 0, "DEBUG_TOKEN_PREFIX": len(request._api_userToken) > 8 ? left(request._api_userToken, 8) : request._api_userToken }); } if (!structKeyExists(request, "BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) { apiAbort({ "OK": false, "ERROR": "no_business_selected" }); } }