feat: auto-inject chat widget on all storefronts via Shopify ScriptTag API

- auth.js: register chat widget ScriptTag during OAuth callback so every
  new install automatically gets the floating chat button — no manual
  theme editing required
- server.js: GET /chat/backfill-scripttags endpoint to register the widget
  on all already-installed shops in one hit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MOHAN 2026-06-12 20:52:18 +05:30
parent a27d586c82
commit 9b5e16b1c1
2 changed files with 47 additions and 1 deletions

25
auth.js
View File

@ -38,7 +38,30 @@ router.get('/auth/callback', async (req, res) => {
saveToken(shop, access_token, scope);
log(shop, '💾 Token saved to data/tokens.json');
// 2) Create fulfillment service (existing step)
// 2) Register chat widget ScriptTag so it auto-injects on every storefront page
try {
const WIDGET_SRC = `https://backend.data4autos.com/chat/widget.js?shop=${encodeURIComponent(shop)}`;
// Check if already registered to avoid duplicates
const existingResp = await axios.get(
`https://${shop}/admin/api/2025-10/script_tags.json?src=${encodeURIComponent(WIDGET_SRC)}`,
{ headers: { 'X-Shopify-Access-Token': access_token, 'Content-Type': 'application/json' } }
);
const already = existingResp.data?.script_tags?.length > 0;
if (!already) {
await axios.post(
`https://${shop}/admin/api/2025-10/script_tags.json`,
{ script_tag: { event: 'onload', src: WIDGET_SRC } },
{ headers: { 'X-Shopify-Access-Token': access_token, 'Content-Type': 'application/json' } }
);
log(shop, '💬 Chat widget ScriptTag registered on storefront');
} else {
log(shop, '💬 Chat widget ScriptTag already registered, skipping');
}
} catch (stErr) {
log(shop, `⚠️ ScriptTag registration failed (non-fatal): ${stErr.message}`);
}
// 3) Create fulfillment service (existing step)
const { fulfillmentService, locationId } = await createFulfillmentService(shop, access_token);
console.log(`Custom Location created: ${locationId}`);
saveToken(shop, access_token, scope, fulfillmentService, locationId);

View File

@ -108,6 +108,29 @@ app.get('/chat/widget.js', (req, res) => {
})();`);
});
// One-time backfill: register chat widget ScriptTag on all already-installed shops
// Hit: GET /chat/backfill-scripttags (admin use only — no sensitive data exposed)
app.get('/chat/backfill-scripttags', async (req, res) => {
const { getToken, listTokens } = require('./tokenStore');
const axios = require('axios');
const stores = listTokens();
const results = [];
for (const [shop, record] of Object.entries(stores)) {
if (!record.accessToken) { results.push({ shop, status: 'no-token' }); continue; }
const WIDGET_SRC = `https://backend.data4autos.com/chat/widget.js?shop=${encodeURIComponent(shop)}`;
const headers = { 'X-Shopify-Access-Token': record.accessToken, 'Content-Type': 'application/json' };
try {
const ex = await axios.get(`https://${shop}/admin/api/2025-10/script_tags.json?src=${encodeURIComponent(WIDGET_SRC)}`, { headers });
if (ex.data?.script_tags?.length > 0) { results.push({ shop, status: 'already-exists' }); continue; }
await axios.post(`https://${shop}/admin/api/2025-10/script_tags.json`, { script_tag: { event: 'onload', src: WIDGET_SRC } }, { headers });
results.push({ shop, status: 'registered' });
} catch (e) {
results.push({ shop, status: 'error', error: e.response?.data || e.message });
}
}
res.json({ results });
});
// Health check
app.get('/health', (req, res) => {
res.json({ ok: true, uptime: process.uptime(), timestamp: new Date().toISOString() });