120 lines
5.4 KiB
TypeScript
120 lines
5.4 KiB
TypeScript
export interface VersionPolicy {
|
|
force: boolean;
|
|
minVersion: string | null;
|
|
apiBlacklist: string[];
|
|
}
|
|
|
|
// Empty means all players; otherwise only these verified WeChat openids are checked.
|
|
export const openidWhitelist: string[] = [
|
|
"o0tJD3asJusHgpSq35y7qOODjb8M", // 420418
|
|
"o0tJD3fvbo5eBKBxJ1ZJ9l1fM_X4", // 916541
|
|
"o0tJD3bAysG5Y3e93RikOtyLyN58", // 100057
|
|
"o0tJD3R-js9YQIz24WGNNWepW1j0", // 1327711
|
|
"o0tJD3dPjo9ghGoZ85q4QkAi4rKM", // 100022
|
|
];
|
|
|
|
export function shouldCheckPlayerVersion(openid: unknown): boolean {
|
|
return openidWhitelist.length === 0 || (typeof openid === "string" && openidWhitelist.includes(openid));
|
|
}
|
|
|
|
// Publish this internal module after editing. Select the SERVER release, never the request version.
|
|
export const currentVersion = "1.0.88";
|
|
export const versions: Record<string, VersionPolicy> = {
|
|
"1.0.82": { force: false, minVersion: null, apiBlacklist: [] },
|
|
"1.0.83": { force: false, minVersion: null, apiBlacklist: [] },
|
|
"1.0.84": { force: false, minVersion: null, apiBlacklist: [] },
|
|
"1.0.85": { force: false, minVersion: null, apiBlacklist: [] },
|
|
"1.0.86": { force: false, minVersion: null, apiBlacklist: [] },
|
|
"1.0.87": { force: false, minVersion: null, apiBlacklist: [] },
|
|
"1.0.88": { force: false, minVersion: "1.0.88", apiBlacklist: ["login", "dayTaskInfo"] }
|
|
};
|
|
|
|
// Explicit version-check exemptions, including all currently called frontend APIs.
|
|
// Entries here take precedence over apiBlacklist, openidWhitelist and policy validation.
|
|
const nonPlayerFunctions = new Set([
|
|
// 微信支付相关
|
|
"wx/payCallBack", "wx/iosPayCall", "wx/KeFuInfo", "wx/getOrderReward", "wx/getPayInfo", "wx/iosgetPayInfo", "wx/iosorderPaySig", "wx/orderPaySig",
|
|
"suRecall",
|
|
// 黄金矿工
|
|
"goldMiner/merchantNotify", "goldMiner/index",
|
|
// 用户付费统计
|
|
"rechargeStats", "repairPayUser",
|
|
"clearPay", "clearSrank", "clearPHBDataById", "creatCdk", "creatCDkTwo",
|
|
"searchOrder", "searchPlayerDataByLevel",
|
|
// Frontend API names after URL routing (getAddressRankSelf -> getAddressRank).
|
|
"activityConfig/list", "address", "canRebornGift", "cloudRise/index", "comment",
|
|
// "dayTaskInfo", "getTaskReward",
|
|
"drawCat", "drawHead", "getAddressRank", "getFilm", "getHeadInfo",
|
|
"getRank", "getSRank", "getShareLv", "getUserInfoByID",
|
|
"jungleTreasure", "limitedTimeEvent",
|
|
// "login",
|
|
"miniProgramWelfare",
|
|
"monthGetReward", "monthlyCard", "passCheck", "passCheckLv", "passCheckSettlement",
|
|
"passCheckUpgrade", "pyramid/api", "rebornGift", "redeem", "setCatArr",
|
|
"setForcedUpdate", "setUserLevel", "setWinStreak", "shareLvSuccess", "shopDouble",
|
|
"signInActivityInfo", "userCoin", "userData", "userHealth", "userLevel", "userPower",
|
|
"userProp", "wucaiTransfer",
|
|
]);
|
|
|
|
export function isNonPlayerFunction(name: string): boolean {
|
|
return name.startsWith("__") || nonPlayerFunctions.has(name)
|
|
|| /(?:Admin|Jobs)$/.test(name) || /(?:^|\/)(?:admin|jobs|testPeriods)(?:\/|$)/.test(name);
|
|
}
|
|
|
|
function parseVersion(value: unknown): number[] | null {
|
|
if (typeof value !== "string" || !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value)) return null;
|
|
const parts = value.split(".").map(Number);
|
|
return parts.every(Number.isSafeInteger) ? parts : null;
|
|
}
|
|
|
|
/** null means malformed; otherwise -1 / 0 / 1 (numeric components, not string ordering). */
|
|
export function compareVersions(left: unknown, right: unknown): number | null {
|
|
const a = parseVersion(left), b = parseVersion(right);
|
|
if (!a || !b) return null;
|
|
for (let index = 0; index < 3; index++) {
|
|
if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
export function configUnavailable() {
|
|
return { code: 0, errorCode: "CLIENT_UPDATE_CONFIG_UNAVAILABLE", data: null, msg: "服务暂不可用,请稍后重试" };
|
|
}
|
|
|
|
export function validatePolicy(release: string, policy: VersionPolicy): boolean {
|
|
if (!parseVersion(release) || !policy || typeof policy.force !== "boolean"
|
|
|| !Array.isArray(policy.apiBlacklist)
|
|
|| !policy.apiBlacklist.every(name => typeof name === "string" && /^[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*$/.test(name))) return false;
|
|
if (policy.minVersion === null) return !policy.force;
|
|
const compared = compareVersions(policy.minVersion, release);
|
|
return compared !== null && compared <= 0 && (!policy.force || compared === 0);
|
|
}
|
|
|
|
/** Returns null to continue normal authentication/business logic, otherwise a business error. */
|
|
export function checkClientVersion(
|
|
functionName: string,
|
|
clientVersion: unknown,
|
|
release = currentVersion,
|
|
policies: Record<string, VersionPolicy> = versions,
|
|
) {
|
|
if (isNonPlayerFunction(functionName)) return null;
|
|
if (!Array.isArray(openidWhitelist)
|
|
|| !openidWhitelist.every(openid => typeof openid === "string" && /^[A-Za-z0-9_-]+$/.test(openid))) return configUnavailable();
|
|
const policy = policies && Object.prototype.hasOwnProperty.call(policies, release) ? policies[release] : null;
|
|
if (!validatePolicy(release, policy)) return configUnavailable();
|
|
if (functionName !== "login" && !policy.apiBlacklist.includes(functionName)) return null;
|
|
if (policy.minVersion === null) return null;
|
|
const compared = compareVersions(clientVersion, policy.minVersion);
|
|
if (compared !== null && compared >= 0) return null;
|
|
return {
|
|
code: 0,
|
|
errorCode: "CLIENT_UPDATE_REQUIRED",
|
|
msg: "当前游戏版本过低,请更新后重新进入",
|
|
data: { version: release, minVersion: policy.minVersion },
|
|
};
|
|
}
|
|
|
|
export default async function (_ctx: FunctionContext) {
|
|
return { data: 'hi, laf' }
|
|
}
|