Files

35 lines
1.9 KiB
TypeScript

import cloud from "@lafjs/cloud";
import { checkClientVersion, configUnavailable, openidWhitelist, shouldCheckPlayerVersion } from "@/versionPolicy";
// Laf detects this signature by function.length: keep exactly two non-default parameters.
export default async function (ctx: FunctionContext, next: (context: FunctionContext) => Promise<any>) {
const method = String(ctx.method || ctx.request?.method || "").toUpperCase();
if (method === "OPTIONS" || method === "CALL") return await next(ctx);
// Set by Laf's router, not by a client-provided body/header. Preserve nested function names.
const functionName = ctx.__function_name || ctx.request?.path?.replace(/^\//, "");
if (!functionName) return configUnavailable();
const params = method === "GET" || method === "HEAD" ? ctx.query : ctx.body;
const rejection = checkClientVersion(functionName, params?.version);
if (rejection?.errorCode === "CLIENT_UPDATE_REQUIRED" && openidWhitelist.length > 0) {
// Login has no trusted openid yet. It checks after code2session, before any user writes.
if (functionName === "login") return await next(ctx);
// Never trust a request's openid/uid for whitelist membership. Resolve the login token.
const token = params?.token;
if (typeof token !== "string" || !token) return await next(ctx);
let openid: unknown;
try {
const db = cloud.database();
const main = await db.collection("users").where({ token }).getOne();
const user = main.data || (await db.collection("usersAd").where({ token }).getOne()).data;
openid = user?.openid;
} catch {
// A failed lookup must not silently let a targeted old client into business logic.
return configUnavailable();
}
// Unauthenticated requests continue to the endpoint's existing authentication checks.
if (!shouldCheckPlayerVersion(openid)) return await next(ctx);
}
if (rejection) return rejection;
return await next(ctx);
}