feat(items): add server field and improve item management
refactor(ai): enhance item structure generation with schema validation feat(ai): add token usage tracking and cost calculation fix(ui): improve item card number formatting with locale support docs(items): add documentation for item value separation test(items): add tests for value field validation
This commit is contained in:
1 parent
dc169f9eba
commit
d3704602fd
22 files changed
+1317
-95
No files matched your search
@@ -6,6 +6,7 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { prisma } from "../app";
|
||||
import { ensureItemsSeeded } from "../../items/import-from-sqlite";
|
||||
import { secureLogger } from "../../secure-logger";
|
||||
|
||||
export type ItemSort =
|
||||
| "name"
|
||||
@@ -193,6 +194,53 @@ function buildMetadata(
|
||||
return merged;
|
||||
}
|
||||
|
||||
// Campos relacionados a valores/preços que NÃO pertencem ao modelo Item.
|
||||
// Qualquer presença deles em metadata deve ser rejeitada.
|
||||
const FORBIDDEN_VALUE_KEYS = new Set<string>([
|
||||
// Geral
|
||||
"price",
|
||||
"prices",
|
||||
"valor",
|
||||
"value",
|
||||
"sell_value",
|
||||
"sellPrice",
|
||||
"buy",
|
||||
"buy_price",
|
||||
// Chaves usadas em Listings
|
||||
"price_pix",
|
||||
"price_tibia_coins",
|
||||
"price_gold_coins",
|
||||
"initial_price_pix",
|
||||
"initial_price_tibia_coins",
|
||||
"initial_price_gold_coins",
|
||||
"current_bid_amount",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Verifica recursivamente se há campos proibidos relacionados a valores/preços.
|
||||
* Lança erro se encontrar.
|
||||
*/
|
||||
export function assertNoValueFieldsInMetadata(meta: Record<string, any> | undefined | null, context?: string): void {
|
||||
if (!meta || typeof meta !== "object") return;
|
||||
|
||||
const stack: Array<{ obj: Record<string, any>; path: string }> = [{ obj: meta as Record<string, any>, path: "metadata" }];
|
||||
while (stack.length) {
|
||||
const { obj, path } = stack.pop()!;
|
||||
for (const key of Object.keys(obj)) {
|
||||
const nextPath = `${path}.${key}`;
|
||||
if (FORBIDDEN_VALUE_KEYS.has(key)) {
|
||||
const msg = `Itens não possuem campos de valor/preço no cadastro. Campo proibido: ${nextPath}. Use listings para preços.`;
|
||||
secureLogger.warn(msg, { context });
|
||||
throw new Error(msg);
|
||||
}
|
||||
const val = obj[key];
|
||||
if (val && typeof val === "object" && !Array.isArray(val)) {
|
||||
stack.push({ obj: val as Record<string, any>, path: nextPath });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getItemById(itemId: number): Promise<Item | null> {
|
||||
const item = await prisma.item.findUnique({ where: { item_id: itemId } });
|
||||
if (!item) {
|
||||
@@ -322,6 +370,8 @@ export async function searchItems(params: SearchItemsParams = {}): Promise<{ ite
|
||||
|
||||
export async function createAdminItem(input: ItemUpsertInput): Promise<ItemWithMeta> {
|
||||
const now = Date.now();
|
||||
// Validação: impedir qualquer campo de valor em metadata (preço etc.)
|
||||
assertNoValueFieldsInMetadata(input.metadata, "createAdminItem");
|
||||
const data: Prisma.ItemCreateInput = {
|
||||
source_table_id: input.source_table_id ?? now,
|
||||
source_table_name: input.source_table_name ?? "custom",
|
||||
@@ -353,6 +403,8 @@ export async function updateAdminItem(itemId: number, input: ItemUpsertInput): P
|
||||
}
|
||||
|
||||
const existingMeta = parseMetadata(existing.metadata);
|
||||
// Validação: impedir qualquer campo de valor em metadata (preço etc.)
|
||||
assertNoValueFieldsInMetadata(input.metadata, "updateAdminItem");
|
||||
const metadata = buildMetadata(existingMeta, {
|
||||
servers: input.servers ?? extractServers(existingMeta),
|
||||
tags: input.tags ?? extractTags(existingMeta),
|
||||
|
||||
Reference in new issue
Block a user