Files
cfw-autumn/server/src/internal/balances/utils/lock/fetchLockReceipt.ts
2026-04-19 15:21:44 +01:00

119 lines
2.8 KiB
TypeScript

import { ErrCode, RecaseError } from "@autumn/shared";
import { redis } from "@/external/redis/initRedis.js";
import { redisV2 } from "@/external/redis/initRedisV2.js";
import type { AutumnContext } from "@/honoUtils/HonoEnv";
import type { MutationLogItem } from "@/internal/balances/utils/types/mutationLogItem.js";
import { tryRedisRead } from "@/utils/cacheUtils/cacheUtils.js";
import { buildLockReceiptKey } from "./buildLockReceiptKey.js";
export type LockReceipt = {
lock_id?: string | null;
customer_id: string;
feature_id: string;
entity_id?: string | null;
expires_at?: number | null;
region?: string | null;
items: MutationLogItem[];
};
export type LockReceiptSource = "redis_v1" | "redis_v2";
const normalizeLockReceiptItems = ({
items,
lockId,
}: {
items: LockReceipt["items"] | Record<string, never> | null | undefined;
lockId: string;
}): MutationLogItem[] => {
if (Array.isArray(items)) {
return items;
}
if (items && typeof items === "object" && Object.keys(items).length === 0) {
return [];
}
throw new RecaseError({
message: `Lock receipt has invalid items for ID: ${lockId}`,
code: ErrCode.InvalidRequest,
});
};
export const fetchLockReceipt = async ({
ctx,
lockId,
}: {
ctx: AutumnContext;
lockId: string;
}) => {
const hashedKey = Bun.hash(lockId).toString();
const lockReceiptKey = buildLockReceiptKey({
orgId: ctx.org.id,
env: ctx.env,
lockKey: hashedKey,
});
const [rawReceiptV1, rawReceiptV2] = await Promise.all([
tryRedisRead(
() =>
redis.call("JSON.GET", lockReceiptKey, "$") as Promise<string | null>,
redis,
),
tryRedisRead(
() =>
redisV2.call("JSON.GET", lockReceiptKey, "$") as Promise<string | null>,
redisV2,
),
]);
// if (rawReceiptV1 && rawReceiptV2) {
// throw new InternalError({
// message: `Lock receipt found in both Redis stores for ID: ${lockId}`,
// code: "lock_receipt_found_in_both_stores",
// });
// }
const rawReceipt = rawReceiptV2 ?? rawReceiptV1;
const source: LockReceiptSource = rawReceiptV2 ? "redis_v2" : "redis_v1";
if (!rawReceipt) {
throw new RecaseError({
message: `Lock not found for ID: ${lockId}`,
code: ErrCode.InvalidRequest,
});
}
const receipt = (JSON.parse(rawReceipt) as LockReceipt[])[0];
if (!receipt?.customer_id) {
throw new RecaseError({
message: `Lock receipt is missing customer_id for ID: ${lockId}`,
code: ErrCode.InvalidRequest,
});
}
if (!receipt.feature_id) {
throw new RecaseError({
message: `Lock receipt is missing feature_id for ID: ${lockId}`,
code: ErrCode.InvalidRequest,
});
}
if (!receipt.items) {
throw new RecaseError({
message: `Lock receipt is missing items for ID: ${lockId}`,
code: ErrCode.InvalidRequest,
});
}
receipt.items = normalizeLockReceiptItems({
items: receipt.items,
lockId,
});
return {
receipt,
lockReceiptKey,
source,
};
};