This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { isOptionalApiRouteMissing } from "./apiErrorUtils";
|
||||
import { isRecord, serverRequest } from "./serverConnection";
|
||||
|
||||
export interface PublicModelPrice {
|
||||
id?: number | string;
|
||||
modelKey: string;
|
||||
displayName?: string;
|
||||
category?: string;
|
||||
pricingType?: string;
|
||||
inputPriceMills: number | null;
|
||||
outputPriceMills: number | null;
|
||||
flatPriceMills: number | null;
|
||||
currency: string;
|
||||
enabled: boolean;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
function readString(
|
||||
record: Record<string, unknown>,
|
||||
keys: string[],
|
||||
): string | undefined {
|
||||
for (const key of keys) {
|
||||
const value = record[key];
|
||||
if (typeof value === "string" && value.trim()) return value.trim();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readNumber(
|
||||
record: Record<string, unknown>,
|
||||
keys: string[],
|
||||
): number | null {
|
||||
for (const key of keys) {
|
||||
const value = record[key];
|
||||
const parsed =
|
||||
typeof value === "number"
|
||||
? value
|
||||
: typeof value === "string"
|
||||
? Number(value)
|
||||
: NaN;
|
||||
if (Number.isFinite(parsed)) return parsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function readBoolean(
|
||||
record: Record<string, unknown>,
|
||||
keys: string[],
|
||||
fallback: boolean,
|
||||
): boolean {
|
||||
for (const key of keys) {
|
||||
const value = record[key];
|
||||
if (typeof value === "boolean") return value;
|
||||
if (typeof value === "number") return value !== 0;
|
||||
if (typeof value === "string") {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (["1", "true", "yes", "enabled"].includes(normalized)) return true;
|
||||
if (["0", "false", "no", "disabled"].includes(normalized)) return false;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function normalizePublicModelPrice(
|
||||
raw: unknown,
|
||||
): PublicModelPrice | null {
|
||||
if (!isRecord(raw)) return null;
|
||||
|
||||
const modelKey = readString(raw, ["modelKey", "model_key", "key", "model"]);
|
||||
if (!modelKey) return null;
|
||||
|
||||
const displayName = readString(raw, ["displayName", "display_name", "name"]);
|
||||
const category = readString(raw, ["category", "type"]);
|
||||
const pricingType = readString(raw, ["pricingType", "pricing_type"]);
|
||||
const currency = readString(raw, ["currency"]) || "CNY";
|
||||
const createdAt = readString(raw, ["createdAt", "created_at"]);
|
||||
const updatedAt = readString(raw, ["updatedAt", "updated_at"]);
|
||||
const idValue = raw.id;
|
||||
|
||||
return {
|
||||
id:
|
||||
typeof idValue === "number" || typeof idValue === "string"
|
||||
? idValue
|
||||
: undefined,
|
||||
modelKey,
|
||||
displayName,
|
||||
category,
|
||||
pricingType,
|
||||
inputPriceMills: readNumber(raw, ["inputPriceMills", "input_price_mills"]),
|
||||
outputPriceMills: readNumber(raw, [
|
||||
"outputPriceMills",
|
||||
"output_price_mills",
|
||||
]),
|
||||
flatPriceMills: readNumber(raw, ["flatPriceMills", "flat_price_mills"]),
|
||||
currency,
|
||||
enabled: readBoolean(raw, ["enabled", "is_enabled"], true),
|
||||
createdAt,
|
||||
updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizePublicModelPrices(
|
||||
payload: unknown,
|
||||
): PublicModelPrice[] {
|
||||
const rawPrices = Array.isArray(payload)
|
||||
? payload
|
||||
: isRecord(payload) && Array.isArray(payload.prices)
|
||||
? payload.prices
|
||||
: isRecord(payload) && Array.isArray(payload.models)
|
||||
? payload.models
|
||||
: [];
|
||||
|
||||
return rawPrices
|
||||
.map((item) => normalizePublicModelPrice(item))
|
||||
.filter((item): item is PublicModelPrice => Boolean(item));
|
||||
}
|
||||
|
||||
let cachedPrices: PublicModelPrice[] | null = null;
|
||||
let pricesRouteMissing = false;
|
||||
|
||||
export const publicPricingClient = {
|
||||
async getPrices(): Promise<PublicModelPrice[]> {
|
||||
if (cachedPrices) return cachedPrices;
|
||||
if (pricesRouteMissing) return [];
|
||||
|
||||
try {
|
||||
const payload = await serverRequest<unknown>("prices", {
|
||||
fallbackMessage: "Model prices request failed",
|
||||
});
|
||||
cachedPrices = normalizePublicModelPrices(payload);
|
||||
return cachedPrices;
|
||||
} catch (error) {
|
||||
if (isOptionalApiRouteMissing(error)) {
|
||||
pricesRouteMissing = true;
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user