This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/dev/timeTravel.cfm
John 16a3b7c9a3 Replace queryExecute with queryTimed across all endpoints for perf tracking
Converts 200+ endpoint files to use queryTimed() wrapper which tracks
DB query count and execution time. Restores perf dashboard files that
were accidentally moved to _scripts/. Includes portal UI updates.
2026-02-02 00:28:37 -08:00

146 lines
4.5 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
/**
* Time Travel Endpoint - DEV ONLY
*
* Allows manipulation of timestamps for testing time-based features.
* Examples: token expiry, trial periods, scheduled events.
*
* POST: { "action": "expireTokens", "userId": 123 }
* POST: { "action": "setUserCreated", "userId": 123, "daysAgo": 30 }
*/
function apiAbort(required struct payload) {
writeOutput(serializeJSON(payload));
abort;
}
// SAFETY: Only allow on dev environment
if (!structKeyExists(application, "isDevEnvironment") || !application.isDevEnvironment) {
apiAbort({
"OK": false,
"ERROR": "forbidden",
"MESSAGE": "This endpoint is only available in development"
});
}
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 {};
}
try {
if (cgi.request_method != "POST") {
apiAbort({
"OK": false,
"ERROR": "method_not_allowed",
"MESSAGE": "POST required"
});
}
data = readJsonBody();
action = structKeyExists(data, "action") ? lcase(data.action) : "";
switch (action) {
case "expiretokens":
// Expire all tokens for a user (simulate session timeout)
if (!structKeyExists(data, "userId")) {
apiAbort({ "OK": false, "ERROR": "missing_userId" });
}
queryTimed("
UPDATE UserTokens
SET CreatedAt = DATE_SUB(NOW(), INTERVAL 30 DAY)
WHERE UserID = :userId
", {
userId: { value: data.userId, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"message": "Tokens expired for user " & data.userId
}));
break;
case "setusercreated":
// Backdate user creation (for testing trial periods, etc)
if (!structKeyExists(data, "userId") || !structKeyExists(data, "daysAgo")) {
apiAbort({ "OK": false, "ERROR": "missing_userId_or_daysAgo" });
}
queryTimed("
UPDATE Users
SET AddedOn = DATE_SUB(NOW(), INTERVAL :days DAY)
WHERE UserID = :userId
", {
userId: { value: data.userId, cfsqltype: "cf_sql_integer" },
days: { value: data.daysAgo, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"message": "User " & data.userId & " created date set to " & data.daysAgo & " days ago"
}));
break;
case "clearotps":
// Clear all OTPs (force re-request)
queryTimed("
UPDATE Users SET MobileVerifyCode = ''
", {}, { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"message": "All OTPs cleared"
}));
break;
case "resetuser":
// Reset a user to unverified state (for retesting signup)
if (!structKeyExists(data, "phone")) {
apiAbort({ "OK": false, "ERROR": "missing_phone" });
}
queryTimed("
UPDATE Users
SET IsContactVerified = 0,
IsActive = 0,
FirstName = NULL,
LastName = NULL,
MobileVerifyCode = '123456'
WHERE ContactNumber = :phone
", {
phone: { value: data.phone, cfsqltype: "cf_sql_varchar" }
}, { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"message": "User with phone " & data.phone & " reset to unverified"
}));
break;
default:
apiAbort({
"OK": false,
"ERROR": "unknown_action",
"MESSAGE": "Valid actions: expireTokens, setUserCreated, clearOTPs, resetUser"
});
}
} catch (any e) {
apiAbort({
"OK": false,
"ERROR": "server_error",
"MESSAGE": application.showDetailedErrors ? e.message : "An error occurred"
});
}
</cfscript>