@marvalt/digivalt-core 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +32 -4
- package/bin/init.cjs +42 -7
- package/dist/config.cjs +520 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.ts +307 -0
- package/dist/config.esm.js +502 -0
- package/dist/config.esm.js.map +1 -0
- package/dist/generators.cjs +2481 -0
- package/dist/generators.cjs.map +1 -0
- package/dist/generators.d.ts +19 -0
- package/dist/generators.esm.js +2475 -0
- package/dist/generators.esm.js.map +1 -0
- package/dist/index.cjs +18 -7955
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +18 -1196
- package/dist/index.esm.js +13 -7929
- package/dist/index.esm.js.map +1 -1
- package/dist/runtime/env.d.ts +4 -0
- package/dist/runtime/lazy.d.ts +1 -0
- package/dist/services/cf-wp-webhook.d.ts +2 -0
- package/dist/services/gravityForms.d.ts +3 -0
- package/dist/services/mautic.d.ts +5 -1
- package/dist/services/suitecrm.d.ts +2 -0
- package/dist/services.cjs +1339 -0
- package/dist/services.cjs.map +1 -0
- package/dist/services.d.ts +432 -0
- package/dist/services.esm.js +1322 -0
- package/dist/services.esm.js.map +1 -0
- package/dist/static/index.d.ts +4 -0
- package/dist/static.cjs +997 -0
- package/dist/static.cjs.map +1 -0
- package/dist/static.d.ts +410 -0
- package/dist/static.esm.js +962 -0
- package/dist/static.esm.js.map +1 -0
- package/dist/types.cjs +3 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.esm.js +2 -0
- package/dist/types.esm.js.map +1 -0
- package/package.json +30 -2
- package/template/DIGIVALT_SETUP.md +62 -0
- package/template/scripts/deploy-secrets.js +55 -23
- package/template/scripts/generate.ts +19 -0
|
@@ -0,0 +1,1322 @@
|
|
|
1
|
+
import { GravityFormsClient } from '@marvalt/wadapter';
|
|
2
|
+
import { MauticClient } from '@marvalt/madapter';
|
|
3
|
+
|
|
4
|
+
const getImportMetaEnv = () => {
|
|
5
|
+
try {
|
|
6
|
+
return typeof import.meta !== 'undefined' ? import.meta.env : undefined;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const getEnv = (key) => {
|
|
13
|
+
const viteEnv = getImportMetaEnv();
|
|
14
|
+
if (viteEnv && key in viteEnv) {
|
|
15
|
+
const value = viteEnv[key];
|
|
16
|
+
return value == null ? undefined : String(value);
|
|
17
|
+
}
|
|
18
|
+
if (typeof process !== 'undefined' && process.env && key in process.env) {
|
|
19
|
+
const value = process.env[key];
|
|
20
|
+
return value == null ? undefined : String(value);
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
};
|
|
24
|
+
const isDevEnvironment = () => {
|
|
25
|
+
const viteEnv = getImportMetaEnv();
|
|
26
|
+
if (viteEnv && 'DEV' in viteEnv) {
|
|
27
|
+
return Boolean(viteEnv.DEV);
|
|
28
|
+
}
|
|
29
|
+
return typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
|
|
30
|
+
};
|
|
31
|
+
const isLocalDevelopmentEnabled = () => {
|
|
32
|
+
return getEnv('VITE_LOCAL_DEVELOPMENT') === 'true';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const createLazyProxy = (factory) => new Proxy({}, {
|
|
36
|
+
get(_target, prop, receiver) {
|
|
37
|
+
const instance = factory();
|
|
38
|
+
const value = Reflect.get(instance, prop, receiver);
|
|
39
|
+
return typeof value === 'function' ? value.bind(instance) : value;
|
|
40
|
+
},
|
|
41
|
+
set(_target, prop, value, receiver) {
|
|
42
|
+
const instance = factory();
|
|
43
|
+
return Reflect.set(instance, prop, value, receiver);
|
|
44
|
+
},
|
|
45
|
+
has(_target, prop) {
|
|
46
|
+
const instance = factory();
|
|
47
|
+
return prop in instance;
|
|
48
|
+
},
|
|
49
|
+
ownKeys() {
|
|
50
|
+
const instance = factory();
|
|
51
|
+
return Reflect.ownKeys(instance);
|
|
52
|
+
},
|
|
53
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
54
|
+
const instance = factory();
|
|
55
|
+
return Object.getOwnPropertyDescriptor(instance, prop);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gravity Forms Integration Service
|
|
61
|
+
* Uses @marvalt/wadapter package for secure form submissions
|
|
62
|
+
*/
|
|
63
|
+
// Convention-based: Uses /api/gravity-forms-submit by default.
|
|
64
|
+
let gravityFormsClientInstance = null;
|
|
65
|
+
const createGravityFormsClient = () => {
|
|
66
|
+
const config = {
|
|
67
|
+
apiUrl: getEnv('VITE_WORDPRESS_API_URL'),
|
|
68
|
+
authMode: 'direct',
|
|
69
|
+
timeout: 30000,
|
|
70
|
+
retries: 3,
|
|
71
|
+
};
|
|
72
|
+
return new GravityFormsClient(config);
|
|
73
|
+
};
|
|
74
|
+
const getGravityFormsClient = () => {
|
|
75
|
+
if (!gravityFormsClientInstance) {
|
|
76
|
+
gravityFormsClientInstance = createGravityFormsClient();
|
|
77
|
+
}
|
|
78
|
+
return gravityFormsClientInstance;
|
|
79
|
+
};
|
|
80
|
+
const resetGravityFormsClient = () => {
|
|
81
|
+
gravityFormsClientInstance = null;
|
|
82
|
+
};
|
|
83
|
+
const gravityFormsClient = createLazyProxy(getGravityFormsClient);
|
|
84
|
+
// Legacy service wrapper for backwards compatibility
|
|
85
|
+
const gravityFormsService = {
|
|
86
|
+
/**
|
|
87
|
+
* Submit a Gravity Form (legacy interface)
|
|
88
|
+
* @param formId - Form ID as string or number
|
|
89
|
+
* @param entry - Entry data with field IDs as keys
|
|
90
|
+
* @param files - Optional files (not yet supported)
|
|
91
|
+
* @param useBasicApi - Ignored (always uses proxy)
|
|
92
|
+
*/
|
|
93
|
+
async submitForm(formId, entry, files, useBasicApi) {
|
|
94
|
+
console.log('🔍 gravityFormsService.submitForm (legacy wrapper) called:', {
|
|
95
|
+
formId,
|
|
96
|
+
entry,
|
|
97
|
+
hasFiles: !!files,
|
|
98
|
+
useBasicApi,
|
|
99
|
+
});
|
|
100
|
+
if (files && Object.keys(files).length > 0) {
|
|
101
|
+
console.warn('⚠️ File uploads not yet supported in new Gravity Forms client');
|
|
102
|
+
}
|
|
103
|
+
// Convert to new API format
|
|
104
|
+
const formIdNumber = typeof formId === 'string' ? parseInt(formId, 10) : formId;
|
|
105
|
+
return getGravityFormsClient().submitForm(formIdNumber, {
|
|
106
|
+
form_id: formIdNumber,
|
|
107
|
+
field_values: entry,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
// Pass through other methods
|
|
111
|
+
getForm: (id) => getGravityFormsClient().getForm(id),
|
|
112
|
+
getForms: () => getGravityFormsClient().getForms(),
|
|
113
|
+
getFormConfig: (id) => getGravityFormsClient().getFormConfig(id),
|
|
114
|
+
getHealth: () => getGravityFormsClient().getHealth(),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Mautic Integration Service (via @marvalt/madapter)
|
|
119
|
+
*/
|
|
120
|
+
// @ts-ignore - internal workspace type resolution
|
|
121
|
+
// Convention-based: Uses /api/mautic-submit Pages Function for server-side OAuth2
|
|
122
|
+
// The Pages Function handles authentication securely without exposing credentials to the browser.
|
|
123
|
+
let mauticServiceInstance = null;
|
|
124
|
+
const createMauticService = () => {
|
|
125
|
+
const useWranglerProxy = isDevEnvironment() && isLocalDevelopmentEnabled();
|
|
126
|
+
return new MauticClient({
|
|
127
|
+
apiUrl: getEnv('VITE_MAUTIC_URL'),
|
|
128
|
+
proxyEndpoint: useWranglerProxy
|
|
129
|
+
? 'http://127.0.0.1:8788/api/mautic-submit'
|
|
130
|
+
: '/api/mautic-submit',
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
const getMauticService = () => {
|
|
134
|
+
if (!mauticServiceInstance) {
|
|
135
|
+
mauticServiceInstance = createMauticService();
|
|
136
|
+
}
|
|
137
|
+
return mauticServiceInstance;
|
|
138
|
+
};
|
|
139
|
+
const resetMauticService = () => {
|
|
140
|
+
mauticServiceInstance = null;
|
|
141
|
+
};
|
|
142
|
+
const mauticService = createLazyProxy(getMauticService);
|
|
143
|
+
|
|
144
|
+
// Detect if we're running in a local development environment
|
|
145
|
+
const isLocalDevelopment = () => {
|
|
146
|
+
// Check if we're in a browser environment
|
|
147
|
+
if (typeof window !== 'undefined') {
|
|
148
|
+
// Check for local development indicators
|
|
149
|
+
const isLocalhost = window.location.hostname === 'localhost' ||
|
|
150
|
+
window.location.hostname === '127.0.0.1' ||
|
|
151
|
+
window.location.hostname.includes('192.168.') ||
|
|
152
|
+
window.location.hostname.includes('10.0.');
|
|
153
|
+
const isDevPort = window.location.port === '3000' ||
|
|
154
|
+
window.location.port === '5173' ||
|
|
155
|
+
window.location.port === '8080';
|
|
156
|
+
const hasLocalEnv = Boolean(getEnv('DEV')) || getEnv('VITE_LOCAL_DEVELOPMENT') === 'true';
|
|
157
|
+
return Boolean(isLocalhost && (isDevPort || hasLocalEnv));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Node.js environment - check environment variables
|
|
161
|
+
return getEnv('NODE_ENV') === 'development' ||
|
|
162
|
+
getEnv('VITE_LOCAL_DEVELOPMENT') === 'true';
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
// Get the appropriate environment configuration
|
|
166
|
+
const getEnvironmentConfig = () => {
|
|
167
|
+
const isLocal = isLocalDevelopment();
|
|
168
|
+
// Default to cloudflare_proxy (via Cloudflare Pages Functions)
|
|
169
|
+
// Use 'direct' only for local development with whitelisted IPs
|
|
170
|
+
const authModeRaw = getEnv('VITE_AUTH_MODE');
|
|
171
|
+
const authMode = authModeRaw || 'cloudflare_proxy';
|
|
172
|
+
// Reduced debug logging - only in development and only once
|
|
173
|
+
if (isDevEnvironment() && !authModeRaw) {
|
|
174
|
+
console.log('ℹ️ VITE_AUTH_MODE not set, defaulting to cloudflare_proxy (Cloudflare Pages Functions)');
|
|
175
|
+
}
|
|
176
|
+
// Validate auth mode value
|
|
177
|
+
if (authMode !== 'direct' && authMode !== 'cloudflare_proxy') {
|
|
178
|
+
console.error('❌ Invalid VITE_AUTH_MODE value. Must be "direct" or "cloudflare_proxy".');
|
|
179
|
+
return {
|
|
180
|
+
mode: 'error',
|
|
181
|
+
authMode: 'error',
|
|
182
|
+
dataSource: 'static',
|
|
183
|
+
baseUrl: '',
|
|
184
|
+
headers: {},
|
|
185
|
+
description: `Invalid configuration - VITE_AUTH_MODE="${authModeRaw}"`,
|
|
186
|
+
suiteCRM: {
|
|
187
|
+
url: undefined,
|
|
188
|
+
proxyUrl: undefined,
|
|
189
|
+
appId: undefined,
|
|
190
|
+
workerSecret: undefined,
|
|
191
|
+
oauth2: {
|
|
192
|
+
clientId: undefined,
|
|
193
|
+
clientSecret: undefined,
|
|
194
|
+
tokenUrl: undefined,
|
|
195
|
+
username: undefined,
|
|
196
|
+
password: undefined,
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Cloudflare proxy mode (uses Cloudflare Pages Functions by default)
|
|
202
|
+
// Worker URL is optional - Pages Functions handle routing automatically
|
|
203
|
+
if (authMode === 'cloudflare_proxy') {
|
|
204
|
+
const workerUrl = getEnv('VITE_CLOUDFLARE_WORKER_URL');
|
|
205
|
+
// Cloudflare Pages Functions work without explicit worker URL
|
|
206
|
+
// The worker URL is only needed for separate Cloudflare Workers
|
|
207
|
+
// Pages Functions automatically handle /api/* routes
|
|
208
|
+
const baseUrl = workerUrl || '/api';
|
|
209
|
+
return {
|
|
210
|
+
mode: isLocal ? 'local' : 'remote',
|
|
211
|
+
authMode: 'cloudflare_proxy',
|
|
212
|
+
dataSource: 'static',
|
|
213
|
+
baseUrl,
|
|
214
|
+
headers: {
|
|
215
|
+
'Content-Type': 'application/json'
|
|
216
|
+
},
|
|
217
|
+
description: workerUrl
|
|
218
|
+
? (isLocal ? 'Local development with Cloudflare Worker proxy' : 'Remote development with Cloudflare Worker proxy')
|
|
219
|
+
: (isLocal ? 'Local development with Cloudflare Pages Functions' : 'Production with Cloudflare Pages Functions'),
|
|
220
|
+
mautic: {
|
|
221
|
+
proxyUrl: getEnv('VITE_MAUTIC_PROXY_URL')
|
|
222
|
+
},
|
|
223
|
+
suiteCRM: {
|
|
224
|
+
url: getEnv('VITE_SUITECRM_URL'),
|
|
225
|
+
proxyUrl: getEnv('VITE_SUITECRM_PROXY_URL'),
|
|
226
|
+
appId: undefined,
|
|
227
|
+
workerSecret: undefined,
|
|
228
|
+
oauth2: {
|
|
229
|
+
clientId: getEnv('VITE_SUITECRM_CLIENT_ID'),
|
|
230
|
+
clientSecret: getEnv('VITE_SUITECRM_CLIENT_SECRET'),
|
|
231
|
+
tokenUrl: getEnv('VITE_SUITECRM_TOKEN_URL'),
|
|
232
|
+
username: getEnv('VITE_SUITECRM_USERNAME'),
|
|
233
|
+
password: getEnv('VITE_SUITECRM_PASSWORD'),
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
// Direct WordPress mode (legacy - for local development only)
|
|
239
|
+
if (authMode === 'direct') {
|
|
240
|
+
const wpApiUrl = getEnv('VITE_WORDPRESS_API_URL');
|
|
241
|
+
if (!wpApiUrl) {
|
|
242
|
+
console.warn('⚠️ Direct mode requires: VITE_WORDPRESS_API_URL');
|
|
243
|
+
return {
|
|
244
|
+
mode: 'error',
|
|
245
|
+
authMode: 'error',
|
|
246
|
+
dataSource: 'static',
|
|
247
|
+
baseUrl: '',
|
|
248
|
+
headers: {},
|
|
249
|
+
description: 'Invalid configuration - missing WordPress API URL',
|
|
250
|
+
suiteCRM: {
|
|
251
|
+
url: undefined,
|
|
252
|
+
proxyUrl: undefined,
|
|
253
|
+
appId: undefined,
|
|
254
|
+
workerSecret: undefined,
|
|
255
|
+
oauth2: {
|
|
256
|
+
clientId: undefined,
|
|
257
|
+
clientSecret: undefined,
|
|
258
|
+
tokenUrl: undefined,
|
|
259
|
+
username: undefined,
|
|
260
|
+
password: undefined,
|
|
261
|
+
},
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (isDevEnvironment()) {
|
|
266
|
+
console.log('🔍 Using direct WordPress mode (legacy - for local development only)');
|
|
267
|
+
}
|
|
268
|
+
const localDev = getEnv('VITE_LOCAL_DEVELOPMENT') === 'true';
|
|
269
|
+
return {
|
|
270
|
+
mode: localDev ? 'local' : 'remote',
|
|
271
|
+
authMode: 'direct',
|
|
272
|
+
dataSource: 'hybrid',
|
|
273
|
+
baseUrl: wpApiUrl,
|
|
274
|
+
headers: { 'Content-Type': 'application/json' },
|
|
275
|
+
description: localDev ? 'Direct (local dev) without Access' : 'Direct mode',
|
|
276
|
+
mautic: { url: getEnv('VITE_MAUTIC_URL') },
|
|
277
|
+
suiteCRM: {
|
|
278
|
+
url: getEnv('VITE_SUITECRM_URL'),
|
|
279
|
+
proxyUrl: getEnv('VITE_SUITECRM_PROXY_URL'),
|
|
280
|
+
appId: undefined,
|
|
281
|
+
workerSecret: undefined,
|
|
282
|
+
oauth2: {
|
|
283
|
+
clientId: getEnv('VITE_SUITECRM_CLIENT_ID'),
|
|
284
|
+
clientSecret: getEnv('VITE_SUITECRM_CLIENT_SECRET'),
|
|
285
|
+
tokenUrl: getEnv('VITE_SUITECRM_TOKEN_URL'),
|
|
286
|
+
username: getEnv('VITE_SUITECRM_USERNAME'),
|
|
287
|
+
password: getEnv('VITE_SUITECRM_PASSWORD'),
|
|
288
|
+
},
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
// This should never be reached, but just in case
|
|
293
|
+
return {
|
|
294
|
+
mode: 'error',
|
|
295
|
+
authMode: 'error',
|
|
296
|
+
dataSource: 'static',
|
|
297
|
+
baseUrl: '',
|
|
298
|
+
headers: {},
|
|
299
|
+
description: 'Invalid configuration - unknown error',
|
|
300
|
+
suiteCRM: {
|
|
301
|
+
url: undefined,
|
|
302
|
+
proxyUrl: undefined,
|
|
303
|
+
appId: undefined,
|
|
304
|
+
workerSecret: undefined,
|
|
305
|
+
oauth2: {
|
|
306
|
+
clientId: undefined,
|
|
307
|
+
clientSecret: undefined,
|
|
308
|
+
tokenUrl: undefined,
|
|
309
|
+
username: undefined,
|
|
310
|
+
password: undefined,
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
// Environment status and diagnostics
|
|
316
|
+
const getEnvironmentStatus = () => {
|
|
317
|
+
const config = getEnvironmentConfig();
|
|
318
|
+
const isLocal = isLocalDevelopment();
|
|
319
|
+
const authMode = getEnv('VITE_AUTH_MODE');
|
|
320
|
+
// Check if required credentials are available for each mode
|
|
321
|
+
const hasCloudflareCredentials = !!getEnv('VITE_CLOUDFLARE_WORKER_URL');
|
|
322
|
+
const hasWordPressApiUrl = !!getEnv('VITE_WORDPRESS_API_URL');
|
|
323
|
+
return {
|
|
324
|
+
currentConfig: config,
|
|
325
|
+
diagnostics: {
|
|
326
|
+
isLocalDevelopment: isLocal,
|
|
327
|
+
authMode: authMode,
|
|
328
|
+
hasCloudflareCredentials,
|
|
329
|
+
hasWordpressApiUrl: hasWordPressApiUrl,
|
|
330
|
+
cloudflareWorkerUrl: !!getEnv('VITE_CLOUDFLARE_WORKER_URL'),
|
|
331
|
+
cfAccessId: !!getEnv('VITE_CF_ACCESS_CLIENT_ID'),
|
|
332
|
+
cfAccessSecret: !!getEnv('VITE_CF_ACCESS_CLIENT_SECRET'),
|
|
333
|
+
mauticProxyUrl: !!getEnv('VITE_MAUTIC_PROXY_URL')
|
|
334
|
+
},
|
|
335
|
+
recommendations: {
|
|
336
|
+
// Only show recommendation if authMode is explicitly set to an invalid value
|
|
337
|
+
// Default is cloudflare_proxy, so no recommendation needed if not set
|
|
338
|
+
authMode: null, // Defaults to cloudflare_proxy, no warning needed
|
|
339
|
+
cloudflare: null // Pages Functions work without explicit worker URL
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
// Export current environment config as default
|
|
344
|
+
getEnvironmentConfig();
|
|
345
|
+
getEnvironmentStatus();
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Integration Configuration
|
|
349
|
+
* Centralized configuration for all third-party integrations
|
|
350
|
+
*/
|
|
351
|
+
/**
|
|
352
|
+
* Get integration configuration with environment variable fallbacks
|
|
353
|
+
*/
|
|
354
|
+
function getIntegrationConfig() {
|
|
355
|
+
isDevEnvironment();
|
|
356
|
+
return {
|
|
357
|
+
wordpress: {
|
|
358
|
+
enabled: true,
|
|
359
|
+
apiUrl: getEnv('VITE_WORDPRESS_API_URL') || '',
|
|
360
|
+
authMode: getEnv('VITE_AUTH_MODE') || 'direct',
|
|
361
|
+
},
|
|
362
|
+
supabase: {
|
|
363
|
+
enabled: getEnv('VITE_AUTH_MODE') === 'supabase_proxy',
|
|
364
|
+
url: getEnv('VITE_SUPABASE_URL') || '',
|
|
365
|
+
anonKey: getEnv('VITE_SUPABASE_ANON_KEY') || '',
|
|
366
|
+
serviceRoleKey: getEnv('VITE_SUPABASE_SERVICE_ROLE_KEY'),
|
|
367
|
+
},
|
|
368
|
+
gravityForms: {
|
|
369
|
+
enabled: !!(getEnv('VITE_CLOUDFLARE_WORKER_URL') || getEnv('VITE_WORDPRESS_API_URL')),
|
|
370
|
+
url: getEnv('VITE_WORDPRESS_API_URL'),
|
|
371
|
+
proxyUrl: getEnv('VITE_CLOUDFLARE_WORKER_URL'),
|
|
372
|
+
consumerKey: getEnv('VITE_GF_CONSUMER_KEY'),
|
|
373
|
+
consumerSecret: getEnv('VITE_GF_CONSUMER_SECRET'),
|
|
374
|
+
forms: {
|
|
375
|
+
'contact': {
|
|
376
|
+
name: 'Contact Form',
|
|
377
|
+
type: 'contact',
|
|
378
|
+
successMessage: 'Thank you for your message. We\'ll get back to you soon!',
|
|
379
|
+
},
|
|
380
|
+
'newsletter': {
|
|
381
|
+
name: 'Newsletter Signup',
|
|
382
|
+
type: 'newsletter',
|
|
383
|
+
successMessage: 'Thank you for subscribing to our newsletter!',
|
|
384
|
+
},
|
|
385
|
+
'support': {
|
|
386
|
+
name: 'Support Request',
|
|
387
|
+
type: 'support',
|
|
388
|
+
successMessage: 'Your support request has been submitted successfully.',
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
mautic: {
|
|
393
|
+
enabled: !!(getEnv('VITE_MAUTIC_PROXY_URL') || getEnv('VITE_MAUTIC_URL')),
|
|
394
|
+
url: getEnv('VITE_MAUTIC_URL') || '',
|
|
395
|
+
proxyUrl: getEnv('VITE_MAUTIC_PROXY_URL'),
|
|
396
|
+
forms: {
|
|
397
|
+
'1': {
|
|
398
|
+
name: 'Newsletter Signup',
|
|
399
|
+
type: 'newsletter',
|
|
400
|
+
successMessage: 'Thank you for subscribing to our newsletter!',
|
|
401
|
+
postAction: 'message',
|
|
402
|
+
postActionProperty: 'Thank you for your submission!'
|
|
403
|
+
},
|
|
404
|
+
'2': {
|
|
405
|
+
name: 'Contact Form',
|
|
406
|
+
type: 'contact',
|
|
407
|
+
successMessage: 'Thank you for contacting us! We\'ll get back to you soon.',
|
|
408
|
+
postAction: 'message',
|
|
409
|
+
postActionProperty: 'Thank you for your message!'
|
|
410
|
+
},
|
|
411
|
+
'3': {
|
|
412
|
+
name: 'Lead Capture',
|
|
413
|
+
type: 'lead',
|
|
414
|
+
successMessage: 'Thank you for your interest! We\'ll be in touch.',
|
|
415
|
+
postAction: 'message',
|
|
416
|
+
postActionProperty: 'Thank you for your submission!'
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
suiteCRM: {
|
|
421
|
+
enabled: !!(getEnv('VITE_SUITECRM_URL') && getEnv('VITE_SUITECRM_CLIENT_ID') && getEnv('VITE_SUITECRM_CLIENT_SECRET')),
|
|
422
|
+
url: getEnv('VITE_SUITECRM_URL'),
|
|
423
|
+
proxyUrl: getEnv('VITE_SUITECRM_PROXY_URL'),
|
|
424
|
+
appId: undefined,
|
|
425
|
+
workerSecret: undefined,
|
|
426
|
+
oauth2: {
|
|
427
|
+
clientId: getEnv('VITE_SUITECRM_CLIENT_ID'),
|
|
428
|
+
clientSecret: getEnv('VITE_SUITECRM_CLIENT_SECRET'),
|
|
429
|
+
username: getEnv('VITE_SUITECRM_USERNAME'),
|
|
430
|
+
password: getEnv('VITE_SUITECRM_PASSWORD'),
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
chatwoot: {
|
|
434
|
+
enabled: !!(getEnv('VITE_CHATWOOT_WEBSITE_TOKEN') && getEnv('VITE_CHATWOOT_BASE_URL')),
|
|
435
|
+
websiteToken: getEnv('VITE_CHATWOOT_WEBSITE_TOKEN') || '',
|
|
436
|
+
baseUrl: getEnv('VITE_CHATWOOT_BASE_URL') || '',
|
|
437
|
+
locale: 'en',
|
|
438
|
+
type: 'standard',
|
|
439
|
+
launcherTitle: 'Chat',
|
|
440
|
+
hideMessageBubble: false,
|
|
441
|
+
position: 'right',
|
|
442
|
+
showPopoutButton: false,
|
|
443
|
+
},
|
|
444
|
+
analytics: {
|
|
445
|
+
googleAnalytics: { enabled: false },
|
|
446
|
+
googleTagManager: { enabled: false },
|
|
447
|
+
facebookPixel: { enabled: false },
|
|
448
|
+
linkedinInsight: { enabled: false },
|
|
449
|
+
},
|
|
450
|
+
seo: {
|
|
451
|
+
defaultTitle: '',
|
|
452
|
+
defaultDescription: '',
|
|
453
|
+
defaultImage: '',
|
|
454
|
+
siteName: '',
|
|
455
|
+
siteUrl: '',
|
|
456
|
+
},
|
|
457
|
+
performance: {
|
|
458
|
+
enableLazyLoading: true,
|
|
459
|
+
enableImageOptimization: true,
|
|
460
|
+
enableCodeSplitting: true,
|
|
461
|
+
enableServiceWorker: false,
|
|
462
|
+
enableCaching: true,
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function getSuiteCRMConfig() {
|
|
467
|
+
return getIntegrationConfig().suiteCRM;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// @ts-nocheck
|
|
471
|
+
/**
|
|
472
|
+
* Password utility functions for handling special characters
|
|
473
|
+
*/
|
|
474
|
+
/**
|
|
475
|
+
* Safely encode a password for API requests
|
|
476
|
+
* Handles special characters like $, @, #, %, etc.
|
|
477
|
+
*/
|
|
478
|
+
function encodePassword(password) {
|
|
479
|
+
if (!password)
|
|
480
|
+
return '';
|
|
481
|
+
// For JSON requests, we don't need to encode the password
|
|
482
|
+
// The JSON.stringify() will handle special characters properly
|
|
483
|
+
return password;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Validate password for common issues
|
|
487
|
+
*/
|
|
488
|
+
function validatePassword(password) {
|
|
489
|
+
const issues = [];
|
|
490
|
+
if (!password) {
|
|
491
|
+
issues.push('Password is empty');
|
|
492
|
+
}
|
|
493
|
+
if (password.length < 1) {
|
|
494
|
+
issues.push('Password is too short');
|
|
495
|
+
}
|
|
496
|
+
// Check for potentially problematic characters
|
|
497
|
+
const problematicChars = ['$', '@', '#', '%', '&', '*', '(', ')', '[', ']', '{', '}', '|', '\\', '`', '~'];
|
|
498
|
+
const foundChars = problematicChars.filter(char => password.includes(char));
|
|
499
|
+
if (foundChars.length > 0) {
|
|
500
|
+
issues.push(`Password contains potentially problematic characters: ${foundChars.join(', ')}`);
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
isValid: issues.length === 0,
|
|
504
|
+
issues
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Get password encoding recommendations
|
|
509
|
+
*/
|
|
510
|
+
function getPasswordRecommendations(password) {
|
|
511
|
+
const recommendations = [];
|
|
512
|
+
if (password.includes('$')) {
|
|
513
|
+
recommendations.push('The $ character in passwords can cause issues in some contexts. Consider using a different character or ensure proper encoding.');
|
|
514
|
+
}
|
|
515
|
+
if (password.includes('@')) {
|
|
516
|
+
recommendations.push('The @ character might be interpreted as email syntax in some contexts.');
|
|
517
|
+
}
|
|
518
|
+
if (password.includes('#')) {
|
|
519
|
+
recommendations.push('The # character might be interpreted as a URL fragment in some contexts.');
|
|
520
|
+
}
|
|
521
|
+
if (password.includes('%')) {
|
|
522
|
+
recommendations.push('The % character might be interpreted as URL encoding in some contexts.');
|
|
523
|
+
}
|
|
524
|
+
if (password.length < 8) {
|
|
525
|
+
recommendations.push('Consider using a longer password for better security.');
|
|
526
|
+
}
|
|
527
|
+
return recommendations;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
class SuiteCRMService {
|
|
531
|
+
baseUrl;
|
|
532
|
+
proxyUrl;
|
|
533
|
+
appId;
|
|
534
|
+
workerSecret;
|
|
535
|
+
useProxy;
|
|
536
|
+
isConfigured;
|
|
537
|
+
oauth2Config;
|
|
538
|
+
accessToken;
|
|
539
|
+
tokenExpiresAt;
|
|
540
|
+
sessionId;
|
|
541
|
+
csrfToken;
|
|
542
|
+
constructor() {
|
|
543
|
+
// Get environment configuration
|
|
544
|
+
const envConfig = getEnvironmentConfig();
|
|
545
|
+
const suitecrm = getSuiteCRMConfig();
|
|
546
|
+
if (import.meta.env.DEV) {
|
|
547
|
+
console.log('🔍 SuiteCRM Service Auth Mode Debug:', {
|
|
548
|
+
authMode: envConfig.authMode,
|
|
549
|
+
hasSuiteCRMConfig: !!suitecrm,
|
|
550
|
+
hasEnvSuiteCRM: !!envConfig.suiteCRM,
|
|
551
|
+
suitecrmConfig: suitecrm ? {
|
|
552
|
+
hasUrl: !!suitecrm.url,
|
|
553
|
+
hasProxyUrl: !!suitecrm.proxyUrl,
|
|
554
|
+
hasAppId: !!suitecrm.appId,
|
|
555
|
+
hasWorkerSecret: !!suitecrm.workerSecret,
|
|
556
|
+
hasOAuth2: !!(suitecrm.oauth2?.clientId && suitecrm.oauth2?.clientSecret)
|
|
557
|
+
} : null,
|
|
558
|
+
envSuiteCRM: envConfig.suiteCRM ? {
|
|
559
|
+
hasUrl: !!envConfig.suiteCRM.url,
|
|
560
|
+
hasProxyUrl: !!envConfig.suiteCRM.proxyUrl,
|
|
561
|
+
hasAppId: !!envConfig.suiteCRM.appId,
|
|
562
|
+
hasWorkerSecret: !!envConfig.suiteCRM.workerSecret,
|
|
563
|
+
hasOAuth2: !!(envConfig.suiteCRM.oauth2?.clientId && envConfig.suiteCRM.oauth2?.clientSecret)
|
|
564
|
+
} : null
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
if (envConfig.authMode === 'cloudflare_proxy' && (suitecrm || envConfig.suiteCRM)) {
|
|
568
|
+
// Cloudflare Worker proxy (recommended - uses Secrets Store)
|
|
569
|
+
const config = suitecrm || envConfig.suiteCRM;
|
|
570
|
+
this.proxyUrl = config?.proxyUrl;
|
|
571
|
+
this.appId = undefined;
|
|
572
|
+
this.workerSecret = undefined;
|
|
573
|
+
this.useProxy = true;
|
|
574
|
+
this.isConfigured = !!this.proxyUrl;
|
|
575
|
+
if (import.meta.env.DEV) {
|
|
576
|
+
console.log('🔍 SuiteCRM Service: Using Cloudflare Worker proxy (Secrets Store)');
|
|
577
|
+
console.log('🔍 Constructor Debug:', {
|
|
578
|
+
proxyUrl: this.proxyUrl,
|
|
579
|
+
appId: this.appId,
|
|
580
|
+
hasWorkerSecret: !!this.workerSecret,
|
|
581
|
+
workerSecretLength: this.workerSecret?.length,
|
|
582
|
+
isConfigured: this.isConfigured
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
else if (envConfig.authMode === 'direct' && (suitecrm || envConfig.suiteCRM)) {
|
|
587
|
+
// Direct SuiteCRM API access with OAuth2
|
|
588
|
+
const config = suitecrm || envConfig.suiteCRM;
|
|
589
|
+
this.baseUrl = config?.url || '';
|
|
590
|
+
this.useProxy = false;
|
|
591
|
+
this.oauth2Config = config?.oauth2;
|
|
592
|
+
this.isConfigured = !!(this.baseUrl && config?.oauth2?.clientId && config?.oauth2?.clientSecret && config?.oauth2?.username && config?.oauth2?.password);
|
|
593
|
+
if (import.meta.env.DEV) {
|
|
594
|
+
console.log('🔍 SuiteCRM Service: Using direct SuiteCRM API access with OAuth2');
|
|
595
|
+
console.log('🔍 OAuth2 Config:', {
|
|
596
|
+
hasClientId: !!this.oauth2Config?.clientId,
|
|
597
|
+
hasClientSecret: !!this.oauth2Config?.clientSecret,
|
|
598
|
+
hasUsername: !!this.oauth2Config?.username,
|
|
599
|
+
hasPassword: !!this.oauth2Config?.password
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
// Fallback or error state
|
|
605
|
+
this.useProxy = false;
|
|
606
|
+
this.isConfigured = false;
|
|
607
|
+
if (import.meta.env.DEV) {
|
|
608
|
+
console.warn('⚠️ SuiteCRM Service: No valid configuration found');
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
validateConfiguration() {
|
|
613
|
+
if (!this.isConfigured) {
|
|
614
|
+
throw new Error('SuiteCRM service is not properly configured');
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async getSessionAndCSRFToken() {
|
|
618
|
+
// Check if we have valid session and CSRF token
|
|
619
|
+
if (this.sessionId && this.csrfToken) {
|
|
620
|
+
return { sessionId: this.sessionId, csrfToken: this.csrfToken };
|
|
621
|
+
}
|
|
622
|
+
if (import.meta.env.DEV) {
|
|
623
|
+
console.log('🔍 Starting session and CSRF token extraction...');
|
|
624
|
+
}
|
|
625
|
+
try {
|
|
626
|
+
// Step 1: Get initial session by accessing the login page
|
|
627
|
+
const loginResponse = await fetch(`${this.baseUrl}/`, {
|
|
628
|
+
method: 'GET',
|
|
629
|
+
headers: {
|
|
630
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
631
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
if (import.meta.env.DEV) {
|
|
635
|
+
console.log('📊 Login response status:', loginResponse.status);
|
|
636
|
+
console.log('📊 Login response ok:', loginResponse.ok);
|
|
637
|
+
}
|
|
638
|
+
if (!loginResponse.ok) {
|
|
639
|
+
throw new Error(`Failed to access SuiteCRM login page: ${loginResponse.status}`);
|
|
640
|
+
}
|
|
641
|
+
// Extract session ID from cookies
|
|
642
|
+
const cookies = loginResponse.headers.get('set-cookie');
|
|
643
|
+
if (import.meta.env.DEV) {
|
|
644
|
+
console.log('🍪 Set-Cookie header:', cookies);
|
|
645
|
+
}
|
|
646
|
+
if (cookies) {
|
|
647
|
+
// Try SCRMSESSID first (SuiteCRM specific)
|
|
648
|
+
const sessionMatch = cookies.match(/SCRMSESSID=([^;]+)/);
|
|
649
|
+
if (sessionMatch) {
|
|
650
|
+
this.sessionId = sessionMatch[1];
|
|
651
|
+
if (import.meta.env.DEV) {
|
|
652
|
+
console.log('✅ Found SCRMSESSID:', this.sessionId);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
if (import.meta.env.DEV) {
|
|
657
|
+
console.log('❌ SCRMSESSID not found in cookies');
|
|
658
|
+
}
|
|
659
|
+
// Fallback to PHPSESSID
|
|
660
|
+
const phpSessionMatch = cookies.match(/PHPSESSID=([^;]+)/);
|
|
661
|
+
if (phpSessionMatch) {
|
|
662
|
+
this.sessionId = phpSessionMatch[1];
|
|
663
|
+
if (import.meta.env.DEV) {
|
|
664
|
+
console.log('✅ Found PHPSESSID:', this.sessionId);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
if (import.meta.env.DEV) {
|
|
669
|
+
console.log('❌ PHPSESSID not found in cookies');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
if (import.meta.env.DEV) {
|
|
676
|
+
console.log('❌ No set-cookie header found');
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// Step 2: Extract CSRF token from cookies (XSRF-TOKEN)
|
|
680
|
+
if (cookies) {
|
|
681
|
+
const xsrfMatch = cookies.match(/XSRF-TOKEN=([^;]+)/);
|
|
682
|
+
if (xsrfMatch) {
|
|
683
|
+
this.csrfToken = xsrfMatch[1];
|
|
684
|
+
if (import.meta.env.DEV) {
|
|
685
|
+
console.log('✅ Found XSRF-TOKEN:', this.csrfToken);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
if (import.meta.env.DEV) {
|
|
690
|
+
console.log('❌ XSRF-TOKEN not found in cookies');
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// If not found in cookies, try HTML
|
|
695
|
+
if (!this.csrfToken) {
|
|
696
|
+
if (import.meta.env.DEV) {
|
|
697
|
+
console.log('🔍 Trying to extract CSRF token from HTML...');
|
|
698
|
+
}
|
|
699
|
+
const loginHtml = await loginResponse.text();
|
|
700
|
+
const csrfMatch = loginHtml.match(/name="csrf_token" value="([^"]+)"/);
|
|
701
|
+
if (csrfMatch) {
|
|
702
|
+
this.csrfToken = csrfMatch[1];
|
|
703
|
+
if (import.meta.env.DEV) {
|
|
704
|
+
console.log('✅ Found csrf_token in HTML:', this.csrfToken);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
if (import.meta.env.DEV) {
|
|
709
|
+
console.log('❌ csrf_token not found in HTML');
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (import.meta.env.DEV) {
|
|
714
|
+
console.log('📋 Final Summary:');
|
|
715
|
+
console.log('Session ID:', this.sessionId ? '✅ Found' : '❌ Not found');
|
|
716
|
+
console.log('CSRF Token:', this.csrfToken ? '✅ Found' : '❌ Not found');
|
|
717
|
+
}
|
|
718
|
+
if (!this.sessionId || !this.csrfToken) {
|
|
719
|
+
throw new Error('Failed to obtain session ID or CSRF token from SuiteCRM');
|
|
720
|
+
}
|
|
721
|
+
if (import.meta.env.DEV) {
|
|
722
|
+
console.log('🔑 Session and CSRF token obtained:', {
|
|
723
|
+
sessionId: this.sessionId ? 'SET' : 'NOT SET',
|
|
724
|
+
csrfToken: this.csrfToken ? 'SET' : 'NOT SET'
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
return { sessionId: this.sessionId, csrfToken: this.csrfToken };
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
if (import.meta.env.DEV) {
|
|
731
|
+
console.error('❌ Error in getSessionAndCSRFToken:', error);
|
|
732
|
+
console.error('Error details:', {
|
|
733
|
+
message: error.message,
|
|
734
|
+
stack: error.stack,
|
|
735
|
+
name: error.name
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
async getAccessToken() {
|
|
742
|
+
if (!this.oauth2Config) {
|
|
743
|
+
throw new Error('OAuth2 configuration not available');
|
|
744
|
+
}
|
|
745
|
+
// Check if we have a valid cached token
|
|
746
|
+
if (this.accessToken && this.tokenExpiresAt && Date.now() < this.tokenExpiresAt - 300000) {
|
|
747
|
+
return this.accessToken;
|
|
748
|
+
}
|
|
749
|
+
// Request new OAuth2 token using password grant
|
|
750
|
+
const tokenUrl = `${this.baseUrl}/Api/access_token`;
|
|
751
|
+
// Ensure password is properly handled for special characters
|
|
752
|
+
const rawPassword = this.oauth2Config.password || '';
|
|
753
|
+
const password = encodePassword(rawPassword);
|
|
754
|
+
// Validate password and log warnings
|
|
755
|
+
const passwordValidation = validatePassword(rawPassword);
|
|
756
|
+
if (!passwordValidation.isValid && import.meta.env.DEV) {
|
|
757
|
+
console.warn('⚠️ Password validation issues:', passwordValidation.issues);
|
|
758
|
+
}
|
|
759
|
+
const recommendations = getPasswordRecommendations(rawPassword);
|
|
760
|
+
if (recommendations.length > 0 && import.meta.env.DEV) {
|
|
761
|
+
console.warn('💡 Password recommendations:', recommendations);
|
|
762
|
+
}
|
|
763
|
+
const tokenData = {
|
|
764
|
+
grant_type: 'password',
|
|
765
|
+
client_id: this.oauth2Config.clientId,
|
|
766
|
+
client_secret: this.oauth2Config.clientSecret,
|
|
767
|
+
username: this.oauth2Config.username,
|
|
768
|
+
password: password,
|
|
769
|
+
};
|
|
770
|
+
if (import.meta.env.DEV) {
|
|
771
|
+
console.log('🔑 Requesting OAuth2 token with password grant:', {
|
|
772
|
+
tokenUrl,
|
|
773
|
+
clientId: tokenData.client_id,
|
|
774
|
+
username: tokenData.username,
|
|
775
|
+
hasPassword: !!tokenData.password
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
const response = await fetch(tokenUrl, {
|
|
779
|
+
method: 'POST',
|
|
780
|
+
headers: {
|
|
781
|
+
'Content-Type': 'application/json',
|
|
782
|
+
},
|
|
783
|
+
body: JSON.stringify(tokenData),
|
|
784
|
+
});
|
|
785
|
+
if (!response.ok) {
|
|
786
|
+
const errorText = await response.text();
|
|
787
|
+
throw new Error(`OAuth2 token request failed: ${response.status} - ${errorText}`);
|
|
788
|
+
}
|
|
789
|
+
const tokenResponse = await response.json();
|
|
790
|
+
if (import.meta.env.DEV) {
|
|
791
|
+
console.log('🔑 OAuth2 token obtained:', {
|
|
792
|
+
tokenType: tokenResponse.token_type,
|
|
793
|
+
expiresIn: tokenResponse.expires_in
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
this.accessToken = tokenResponse.access_token || '';
|
|
797
|
+
this.tokenExpiresAt = Date.now() + (tokenResponse.expires_in * 1000);
|
|
798
|
+
return this.accessToken || '';
|
|
799
|
+
}
|
|
800
|
+
async getAuthHeaders() {
|
|
801
|
+
if (this.useProxy) {
|
|
802
|
+
return {
|
|
803
|
+
'Content-Type': 'application/json'
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// Direct SuiteCRM API access with OAuth2 Bearer token
|
|
808
|
+
if (this.oauth2Config) {
|
|
809
|
+
const accessToken = await this.getAccessToken();
|
|
810
|
+
return {
|
|
811
|
+
'Content-Type': 'application/json',
|
|
812
|
+
'Accept': 'application/json',
|
|
813
|
+
'Authorization': `Bearer ${accessToken}`
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
// Fallback to session-based authentication
|
|
818
|
+
const { sessionId, csrfToken } = await this.getSessionAndCSRFToken();
|
|
819
|
+
return {
|
|
820
|
+
'Content-Type': 'application/json',
|
|
821
|
+
'Accept': 'application/json',
|
|
822
|
+
'X-CSRF-Token': csrfToken,
|
|
823
|
+
'Cookie': `SCRMSESSID=${sessionId}`
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
async makeRequest(endpoint, options = {}) {
|
|
829
|
+
this.validateConfiguration();
|
|
830
|
+
const headers = {
|
|
831
|
+
...(await this.getAuthHeaders()),
|
|
832
|
+
...options.headers
|
|
833
|
+
};
|
|
834
|
+
let url;
|
|
835
|
+
if (this.useProxy) {
|
|
836
|
+
// Use Cloudflare Worker proxy - pass endpoint as query parameter
|
|
837
|
+
const queryParams = new URLSearchParams({ endpoint });
|
|
838
|
+
url = `${this.proxyUrl}?${queryParams.toString()}`;
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
// Direct SuiteCRM API access
|
|
842
|
+
const directUrl = `${this.baseUrl}/api${endpoint}`;
|
|
843
|
+
const localDev = import.meta.env.VITE_LOCAL_DEVELOPMENT === 'true';
|
|
844
|
+
const shouldUseAccess = !localDev && import.meta.env.VITE_AUTH_MODE === 'direct'
|
|
845
|
+
&& !!import.meta.env.VITE_CF_ACCESS_CLIENT_ID && !!import.meta.env.VITE_CF_ACCESS_CLIENT_SECRET;
|
|
846
|
+
url = shouldUseAccess ? `/api/fetch-with-access?target=${encodeURIComponent(directUrl)}` : directUrl;
|
|
847
|
+
}
|
|
848
|
+
if (import.meta.env.DEV) {
|
|
849
|
+
console.log(`🔗 SuiteCRM API Request: ${options.method || 'GET'} ${url}`);
|
|
850
|
+
console.log('🔑 Headers:', Object.keys(headers).map(k => `${k}: ${headers[k] ? '***' : 'undefined'}`).join(', '));
|
|
851
|
+
}
|
|
852
|
+
const response = await fetch(url, {
|
|
853
|
+
...options,
|
|
854
|
+
headers
|
|
855
|
+
});
|
|
856
|
+
if (!response.ok) {
|
|
857
|
+
const errorText = await response.text();
|
|
858
|
+
console.error('❌ SuiteCRM API Error:', response.status, errorText);
|
|
859
|
+
throw new Error(`SuiteCRM API error: ${response.status} - ${errorText}`);
|
|
860
|
+
}
|
|
861
|
+
const data = await response.json();
|
|
862
|
+
if (import.meta.env.DEV) {
|
|
863
|
+
console.log('✅ SuiteCRM API Response:', data);
|
|
864
|
+
}
|
|
865
|
+
return data;
|
|
866
|
+
}
|
|
867
|
+
// Check if SuiteCRM is configured
|
|
868
|
+
isServiceConfigured() {
|
|
869
|
+
return this.isConfigured;
|
|
870
|
+
}
|
|
871
|
+
// Lead Management
|
|
872
|
+
async createLead(lead) {
|
|
873
|
+
if (import.meta.env.DEV) {
|
|
874
|
+
console.log('🚀 Starting lead creation for:', lead.first_name, lead.last_name);
|
|
875
|
+
}
|
|
876
|
+
// Use REST API by default since GraphQL has permission issues
|
|
877
|
+
// GraphQL can be used later when permissions are resolved
|
|
878
|
+
return this.createLeadREST(lead);
|
|
879
|
+
}
|
|
880
|
+
async createLeadREST(lead) {
|
|
881
|
+
if (import.meta.env.DEV) {
|
|
882
|
+
console.log('🔍 Attempting REST API lead creation...');
|
|
883
|
+
}
|
|
884
|
+
const leadData = {
|
|
885
|
+
data: {
|
|
886
|
+
type: 'Leads',
|
|
887
|
+
attributes: {
|
|
888
|
+
first_name: lead.first_name,
|
|
889
|
+
last_name: lead.last_name,
|
|
890
|
+
email1: lead.email1,
|
|
891
|
+
phone_work: lead.phone_work,
|
|
892
|
+
lead_source: lead.lead_source || 'Website',
|
|
893
|
+
status: lead.status || 'New',
|
|
894
|
+
description: lead.description,
|
|
895
|
+
campaign_id: lead.campaign_id
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
const result = await this.makeRequest('/legacy/Api/V8/module', {
|
|
900
|
+
method: 'POST',
|
|
901
|
+
body: JSON.stringify(leadData),
|
|
902
|
+
});
|
|
903
|
+
if (import.meta.env.DEV) {
|
|
904
|
+
console.log('✅ REST API lead creation successful:', result);
|
|
905
|
+
}
|
|
906
|
+
return result;
|
|
907
|
+
}
|
|
908
|
+
async verifyLeadCreation(email, firstName, lastName) {
|
|
909
|
+
try {
|
|
910
|
+
if (import.meta.env.DEV) {
|
|
911
|
+
console.log('🔍 Verifying lead creation for:', email);
|
|
912
|
+
}
|
|
913
|
+
// Try to get all leads and filter client-side since SuiteCRM filter syntax is complex
|
|
914
|
+
const result = await this.makeRequest('/legacy/Api/V8/module/Leads');
|
|
915
|
+
if (result.data && Array.isArray(result.data)) {
|
|
916
|
+
// Check if any lead matches the email and name
|
|
917
|
+
const matchingLead = result.data.find((lead) => lead.attributes?.email1 === email &&
|
|
918
|
+
lead.attributes?.first_name === firstName &&
|
|
919
|
+
lead.attributes?.last_name === lastName);
|
|
920
|
+
if (import.meta.env.DEV) {
|
|
921
|
+
console.log('✅ Lead verification result:', !!matchingLead);
|
|
922
|
+
console.log('🔍 Found leads:', result.data.length);
|
|
923
|
+
console.log('🔍 Matching lead:', matchingLead);
|
|
924
|
+
}
|
|
925
|
+
return !!matchingLead;
|
|
926
|
+
}
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
if (import.meta.env.DEV) {
|
|
931
|
+
console.log('❌ Lead verification failed:', error);
|
|
932
|
+
}
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
async getLead(leadId) {
|
|
937
|
+
return this.makeRequest(`/legacy/Api/V8/module/Leads/${leadId}`);
|
|
938
|
+
}
|
|
939
|
+
async updateLead(leadId, lead) {
|
|
940
|
+
const leadData = {
|
|
941
|
+
data: {
|
|
942
|
+
type: 'Leads',
|
|
943
|
+
id: leadId,
|
|
944
|
+
attributes: lead
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
return this.makeRequest(`/legacy/Api/V8/module/Leads/${leadId}`, {
|
|
948
|
+
method: 'PATCH',
|
|
949
|
+
body: JSON.stringify(leadData),
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
async getLeads(params = {}) {
|
|
953
|
+
const queryParams = new URLSearchParams();
|
|
954
|
+
if (params.page)
|
|
955
|
+
queryParams.append('page[number]', params.page.toString());
|
|
956
|
+
if (params.per_page)
|
|
957
|
+
queryParams.append('page[size]', params.per_page.toString());
|
|
958
|
+
if (params.filter)
|
|
959
|
+
queryParams.append('filter', params.filter);
|
|
960
|
+
if (params.sort)
|
|
961
|
+
queryParams.append('sort', params.sort);
|
|
962
|
+
const queryString = queryParams.toString();
|
|
963
|
+
const endpoint = queryString ? `/legacy/Api/V8/module/Leads?${queryString}` : '/legacy/Api/V8/module/Leads';
|
|
964
|
+
return this.makeRequest(endpoint);
|
|
965
|
+
}
|
|
966
|
+
// Contact Management
|
|
967
|
+
async createContact(contact) {
|
|
968
|
+
const contactData = {
|
|
969
|
+
data: {
|
|
970
|
+
type: 'Contacts',
|
|
971
|
+
attributes: contact
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
return this.makeRequest('/contacts', {
|
|
975
|
+
method: 'POST',
|
|
976
|
+
body: JSON.stringify(contactData),
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
async getContact(contactId) {
|
|
980
|
+
return this.makeRequest(`/contacts/${contactId}`);
|
|
981
|
+
}
|
|
982
|
+
async updateContact(contactId, contact) {
|
|
983
|
+
const contactData = {
|
|
984
|
+
data: {
|
|
985
|
+
type: 'Contacts',
|
|
986
|
+
id: contactId,
|
|
987
|
+
attributes: contact
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
return this.makeRequest(`/contacts/${contactId}`, {
|
|
991
|
+
method: 'PATCH',
|
|
992
|
+
body: JSON.stringify(contactData),
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
// Account Management
|
|
996
|
+
async createAccount(account) {
|
|
997
|
+
const accountData = {
|
|
998
|
+
data: {
|
|
999
|
+
type: 'Accounts',
|
|
1000
|
+
attributes: account
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
return this.makeRequest('/accounts', {
|
|
1004
|
+
method: 'POST',
|
|
1005
|
+
body: JSON.stringify(accountData),
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
async getAccount(accountId) {
|
|
1009
|
+
return this.makeRequest(`/accounts/${accountId}`);
|
|
1010
|
+
}
|
|
1011
|
+
// Opportunity Management
|
|
1012
|
+
async createOpportunity(opportunity) {
|
|
1013
|
+
const opportunityData = {
|
|
1014
|
+
data: {
|
|
1015
|
+
type: 'Opportunities',
|
|
1016
|
+
attributes: opportunity
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
return this.makeRequest('/opportunities', {
|
|
1020
|
+
method: 'POST',
|
|
1021
|
+
body: JSON.stringify(opportunityData),
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
async getOpportunity(opportunityId) {
|
|
1025
|
+
return this.makeRequest(`/opportunities/${opportunityId}`);
|
|
1026
|
+
}
|
|
1027
|
+
// Search and Filter
|
|
1028
|
+
async searchLeads(query) {
|
|
1029
|
+
const searchParams = new URLSearchParams({
|
|
1030
|
+
filter: `contains(first_name,"${query}") or contains(last_name,"${query}") or contains(email1,"${query}")`
|
|
1031
|
+
});
|
|
1032
|
+
return this.makeRequest(`/legacy/Api/V8/module/Leads?${searchParams.toString()}`);
|
|
1033
|
+
}
|
|
1034
|
+
// Utility Methods
|
|
1035
|
+
async getLeadSources() {
|
|
1036
|
+
return this.makeRequest('/enum/lead_sources');
|
|
1037
|
+
}
|
|
1038
|
+
async getLeadStatuses() {
|
|
1039
|
+
return this.makeRequest('/enum/lead_statuses');
|
|
1040
|
+
}
|
|
1041
|
+
// GraphQL support for SuiteCRM 8.8.0+
|
|
1042
|
+
async graphqlRequest(query, variables) {
|
|
1043
|
+
this.validateConfiguration();
|
|
1044
|
+
const headers = await this.getAuthHeaders();
|
|
1045
|
+
// Use the correct endpoint based on mode
|
|
1046
|
+
let url;
|
|
1047
|
+
if (this.useProxy) {
|
|
1048
|
+
// Use Cloudflare Worker proxy for GraphQL - pass endpoint as query parameter
|
|
1049
|
+
const queryParams = new URLSearchParams({ endpoint: '/Api/graphql' });
|
|
1050
|
+
url = `${this.proxyUrl}?${queryParams.toString()}`;
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
// Direct SuiteCRM API access
|
|
1054
|
+
url = `${this.baseUrl}/Api/graphql`;
|
|
1055
|
+
}
|
|
1056
|
+
if (import.meta.env.DEV) {
|
|
1057
|
+
console.log(`🔗 SuiteCRM GraphQL Request: POST ${url}`);
|
|
1058
|
+
console.log('📝 Query:', query);
|
|
1059
|
+
console.log('🔧 Variables:', variables);
|
|
1060
|
+
console.log('🔑 Headers:', Object.keys(headers).map(k => `${k}: ${headers[k] ? '***' : 'undefined'}`).join(', '));
|
|
1061
|
+
}
|
|
1062
|
+
const response = await fetch(url, {
|
|
1063
|
+
method: 'POST',
|
|
1064
|
+
headers,
|
|
1065
|
+
body: JSON.stringify({
|
|
1066
|
+
query,
|
|
1067
|
+
variables
|
|
1068
|
+
})
|
|
1069
|
+
});
|
|
1070
|
+
if (!response.ok) {
|
|
1071
|
+
const errorText = await response.text();
|
|
1072
|
+
console.error('❌ SuiteCRM GraphQL Error:', response.status, errorText);
|
|
1073
|
+
throw new Error(`SuiteCRM GraphQL error: ${response.status} - ${errorText}`);
|
|
1074
|
+
}
|
|
1075
|
+
const data = await response.json();
|
|
1076
|
+
if (import.meta.env.DEV) {
|
|
1077
|
+
console.log('✅ SuiteCRM GraphQL Response:', data);
|
|
1078
|
+
}
|
|
1079
|
+
return data;
|
|
1080
|
+
}
|
|
1081
|
+
// Test connection with GraphQL
|
|
1082
|
+
async testConnection() {
|
|
1083
|
+
try {
|
|
1084
|
+
// Try GraphQL first
|
|
1085
|
+
const graphqlQuery = `
|
|
1086
|
+
query TestConnection {
|
|
1087
|
+
__schema {
|
|
1088
|
+
types {
|
|
1089
|
+
name
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
`;
|
|
1094
|
+
await this.graphqlRequest(graphqlQuery);
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
catch (graphqlError) {
|
|
1098
|
+
console.log('GraphQL test failed, trying REST API:', graphqlError);
|
|
1099
|
+
try {
|
|
1100
|
+
// Fallback to REST API
|
|
1101
|
+
await this.makeRequest('/leads?page[size]=1');
|
|
1102
|
+
return true;
|
|
1103
|
+
}
|
|
1104
|
+
catch (restError) {
|
|
1105
|
+
console.error('❌ SuiteCRM connection test failed:', { graphqlError, restError });
|
|
1106
|
+
return false;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
// Simple Lead Creation - Create new lead directly
|
|
1111
|
+
async createLeadSimple(leadData) {
|
|
1112
|
+
if (import.meta.env.DEV) {
|
|
1113
|
+
console.log('🚀 Creating new lead for:', leadData.first_name, leadData.last_name, leadData.email1);
|
|
1114
|
+
}
|
|
1115
|
+
try {
|
|
1116
|
+
const result = await this.createLeadREST(leadData);
|
|
1117
|
+
if (import.meta.env.DEV) {
|
|
1118
|
+
console.log('✅ Lead created successfully');
|
|
1119
|
+
}
|
|
1120
|
+
return {
|
|
1121
|
+
data: result.data,
|
|
1122
|
+
meta: { action: 'lead_created' }
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
catch (error) {
|
|
1126
|
+
if (import.meta.env.DEV) {
|
|
1127
|
+
console.error('❌ Lead creation failed:', error);
|
|
1128
|
+
}
|
|
1129
|
+
throw error;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
let suitecrmServiceInstance = null;
|
|
1134
|
+
const getSuiteCRMService = () => {
|
|
1135
|
+
if (!suitecrmServiceInstance) {
|
|
1136
|
+
suitecrmServiceInstance = new SuiteCRMService();
|
|
1137
|
+
}
|
|
1138
|
+
return suitecrmServiceInstance;
|
|
1139
|
+
};
|
|
1140
|
+
const resetSuiteCRMService = () => {
|
|
1141
|
+
suitecrmServiceInstance = null;
|
|
1142
|
+
};
|
|
1143
|
+
const suitecrmService = createLazyProxy(getSuiteCRMService);
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* Cloudflare WordPress Webhook Service
|
|
1147
|
+
* Handles webhook status and data regeneration monitoring
|
|
1148
|
+
*/
|
|
1149
|
+
class CfWpWebhookService {
|
|
1150
|
+
baseUrl;
|
|
1151
|
+
refreshSecret;
|
|
1152
|
+
constructor() {
|
|
1153
|
+
this.baseUrl = getEnv('VITE_WORDPRESS_API_URL') || '';
|
|
1154
|
+
this.refreshSecret = getEnv('VITE_WP_REFRESH_SECRET') || '';
|
|
1155
|
+
if (!this.refreshSecret) {
|
|
1156
|
+
console.warn('⚠️ VITE_WP_REFRESH_SECRET not configured - webhook authentication will fail');
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Get webhook status from WordPress
|
|
1161
|
+
*/
|
|
1162
|
+
async getWebhookStatus() {
|
|
1163
|
+
try {
|
|
1164
|
+
const response = await fetch(`${this.baseUrl}/wp-json/cf-wp/v1/webhook-status`, {
|
|
1165
|
+
method: 'GET',
|
|
1166
|
+
headers: {
|
|
1167
|
+
'Content-Type': 'application/json',
|
|
1168
|
+
'Authorization': `Bearer ${this.refreshSecret}`
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
if (!response.ok) {
|
|
1172
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1173
|
+
}
|
|
1174
|
+
return await response.json();
|
|
1175
|
+
}
|
|
1176
|
+
catch (error) {
|
|
1177
|
+
console.error('Failed to fetch webhook status:', error);
|
|
1178
|
+
return {
|
|
1179
|
+
last_triggered: null,
|
|
1180
|
+
last_success: null,
|
|
1181
|
+
last_error: error instanceof Error ? error.message : 'Unknown error',
|
|
1182
|
+
total_triggers: 0,
|
|
1183
|
+
success_rate: 0,
|
|
1184
|
+
status: 'error'
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Get webhook configuration from WordPress
|
|
1190
|
+
*/
|
|
1191
|
+
async getWebhookConfig() {
|
|
1192
|
+
try {
|
|
1193
|
+
const response = await fetch(`${this.baseUrl}/wp-json/cf-wp/v1/webhook-config`, {
|
|
1194
|
+
method: 'GET',
|
|
1195
|
+
headers: {
|
|
1196
|
+
'Content-Type': 'application/json',
|
|
1197
|
+
'Authorization': `Bearer ${this.refreshSecret}`
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
if (!response.ok) {
|
|
1201
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1202
|
+
}
|
|
1203
|
+
return await response.json();
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
console.error('Failed to fetch webhook config:', error);
|
|
1207
|
+
return null;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Test webhook endpoint
|
|
1212
|
+
*/
|
|
1213
|
+
async testWebhook() {
|
|
1214
|
+
try {
|
|
1215
|
+
const testPayload = {
|
|
1216
|
+
post_id: 0,
|
|
1217
|
+
post_type: 'test',
|
|
1218
|
+
action: 'test',
|
|
1219
|
+
timestamp: new Date().toISOString(),
|
|
1220
|
+
frontend_id: 'test'
|
|
1221
|
+
};
|
|
1222
|
+
const response = await fetch('/api/webhook-test', {
|
|
1223
|
+
method: 'POST',
|
|
1224
|
+
headers: {
|
|
1225
|
+
'Content-Type': 'application/json',
|
|
1226
|
+
'Authorization': `Bearer ${this.refreshSecret}`
|
|
1227
|
+
},
|
|
1228
|
+
body: JSON.stringify(testPayload)
|
|
1229
|
+
});
|
|
1230
|
+
if (!response.ok) {
|
|
1231
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1232
|
+
}
|
|
1233
|
+
const result = await response.json();
|
|
1234
|
+
return {
|
|
1235
|
+
success: result.success,
|
|
1236
|
+
message: result.message || 'Webhook test completed'
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
catch (error) {
|
|
1240
|
+
console.error('Webhook test failed:', error);
|
|
1241
|
+
return {
|
|
1242
|
+
success: false,
|
|
1243
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Get last webhook response
|
|
1249
|
+
*/
|
|
1250
|
+
async getLastWebhookResponse() {
|
|
1251
|
+
try {
|
|
1252
|
+
const response = await fetch('/webhook-response.json');
|
|
1253
|
+
if (!response.ok) {
|
|
1254
|
+
return null;
|
|
1255
|
+
}
|
|
1256
|
+
return await response.json();
|
|
1257
|
+
}
|
|
1258
|
+
catch (error) {
|
|
1259
|
+
console.error('Failed to fetch last webhook response:', error);
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Manually trigger data regeneration
|
|
1265
|
+
*/
|
|
1266
|
+
async triggerDataRegeneration() {
|
|
1267
|
+
try {
|
|
1268
|
+
const response = await fetch('/api/trigger-regeneration', {
|
|
1269
|
+
method: 'POST',
|
|
1270
|
+
headers: {
|
|
1271
|
+
'Content-Type': 'application/json',
|
|
1272
|
+
'Authorization': `Bearer ${this.refreshSecret}`
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
if (!response.ok) {
|
|
1276
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1277
|
+
}
|
|
1278
|
+
const result = await response.json();
|
|
1279
|
+
return {
|
|
1280
|
+
success: result.success,
|
|
1281
|
+
message: result.message || 'Data regeneration triggered'
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
catch (error) {
|
|
1285
|
+
console.error('Failed to trigger data regeneration:', error);
|
|
1286
|
+
return {
|
|
1287
|
+
success: false,
|
|
1288
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Check if webhook is properly configured
|
|
1294
|
+
*/
|
|
1295
|
+
isConfigured() {
|
|
1296
|
+
return !!(this.baseUrl && this.refreshSecret);
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Get configuration status
|
|
1300
|
+
*/
|
|
1301
|
+
getConfigurationStatus() {
|
|
1302
|
+
return {
|
|
1303
|
+
wordpress_url: !!this.baseUrl,
|
|
1304
|
+
refresh_secret: !!this.refreshSecret,
|
|
1305
|
+
fully_configured: !!(this.baseUrl && this.refreshSecret)
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
let cfWpWebhookServiceInstance = null;
|
|
1310
|
+
const getCfWpWebhookService = () => {
|
|
1311
|
+
if (!cfWpWebhookServiceInstance) {
|
|
1312
|
+
cfWpWebhookServiceInstance = new CfWpWebhookService();
|
|
1313
|
+
}
|
|
1314
|
+
return cfWpWebhookServiceInstance;
|
|
1315
|
+
};
|
|
1316
|
+
const resetCfWpWebhookService = () => {
|
|
1317
|
+
cfWpWebhookServiceInstance = null;
|
|
1318
|
+
};
|
|
1319
|
+
const cfWpWebhookService = createLazyProxy(getCfWpWebhookService);
|
|
1320
|
+
|
|
1321
|
+
export { cfWpWebhookService, createGravityFormsClient, createMauticService, getCfWpWebhookService, getGravityFormsClient, getMauticService, getSuiteCRMService, gravityFormsClient, gravityFormsService, mauticService, resetCfWpWebhookService, resetGravityFormsClient, resetMauticService, resetSuiteCRMService, suitecrmService };
|
|
1322
|
+
//# sourceMappingURL=services.esm.js.map
|