Data4Autos-Shopify-Backend/routes/manageProducts_0710.js
2026-04-13 05:23:25 +00:00

1138 lines
32 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// routes/manageBrands.js
const express = require('express');
const axios = require('axios');
const { v4: uuid } = require('uuid');
const { getToken } = require('../tokenStore');
const { log } = require('../logger');
const router = express.Router();
// Simple in-memory process tracker
const processes = {};
function slugify(str) {
return str
.toString()
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
const GetAllProductsOfBranch = async (brandId, turn14accessToken, shop, procId, productCount) => {
var AllProductsOfBrans = [];
try {
log(shop, `🔍 [${procId}] Fetching products for brand ${brandId}`);
// const res = await fetch(`https://turn14.data4autos.com/v1/items/brandallitems/${brandId}`, {
const res = await fetch(`https://turn14.data4autos.com/v1/items/brandallitemswithfitment/${brandId}`, {
headers: {
Authorization: `Bearer ${turn14accessToken}`,
"Content-Type": "application/json",
},
});
const res_data = await res.json();
const data = res_data.items || [];
const fitmentTags = res_data.fitmentTags || [];
// Ensure we have an array of valid items
const validItems = Array.isArray(data)
? data.filter(item => item && item.id && item.attributes)
: [];
AllProductsOfBrans = validItems;
log(shop, `📦 [${procId}] Found ${AllProductsOfBrans.length} products for brand ${brandId}`);
const items = Array.isArray(AllProductsOfBrans) ? AllProductsOfBrans : [];
log(shop, `📝 [${procId}] Processing ${items.length} sample products`);
return { items, fitmentTags };
} catch (err) {
log(shop, `❌ [${procId}] Error fetching items: ${err.message}`);
return null;
}
}
const AddProductToStore = async (shop, accessToken, product, procId, fulfillmentServiceId, locationId) => {
var results = [];
const SHOP = shop;
const ACCESS_TOKEN = accessToken;
const item = product;
const attrs = item.attributes;
const globalUniqueFitmentMap = {
make: new Set(),
model: new Set(),
year: new Set(),
drive: new Set(),
baseModel: new Set()
};
// Loop over all processed items
const tags = item.attributes?.fitmmentTags;
for (const key in globalUniqueFitmentMap) {
if (tags[key]) {
tags[key].forEach(value => {
globalUniqueFitmentMap[key].add(value);
});
}
}
// Convert sets to arrays
const convertedGlobalUniqueFitmentMap = {};
for (const key in globalUniqueFitmentMap) {
convertedGlobalUniqueFitmentMap[key] = Array.from(globalUniqueFitmentMap[key]);
}
const fitmentTags = convertedGlobalUniqueFitmentMap;
const allFitmentTagsSet = new Set();
for (const arr of Object.values(convertedGlobalUniqueFitmentMap)) {
arr.forEach(val => allFitmentTagsSet.add(val));
}
const allFitmentTags = Array.from(allFitmentTagsSet);
// Now allFitmentTags is a flat array of unique values
log(shop, `All Fitment Tags for ${attrs.product_name || attrs.part_number}: ${JSON.stringify(allFitmentTags, null, 2)}`);
log(shop, `Fitment Tags for ${attrs.product_name || attrs.part_number}: ${JSON.stringify(fitmentTags, null, 2)}`);
try {
var inventoryData = attrs.inventorydata.inventory
const totalQuantity = Object.values(inventoryData).reduce((sum, val) => sum + val, 0);
//console.log(totalQuantity, "1234567890")
const client = axios.create({
// baseURL: `https://${SHOP}/admin/api/${API_VERSION}/graphql.json`,
baseURL: `https://${SHOP}/admin/api/2025-10/graphql.json`,
headers: {
'X-Shopify-Access-Token': ACCESS_TOKEN,
'Content-Type': 'application/json',
},
});
const client_2024_01 = axios.create({
// baseURL: `https://${SHOP}/admin/api/${API_VERSION}/graphql.json`,
baseURL: `https://${SHOP}/admin/api/2024-01/graphql.json`,
headers: {
'X-Shopify-Access-Token': ACCESS_TOKEN,
'Content-Type': 'application/json',
},
});
log(shop, `🛒 [${procId}] Processing product: ${attrs.product_name || attrs.part_number}`);
// Build and normalize collection titles
const category = attrs.category;
const subcategory = attrs.subcategory || "";
const brand = attrs.brand;
const subcats = subcategory
.split(/[,\/]/)
.map((s) => s.trim())
.filter(Boolean);
const collectionTitles = Array.from(
new Set([category, ...subcats, brand, ...allFitmentTags].filter(Boolean))
);
// Find or create collections, collect their IDs
const collectionIds = [];
for (const title of collectionTitles) {
log(shop, `🏷️ [${procId}] Handling collection: ${title}`);
// 1. Query existing manual collection by title
const lookupQuery = `
query {
collections(first: 1, query: "title:\\"${title}\\" AND collection_type:manual") {
nodes { id }
}
}
`;
const lookupResp = await client.post('', { query: lookupQuery });
const existing = lookupResp.data.data.collections.nodes;
if (existing.length) {
log(shop, `✅ [${procId}] Found existing collection: ${title}`);
collectionIds.push(existing[0].id);
continue;
}
// 2. Otherwise, create it
log(shop, ` [${procId}] Creating new collection: ${title}`);
const createMutation = `
mutation collectionCreate($input: CollectionInput!) {
collectionCreate(input: $input) {
collection { id }
userErrors { field message }
}
}
`;
const createResp = await client.post('', {
query: createMutation,
variables: { input: { title } }
});
const createData = createResp.data.data.collectionCreate;
if (createData.userErrors.length) {
throw new Error(
`Could not create collection "${title}": ` +
createData.userErrors.map(e => e.message).join(', ')
);
}
const newId = createData.collection.id;
log(shop, `✨ [${procId}] Created collection: ${title} (ID: ${newId})`);
collectionIds.push(newId);
}
// Build tags
const tags = [
attrs.category,
...subcats,
...allFitmentTags,
attrs.brand,
attrs.part_number,
attrs.mfr_part_number,
attrs.price_group,
attrs.units_per_sku && `${attrs.units_per_sku} per SKU`,
attrs.barcode
].filter(Boolean).map((t) => t.trim());
// Prepare media inputs
const mediaInputs = (attrs.files || [])
.filter((f) => f.type === "Image" && f.url)
.map((file) => ({
originalSource: file.url,
mediaContentType: "IMAGE",
alt: `${attrs.product_name}${file.media_content}`,
}));
// Pick the longest "Market Description" or fallback to part_description
const marketDescs = (attrs.descriptions || [])
.filter((d) => d.type === "Market Description")
.map((d) => d.description);
const descriptionHtml = marketDescs.length
? marketDescs.reduce((a, b) => (b.length > a.length ? b : a))
: attrs.part_description;
log(shop, `🔄 [${procId}] Creating product: ${attrs.product_name}`);
const handle = slugify(item.id)
// const handle = slugify(item.id + "-" + (attrs.mfr_part_number || attrs.product_name))
const searchRes = await client.post('', {
query: `
query {
products(first: 1, query: "handle:${handle}") {
nodes { id handle }
}
}
`
});
// console.log(`[AddProductToStore] Search result for handle "${handle}":`, searchRes.data.data.products);
const exists = searchRes.data?.data?.products?.nodes?.length > 0;
if (exists) {
log(shop, `⏭️ [${procId}] Skipping duplicate product: ${attrs.part_number}`);
results.push({ skippedHandle: attrs.part_number, reason: "handle in use" });
return null;
} else {
// Proceed with productCreate mutation
// const createProdRes = await client.post('', {
// query: `
// mutation($prod: ProductInput!, $media: [CreateMediaInput!]) {
// productCreate(input: $prod, media: $media) {
// product {
// id
// variants(first: 1) {
// nodes {
// id
// inventoryItem { id }
// price
// compareAtPrice
// barcode
// }
// }
// }
// userErrors { field message }
// }
// }
// `,
// variables: {
// prod: {
// title: attrs.product_name,
// descriptionHtml: descriptionHtml,
// vendor: attrs.brand,
// productType: attrs.category,
// handle: handle,
// tags,
// collectionsToJoin: collectionIds,
// status: "ACTIVE",
// },
// media: mediaInputs,
// },
// });
const createProdRes = await client.post('', {
query: `
mutation ProductCreate($product: ProductCreateInput!, $media: [CreateMediaInput!]) {
productCreate(product: $product, media: $media) {
product {
id
variants(first: 1) {
nodes {
id
inventoryItem { id }
price
compareAtPrice
barcode
}
}
}
userErrors { field message }
}
}
`,
variables: {
product: {
title: attrs.product_name,
descriptionHtml: descriptionHtml,
vendor: attrs.brand,
productType: attrs.category,
handle: handle,
tags,
collectionsToJoin: collectionIds,
status: "ACTIVE",
},
media: mediaInputs,
},
});
const createProdJson = createProdRes.data;
const prodErrs = createProdJson.data?.productCreate?.userErrors || [];
if (prodErrs.length) {
const taken = prodErrs.some(e => /already in use/i.test(e.message));
if (taken) {
log(shop, `⏭️ [${procId}] Skipping duplicate product: ${attrs.part_number}`);
results.push({ skippedHandle: attrs.part_number, reason: "handle in use" });
return null;
}
throw new Error(`ProductCreate errors: ${prodErrs.map(e => e.message).join(", ")}`);
}
const product = createProdJson.data.productCreate.product;
const variantNode = product.variants?.nodes?.[0];
if (!variantNode) {
log(shop, `⚠️ [${procId}] No variant found for product: ${product.id}`);
return null;
}
const variantId = variantNode.id;
const inventoryItemId = variantNode.inventoryItem?.id;
console.log("Invemtory Item ID : ", inventoryItemId)
// Bulk-update variant (price, compare-at, barcode)
const baseprice = parseFloat(attrs.price) || 0;
const pricingConfigRes = await client.post('', {
query: `
query {
shop {
metafield(namespace: "turn14", key: "pricing_config") {
value
}
}
}
`
});
let priceType = 'map';
let percentage = 0;
const pricingMf = pricingConfigRes.data?.data?.shop?.metafield;
if (pricingMf?.value) {
try {
const parsed = JSON.parse(pricingMf.value);
priceType = parsed.priceType || 'map';
percentage = Number(parsed.percentage) || 0;
} catch (err) {
console.error('Failed to parse pricing_config metafield:', err);
}
}
// 2) Apply your price calculation using the metafield values
let price = baseprice;
if (priceType === 'percentage') {
price = baseprice + (baseprice * (percentage / 100));
}
log(shop, `📢 [${procId}] Calculated price: ${price} (type: ${priceType}, percentage: ${percentage})`);
const comparePrice = parseFloat(attrs.compare_price) || null;
const barcode = attrs.barcode || "";
log(shop, `💲 [${procId}] Updating pricing for variant: ${variantId}`);
const weightValue = parseFloat(attrs.dimensions?.[0]?.weight) || 0;
const bulkRes_new = await client.post('', {
query: `
mutation UpdateProductVariant(
$productId: ID!,
$variants: [ProductVariantsBulkInput!]!
) {
productVariantsBulkUpdate(productId: $productId, variants: $variants) {
productVariants {
id
price
compareAtPrice
barcode
inventoryItem {
measurement {
weight {
value
unit
}
}
tracked
}
}
userErrors {
field
message
}
}
}
`,
variables: {
productId: product.id,
variants: [{
id: variantId,
price,
...(comparePrice !== null && { compareAtPrice: comparePrice }),
...(barcode && { barcode }),
inventoryItem: {
measurement: {
weight: { value: weightValue, unit: "POUNDS" }
},
// tracke d: true
}
}]
},
});
const bulkRes = await client.post('', {
query: `
mutation UpdateProductVariant(
$productId: ID!,
$variants: [ProductVariantsBulkInput!]!
) {
productVariantsBulkUpdate(productId: $productId, variants: $variants) {
productVariants {
id
price
compareAtPrice
barcode
inventoryItem {
measurement {
weight {
value
unit
}
}
tracked
}
}
userErrors {
field
message
}
}
}
`,
variables: {
productId: product.id,
variants: [{
id: variantId,
price,
...(comparePrice !== null && { compareAtPrice: comparePrice }),
...(barcode && { barcode }),
// sku: attrs.part_number,
inventoryItem: {
measurement: {
weight: { value: weightValue, unit: "POUNDS" }
},
// tracked: true
}
}]
},
});
log(shop, `🔄 [${procId}] Bulk updating variant: ${variantId}`);
//console.warn(JSON.stringify(bulkRes.data, null, 2))
const bulkJson = bulkRes.data;
const bulkErrs = bulkJson.data.productVariantsBulkUpdate.userErrors;
if (bulkErrs.length) {
throw new Error(`Bulk update errors: ${bulkErrs.map(e => e.message).join(", ")}`);
}
try {
console.log("111111111111")
const response = await client.post('', {
query: `
mutation UpdateVariantSKU($input: ProductVariantUpdateInput!) {
productVariantUpdate(input: $input) {
productVariant {
id
sku
}
userErrors {
field
message
}
}
}
`,
variables: {
input: {
id: variantId,
sku: attrs.part_number
}
}
}
);
// Extract result
console.log(".............",JSON.stringify(response.data, null, 2))
const result = response?.data?.data?.productVariantUpdate;
console.log(result)
if (result?.userErrors?.length) {
console.error("User Errors:", result.userErrors);
return {
success: false,
message: result.userErrors.map(e => `${e.field}: ${e.message}`).join(", "),
details: result.userErrors
};
}
console.log("Variant updated successfully:", result.productVariant);
} catch (error) {
console.error("GraphQL request failed:", error);
return {
success: false,
message: "An unexpected error occurred while updating the variant.",
error: error.message
};
}
// Fetch the Online Store publication ID
log(shop, `📢 [${procId}] Publishing product to Online Store`);
const publicationsRes = await client.post('', {
query: `
query {
publications(first: 10) {
edges {
node {
id
name
}
}
}
}
`
});
const publicationsJson = publicationsRes.data;
const onlineStorePublication = publicationsJson.data.publications.edges.find(
pub => pub.node.name === 'Online Store'
);
const onlineStorePublicationId = onlineStorePublication ? onlineStorePublication.node.id : null;
if (onlineStorePublicationId) {
const publishRes = await client.post('', {
query: `
mutation($id: ID!, $publicationId: ID!) {
publishablePublish(id: $id, input: { publicationId: $publicationId }) {
publishable {
... on Product {
id
title
status
}
}
userErrors { field message }
}
}
`,
variables: {
id: product.id,
publicationId: onlineStorePublicationId,
},
});
const publishJson = publishRes.data;
const publishErrs = publishJson.data.publishablePublish.userErrors;
if (publishErrs.length) {
throw new Error(`Publish errors: ${publishErrs.map(e => e.message).join(", ")}`);
}
log(shop, `🌐 [${procId}] Published product to Online Store`);
} else {
throw new Error("Online Store publication not found.");
}
const costPerItem = parseFloat(attrs.purchase_cost) || 0;
log(shop, `📦 [${procId}] Updating inventory for product`);
// const invRes = await client.post('', {
// query: `
// mutation($id: ID!, $input: InventoryItemUpdateInput!) {
// inventoryItemUpdate(id: $id, input: $input) {
// inventoryItem {
// id
// sku
// unitCost { amount }
// tracked
// measurement {
// weight {
// value
// unit
// }
// }
// }
// userErrors { field message }
// }
// }
// `,
// variables: {
// id: inventoryItemId,
// input: {
// cost: parseFloat(attrs.purchase_cost) || 0,
// tracked: true
// }
// }
// });
const invRes = await client.post('', {
query: `
mutation InventoryItemUpdate($id: ID!, $input: InventoryItemInput!) {
inventoryItemUpdate(id: $id, input: $input) {
inventoryItem {
id
sku
unitCost {
amount
currencyCode
}
tracked
measurement {
weight {
value
unit
}
}
}
userErrors {
field
message
}
}
}
`,
variables: {
id: inventoryItemId,
input: {
cost: parseFloat(attrs.purchase_cost) || 0,
tracked: true
}
}
});
const invJson = invRes.data;
//console.log(JSON.stringify(invJson, null, 2))
const invErrs = invJson.data.inventoryItemUpdate.userErrors;
if (invErrs.length) {
throw new Error(`Inventory update errors: ${invErrs.map(e => e.message).join(", ")}`);
}
// log(shop, `⚙️ [${procId}] Assigning variant to fulfillment service`);
// const assignVariantMutation = `
// mutation AssignVariantToFulfillmentService($variantId: ID!, $fulfillmentServiceId: ID!) {
// productVariantUpdate(input: {
// id: $variantId,
// fulfillmentServiceId: $fulfillmentServiceId
// }) {
// productVariant {
// id
// fulfillmentService {
// id
// serviceName
// }
// }
// userErrors {
// field
// message
// }
// }
// }
// `;
// const assignVariantVariables = {
// variantId: variantId, // your variant ID
// fulfillmentServiceId: fulfillmentServiceId // your fulfillment service ID
// };
// const assignVariantRes = await client.post('', {
// query: assignVariantMutation,
// variables: assignVariantVariables
// });
// console.log('Assign Variant:', JSON.stringify(assignVariantRes.data, null, 2));
const activateInventoryMutation = `
mutation ActivateInventoryItem($inventoryItemId: ID!, $locationId: ID!) {
inventoryActivate(inventoryItemId: $inventoryItemId, locationId: $locationId) {
inventoryLevel {
id
quantities(names: ["available"]) {
name
quantity
}
item { id }
location { id }
}
userErrors {
field
message
}
}
}
`;
const activateInventoryVariables = {
inventoryItemId: inventoryItemId, // your inventory item ID
locationId: locationId
};
const activateInventoryRes = await client.post('', {
query: activateInventoryMutation,
variables: activateInventoryVariables
});
//.log('Activate Inventory:', JSON.stringify(activateInventoryRes.data, null, 2));
const mutation = `
mutation InventorySet($input: InventorySetQuantitiesInput!) {
inventorySetQuantities(input: $input) {
inventoryAdjustmentGroup {
createdAt
reason
referenceDocumentUri
changes {
name
delta
}
}
userErrors {
field
message
}
}
}
`;
const variables = {
input: {
name: "available",
reason: "correction",
referenceDocumentUri: "logistics://some.warehouse/take/2023-01-23T13:14:15Z",
ignoreCompareQuantity: true,
quantities: [
{
inventoryItemId: inventoryItemId,
locationId: locationId,
quantity: totalQuantity,
compareQuantity: 1
}
]
}
};
var setInventoryRes
try {
// console.log("newwww")
setInventoryRes = await client.post('', {
query: mutation,
variables: variables
});
// Print the full setInventoryRes from Shopify
// console.log(JSON.stringify(setInventoryRes.data, null, 2));
} catch (error) {
if (error.setInventoryRes) {
console.error('Error:', error.setInventoryRes.data);
} else {
console.error('Error:', error.message);
}
}
const setInventoryData = setInventoryRes.data.inventorySetQuantities;
if (setInventoryData?.userErrors.length) {
throw new Error(
"Inventory update errors: " +
setInventoryData?.userErrors.map(e => e.message).join(", ")
);
}
const prodRes = await client.post('', {
query: `
mutation ProductUpdate($product: ProductUpdateInput!) {
productUpdate(product: $product) {
product {
id
title
seo {
title
description
}
}
userErrors {
field
message
}
}
}
`,
variables: {
product: {
id: product.id, // e.g. "gid://shopify/Product/1234567890"
seo: {
title: "Test PProduct Seo Title" || attrs.seo_title, // e.g. "My SEO Title"
description: "Test PRoduct Seo Title description" || attrs.seo_description // e.g. "My SEO Description"
}
// ...other fields as needed
}
}
});
const prodJson = prodRes.data;
const prodErrsseo = prodJson.data.productUpdate.userErrors;
if (prodErrs.length) {
throw new Error(`Product update errors: ${prodErrs.map(e => e.message).join(", ")}`);
}
// Get the updated inventory item from the response
const updatedInventoryItem = invJson.data.inventoryItemUpdate.inventoryItem;
// Collect results
results.push({
productId: product.id,
variant: {
id: variantId,
price: variantNode.price,
compareAtPrice: variantNode.compareAtPrice,
sku: updatedInventoryItem.sku || attrs.part_number || '',
barcode: variantNode.barcode || attrs.barcode || '',
weight: updatedInventoryItem?.measurement?.weight?.value || 0,
weightUnit: updatedInventoryItem?.measurement?.weight?.unit || 'kg',
},
collections: collectionTitles,
tags,
});
log(shop, `✅ [${procId}] Successfully processed product: ${attrs.product_name}`);
return results;
}
} catch (err) {
log(shop, `❌ [${procId}] Error processing product: ${err.message}`);
results.push({
error: `Failed to process item ${item.id}: ${err.message}`,
product: attrs.product_name || attrs.part_number || 'Unknown'
});
return results;
}
}
const GetAllProductsandAddToStore = async (shop, accessToken, brandId, turn14accessToken, procId, tokens, selectedProductIds, productCount) => {
const fulfillmentServiceTokens = tokens.fulfillmentService || {}
const fulfillmentServiceId = fulfillmentServiceTokens.id || null;
// const locationId = fulfillmentServiceTokens.location ? fulfillmentServiceTokens.location.id : null;
const locationId = tokens.locationId ? tokens.locationId : null;
console.log("Custom Location ID to Store : ", locationId)
log(shop, `🔍 [${procId}] Fetching products for brand ${brandId}`);
const products_res = await GetAllProductsOfBranch(brandId, turn14accessToken, shop, procId, productCount);
const items = products_res ? products_res.items : [];
// Update total products count
const results = [];
const products_filter = items.filter(item => {
return selectedProductIds.includes(item.id);
});
const products = Array.isArray(products_filter) ? products_filter : [];
//const products = Array.isArray(products_filter) ? products_filter.slice(0, 10) : [];
// const globalUniqueFitmentMap = {
// make: new Set(),
// model: new Set(),
// year: new Set(),
// drive: new Set(),
// baseModel: new Set()
// };
// // Loop over all processed items
// for (const item of products) {
// const tags = item.attributes?.fitmmentTags;
// if (!tags) continue;
// for (const key in globalUniqueFitmentMap) {
// if (tags[key]) {
// tags[key].forEach(value => {
// globalUniqueFitmentMap[key].add(value);
// });
// }
// }
// }
// // Convert sets to arrays
// const convertedGlobalUniqueFitmentMap = {};
// for (const key in globalUniqueFitmentMap) {
// convertedGlobalUniqueFitmentMap[key] = Array.from(globalUniqueFitmentMap[key]);
// }
// const fitmentTags = convertedGlobalUniqueFitmentMap;
// const allFitmentTagsSet = new Set();
// for (const arr of Object.values(convertedGlobalUniqueFitmentMap)) {
// arr.forEach(val => allFitmentTagsSet.add(val));
// }
// const allFitmentTags = Array.from(allFitmentTagsSet);
// // Now allFitmentTags is a flat array of unique values
// log(shop, `All Fitment Tags: ${JSON.stringify(allFitmentTags, null, 2)}`);
// log(shop, `Fitment Tags: ${JSON.stringify(fitmentTags, null, 2)}`);
processes[procId].totalProducts = products.length;
processes[procId].processedProducts = 0;
log(shop, `🔄 [${procId}] Processing ${products.length} products after the filter`);
if (!products) {
log(shop, `⚠️ [${procId}] No products found for brand ${brandId}`);
return [];
}
for (const [index, item] of products.entries()) {
try {
// Update current product being processed
const attrs = item.attributes;
processes[procId].currentProduct = {
name: attrs.product_name || attrs.part_number || 'Unknown',
number: index + 1,
total: products.length
};
processes[procId].status = `processing (${index + 1}/${products.length})`;
const res = await AddProductToStore(shop, accessToken, item, procId, fulfillmentServiceId, locationId);
if (res) results.push(...res);
// Update processed count
processes[procId].processedProducts = index + 1;
processes[procId].detail = `Processed ${index + 1} of ${products.length} products`;
} catch (err) {
log(shop, `⚠️ [${procId}] Error processing product ${index + 1}: ${err.message}`);
results.push({
error: `Failed to process product ${index + 1}: ${err.message}`,
product: item.attributes.product_name || item.attributes.part_number || 'Unknown'
});
}
}
// Clear current product when done
processes[procId].currentProduct = null;
log(shop, `✅ [${procId}] Completed processing ${results.length} products`);
return results;
}
router.post('/', async (req, res) => {
const { shop, brandID, turn14accessToken, productCount, selectedProductIds } = req.body;
const procId = uuid();
processes[procId] = {
status: 'started',
detail: null,
totalProducts: 0,
processedProducts: 0,
currentProduct: null,
results: []
};
log(shop, `🔔 [${procId}] Starting product import for brand ${brandID}`);
res.json({ processId: procId, status: 'started' });
(async () => {
try {
processes[procId].status = 'fetching_products';
log(shop, `🔍 [${procId}] Fetching token for shop`);
// 1. Get token
if (!turn14accessToken) throw new Error('No Turn14 access token provided');
if (!brandID) throw new Error('No brand ID provided');
if (!shop) throw new Error('No shop provided');
if (!selectedProductIds) throw new Error('No selected product IDs provided');
log(shop, `Selected Product IDs: ${selectedProductIds}`);
// console.log("Selected Product IDs:", selectedProductIds);
if (!Array.isArray(selectedProductIds) || selectedProductIds.length === 0) {
throw new Error('Selected product IDs must be a non-empty array');
}
log(shop, `🔍 [${procId}] Fetching products for brand ${brandID}`);
const tokenRecord = getToken(shop);
if (!tokenRecord) throw new Error('No token for shop');
processes[procId].status = 'importing_products';
processes[procId].detail = 'Starting product import';
const importResults = await GetAllProductsandAddToStore(shop, tokenRecord.accessToken, brandID, turn14accessToken, procId, tokenRecord, selectedProductIds, productCount);
log(shop, `✅ [${procId}] Successfully imported ${importResults.length} products`);
processes[procId].status = 'done';
processes[procId].detail = `Imported ${importResults.length} products`;
processes[procId].results = importResults;
} catch (err) {
processes[procId].status = 'error';
processes[procId].detail = err.message;
log(shop, `❌ [${procId}] Error: ${err.message}`);
}
})();
});
router.get('/status/:processId', (req, res) => {
const info = processes[req.params.processId];
if (!info) return res.status(404).json({ error: 'Not found' });
const response = {
status: info.status,
detail: info.detail,
progress: info.totalProducts > 0
? Math.round((info.processedProducts / info.totalProducts) * 100)
: 0,
current: info.currentProduct,
stats: {
total: info.totalProducts,
processed: info.processedProducts,
remaining: info.totalProducts - info.processedProducts
},
results: info.results || []
};
res.json(response);
});
module.exports = router;