Portal local development: - Add BASE_PATH detection to all portal files (login, portal.js, menu-builder, station-assignment) - Allows portal to work at /biz.payfrit.com/ path locally Menu Builder fixes: - Fix duplicate template options in getForBuilder.cfm query - Filter template children by business ID with DISTINCT New APIs: - api/portal/myBusinesses.cfm - List businesses for logged-in user - api/stations/list.cfm - List KDS stations - api/menu/updateStations.cfm - Update item station assignments - api/setup/reimportBigDeans.cfm - Full Big Dean's menu import script Admin utilities: - Various debug and migration scripts for menu/template management - Beacon switching, category cleanup, modifier template setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
116 lines
2.8 KiB
Text
116 lines
2.8 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
<cfheader name="Cache-Control" value="no-store">
|
|
|
|
<cfscript>
|
|
/*
|
|
PATH:
|
|
C:\lucee\tomcat\webapps\ROOT\biz.payfrit.com\api\auth\login.cfm
|
|
|
|
INPUT (JSON):
|
|
{ "username": "...", "password": "..." }
|
|
|
|
OUTPUT (JSON):
|
|
{ OK:true, ERROR:"", UserID:123, UserFirstName:"...", Token:"..." }
|
|
|
|
Uses existing UserTokens table:
|
|
TokenID (auto), UserID, Token, CreatedAt (DEFAULT CURRENT_TIMESTAMP)
|
|
-> INSERT does NOT include CreatedAt.
|
|
*/
|
|
|
|
function apiAbort(required struct payload) {
|
|
writeOutput(serializeJSON(payload));
|
|
abort;
|
|
}
|
|
|
|
function readJsonBody() {
|
|
var raw = getHttpRequestData().content;
|
|
if (isNull(raw)) raw = "";
|
|
if (!len(trim(raw))) return {};
|
|
try {
|
|
var data = deserializeJSON(raw);
|
|
if (isStruct(data)) return data;
|
|
} catch (any e) {}
|
|
return {};
|
|
}
|
|
|
|
function normalizeUsername(required string u) {
|
|
var x = trim(arguments.u);
|
|
x = replace(x, " ", "", "all");
|
|
x = replace(x, "(", "", "all");
|
|
x = replace(x, ")", "", "all");
|
|
x = replace(x, "-", "", "all");
|
|
return x;
|
|
}
|
|
|
|
data = readJsonBody();
|
|
username = structKeyExists(data, "username") ? normalizeUsername("" & data.username) : "";
|
|
password = structKeyExists(data, "password") ? ("" & data.password) : "";
|
|
|
|
if (!len(username) || !len(password)) {
|
|
apiAbort({ "OK": false, "ERROR": "missing_fields" });
|
|
}
|
|
|
|
try {
|
|
q = queryExecute(
|
|
"
|
|
SELECT UserID, UserFirstName
|
|
FROM Users
|
|
WHERE
|
|
(
|
|
(UserEmailAddress = ?) OR
|
|
(UserContactNumber = ?)
|
|
)
|
|
AND UserPassword = ?
|
|
AND UserIsEmailVerified = 1
|
|
AND UserIsContactVerified > 0
|
|
LIMIT 1
|
|
",
|
|
[
|
|
{ value = username, cfsqltype = "cf_sql_varchar" },
|
|
{ value = username, cfsqltype = "cf_sql_varchar" },
|
|
{ value = hash(password), cfsqltype = "cf_sql_varchar" }
|
|
],
|
|
{ datasource = "payfrit" }
|
|
);
|
|
|
|
if (q.recordCount NEQ 1) {
|
|
apiAbort({ "OK": false, "ERROR": "bad_credentials" });
|
|
}
|
|
|
|
token = replace(createUUID(), "-", "", "all");
|
|
|
|
queryExecute(
|
|
"INSERT INTO UserTokens (UserID, Token) VALUES (?, ?)",
|
|
[
|
|
{ value = q.UserID, cfsqltype = "cf_sql_integer" },
|
|
{ value = token, cfsqltype = "cf_sql_varchar" }
|
|
],
|
|
{ datasource = "payfrit" }
|
|
);
|
|
|
|
// Optional: also set session for browser tools
|
|
lock timeout="15" throwontimeout="yes" type="exclusive" scope="session" {
|
|
session.UserID = q.UserID;
|
|
}
|
|
request.UserID = q.UserID;
|
|
|
|
writeOutput(serializeJSON({
|
|
"OK": true,
|
|
"ERROR": "",
|
|
"UserID": q.UserID,
|
|
"UserFirstName": q.UserFirstName,
|
|
"Token": token
|
|
}));
|
|
abort;
|
|
|
|
} catch (any e) {
|
|
apiAbort({
|
|
"OK": false,
|
|
"ERROR": "server_error",
|
|
"MESSAGE": "DB error during login",
|
|
"DETAIL": e.message
|
|
});
|
|
}
|
|
</cfscript>
|