Files
koillection-uploader/createCollectionsFromCSVs.js
T
joseph.nelson4456 61ec339faf
Build and Push Image / build-and-push (push) Successful in 1m12s
console logging data returned when uploading image
2026-04-28 23:31:43 -07:00

243 lines
8.6 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const csvParser = require('csv-parser');
const koillectionBaseUrl = 'https://koillection.nelson-household.com/api'; // Verify this URL
const authenticationUrl = `${koillectionBaseUrl}/authentication_token`; // URL to obtain JWT
const imagesDir = path.join(__dirname, 'master-set-images/perfect-order'); // Use __dirname for correct path
// Define mapping of collection names to their corresponding CSV files
const collectionsMapping = {
'Perfect Order': './csv/ME03PerfectOrderProductsAndPrices.csv',
};
const createJWTToken = async (username, password) => {
try {
const response = await fetch(authenticationUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
if (!response.ok) {
throw new Error(`Failed to obtain JWT token: ${response.status} - ${response.statusText}`);
}
const data = await response.json();
console.log(`JWT Token obtained: ${data.token}`);
return data.token;
} catch (error) {
console.error(`Error obtaining JWT token:`, error);
throw error; // Re-throw to be caught in other functions
}
};
const createCollectionIfNotExists = async (name, parentCollectionId, token) => {
try {
const url = `${koillectionBaseUrl}/collections`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ title: name, visibility: 'public', parent: `/api/collections/${parentCollectionId}` }),
});
if (!response.ok) {
throw new Error(`Failed to create collection "${name}": ${response.status} - ${response.statusText} - ${JSON.stringify(await response.json())}`);
}
const data = await response.json();
console.log(`Successfully created collection "${name}" with ID: ${data.id}`);
return data.id;
} catch (error) {
console.error(`Error creating collection ${name} from CSV:`, error);
throw error;
}
};
const createItem = async (payload, token) => {
try {
const response = await fetch(`${koillectionBaseUrl}/items`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Failed to create item: ${response.status} - ${await response.text()}`);
}
const createdItem = await response.json();
return createdItem;
} catch (error) {
console.error('Error creating item:', error);
throw error;
}
};
async function downloadTcgImage(productId, imageUrl) {
try {
// 1. Ensure directory exists
if (!fs.existsSync(imagesDir)) {
fs.mkdirSync(imagesDir, { recursive: true });
}
// 2. Construct file destination
const filename = `${productId}_${path.basename(new URL(imageUrl).pathname)}`;
const dest = path.join(imagesDir, filename);
// 3. Fetch the image
const response = await fetch(imageUrl);
if (!response.ok) {
console.error(`Failed to fetch ${imageUrl}: ${response.statusText}`);
return {};
}
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
fs.writeFileSync(dest, buffer); // Use the correct destination path
console.log(`Successfully saved: ${dest}`);
return { success: true, destination: dest };
} catch (error) {
console.error('Error downloading image:', error);
return {};
}
}
const applyImageToItem = async (itemId, localImagePath, token) => {
try {
console.log(`Local Image Path: ${localImagePath}`);
if (!fs.existsSync(localImagePath)) {
console.log('Looks like the image does not exists?');
return {};
}
const formData = new FormData();
formData.append(localImagePath, fs.readFileSync(localImagePath));
const response = await fetch(`${koillectionBaseUrl}/items/${itemId}/image`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
},
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`HTTP error! Status: ${response.status}, Error: ${JSON.stringify(errorData)}`);
}
const data = await response.json();
console.log(`Returned item with image: ${JSON.stringify(data, null, 2)}`);
return data; // Returns the item with the new image
} catch (error) {
console.error('Error applying image:', error);
throw error; // Re-throw for handling in the caller
}
};
const parseCsv = async (collectionId, csvFilePath, token) => {
try {
const rows = [];
const results = {};
const stream = fs.createReadStream(csvFilePath)
.pipe(csvParser()); // Now using the stream directly
return await new Promise((resolve, reject) => {
stream.on('data', (row) => rows.push(row));
stream.on('end', async () => {
try {
const processingPromises = rows.map(async (row) => {
const payload = {
name: row.name,
collection: `/api/collections/${collectionId}`,
visibility: 'public',
};
const createdItem = await createItem(payload, token);
console.log(`Created item: ${createdItem.name}`);
//const imageUrl = await downloadTcgImage(itemId, row.imageUrl); // Await the image download
results[row.productId] = { imageUrl: row.imageUrl, itemId: createdItem.id };
});
await Promise.all(processingPromises);
resolve(results);
} catch (err) {
reject(err);
}
});
stream.on('error', (err) => reject(err));
});
} catch (error) {
console.error(`Error creating collection ${name} from CSV:`, error);
return {};
}
}
function mergeObjects(o1, o2) {
const merged = { ...o1 };
for (const key in o2) {
if (merged[key]) {
// Key exists, convert itemId to itemIds array
const existing = merged[key];
const incoming = o2[key];
// Keep existing itemId if it's already an array, otherwise make it one
const items = Array.isArray(existing.itemIds) ? existing.itemIds : [existing.itemId];
items.push(incoming.itemId);
merged[key] = {
...existing,
...incoming,
itemIds: items,
itemId: undefined // Remove old field
};
// Optionally cleanup undefined
delete merged[key].itemId;
} else {
// Key is new, add as is
merged[key] = o2[key];
}
}
return merged;
}
const createCollectionFromCSV = async (name, csvFilePath) => {
const token = await createJWTToken('bigassdragon', 'd7Bis4Y4EUhfMf');
const joeCollectionId = await createCollectionIfNotExists(name, '019dc310-a130-799e-8c0d-db90774b7b77', token);
const tylerCollectionId = await createCollectionIfNotExists(name, '019dc310-d175-7e59-b465-c8e684ee7186', token);
const joeCollectionData = await parseCsv(joeCollectionId, csvFilePath, token).then((results) => results);
const tylerCollectionData = await parseCsv(tylerCollectionId, csvFilePath, token).then((results) => results);
const mergedCollectionData = mergeObjects(joeCollectionData, tylerCollectionData);
const itemIdImageLocations = [];
for (const productId of Object.keys(mergedCollectionData)) {
const item = mergedCollectionData[productId];
// Await each download before starting the next
// const { destination } = await downloadTcgImage(productId, item.imageUrl);
item.itemIds.forEach((id) => itemIdImageLocations.push({ itemId: id, imageDest: path.join(imagesDir, `${productId}.jpg`) }));
}
// After processing all collections, check the imagesDir
console.log("Finished processing collections.");
const files = fs.readdirSync(imagesDir);
console.log(`Files in ${imagesDir}: ${files}`);
console.log(`Item ID Image Locations: ${JSON.stringify(itemIdImageLocations, null, 2)}`);
// Process multiple images concurrently using Promise.all()
const promises = itemIdImageLocations.map(async (item) => {
await applyImageToItem(item.itemId, item.imageDest, token);
});
await Promise.all(promises);
};
// Main execution
async function main() {
for (const collectionName in collectionsMapping) {
try {
console.log(`Processing collection: ${collectionName}`);
await createCollectionFromCSV(collectionName, collectionsMapping[collectionName]);
console.log(`Collection ${collectionName} processed successfully.`);
} catch (error) {
console.error(`Error processing collection - ${collectionName}:`, error);
// Break the loop if an error occurs
break;
}
}
}
main();