Use server enterprise video pricing
This commit is contained in:
+105
-10
@@ -1,5 +1,6 @@
|
||||
import { isOptionalApiRouteMissing } from "./apiErrorUtils";
|
||||
import { isRecord, serverRequest } from "./serverConnection";
|
||||
import type { EnterpriseVideoPricingConfig, EnterpriseVideoPricingRule } from "../utils/enterpriseVideoPolicy";
|
||||
|
||||
export interface PublicModelPrice {
|
||||
id?: number | string;
|
||||
@@ -16,6 +17,11 @@ export interface PublicModelPrice {
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface PublicPricingPayload {
|
||||
modelPrices: PublicModelPrice[];
|
||||
enterpriseVideoPricing: EnterpriseVideoPricingConfig | null;
|
||||
}
|
||||
|
||||
function readString(
|
||||
record: Record<string, unknown>,
|
||||
keys: string[],
|
||||
@@ -62,6 +68,51 @@ function readBoolean(
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function readStringArray(record: Record<string, unknown>, keys: string[]): string[] {
|
||||
for (const key of keys) {
|
||||
const value = record[key];
|
||||
if (!Array.isArray(value)) continue;
|
||||
return value
|
||||
.map((item) => (typeof item === "string" ? item.trim() : ""))
|
||||
.filter(Boolean);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function normalizeRateMap(raw: unknown): Record<string, number> | null {
|
||||
if (!isRecord(raw)) return null;
|
||||
const result: Record<string, number> = {};
|
||||
for (const [key, value] of Object.entries(raw)) {
|
||||
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
||||
if (Number.isFinite(parsed) && parsed >= 0) result[key] = parsed;
|
||||
}
|
||||
return Object.keys(result).length ? result : null;
|
||||
}
|
||||
|
||||
function normalizeEnterpriseVideoPricingRule(raw: unknown): EnterpriseVideoPricingRule | null {
|
||||
if (!isRecord(raw)) return null;
|
||||
const id = readString(raw, ["id", "key", "name"]);
|
||||
const modelIncludes = readStringArray(raw, ["modelIncludes", "model_includes", "modelPatterns", "model_patterns"]);
|
||||
const rates = normalizeRateMap(raw.rates);
|
||||
if (!id || modelIncludes.length === 0 || !rates) return null;
|
||||
|
||||
const when = isRecord(raw.when)
|
||||
? {
|
||||
...(typeof raw.when.muted === "boolean" ? { muted: raw.when.muted } : {}),
|
||||
...(typeof raw.when.hasReferenceVideo === "boolean"
|
||||
? { hasReferenceVideo: raw.when.hasReferenceVideo }
|
||||
: {}),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
id,
|
||||
modelIncludes,
|
||||
...(when && Object.keys(when).length ? { when } : {}),
|
||||
rates,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizePublicModelPrice(
|
||||
raw: unknown,
|
||||
): PublicModelPrice | null {
|
||||
@@ -107,35 +158,79 @@ export function normalizePublicModelPrices(
|
||||
? payload
|
||||
: isRecord(payload) && Array.isArray(payload.prices)
|
||||
? payload.prices
|
||||
: isRecord(payload) && Array.isArray(payload.models)
|
||||
? payload.models
|
||||
: [];
|
||||
: isRecord(payload) && Array.isArray(payload.modelPrices)
|
||||
? payload.modelPrices
|
||||
: isRecord(payload) && Array.isArray(payload.model_prices)
|
||||
? payload.model_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;
|
||||
export function normalizeEnterpriseVideoPricingConfig(raw: unknown): EnterpriseVideoPricingConfig | null {
|
||||
if (!isRecord(raw)) return null;
|
||||
const rules = Array.isArray(raw.rules)
|
||||
? raw.rules
|
||||
.map((item) => normalizeEnterpriseVideoPricingRule(item))
|
||||
.filter((item): item is EnterpriseVideoPricingRule => Boolean(item))
|
||||
: [];
|
||||
if (rules.length === 0) return null;
|
||||
|
||||
const creditsPerCny = readNumber(raw, ["creditsPerCny", "credits_per_cny"]);
|
||||
const defaultResolution = readString(raw, ["defaultResolution", "default_resolution"]);
|
||||
const billingUnit = readString(raw, ["billingUnit", "billing_unit"]);
|
||||
const currency = readString(raw, ["currency"]);
|
||||
const resolutions = readStringArray(raw, ["resolutions", "supportedResolutions", "supported_resolutions"]);
|
||||
|
||||
return {
|
||||
...(currency ? { currency } : {}),
|
||||
...(creditsPerCny !== null ? { creditsPerCny } : {}),
|
||||
...(billingUnit ? { billingUnit } : {}),
|
||||
...(defaultResolution ? { defaultResolution } : {}),
|
||||
...(resolutions.length ? { resolutions } : {}),
|
||||
rules,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizePublicPricingPayload(payload: unknown): PublicPricingPayload {
|
||||
const enterpriseVideoPricingRaw =
|
||||
isRecord(payload) && (payload.enterpriseVideoPricing ?? payload.enterprise_video_pricing);
|
||||
|
||||
return {
|
||||
modelPrices: normalizePublicModelPrices(payload),
|
||||
enterpriseVideoPricing: normalizeEnterpriseVideoPricingConfig(enterpriseVideoPricingRaw),
|
||||
};
|
||||
}
|
||||
|
||||
let cachedPricing: PublicPricingPayload | null = null;
|
||||
let pricesRouteMissing = false;
|
||||
|
||||
export const publicPricingClient = {
|
||||
async getPrices(): Promise<PublicModelPrice[]> {
|
||||
if (cachedPrices) return cachedPrices;
|
||||
if (pricesRouteMissing) return [];
|
||||
async getPricing(): Promise<PublicPricingPayload> {
|
||||
if (cachedPricing) return cachedPricing;
|
||||
if (pricesRouteMissing) return { modelPrices: [], enterpriseVideoPricing: null };
|
||||
|
||||
try {
|
||||
const payload = await serverRequest<unknown>("prices", {
|
||||
fallbackMessage: "Model prices request failed",
|
||||
});
|
||||
cachedPrices = normalizePublicModelPrices(payload);
|
||||
return cachedPrices;
|
||||
cachedPricing = normalizePublicPricingPayload(payload);
|
||||
return cachedPricing;
|
||||
} catch (error) {
|
||||
if (isOptionalApiRouteMissing(error)) {
|
||||
pricesRouteMissing = true;
|
||||
return [];
|
||||
return { modelPrices: [], enterpriseVideoPricing: null };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async getPrices(): Promise<PublicModelPrice[]> {
|
||||
const pricing = await publicPricingClient.getPricing();
|
||||
return pricing.modelPrices;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user