147 lines
4.4 KiB
JavaScript
147 lines
4.4 KiB
JavaScript
const { PixelbinConfig, PixelbinClient } = require('@pixelbin/admin');
|
|
const { Readable } = require('stream');
|
|
const axios = require('axios');
|
|
const { businessRoot } = require('./storagePaths');
|
|
|
|
function getPixelbinClient() {
|
|
const apiToken = process.env.PIXELBIN_API_TOKEN;
|
|
if (!apiToken) throw new Error('PIXELBIN_API_TOKEN not set');
|
|
const config = new PixelbinConfig({
|
|
domain: 'https://api.pixelbin.io',
|
|
apiSecret: apiToken,
|
|
});
|
|
return new PixelbinClient(config);
|
|
}
|
|
|
|
/** Upload a JSON object to Pixelbin. Overwrites if already exists. */
|
|
async function uploadJSON(folderPath, filename, jsonObject) {
|
|
const pixelbin = getPixelbinClient();
|
|
const buffer = Buffer.from(JSON.stringify(jsonObject, null, 2), 'utf-8');
|
|
const stream = Readable.from(buffer);
|
|
stream.path = `${filename}.json`;
|
|
|
|
await pixelbin.assets.fileUpload({
|
|
file: stream,
|
|
path: folderPath,
|
|
name: filename,
|
|
access: 'public-read',
|
|
overwrite: true,
|
|
});
|
|
}
|
|
|
|
/** Fetch a JSON file from Pixelbin. Returns null if not found. */
|
|
async function fetchJSON(folderPath, filename) {
|
|
const pixelbin = getPixelbinClient();
|
|
try {
|
|
const result = await pixelbin.assets.listFiles({ path: folderPath });
|
|
const items = result.items || [];
|
|
const file = items.find(
|
|
f => f.type === 'file' && (f.name === filename || f.name === `${filename}.json`)
|
|
);
|
|
if (!file?.url) return null;
|
|
const bustUrl = `${file.url}?t=${Date.now()}`;
|
|
const res = await axios.get(bustUrl, { timeout: 10000 });
|
|
return res.data;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Download an image URL and upload it to Pixelbin. Returns CDN URL or null. */
|
|
async function uploadImageFromUrl(imageUrl, folderPath, filename) {
|
|
try {
|
|
const pixelbin = getPixelbinClient();
|
|
const res = await axios.get(imageUrl, { responseType: 'arraybuffer', timeout: 15000 });
|
|
const buffer = Buffer.from(res.data);
|
|
const rawExt = (imageUrl.split('?')[0].split('.').pop() || 'jpg').toLowerCase();
|
|
const ext = ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(rawExt) ? rawExt : 'jpg';
|
|
const stream = Readable.from(buffer);
|
|
stream.path = `${filename}.${ext}`;
|
|
const uploaded = await pixelbin.assets.fileUpload({
|
|
file: stream,
|
|
path: folderPath,
|
|
name: filename,
|
|
access: 'public-read',
|
|
overwrite: true,
|
|
});
|
|
return uploaded?.url || null;
|
|
} catch (err) {
|
|
console.error(`Image upload failed (${imageUrl}):`, err.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** List all image files in a Pixelbin folder. Returns [{ name, url }] */
|
|
async function listImages(folderPath) {
|
|
const pixelbin = getPixelbinClient();
|
|
try {
|
|
const result = await pixelbin.assets.listFiles({ path: folderPath });
|
|
return (result.items || [])
|
|
.filter(f => f.type === 'file')
|
|
.map(f => ({ name: f.name, url: f.url }));
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/** List template file names (slugs) in a Pixelbin folder. */
|
|
async function listTemplateFiles(folderPath) {
|
|
const pixelbin = getPixelbinClient();
|
|
try {
|
|
const result = await pixelbin.assets.listFiles({ path: folderPath });
|
|
return (result.items || [])
|
|
.filter(f => f.type === 'file')
|
|
.map(f => f.name);
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/** Delete a single file from Pixelbin by its fileId. */
|
|
async function deleteFile(fileId) {
|
|
const pixelbin = getPixelbinClient();
|
|
try {
|
|
await pixelbin.assets.deleteFile({ fileId });
|
|
} catch (err) {
|
|
console.error(`Failed to delete ${fileId}:`, err.message);
|
|
}
|
|
}
|
|
|
|
/** List all file items (with fileId) from a folder. */
|
|
async function listFilesWithId(folderPath) {
|
|
const pixelbin = getPixelbinClient();
|
|
try {
|
|
const result = await pixelbin.assets.listFiles({ path: folderPath });
|
|
return (result.items || []).filter(f => f.type === 'file');
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete all files belonging to a specific business.
|
|
* Business root: {merchantId}/{businessId}/
|
|
*/
|
|
async function deleteBusinessFiles(merchantId, businessId) {
|
|
const root = businessRoot(merchantId, businessId);
|
|
const [rootFiles, templateFiles, imageFiles] = await Promise.all([
|
|
listFilesWithId(root),
|
|
listFilesWithId(`${root}/templates`),
|
|
listFilesWithId(`${root}/images`),
|
|
]);
|
|
|
|
const all = [...rootFiles, ...templateFiles, ...imageFiles];
|
|
await Promise.all(all.map(f => deleteFile(f.fileId)));
|
|
}
|
|
|
|
module.exports = {
|
|
uploadJSON,
|
|
fetchJSON,
|
|
uploadImageFromUrl,
|
|
listImages,
|
|
listTemplateFiles,
|
|
deleteFile,
|
|
listFilesWithId,
|
|
deleteBusinessFiles,
|
|
};
|