158 lines
5.9 KiB
Text
158 lines
5.9 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
<cfheader name="Cache-Control" value="no-store">
|
|
|
|
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
|
<cfargument name="payload" type="struct" required="true">
|
|
<cfcontent type="application/json; charset=utf-8">
|
|
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
|
<cfabort>
|
|
</cffunction>
|
|
|
|
<cftry>
|
|
<cfset headersDir = expandPath("/uploads/headers")>
|
|
<cfscript>
|
|
// Get BusinessID from form, request scope, or header
|
|
bizId = 0;
|
|
if (structKeyExists(form, "BusinessID") && isNumeric(form.BusinessID) && form.BusinessID GT 0) {
|
|
bizId = int(form.BusinessID);
|
|
} else if (structKeyExists(request, "BusinessID") && isNumeric(request.BusinessID) && request.BusinessID GT 0) {
|
|
bizId = int(request.BusinessID);
|
|
} else {
|
|
httpHeaders = getHttpRequestData().headers;
|
|
if (structKeyExists(httpHeaders, "X-Business-ID") && isNumeric(httpHeaders["X-Business-ID"]) && httpHeaders["X-Business-ID"] GT 0) {
|
|
bizId = int(httpHeaders["X-Business-ID"]);
|
|
}
|
|
}
|
|
|
|
if (bizId LTE 0) {
|
|
apiAbort({ "OK": false, "ERROR": "missing_businessid", "MESSAGE": "BusinessID is required" });
|
|
}
|
|
</cfscript>
|
|
|
|
<!--- Check if file was uploaded --->
|
|
<cfif NOT structKeyExists(form, "header") OR form.header EQ "">
|
|
<cfoutput>#serializeJSON({ "OK": false, "ERROR": "no_file", "MESSAGE": "No file was uploaded" })#</cfoutput>
|
|
<cfabort>
|
|
</cfif>
|
|
|
|
<!--- Upload the file to temp location first --->
|
|
<cffile action="UPLOAD" filefield="header" destination="#headersDir#/" nameconflict="MAKEUNIQUE" mode="755" result="uploadResult">
|
|
|
|
<!--- Get image info and detect actual format --->
|
|
<cfset actualExt = "">
|
|
<cftry>
|
|
<cfimage source="#headersDir#/#uploadResult.ServerFile#" action="info" structName="imageInfo">
|
|
<!--- Use the actual detected format from source_file if available --->
|
|
<cfif structKeyExists(imageInfo, "source_file")>
|
|
<cfset actualFormat = lCase(imageInfo.source_file)>
|
|
<cfif findNoCase("jpeg", actualFormat) OR findNoCase("jpg", actualFormat)>
|
|
<cfset actualExt = "jpg">
|
|
<cfelseif findNoCase("png", actualFormat)>
|
|
<cfset actualExt = "png">
|
|
<cfelseif findNoCase("gif", actualFormat)>
|
|
<cfset actualExt = "gif">
|
|
<cfelseif findNoCase("webp", actualFormat)>
|
|
<cfset actualExt = "webp">
|
|
</cfif>
|
|
</cfif>
|
|
<cfcatch>
|
|
<!--- cfimage failed - will use fallback detection below --->
|
|
</cfcatch>
|
|
</cftry>
|
|
|
|
<!--- Fallback: if cfimage didn't give us format, detect from file --->
|
|
<cfif NOT len(actualExt)>
|
|
<!--- Fallback: detect by reading first bytes (magic numbers) --->
|
|
<cffile action="readbinary" file="#headersDir#/#uploadResult.ServerFile#" variable="fileBytes">
|
|
<cfset firstBytes = left(binaryEncode(fileBytes, "hex"), 16)>
|
|
<cfif left(firstBytes, 4) EQ "FFD8">
|
|
<cfset actualExt = "jpg">
|
|
<cfelseif left(firstBytes, 16) EQ "89504E470D0A1A0A">
|
|
<cfset actualExt = "png">
|
|
<cfelseif left(firstBytes, 6) EQ "474946">
|
|
<cfset actualExt = "gif">
|
|
<cfelse>
|
|
<!--- Last resort: use client extension --->
|
|
<cfset actualExt = lCase(uploadResult.ClientFileExt)>
|
|
</cfif>
|
|
</cfif>
|
|
|
|
<!--- Validate file type (include HEIC for iPhone) --->
|
|
<cfset allowedExtensions = "jpg,jpeg,gif,png,webp,heic,heif">
|
|
<cfif NOT listFindNoCase(allowedExtensions, actualExt)>
|
|
<cffile action="DELETE" file="#headersDir#/#uploadResult.ServerFile#">
|
|
<cfoutput>#serializeJSON({ "OK": false, "ERROR": "invalid_type", "MESSAGE": "Only image files are accepted (jpg, jpeg, gif, png, webp, heic)" })#</cfoutput>
|
|
<cfabort>
|
|
</cfif>
|
|
|
|
<!--- Convert HEIC/HEIF extension to jpg for consistency --->
|
|
<cfif actualExt EQ "heic" OR actualExt EQ "heif">
|
|
<cfset actualExt = "jpg">
|
|
</cfif>
|
|
|
|
<!--- No resize - accept image as-is --->
|
|
|
|
<!--- Delete old header if exists --->
|
|
<cfquery name="qOldHeader" datasource="payfrit">
|
|
SELECT HeaderImageExtension AS BusinessHeaderImageExtension
|
|
FROM Businesses
|
|
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
|
|
</cfquery>
|
|
|
|
<cfif qOldHeader.recordCount GT 0 AND len(trim(qOldHeader.BusinessHeaderImageExtension)) GT 0>
|
|
<cfset oldFile = "#headersDir#/#bizId#.#qOldHeader.BusinessHeaderImageExtension#">
|
|
<cfif fileExists(oldFile)>
|
|
<cftry>
|
|
<cffile action="DELETE" file="#oldFile#">
|
|
<cfcatch></cfcatch>
|
|
</cftry>
|
|
</cfif>
|
|
</cfif>
|
|
|
|
<!--- Also delete destination file if it exists (same extension re-upload) --->
|
|
<cfset destFile = "#headersDir#/#bizId#.#actualExt#">
|
|
<cfif fileExists(destFile)>
|
|
<cftry>
|
|
<cffile action="DELETE" file="#destFile#">
|
|
<cfcatch></cfcatch>
|
|
</cftry>
|
|
</cfif>
|
|
|
|
<!--- Verify uploaded file still exists before rename --->
|
|
<cfset sourceFile = "#headersDir#/#uploadResult.ServerFile#">
|
|
<cfif NOT fileExists(sourceFile)>
|
|
<cfoutput>#serializeJSON({ "OK": false, "ERROR": "upload_failed", "MESSAGE": "Uploaded file not found - upload may have failed", "DEBUG_SOURCE": sourceFile })#</cfoutput>
|
|
<cfabort>
|
|
</cfif>
|
|
|
|
<!--- Rename to BusinessID.ext --->
|
|
<cffile action="RENAME" source="#sourceFile#" destination="#destFile#" mode="755">
|
|
|
|
<!--- Update database --->
|
|
<cfquery datasource="payfrit">
|
|
UPDATE Businesses
|
|
SET HeaderImageExtension = <cfqueryparam cfsqltype="cf_sql_varchar" value="#actualExt#">
|
|
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
|
|
</cfquery>
|
|
|
|
<!--- Return success with image URL --->
|
|
<cfset imgWidth = isDefined("imageInfo.width") ? imageInfo.width : 0>
|
|
<cfset imgHeight = isDefined("imageInfo.height") ? imageInfo.height : 0>
|
|
<cfoutput>#serializeJSON({
|
|
"OK": true,
|
|
"ERROR": "",
|
|
"MESSAGE": "Header uploaded successfully",
|
|
"HEADERURL": "/uploads/headers/#bizId#.#actualExt#",
|
|
"WIDTH": imgWidth,
|
|
"HEIGHT": imgHeight
|
|
})#</cfoutput>
|
|
|
|
<cfcatch type="any">
|
|
<cfheader statuscode="200" statustext="OK">
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
<cfoutput>#serializeJSON({ "OK": false, "ERROR": "server_error", "MESSAGE": cfcatch.message, "DETAIL": cfcatch.detail })#</cfoutput>
|
|
</cfcatch>
|
|
</cftry>
|