Files
goods/public-frontend/src/api.ts
T
novaalphastrikeomegaz663 69a0149bbe
CI / Python (ingestion) (pull_request) Successful in 12s
CI / Migrations (postgres) (pull_request) Successful in 22s
CI / Go (api) (pull_request) Successful in 47s
feat(search+docs): trigram fuzzy search, brand/country filters, developer docs
Search:
- migration 0009: trigram GIN index on brand.name + btree on country_of_origin
- SearchProducts: typo-tolerant word_similarity matching (>=0.42) on top of
  ILIKE substring + barcode; new brand/country filters; rank by
  similarity * (0.5 + quality_score). Response gains country_of_origin,
  quality_score and per-result relevance score.
- public search UI: brand/country filter inputs; show country in results

Docs:
- serve embedded OpenAPI 3 spec at GET /api/v1/openapi.json (not rate limited)
- ApiDocs page: auth + rate-limit section, updated search params/response
- docs/api.md developer guide

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-20 09:38:27 +00:00

52 lines
1.4 KiB
TypeScript

import type { Category, Product, ProductSummary, SubmissionInput } from "./types";
async function req<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(path, {
...init,
headers: { "Content-Type": "application/json", ...(init?.headers || {}) },
});
if (!res.ok) {
let msg = `请求失败 (${res.status})`;
try {
const body = await res.json();
if (body?.error?.message) msg = body.error.message;
} catch {
/* ignore */
}
throw new Error(msg);
}
return res.json() as Promise<T>;
}
export interface SearchResult {
items: ProductSummary[];
page: number;
size: number;
total: number;
}
export interface SearchFilters {
brand?: string;
country?: string;
}
export const api = {
search: (q: string, page = 1, size = 20, filters: SearchFilters = {}) => {
const params = new URLSearchParams({
q,
page: String(page),
size: String(size),
});
if (filters.brand) params.set("brand", filters.brand);
if (filters.country) params.set("country", filters.country);
return req<SearchResult>(`/api/v1/products/search?${params.toString()}`);
},
product: (id: string) => req<Product>(`/api/v1/products/${id}`),
categories: () => req<{ items: Category[] }>(`/api/v1/categories`),
submit: (input: SubmissionInput) =>
req<{ id: string; status: string }>(`/api/public/submissions`, {
method: "POST",
body: JSON.stringify(input),
}),
};