Files
goods/admin-frontend/src/types.ts
T
sulaimaannaasif6866 7434e5195e
CI / Go (api) (pull_request) Successful in 15s
CI / Python (ingestion) (pull_request) Successful in 10s
CI / Migrations (postgres) (pull_request) Successful in 16s
feat(api): tiered cumulative quota + self-service registration
Anonymous callers get a free cumulative quota (1000 calls per IP); once
exhausted they get 403 quota_exhausted and must register. Public users can
self-register (email+password) to obtain a higher-quota API key, view usage,
and regenerate the key. Quota counters live in Redis; the public API stays
read-only except for the registration writes.

- migration 0011: app_user table + api_key.quota_total + 'registered' tier
- ratelimit: IncrTotal/TotalUsed/CopyTotal lifetime counters
- middleware: enforce cumulative quota + X-Quota-* headers
- store: RegisterUser/Authenticate/RegenerateKey (bcrypt)
- handlers: POST /api/v1/register, /account, /account/regenerate
- admin: quota_total column + registered tier
- public: 'API 密钥' account page + API docs quota section

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-21 07:26:41 +00:00

220 lines
4.5 KiB
TypeScript

export interface ProductRow {
id: string;
gtin: string | null;
name: string;
brand: string | null;
category_path: string | null;
status: string;
quality_score: number;
missing: string[];
updated_at: string;
}
export interface Barcode {
id: string;
gtin: string;
gtin_type: string;
pack_level: string;
region: string | null;
is_primary: boolean;
}
export interface ProductImage {
id: string;
url: string;
kind: string;
license: string | null;
}
export interface MSRP {
id: string;
amount: number;
currency: string;
region: string;
effective_date: string | null;
source_url: string | null;
note: string | null;
}
export interface ProductDetail {
id: string;
gtin: string | null;
name: string;
brand_id: string | null;
brand: string | null;
category_id: string | null;
category_path: string | null;
archive_kind: string;
attributes: Record<string, unknown>;
net_content_value: number | null;
net_content_unit: string | null;
country_of_origin: string | null;
status: string;
quality_score: number;
ingredients_text: string | null;
allergens: string[];
additives: string[];
nutriments: Record<string, number> | null;
nutrition_basis: string | null;
serving_size: string | null;
nutri_score: string | null;
barcodes: Barcode[];
images: ProductImage[];
msrp: MSRP[];
missing: string[];
updated_at: string;
}
export interface Brand {
id: string;
name: string;
product_count: number;
}
export interface Category {
id: string;
name_zh: string;
name_en: string | null;
path: string;
level: number;
parent_id: string | null;
gpc_brick_code: string | null;
archive_kind: string;
product_count: number;
}
export interface KindField {
kind: string;
field_key: string;
group_label: string;
label_zh: string;
field_type: string;
unit: string | null;
options: string[];
placeholder: string | null;
sort_order: number;
qualified: boolean;
}
export interface CategoryInput {
name_zh: string;
name_en?: string | null;
slug?: string | null;
parent_id?: string | null;
gpc_brick_code?: string | null;
}
export interface AuditEntry {
id: string;
actor: string;
action: string;
fields: string[];
created_at: string;
}
export interface AuditLogRow {
id: string;
actor: string;
action: string;
entity: string;
entity_id: string | null;
fields: string[];
created_at: string;
}
export interface AdminStats {
products: number;
qualified: number;
min_score: number;
by_status: Record<string, number>;
brands: number;
categories: number;
pending_submissions: number;
avg_quality: number;
}
export interface SubmissionRow {
id: string;
gtin: string | null;
name: string;
status: string;
submitter_name: string | null;
matched: boolean;
created_at: string;
reviewed_at: string | null;
}
export interface SubmissionImage {
url: string;
kind: string;
}
export interface SubmissionPayload {
gtin: string | null;
name: string;
brand_name: string | null;
category_id: string | null;
net_content_value: number | null;
net_content_unit: string | null;
country_of_origin: string | null;
ingredients_text: string | null;
nutriments: Record<string, number> | null;
nutrition_basis: string | null;
serving_size: string | null;
nutri_score: string | null;
images: SubmissionImage[] | null;
submitter_name: string | null;
submitter_contact: string | null;
note: string | null;
}
export interface SubmissionDetail {
id: string;
status: string;
gtin: string | null;
name: string;
submitter_name: string | null;
submitter_contact: string | null;
note: string | null;
review_note: string | null;
reviewed_by: string | null;
reviewed_at: string | null;
created_at: string;
target_product_id: string | null;
result_product_id: string | null;
payload: SubmissionPayload;
existing_product?: ProductDetail;
}
export interface ApiKeyUsage {
total: number;
today: number;
last_used_at?: number | null;
}
export interface ApiKey {
id: string;
name: string;
key_prefix: string;
owner_email: string | null;
tier: string;
rate_limit_per_min: number;
quota_total: number;
revoked_at: string | null;
created_by: string | null;
created_at: string;
usage: ApiKeyUsage;
}
export const FIELD_LABELS: Record<string, string> = {
name: "名称",
gtin: "条码",
brand: "品牌",
category: "品类",
net_content: "净含量",
country_of_origin: "产地",
nutriments: "营养成分",
ingredients: "配料",
image: "图片",
};