/** * User Profile API * * GET: Returns current user's profile info * POST: Updates profile (firstName, lastName) */ function apiAbort(required struct payload) { writeOutput(serializeJSON(payload)); abort; } // Helper to get header value - use servlet request object (CGI scope doesn't expose custom HTTP headers in Lucee) function getHeader(name) { 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 ""; } // Get authenticated user - try request scope first, then do token lookup userId = 0; if (structKeyExists(request, "UserID") && isNumeric(request.UserID) && request.UserID > 0) { userId = request.UserID; } else { userToken = getHeader("X-User-Token"); if (len(userToken)) { try { qTok = queryExecute( "SELECT UserID FROM UserTokens WHERE Token = ? LIMIT 1", [ { value = userToken, cfsqltype = "cf_sql_varchar" } ], { datasource = "payfrit" } ); if (qTok.recordCount EQ 1) { userId = qTok.UserID; } } catch (any e) { /* ignore */ } } } if (userId <= 0) { apiAbort({ "OK": false, "ERROR": "not_logged_in", "MESSAGE": "Authentication required" }); } // Handle GET - return profile if (cgi.REQUEST_METHOD == "GET") { try { qUser = queryExecute(" SELECT UserID, UserFirstName, UserLastName, UserEmailAddress, UserContactNumber FROM Users WHERE UserID = :userId LIMIT 1 ", { userId: { value = userId, cfsqltype = "cf_sql_integer" } }); if (qUser.recordCount == 0) { apiAbort({ "OK": false, "ERROR": "user_not_found", "MESSAGE": "User not found" }); } writeOutput(serializeJSON({ "OK": true, "USER": { "UserID": qUser.UserID, "FirstName": qUser.UserFirstName ?: "", "LastName": qUser.UserLastName ?: "", "Email": qUser.UserEmailAddress ?: "", "Phone": qUser.UserContactNumber ?: "" } })); abort; } catch (any e) { apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": "Failed to load profile", "DETAIL": e.message }); } } // Handle POST - update profile if (cgi.REQUEST_METHOD == "POST") { try { requestBody = toString(getHttpRequestData().content); if (!len(requestBody)) { apiAbort({ "OK": false, "ERROR": "missing_body", "MESSAGE": "Request body required" }); } data = deserializeJSON(requestBody); // Build update fields updates = []; params = { userId: { value = userId, cfsqltype = "cf_sql_integer" } }; if (structKeyExists(data, "firstName")) { arrayAppend(updates, "UserFirstName = :firstName"); params.firstName = { value = data.firstName, cfsqltype = "cf_sql_varchar" }; } if (structKeyExists(data, "lastName")) { arrayAppend(updates, "UserLastName = :lastName"); params.lastName = { value = data.lastName, cfsqltype = "cf_sql_varchar" }; } if (arrayLen(updates) == 0) { apiAbort({ "OK": false, "ERROR": "no_changes", "MESSAGE": "No fields to update" }); } // Execute update queryExecute(" UPDATE Users SET #arrayToList(updates, ', ')# WHERE UserID = :userId ", params); // Return updated profile qUser = queryExecute(" SELECT UserID, UserFirstName, UserLastName, UserEmailAddress, UserContactNumber FROM Users WHERE UserID = :userId LIMIT 1 ", { userId: { value = userId, cfsqltype = "cf_sql_integer" } }); writeOutput(serializeJSON({ "OK": true, "MESSAGE": "Profile updated", "USER": { "UserID": qUser.UserID, "FirstName": qUser.UserFirstName ?: "", "LastName": qUser.UserLastName ?: "", "Email": qUser.UserEmailAddress ?: "", "Phone": qUser.UserContactNumber ?: "" } })); abort; } catch (any e) { apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": "Failed to update profile", "DETAIL": e.message }); } } // Unknown method apiAbort({ "OK": false, "ERROR": "bad_method", "MESSAGE": "Use GET or POST" });