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 Mizerek 05cf73446f Add dev environment configuration and tools
- api/config/environment.cfm: Central config for dev vs prod settings
  - Verbose errors, debug logging, magic OTP bypass
  - Rate limiting toggle, email catch-all, token expiry settings
- api/dev/: Development-only endpoints
  - seedData.cfm: Create/reset test users
  - timeTravel.cfm: Manipulate timestamps for testing
  - index.cfm: Dev tools index

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 12:39:28 -08:00

146 lines
4.6 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" });
}
queryExecute("
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" });
}
queryExecute("
UPDATE Users
SET UserAddedOn = 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)
queryExecute("
UPDATE Users SET UserMobileVerifyCode = ''
", {}, { 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" });
}
queryExecute("
UPDATE Users
SET UserIsContactVerified = 0,
UserIsActive = 0,
UserFirstName = NULL,
UserLastName = NULL,
UserMobileVerifyCode = '123456'
WHERE UserContactNumber = :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>