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 ID - try request scope first, then do our own token lookup userId = 0; debugInfo = { "requestUserID": structKeyExists(request, "UserID") ? request.UserID : "not_set", "headerToken": "", "tokenLookupResult": "not_attempted" }; if (structKeyExists(request, "UserID") && isNumeric(request.UserID) && request.UserID > 0) { userId = request.UserID; debugInfo.source = "request_scope"; } else { // Do our own token lookup for multipart requests userToken = getHeader("X-User-Token"); debugInfo.headerToken = len(userToken) ? left(userToken, 8) & "..." : "empty"; if (len(userToken)) { try { qTok = queryExecute( "SELECT UserID FROM UserTokens WHERE Token = ? LIMIT 1", [ { value = userToken, cfsqltype = "cf_sql_varchar" } ], { datasource = "payfrit" } ); debugInfo.tokenLookupResult = "found_#qTok.recordCount#_records"; if (qTok.recordCount EQ 1) { userId = qTok.UserID; debugInfo.source = "token_lookup"; } } catch (any e) { debugInfo.tokenLookupResult = "error: " & e.message; } } } if (userId <= 0) { apiAbort({ "OK": false, "ERROR": "not_logged_in", "MESSAGE": "Authentication required", "DEBUG": debugInfo }); } // Use absolute path from web root uploadsPath = expandPath("/uploads/users/"); // Check for avatar with various extensions (case-insensitive) function findAvatarFile(basePath, userId) { extensions = ["jpg", "jpeg", "png", "gif", "webp", "JPG", "JPEG", "PNG", "GIF", "WEBP"]; for (ext in extensions) { testPath = basePath & userId & "." & ext; if (fileExists(testPath)) { return { "exists": true, "path": testPath, "filename": userId & "." & ext }; } } return { "exists": false, "path": basePath & userId & ".jpg", "filename": userId & ".jpg" }; } avatarInfo = findAvatarFile(uploadsPath, userId); avatarPath = avatarInfo.path; avatarFilename = avatarInfo.filename; avatarUrl = "https://biz.payfrit.com/uploads/users/" & avatarFilename; // Handle GET - return current avatar URL if (cgi.REQUEST_METHOD == "GET") { hasAvatar = avatarInfo.exists; writeOutput(serializeJSON({ "OK": true, "HAS_AVATAR": hasAvatar, "AVATAR_URL": hasAvatar ? (avatarUrl & "?t=" & getTickCount()) : "" })); abort; } // Handle POST - upload new avatar if (cgi.REQUEST_METHOD == "POST") { try { // Check if file was uploaded if (!structKeyExists(form, "avatar") || !len(form.avatar)) { apiAbort({ "OK": false, "ERROR": "missing_file", "MESSAGE": "No avatar file provided" }); } // Get uploaded file info uploadedFile = form.avatar; // Ensure uploads directory exists if (!directoryExists(uploadsPath)) { directoryCreate(uploadsPath); } // Process the upload uploadResult = fileUpload( destination = uploadsPath, fileField = "avatar", nameConflict = "overwrite", accept = "image/jpeg,image/png,image/gif,image/webp" ); // Rename to userId.jpg (convert if needed) uploadedPath = uploadsPath & uploadResult.serverFile; // Read the image and save as JPEG try { img = imageRead(uploadedPath); // Resize if too large (max 500x500 for avatars) if (img.width > 500 || img.height > 500) { if (img.width > img.height) { imageScaleToFit(img, 500, ""); } else { imageScaleToFit(img, "", 500); } } // Save as JPEG imageWrite(img, avatarPath, 0.85); // Delete original if different from target if (uploadedPath != avatarPath && fileExists(uploadedPath)) { fileDelete(uploadedPath); } } catch (any imgErr) { // If image processing fails, just rename the file if (uploadedPath != avatarPath) { if (fileExists(avatarPath)) { fileDelete(avatarPath); } fileMove(uploadedPath, avatarPath); } } writeOutput(serializeJSON({ "OK": true, "MESSAGE": "Avatar uploaded successfully", "AVATAR_URL": avatarUrl & "?t=" & getTickCount() })); abort; } catch (any e) { apiAbort({ "OK": false, "ERROR": "upload_error", "MESSAGE": "Failed to upload avatar", "DETAIL": e.message }); } } // Unknown method apiAbort({ "OK": false, "ERROR": "bad_method", "MESSAGE": "Use GET or POST" });