2026-04-13 05:23:25 +00:00

102 lines
4.0 KiB
JavaScript
Executable File

// routes/configurePricing.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();
const processes = {};
// POST /configure-pricing
// body: { shop: string, priceType: "map" | "percentage", percentage: number }
router.post('/', async (req, res) => {
const { shop, priceType, percentage } = req.body;
if (!shop) return res.status(400).json({ error: 'Missing `shop`' });
const procId = uuid();
processes[procId] = { status: 'started', detail: null };
log(shop, `🔔 [${procId}] ConfigurePricing initiated (priceType=${priceType}, percentage=${percentage})`);
res.json({ processId: procId, status: 'started' });
(async () => {
try {
processes[procId].status = 'preparing';
// 1) Get token
const tokenRecord = getToken(shop);
if (!tokenRecord) throw new Error('No token for shop');
const adminUrl = `https://${shop}/admin/api/2025-10/graphql.json`;
const headers = {
'X-Shopify-Access-Token': tokenRecord.accessToken,
'Content-Type': 'application/json',
};
log(shop, `🔑 [${procId}] Using access token for shop ${shop}`);
// 2) Fetch Shop GID
processes[procId].status = 'fetching_shop';
const shopIdQuery = `{ shop { id name } }`;
const shopIdResp = await axios.post(adminUrl, { query: shopIdQuery }, { headers });
const shopId = shopIdResp.data?.data?.shop?.id;
if (!shopId) throw new Error(`Could not fetch shop id: ${JSON.stringify(shopIdResp.data)}`);
log(shop, `🏷️ [${procId}] Shop GID: ${shopId}`);
// 3) Sanitize + build config JSON
const normalizedType = (priceType || 'map').toString().toLowerCase();
const validType = ['map', 'percentage'].includes(normalizedType) ? normalizedType : 'map';
const rawPct = Number(percentage);
const validPct = Number.isFinite(rawPct) ? rawPct : 0;
const cfg = { priceType: validType, percentage: validPct };
const cfgValue = JSON.stringify(cfg);
log(shop, `🧾 [${procId}] Saving pricing_config: ${cfg.priceType}/${cfg.percentage}%`);
// 4) Save metafield (inline GraphQL, double-stringify value to embed JSON safely)
processes[procId].status = 'saving_metafield';
const resp = await axios.post(adminUrl, {
query: `
mutation {
metafieldsSet(metafields: [{
namespace: "turn14",
key: "pricing_config",
type: "json",
ownerId: "${shopId}",
value: ${JSON.stringify(cfgValue)}
}]) {
metafields { id key }
userErrors { field message }
}
}
`
}, { headers });
const errs = resp.data?.data?.metafieldsSet?.userErrors || [];
if (errs.length) {
log(shop, `⚠️ [${procId}] metafieldsSet errors: ${JSON.stringify(errs)}`);
processes[procId].status = 'error';
processes[procId].detail = JSON.stringify(errs);
return;
}
log(shop, `✅ [${procId}] pricing_config metafield saved`);
processes[procId].status = 'done';
processes[procId].detail = null;
} catch (err) {
processes[procId].status = 'error';
processes[procId].detail = err.message;
log(shop, `❌ [${procId}] Error: ${err.message}`);
}
})();
});
// Optional: check status (same shape as manageBrands)
router.get('/status/:processId', (req, res) => {
const info = processes[req.params.processId];
if (!info) return res.status(404).json({ error: 'Not found' });
res.json(info);
});
module.exports = router;