Feature insertion- File importing
This commit is contained in:
@@ -131,6 +131,52 @@ async function upload(path, file, { fieldName = 'file' } = {}) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Post several files under one field name.
|
||||
*
|
||||
* Separate from `upload` rather than a flag on it: `upload` is used by six
|
||||
* call sites that send exactly one file, and widening its signature would put
|
||||
* an array check in front of all of them for the benefit of one.
|
||||
*
|
||||
* FastAPI reads `files: List[UploadFile]` from repeated parts sharing a name,
|
||||
* which is why every file is appended under the same key. As with `upload`,
|
||||
* Content-Type is deliberately left unset so the browser writes the multipart
|
||||
* boundary itself.
|
||||
*/
|
||||
async function uploadMany(path, files, { fieldName = 'files' } = {}) {
|
||||
const formData = new FormData();
|
||||
Array.from(files || []).forEach((file) => formData.append(fieldName, file));
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(`${BASE}${path}`, {
|
||||
method: 'POST',
|
||||
headers: authHeaders(),
|
||||
body: formData,
|
||||
});
|
||||
} catch {
|
||||
throw new ApiError(
|
||||
`Could not reach the API at ${BASE || '(same origin)'}${path}. Is the backend running ` +
|
||||
`and reachable?`,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
let detail = `Upload failed (${res.status})`;
|
||||
try {
|
||||
const body = await res.json();
|
||||
detail = body.detail || JSON.stringify(body);
|
||||
} catch {
|
||||
/* ignore parse errors, keep generic message */
|
||||
}
|
||||
if (res.status === 401 && onUnauthorized) onUnauthorized(detail);
|
||||
throw new ApiError(detail, res.status);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
function qs(params = {}) {
|
||||
const usp = new URLSearchParams();
|
||||
Object.entries(params).forEach(([k, v]) => {
|
||||
@@ -304,6 +350,36 @@ export const api = {
|
||||
getStoreCatalogJob: (jobId) =>
|
||||
request(`/api/admin/store-catalog/jobs/${encodeURIComponent(jobId)}`),
|
||||
|
||||
// --- Admin: batch catalog ingestion (many spreadsheets -> one batch) ---
|
||||
// The multi-file sibling of the block above. See
|
||||
// app/api/routers/batch_catalog.py.
|
||||
//
|
||||
// NOTE the flag defaults are the OPPOSITE of ingestStoreCatalog's. One file
|
||||
// with image search on is a considered trade; twenty files is thousands of
|
||||
// outbound requests and a Playwright subprocess per row, on a single-vCPU
|
||||
// container that is also serving the API. A batch opts in.
|
||||
previewCatalogBatch: (files) =>
|
||||
uploadMany('/api/admin/catalog-batch/preview', files),
|
||||
|
||||
ingestCatalogBatch: (files, { use_llm = false, fetch_images = false } = {}) =>
|
||||
uploadMany(`/api/admin/catalog-batch/ingest${qs({ use_llm, fetch_images })}`, files),
|
||||
|
||||
getCatalogBatch: (batchId) =>
|
||||
request(`/api/admin/catalog-batch/batches/${encodeURIComponent(batchId)}`),
|
||||
|
||||
listCatalogBatches: (limit = 20) =>
|
||||
request(`/api/admin/catalog-batch/batches${qs({ limit })}`),
|
||||
|
||||
resumeCatalogBatch: (batchId) =>
|
||||
request(`/api/admin/catalog-batch/batches/${encodeURIComponent(batchId)}/resume`, {
|
||||
method: 'POST',
|
||||
}),
|
||||
|
||||
cancelCatalogBatch: (batchId) =>
|
||||
request(`/api/admin/catalog-batch/batches/${encodeURIComponent(batchId)}/cancel`, {
|
||||
method: 'POST',
|
||||
}),
|
||||
|
||||
// --- User workspace: single + batch product entry ---
|
||||
// Guarded by `add_product` / `upload_batch_products` - see
|
||||
// app/api/routers/user_products.py.
|
||||
|
||||
Reference in New Issue
Block a user