Sidebar unified and fynd sdk backend so we fetch sales channel directly
This commit is contained in:
parent
926c332c7e
commit
72a8adb142
|
|
@ -1,19 +1,102 @@
|
|||
import { useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import apiClient from '../api/client';
|
||||
|
||||
function normalizeChannelsPayload(data) {
|
||||
if (Array.isArray(data)) return data;
|
||||
if (Array.isArray(data?.salesChannels)) return data.salesChannels;
|
||||
if (Array.isArray(data?.channels)) return data.channels;
|
||||
return [];
|
||||
}
|
||||
|
||||
function getChannelId(channel) {
|
||||
return channel?.salesChannelId || channel?.id || channel?._id || '';
|
||||
}
|
||||
|
||||
export default function RegisterBusinessModal({ onClose }) {
|
||||
const [url, setUrl] = useState('');
|
||||
const [status, setStatus] = useState('idle'); // idle | loading | success | error
|
||||
const [brandName, setBrandName] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [entryMode, setEntryMode] = useState('sales-channel');
|
||||
const [salesChannels, setSalesChannels] = useState([]);
|
||||
const [salesChannelsStatus, setSalesChannelsStatus] = useState('loading'); // loading | success | error
|
||||
const [salesChannelsError, setSalesChannelsError] = useState('');
|
||||
const [selectedSalesChannelId, setSelectedSalesChannelId] = useState('');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
async function fetchSalesChannels() {
|
||||
setSalesChannelsStatus('loading');
|
||||
setSalesChannelsError('');
|
||||
try {
|
||||
const res = await apiClient.get('/api/businesses/sales-channels');
|
||||
if (cancelled) return;
|
||||
|
||||
const channels = normalizeChannelsPayload(res.data);
|
||||
setSalesChannels(channels);
|
||||
|
||||
if (channels.length > 0) {
|
||||
setSelectedSalesChannelId(getChannelId(channels[0]));
|
||||
setSalesChannelsStatus('success');
|
||||
setEntryMode('sales-channel');
|
||||
} else {
|
||||
setSalesChannelsStatus('error');
|
||||
setSalesChannelsError('No sales channels were found for this company yet. You can still enter the website URL manually.');
|
||||
setEntryMode('manual');
|
||||
}
|
||||
} catch (err) {
|
||||
if (cancelled) return;
|
||||
setSalesChannels([]);
|
||||
setSalesChannelsStatus('error');
|
||||
setSalesChannelsError(err.response?.data?.error || 'Sales channels could not be fetched right now. You can still enter the website URL manually.');
|
||||
setEntryMode('manual');
|
||||
}
|
||||
}
|
||||
|
||||
fetchSalesChannels();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const filteredSalesChannels = useMemo(() => {
|
||||
if (!searchQuery.trim()) return salesChannels;
|
||||
const query = searchQuery.trim().toLowerCase();
|
||||
return salesChannels.filter((channel) => {
|
||||
const name = String(channel?.name || '').toLowerCase();
|
||||
const domain = String(channel?.domain || '').toLowerCase();
|
||||
return name.includes(query) || domain.includes(query);
|
||||
});
|
||||
}, [salesChannels, searchQuery]);
|
||||
|
||||
const canUseSalesChannels = salesChannelsStatus === 'success' && salesChannels.length > 0;
|
||||
const isSalesChannelMode = entryMode === 'sales-channel' && canUseSalesChannels;
|
||||
const effectiveSelectedSalesChannelId = filteredSalesChannels.some(
|
||||
(channel) => getChannelId(channel) === selectedSalesChannelId
|
||||
)
|
||||
? selectedSalesChannelId
|
||||
: (filteredSalesChannels[0] ? getChannelId(filteredSalesChannels[0]) : '');
|
||||
const submitDisabled = status === 'loading'
|
||||
|| (isSalesChannelMode ? !effectiveSelectedSalesChannelId : !url.trim());
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
if (!url.trim()) return;
|
||||
|
||||
const isSalesChannelMode = entryMode === 'sales-channel' && salesChannelsStatus === 'success';
|
||||
if (isSalesChannelMode && !effectiveSelectedSalesChannelId) return;
|
||||
if (!isSalesChannelMode && !url.trim()) return;
|
||||
|
||||
setStatus('loading');
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const res = await apiClient.post('/api/businesses', { websiteUrl: url.trim() });
|
||||
const payload = isSalesChannelMode
|
||||
? { salesChannelId: effectiveSelectedSalesChannelId }
|
||||
: { websiteUrl: url.trim() };
|
||||
|
||||
const res = await apiClient.post('/api/businesses', payload);
|
||||
setBrandName(res.data.brandName);
|
||||
setStatus('success');
|
||||
} catch (err) {
|
||||
|
|
@ -26,7 +109,6 @@ export default function RegisterBusinessModal({ onClose }) {
|
|||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 backdrop-blur-sm">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 w-full max-w-md shadow-xl">
|
||||
|
||||
{/* Success */}
|
||||
{status === 'success' && (
|
||||
<div className="text-center">
|
||||
<div className="w-14 h-14 rounded-full bg-green-50 text-green-600 flex items-center justify-center mx-auto mb-4 text-2xl">✓</div>
|
||||
|
|
@ -42,29 +124,110 @@ export default function RegisterBusinessModal({ onClose }) {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Form */}
|
||||
{(status === 'idle' || status === 'loading' || status === 'error') && (
|
||||
<>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-2 tracking-tight">Add a Business</h2>
|
||||
<p className="text-gray-500 text-sm leading-relaxed">
|
||||
Enter your website URL. We'll scrape your site and extract brand context to generate TRAI-compliant SMS templates.
|
||||
Choose a sales channel first when available. If that lookup fails, you can still enter the website URL and we'll scrape it directly.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="rounded-xl bg-gray-50 p-1 grid grid-cols-2 gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => canUseSalesChannels && setEntryMode('sales-channel')}
|
||||
disabled={!canUseSalesChannels || status === 'loading'}
|
||||
className={`px-3 py-2 rounded-[10px] text-sm font-semibold transition ${
|
||||
isSalesChannelMode
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: canUseSalesChannels
|
||||
? 'text-gray-500 hover:text-gray-900'
|
||||
: 'text-gray-300 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
Sales Channel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEntryMode('manual')}
|
||||
disabled={status === 'loading'}
|
||||
className={`px-3 py-2 rounded-[10px] text-sm font-semibold transition ${
|
||||
!isSalesChannelMode ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-500 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
Website URL
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isSalesChannelMode ? (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-1.5 tracking-wide">Search Sales Channels</label>
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Search by name or domain"
|
||||
disabled={status === 'loading' || salesChannelsStatus === 'loading'}
|
||||
className="w-full px-4 py-2.5 rounded-lg bg-white border border-gray-300 text-gray-900 placeholder-gray-400 font-medium focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition disabled:opacity-50 text-sm shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-1.5 tracking-wide">Sales Channel</label>
|
||||
{filteredSalesChannels.length > 0 ? (
|
||||
<select
|
||||
value={effectiveSelectedSalesChannelId}
|
||||
onChange={(e) => setSelectedSalesChannelId(e.target.value)}
|
||||
disabled={status === 'loading'}
|
||||
className="w-full px-4 py-2.5 rounded-lg bg-white border border-gray-300 text-gray-900 font-medium focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition disabled:opacity-50 text-sm shadow-sm"
|
||||
>
|
||||
{filteredSalesChannels.map((channel) => (
|
||||
<option key={getChannelId(channel)} value={getChannelId(channel)}>
|
||||
{channel.name}{channel.domain ? ` • ${channel.domain}` : ''}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<div className="w-full px-4 py-2.5 rounded-lg bg-gray-50 border border-gray-200 text-sm text-gray-500 font-medium">
|
||||
No sales channels matched your search.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{salesChannelsStatus === 'loading' && (
|
||||
<p className="text-xs text-gray-500 font-medium">
|
||||
Loading sales channels for this company…
|
||||
</p>
|
||||
)}
|
||||
|
||||
{salesChannelsError && (
|
||||
<p className="text-sm text-amber-700 font-medium bg-amber-50 border border-amber-200 rounded-lg px-3 py-2">
|
||||
{salesChannelsError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-1.5 tracking-wide">Website URL</label>
|
||||
<input
|
||||
type="url"
|
||||
value={url}
|
||||
onChange={e => setUrl(e.target.value)}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
placeholder="https://yourstore.com"
|
||||
disabled={status === 'loading'}
|
||||
className="w-full px-4 py-2.5 rounded-lg bg-white border border-gray-300 text-gray-900 placeholder-gray-400 font-medium focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition disabled:opacity-50 text-sm shadow-sm"
|
||||
required
|
||||
required={!isSalesChannelMode}
|
||||
/>
|
||||
{salesChannelsError && (
|
||||
<p className="mt-2 text-xs text-gray-500 font-medium">
|
||||
{salesChannelsError}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === 'error' && (
|
||||
<p className="text-sm text-red-600 font-medium bg-red-50 border border-red-200 rounded-lg px-3 py-2">{error}</p>
|
||||
|
|
@ -81,7 +244,7 @@ export default function RegisterBusinessModal({ onClose }) {
|
|||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === 'loading' || !url.trim()}
|
||||
disabled={submitDisabled}
|
||||
className="flex-[1.2] py-2.5 rounded-lg bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium transition shadow-sm disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
{status === 'loading' ? (
|
||||
|
|
@ -92,7 +255,7 @@ export default function RegisterBusinessModal({ onClose }) {
|
|||
|
||||
{status === 'loading' && (
|
||||
<p className="text-xs text-gray-500 font-medium text-center pt-2">
|
||||
Scraping your site and extracting brand context. This may take 20–30 seconds.
|
||||
Fetching the website context and extracting brand details. This may take 20–30 seconds.
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,28 @@ function TopLevelStatus({ done, active }) {
|
|||
return <span className="inline-block h-2.5 w-2.5 rounded-full bg-gray-200" />;
|
||||
}
|
||||
|
||||
function StageMarker({ done, active, enabled }) {
|
||||
if (done) {
|
||||
return (
|
||||
<span className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-indigo-600 text-white shadow-sm">
|
||||
<svg className="h-2.5 w-2.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (active) {
|
||||
return <span className="inline-block h-3 w-3 rounded-full border-2 border-white bg-[#8b2bfa] shadow-[0_0_0_1px_rgba(139,43,250,0.2)]" />;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
return <span className="inline-block h-3 w-3 rounded-full bg-gray-200" />;
|
||||
}
|
||||
|
||||
return <span className="inline-block h-3 w-3 rounded-full bg-indigo-200" />;
|
||||
}
|
||||
|
||||
export default function Sidebar() {
|
||||
const {
|
||||
activeBusiness,
|
||||
|
|
@ -124,9 +146,18 @@ export default function Sidebar() {
|
|||
|
||||
{/* Nav */}
|
||||
<nav className="flex-1 px-3 pt-5">
|
||||
<div className="space-y-2">
|
||||
{stepItems.map((item) => (
|
||||
<div key={item.id}>
|
||||
<div className="space-y-1">
|
||||
{stepItems.map((item, index) => (
|
||||
<div key={item.id} className="relative grid grid-cols-[26px_minmax(0,1fr)] gap-2">
|
||||
<div className="relative flex min-h-full justify-center">
|
||||
{index > 0 && <div className="absolute left-1/2 -top-2 h-7 w-px -translate-x-1/2 bg-gray-200" />}
|
||||
{index < stepItems.length - 1 && <div className="absolute left-1/2 top-5 -bottom-2 w-px -translate-x-1/2 bg-gray-200" />}
|
||||
<div className="absolute left-1/2 top-5 -translate-x-1/2 -translate-y-1/2">
|
||||
<StageMarker done={item.done} active={item.active} enabled={item.enabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{item.enabled ? (
|
||||
<NavLink
|
||||
to={item.to}
|
||||
|
|
@ -189,6 +220,7 @@ export default function Sidebar() {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
require('dotenv').config();
|
||||
|
||||
const { setupFdk } = require('@gofynd/fdk-extension-javascript/express');
|
||||
const { MemoryStorage } = require('@gofynd/fdk-extension-javascript/express/storage');
|
||||
|
||||
function normalizeEnvText(value) {
|
||||
return typeof value === 'string' ? value.trim() : '';
|
||||
}
|
||||
|
||||
function trimTrailingSlash(value) {
|
||||
return normalizeEnvText(value).replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
function buildRedirectUrl(baseUrl, companyId) {
|
||||
const normalizedBaseUrl = trimTrailingSlash(baseUrl);
|
||||
const query = companyId ? `?blt-gtw-fc-cid=${encodeURIComponent(companyId)}` : '';
|
||||
return `${normalizedBaseUrl}/${query}`.replace(/\/\?/, '/?');
|
||||
}
|
||||
|
||||
function createFdkExtension() {
|
||||
const apiKey = normalizeEnvText(process.env.EXTENSION_API_KEY);
|
||||
const apiSecret = normalizeEnvText(process.env.EXTENSION_API_SECRET);
|
||||
const baseUrl = normalizeEnvText(process.env.EXTENSION_BASE_URL || process.env.EXTENSION_URL);
|
||||
const cluster = normalizeEnvText(process.env.EXTENSION_CLUSTER_URL) || 'https://api.fynd.com';
|
||||
|
||||
if (!apiKey || !apiSecret || !baseUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return setupFdk({
|
||||
api_key: apiKey,
|
||||
api_secret: apiSecret,
|
||||
base_url: trimTrailingSlash(baseUrl),
|
||||
cluster,
|
||||
scopes: ['company/applications/read'],
|
||||
callbacks: {
|
||||
auth: async (req) => buildRedirectUrl(baseUrl, req?.fdkSession?.company_id || req?.query?.company_id || ''),
|
||||
uninstall: async (req) => {
|
||||
const companyId = req?.body?.company_id || req?.query?.company_id || req?.fdkSession?.company_id || '';
|
||||
console.log(`[FDK] uninstall callback received for company ${companyId || 'unknown'}`);
|
||||
},
|
||||
},
|
||||
storage: new MemoryStorage('sms_extension_'),
|
||||
access_mode: 'offline',
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn(`[FDK] Failed to initialize FDK: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const fdkExtension = createFdkExtension();
|
||||
|
||||
async function getPlatformClientForCompany(companyId) {
|
||||
if (!fdkExtension) {
|
||||
throw new Error('FDK is not configured');
|
||||
}
|
||||
|
||||
if (!normalizeEnvText(companyId)) {
|
||||
throw new Error('companyId is required to fetch a platform client');
|
||||
}
|
||||
|
||||
return fdkExtension.getPlatformClient(companyId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fdkExtension,
|
||||
isFdkConfigured: Boolean(fdkExtension),
|
||||
getPlatformClientForCompany,
|
||||
};
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const businessesRoutes = require('./routes/businesses');
|
||||
const { fdkExtension, isFdkConfigured } = require('./fdk');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
|
@ -13,11 +15,21 @@ const CLIENT_DIST_DIR = [
|
|||
path.join(__dirname, '..', 'client', 'dist'),
|
||||
].find((dir) => fs.existsSync(path.join(dir, 'index.html')));
|
||||
|
||||
app.use(cors());
|
||||
app.use(cors({ origin: true, credentials: true }));
|
||||
app.use(cookieParser('ext.session'));
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// Health check
|
||||
app.get('/api/health', (req, res) => res.json({ ok: true, timestamp: new Date().toISOString() }));
|
||||
app.get('/api/health', (req, res) => res.json({
|
||||
ok: true,
|
||||
timestamp: new Date().toISOString(),
|
||||
fdkConfigured: isFdkConfigured,
|
||||
}));
|
||||
|
||||
if (fdkExtension) {
|
||||
app.use(fdkExtension.fdkHandler);
|
||||
}
|
||||
|
||||
// Routes
|
||||
app.use('/api/businesses', businessesRoutes);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
"name": "sms-extension-server",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@gofynd/fdk-extension-javascript": "^1.1.5-beta.1",
|
||||
"@pixelbin/admin": "^2.0.0",
|
||||
"axios": "^1.6.8",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
|
|
@ -21,6 +23,132 @@
|
|||
"nodemon": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
||||
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/@dabh/diagnostics": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
|
||||
"integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@so-ric/colorspace": "^1.1.6",
|
||||
"enabled": "2.0.x",
|
||||
"kuler": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fdk-client-javascript": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@gofynd/fdk-client-javascript/-/fdk-client-javascript-3.21.0.tgz",
|
||||
"integrity": "sha512-BadF2lynNAHXkr6zx6bmnqGEI+3EB+eaGd46etd9f64Z5g9RWaIrxR8rsi/iNx10nfPmzhprQDpxKaosh0QsPw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@gofynd/fp-signature": "^1.0.1",
|
||||
"axios": "^1.6.4",
|
||||
"camelcase": "^6.3.0",
|
||||
"joi": "^17.7.0",
|
||||
"loglevel": "^1.8.1",
|
||||
"query-string": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fdk-client-javascript/node_modules/camelcase": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fdk-client-javascript/node_modules/joi": {
|
||||
"version": "17.13.3",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
|
||||
"integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.3.0",
|
||||
"@hapi/topo": "^5.1.0",
|
||||
"@sideway/address": "^4.1.5",
|
||||
"@sideway/formula": "^3.0.1",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fdk-client-javascript/node_modules/query-string": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decode-uri-component": "^0.2.2",
|
||||
"filter-obj": "^1.1.0",
|
||||
"split-on-first": "^1.0.0",
|
||||
"strict-uri-encode": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fdk-extension-javascript": {
|
||||
"version": "1.1.5-beta.1",
|
||||
"resolved": "https://registry.npmjs.org/@gofynd/fdk-extension-javascript/-/fdk-extension-javascript-1.1.5-beta.1.tgz",
|
||||
"integrity": "sha512-C41nKlRVSota8RKCJ3xu/KO59kDoCVCk4om3zsRp9TNBvwyCrW6xFemQP9Ohs5CQnonut7KCWaS1k9AL4Dy17g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.4",
|
||||
"crypto-js": "^4.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"querystring": "^0.2.1",
|
||||
"url-join": "^4.0.1",
|
||||
"uuid": "^8.3.2",
|
||||
"validator": "^13.5.2",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@gofynd/fdk-client-javascript": ">=1.4.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fdk-extension-javascript/node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@gofynd/fdk-extension-javascript/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fp-signature": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@gofynd/fp-signature/-/fp-signature-1.0.2.tgz",
|
||||
"integrity": "sha512-7cDq9nzfHyxAanV3g5MYcDvLwzYIdrzSz5TOpyHfneFvbrYYjkB6+7OktsY8IErHdS+TKt1Emoo9ES/C7eZZpw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"crypto-js": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gofynd/fp-signature/node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
|
|
@ -80,6 +208,16 @@
|
|||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@so-ric/colorspace": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
|
||||
"integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color": "^5.0.2",
|
||||
"text-hex": "1.0.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.19.130",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
|
||||
|
|
@ -115,6 +253,12 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/triple-beam": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
|
||||
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
|
|
@ -178,6 +322,12 @@
|
|||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
|
@ -376,6 +526,52 @@
|
|||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
|
||||
"integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^3.1.3",
|
||||
"color-string": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
|
||||
"integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
|
||||
"integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
}
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
|
||||
"integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
|
@ -433,6 +629,25 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
|
||||
"integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "0.7.2",
|
||||
"cookie-signature": "1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser/node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
||||
|
|
@ -546,6 +761,12 @@
|
|||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/enabled": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
|
||||
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
|
|
@ -670,6 +891,12 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/fecha": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
||||
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
|
|
@ -710,6 +937,12 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fn.name": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
|
|
@ -1025,6 +1258,18 @@
|
|||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
|
|
@ -1044,6 +1289,54 @@
|
|||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/kuler": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/logform": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
|
||||
"integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@colors/colors": "1.6.0",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"fecha": "^4.2.0",
|
||||
"ms": "^2.1.1",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"triple-beam": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/logform/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loglevel": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
|
||||
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/loglevel"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
|
|
@ -1321,6 +1614,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/one-time": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
|
||||
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fn.name": "1.x.x"
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "4.104.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz",
|
||||
|
|
@ -1444,6 +1746,16 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/querystring": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz",
|
||||
"integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==",
|
||||
"deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
|
|
@ -1522,6 +1834,15 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
|
|
@ -1686,6 +2007,15 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/stack-trace": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
|
|
@ -1740,6 +2070,12 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/text-hex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
||||
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
|
@ -1778,6 +2114,15 @@
|
|||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/triple-beam": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
|
||||
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
|
|
@ -1819,6 +2164,12 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
|
@ -1847,6 +2198,15 @@
|
|||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.26",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz",
|
||||
"integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
|
@ -1881,6 +2241,70 @@
|
|||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston": {
|
||||
"version": "3.19.0",
|
||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
|
||||
"integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@colors/colors": "^1.6.0",
|
||||
"@dabh/diagnostics": "^2.0.8",
|
||||
"async": "^3.2.3",
|
||||
"is-stream": "^2.0.0",
|
||||
"logform": "^2.7.0",
|
||||
"one-time": "^1.0.0",
|
||||
"readable-stream": "^3.4.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"stack-trace": "0.0.x",
|
||||
"triple-beam": "^1.3.0",
|
||||
"winston-transport": "^4.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-transport": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
|
||||
"integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"logform": "^2.7.0",
|
||||
"readable-stream": "^3.6.2",
|
||||
"triple-beam": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-transport/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/winston/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
"dev": "nodemon index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gofynd/fdk-extension-javascript": "^1.1.5-beta.1",
|
||||
"@pixelbin/admin": "^2.0.0",
|
||||
"axios": "^1.6.8",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const {
|
|||
deleteBusinessFiles,
|
||||
} = require('../services/pixelbin');
|
||||
const DEFAULT_EVENTS = require('../config/defaultEvents');
|
||||
const { getPlatformClientForCompany } = require('../fdk');
|
||||
const axios = require('axios');
|
||||
|
||||
const MERCHANT_ID = () => process.env.MERCHANT_ID;
|
||||
|
|
@ -25,7 +26,10 @@ function normalizeScopeId(value) {
|
|||
|
||||
function getCompanyId(req) {
|
||||
return normalizeScopeId(
|
||||
req.get('x-company-id')
|
||||
req.fdkSession?.company_id
|
||||
|| req.get('x-company-id')
|
||||
|| req.params?.companyId
|
||||
|| req.params?.company_id
|
||||
|| req.query?.companyId
|
||||
|| req.query?.company_id
|
||||
|| req.body?.companyId
|
||||
|
|
@ -37,6 +41,7 @@ function getCompanyId(req) {
|
|||
function getApplicationId(req) {
|
||||
return normalizeScopeId(
|
||||
req.get('x-application-id')
|
||||
|| req.body?.salesChannelId
|
||||
|| req.query?.applicationId
|
||||
|| req.query?.application_id
|
||||
|| req.body?.applicationId
|
||||
|
|
@ -197,6 +202,135 @@ function normalizeProvider(provider = {}, fallbackUpdatedAt = null) {
|
|||
};
|
||||
}
|
||||
|
||||
function normalizeWebsiteUrl(value) {
|
||||
const rawValue = normalizeText(value);
|
||||
if (!rawValue) return '';
|
||||
|
||||
const candidate = /^https?:\/\//i.test(rawValue) ? rawValue : `https://${rawValue}`;
|
||||
|
||||
try {
|
||||
const url = new URL(candidate);
|
||||
return url.toString().replace(/\/$/, '');
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function extractDomainName(domain) {
|
||||
if (!domain) return '';
|
||||
if (typeof domain === 'string') return normalizeText(domain);
|
||||
if (typeof domain === 'object') {
|
||||
return firstNonEmptyText(domain.name, domain.domain_url, domain.url, domain.host);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function rankDomain(domain) {
|
||||
if (!domain || typeof domain !== 'object') return 0;
|
||||
let score = 0;
|
||||
if (domain.is_primary) score += 4;
|
||||
if (domain.verified) score += 2;
|
||||
if (!domain.is_shortlink) score += 1;
|
||||
return score;
|
||||
}
|
||||
|
||||
function pickPreferredDomain(...sources) {
|
||||
const candidates = [];
|
||||
|
||||
for (const source of sources) {
|
||||
if (!source) continue;
|
||||
if (Array.isArray(source)) {
|
||||
for (const item of source) {
|
||||
const name = extractDomainName(item);
|
||||
if (name) candidates.push({ raw: item, name });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = extractDomainName(source);
|
||||
if (name) candidates.push({ raw: source, name });
|
||||
}
|
||||
|
||||
if (!candidates.length) return '';
|
||||
|
||||
candidates.sort((left, right) => rankDomain(right.raw) - rankDomain(left.raw));
|
||||
return candidates[0].name;
|
||||
}
|
||||
|
||||
function normalizeSalesChannel(application = {}, domains = []) {
|
||||
const domainName = pickPreferredDomain(application.domain, application.domains, domains);
|
||||
const id = normalizeScopeId(application._id || application.id || application.application_id);
|
||||
|
||||
return {
|
||||
id,
|
||||
salesChannelId: id,
|
||||
applicationId: id,
|
||||
name: firstNonEmptyText(application.name, application.display_name, application.slug),
|
||||
domain: domainName,
|
||||
websiteUrl: normalizeWebsiteUrl(domainName),
|
||||
isActive: application.is_active !== false,
|
||||
logoUrl: firstNonEmptyText(
|
||||
application.logo?.secure_url,
|
||||
application.logo?.url,
|
||||
application.logo,
|
||||
application.favicon?.secure_url,
|
||||
application.favicon?.url
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async function getPlatformClient(req, companyId) {
|
||||
if (req?.platformClient) return req.platformClient;
|
||||
return getPlatformClientForCompany(companyId);
|
||||
}
|
||||
|
||||
async function listAllSalesChannels(platformClient, query = '') {
|
||||
const items = [];
|
||||
const pageSize = 100;
|
||||
let pageNo = 1;
|
||||
|
||||
while (true) {
|
||||
const response = await platformClient.configuration.getApplications({
|
||||
pageNo,
|
||||
pageSize,
|
||||
q: normalizeText(query) || undefined,
|
||||
});
|
||||
|
||||
const batch = Array.isArray(response?.items) ? response.items : [];
|
||||
items.push(...batch);
|
||||
|
||||
const totalPages = Number(response?.page?.total_page) || Number(response?.page?.total_pages) || 0;
|
||||
if (!batch.length || batch.length < pageSize || (totalPages && pageNo >= totalPages)) {
|
||||
break;
|
||||
}
|
||||
|
||||
pageNo += 1;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
async function getSalesChannelDetails(platformClient, salesChannelId) {
|
||||
const applicationClient = platformClient.application(salesChannelId).configuration;
|
||||
|
||||
const [applicationResponse, domainsResponse] = await Promise.allSettled([
|
||||
applicationClient.getApplicationById(),
|
||||
applicationClient.getDomains(),
|
||||
]);
|
||||
|
||||
if (applicationResponse.status !== 'fulfilled' && domainsResponse.status !== 'fulfilled') {
|
||||
const error = applicationResponse.reason || domainsResponse.reason || new Error('Unable to fetch sales channel details');
|
||||
throw error;
|
||||
}
|
||||
|
||||
const application = applicationResponse.status === 'fulfilled'
|
||||
? applicationResponse.value
|
||||
: { _id: salesChannelId };
|
||||
const domains = domainsResponse.status === 'fulfilled' ? domainsResponse.value?.domains || [] : [];
|
||||
|
||||
return normalizeSalesChannel(application, domains);
|
||||
}
|
||||
|
||||
const LEGACY_DEFAULT_EVENT_SLUGS = new Set(['confirmed', 'pack', 'cancelled']);
|
||||
const EVENT_TEMPLATE_FALLBACKS = {
|
||||
bag_confirmed: ['confirmed'],
|
||||
|
|
@ -754,14 +888,91 @@ router.get('/', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// POST /api/businesses — create new business from websiteUrl
|
||||
// GET /api/businesses/sales-channels
|
||||
router.get('/sales-channels', async (req, res) => {
|
||||
try {
|
||||
const companyId = getCompanyId(req);
|
||||
if (!companyId) {
|
||||
throw createHttpError(400, 'companyId is required');
|
||||
}
|
||||
|
||||
const platformClient = await getPlatformClient(req, companyId);
|
||||
const applications = await listAllSalesChannels(platformClient);
|
||||
const salesChannels = applications
|
||||
.map((application) => normalizeSalesChannel(application))
|
||||
.filter((channel) => channel.id && channel.name)
|
||||
.sort((left, right) => left.name.localeCompare(right.name));
|
||||
|
||||
res.json({ salesChannels });
|
||||
} catch (err) {
|
||||
if (err.message === 'FDK is not configured') {
|
||||
return res.status(503).json({
|
||||
error: 'Sales channel fetch is unavailable',
|
||||
code: 'SALES_CHANNEL_FETCH_UNAVAILABLE',
|
||||
});
|
||||
}
|
||||
|
||||
sendRouteError(res, createHttpError(
|
||||
err.status || 502,
|
||||
err.message || 'Failed to fetch sales channels',
|
||||
err.code ? { code: err.code, details: err.details } : {}
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/businesses — create new business from sales channel or websiteUrl fallback
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { websiteUrl } = req.body;
|
||||
if (!websiteUrl) return res.status(400).json({ error: 'websiteUrl is required' });
|
||||
|
||||
const merchantId = getCompanyId(req);
|
||||
const applicationId = getApplicationId(req);
|
||||
if (!merchantId) {
|
||||
throw createHttpError(400, 'companyId is required');
|
||||
}
|
||||
|
||||
const requestedSalesChannelId = normalizeScopeId(
|
||||
req.body?.salesChannelId
|
||||
|| req.body?.applicationId
|
||||
|| req.body?.application_id
|
||||
);
|
||||
let websiteUrl = normalizeWebsiteUrl(req.body?.websiteUrl);
|
||||
let salesChannel = null;
|
||||
|
||||
if (!requestedSalesChannelId && !websiteUrl) {
|
||||
throw createHttpError(
|
||||
400,
|
||||
'Either salesChannelId or websiteUrl is required',
|
||||
{ code: 'MISSING_BUSINESS_SOURCE' }
|
||||
);
|
||||
}
|
||||
|
||||
if (requestedSalesChannelId) {
|
||||
try {
|
||||
const platformClient = await getPlatformClient(req, merchantId);
|
||||
salesChannel = await getSalesChannelDetails(platformClient, requestedSalesChannelId);
|
||||
} catch (error) {
|
||||
if (!websiteUrl) {
|
||||
throw createHttpError(
|
||||
error.message === 'FDK is not configured' ? 503 : 502,
|
||||
'Unable to fetch sales channel details',
|
||||
{
|
||||
code: 'SALES_CHANNEL_DETAILS_UNAVAILABLE',
|
||||
details: { salesChannelId: requestedSalesChannelId, reason: error.message },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
websiteUrl = websiteUrl || salesChannel?.websiteUrl || '';
|
||||
}
|
||||
|
||||
if (!websiteUrl) {
|
||||
throw createHttpError(
|
||||
422,
|
||||
'A website URL could not be derived from the selected sales channel. Please enter it manually.',
|
||||
{ code: 'MISSING_SALES_CHANNEL_WEBSITE' }
|
||||
);
|
||||
}
|
||||
|
||||
const applicationId = requestedSalesChannelId || getApplicationId(req);
|
||||
const businesses = await getIndex(merchantId);
|
||||
|
||||
if (applicationId && businesses.some((business) => normalizeScopeId(business.applicationId) === applicationId)) {
|
||||
|
|
@ -820,10 +1031,17 @@ router.post('/', async (req, res) => {
|
|||
});
|
||||
await saveIndex(merchantId, businesses);
|
||||
|
||||
res.json(contextJson);
|
||||
res.json({
|
||||
...contextJson,
|
||||
salesChannel: salesChannel ? {
|
||||
salesChannelId: salesChannel.id,
|
||||
name: salesChannel.name,
|
||||
domain: salesChannel.domain,
|
||||
} : null,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Create business error:', err.message);
|
||||
res.status(500).json({ error: err.message });
|
||||
sendRouteError(res, err);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user