@marvalt/digivalt-core 0.2.17 → 0.2.22
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 +24 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/integrations/digivalt-tracker.d.ts +1 -0
- package/dist/config/integrations.d.ts +16 -0
- package/dist/config.cjs +15 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.ts +17 -1
- package/dist/config.esm.js +15 -1
- package/dist/config.esm.js.map +1 -1
- package/dist/generators/index.d.ts +1 -0
- package/dist/generators/phplist.d.ts +3 -0
- package/dist/generators.cjs +312 -0
- package/dist/generators.cjs.map +1 -1
- package/dist/generators.d.ts +5 -1
- package/dist/generators.esm.js +312 -1
- package/dist/generators.esm.js.map +1 -1
- package/dist/hooks/useGravityForms.d.ts +12 -6
- package/dist/hooks/useGravityFormsNew.d.ts +9 -6
- package/dist/hooks/useSuiteCRM.d.ts +22 -15
- package/dist/index.cjs +313 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.esm.js +313 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/integrations-entry.d.ts +3 -0
- package/dist/integrations.cjs +316 -0
- package/dist/integrations.cjs.map +1 -0
- package/dist/integrations.d.ts +55 -0
- package/dist/integrations.esm.js +308 -0
- package/dist/integrations.esm.js.map +1 -0
- package/dist/seo.cjs +11 -0
- package/dist/seo.cjs.map +1 -1
- package/dist/seo.esm.js +11 -0
- package/dist/seo.esm.js.map +1 -1
- package/dist/services.cjs +11 -0
- package/dist/services.cjs.map +1 -1
- package/dist/services.esm.js +11 -0
- package/dist/services.esm.js.map +1 -1
- package/docs/ENVIRONMENT.md +12 -1
- package/docs/README.md +14 -0
- package/package.json +12 -3
- package/template/DIGIVALT_SETUP.md +36 -0
- package/template/scripts/deploy-secrets.js +44 -30
|
@@ -3,19 +3,25 @@ import { GravityFormField } from '../services/gravityForms';
|
|
|
3
3
|
/**
|
|
4
4
|
* Custom hook for fetching all Gravity Forms
|
|
5
5
|
*/
|
|
6
|
-
export declare const useGravityForms: () => any
|
|
6
|
+
export declare const useGravityForms: () => import("@tanstack/react-query").UseQueryResult<any, Error>;
|
|
7
7
|
/**
|
|
8
8
|
* Custom hook for fetching a specific Gravity Form
|
|
9
9
|
*/
|
|
10
|
-
export declare const useGravityForm: (formId: string) => any
|
|
10
|
+
export declare const useGravityForm: (formId: string) => import("@tanstack/react-query").UseQueryResult<any, Error>;
|
|
11
11
|
/**
|
|
12
12
|
* Custom hook for fetching a Gravity Form schema for rendering
|
|
13
13
|
*/
|
|
14
|
-
export declare const useGravityFormSchema: (formId: string) => any
|
|
14
|
+
export declare const useGravityFormSchema: (formId: string) => import("@tanstack/react-query").UseQueryResult<any, Error>;
|
|
15
15
|
/**
|
|
16
16
|
* Custom hook for submitting Gravity Forms
|
|
17
17
|
*/
|
|
18
|
-
export declare const useGravityFormSubmission: () =>
|
|
18
|
+
export declare const useGravityFormSubmission: () => import("@tanstack/react-query").UseMutationResult<import("@marvalt/wadapter").GravityFormSubmissionResult, Error, {
|
|
19
|
+
formId: string;
|
|
20
|
+
entry: GravityFormEntry;
|
|
21
|
+
files?: {
|
|
22
|
+
[fieldId: string]: File[];
|
|
23
|
+
};
|
|
24
|
+
}, unknown>;
|
|
19
25
|
/**
|
|
20
26
|
* Custom hook for fetching Gravity Form entries
|
|
21
27
|
*/
|
|
@@ -25,11 +31,11 @@ export declare const useGravityFormEntries: (formId: string, params?: {
|
|
|
25
31
|
search?: string;
|
|
26
32
|
start_date?: string;
|
|
27
33
|
end_date?: string;
|
|
28
|
-
}) => any
|
|
34
|
+
}) => import("@tanstack/react-query").UseQueryResult<any, Error>;
|
|
29
35
|
/**
|
|
30
36
|
* Custom hook for fetching a specific Gravity Form entry
|
|
31
37
|
*/
|
|
32
|
-
export declare const useGravityFormEntry: (entryId: string) => any
|
|
38
|
+
export declare const useGravityFormEntry: (entryId: string) => import("@tanstack/react-query").UseQueryResult<any, Error>;
|
|
33
39
|
/**
|
|
34
40
|
* Custom hook for form validation
|
|
35
41
|
*/
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Custom hook for fetching all Gravity Forms using static data
|
|
3
3
|
*/
|
|
4
|
-
export declare const useGravityForms: () =>
|
|
4
|
+
export declare const useGravityForms: () => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../data/gravityFormsDataStore").GravityForm[]>, Error>;
|
|
5
5
|
/**
|
|
6
6
|
* Custom hook for fetching a specific Gravity Form using static data
|
|
7
7
|
*/
|
|
8
|
-
export declare const useGravityForm: (formId: string) =>
|
|
8
|
+
export declare const useGravityForm: (formId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../data/gravityFormsDataStore").GravityForm>, Error>;
|
|
9
9
|
/**
|
|
10
10
|
* Custom hook for fetching a Gravity Form schema for rendering using static data
|
|
11
11
|
*/
|
|
12
|
-
export declare const useGravityFormSchema: (formId: string) =>
|
|
12
|
+
export declare const useGravityFormSchema: (formId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../data/gravityFormsDataStore").GravityForm>, Error>;
|
|
13
13
|
/**
|
|
14
14
|
* Custom hook for submitting Gravity Forms using the npm package
|
|
15
15
|
*/
|
|
16
|
-
export declare const useGravityFormSubmission: () => any
|
|
16
|
+
export declare const useGravityFormSubmission: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
|
|
17
|
+
formId: string;
|
|
18
|
+
values: Record<string, any>;
|
|
19
|
+
}, unknown>;
|
|
17
20
|
/**
|
|
18
21
|
* Custom hook for Gravity Form submissions using the npm package
|
|
19
22
|
*/
|
|
20
|
-
export declare const useGravityFormSubmissions: (formId: string) => any
|
|
23
|
+
export declare const useGravityFormSubmissions: (formId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<any[]>, Error>;
|
|
21
24
|
/**
|
|
22
25
|
* Custom hook for Gravity Form entries using the npm package
|
|
23
26
|
*/
|
|
24
|
-
export declare const useGravityFormEntries: (formId: string, page?: number, perPage?: number) => any
|
|
27
|
+
export declare const useGravityFormEntries: (formId: string, page?: number, perPage?: number) => import("@tanstack/react-query").UseQueryResult<NoInfer<any[]>, Error>;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { SuiteCRMLead, SuiteCRMContact, SuiteCRMAccount, SuiteCRMOpportunity } from '../services/suitecrm';
|
|
1
2
|
/**
|
|
2
3
|
* Custom hook for creating SuiteCRM leads
|
|
3
4
|
*/
|
|
4
|
-
export declare const useSuiteCRMCreateLead: () => any
|
|
5
|
+
export declare const useSuiteCRMCreateLead: () => import("@tanstack/react-query").UseMutationResult<import("../services").SuiteCRMAPIResponse<any>, Error, SuiteCRMLead, unknown>;
|
|
5
6
|
/**
|
|
6
7
|
* Custom hook for updating SuiteCRM leads
|
|
7
8
|
*/
|
|
8
|
-
export declare const useSuiteCRMUpdateLead: () => any
|
|
9
|
+
export declare const useSuiteCRMUpdateLead: () => import("@tanstack/react-query").UseMutationResult<import("../services").SuiteCRMAPIResponse<any>, Error, {
|
|
10
|
+
leadId: string;
|
|
11
|
+
lead: Partial<SuiteCRMLead>;
|
|
12
|
+
}, unknown>;
|
|
9
13
|
/**
|
|
10
14
|
* Custom hook for fetching SuiteCRM leads
|
|
11
15
|
*/
|
|
@@ -14,52 +18,55 @@ export declare const useSuiteCRMLeads: (params?: {
|
|
|
14
18
|
per_page?: number;
|
|
15
19
|
filter?: string;
|
|
16
20
|
sort?: string;
|
|
17
|
-
}) => any
|
|
21
|
+
}) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
18
22
|
/**
|
|
19
23
|
* Custom hook for fetching a specific SuiteCRM lead
|
|
20
24
|
*/
|
|
21
|
-
export declare const useSuiteCRMLead: (leadId: string) => any
|
|
25
|
+
export declare const useSuiteCRMLead: (leadId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
22
26
|
/**
|
|
23
27
|
* Custom hook for searching SuiteCRM leads
|
|
24
28
|
*/
|
|
25
|
-
export declare const useSuiteCRMSearchLeads: (query: string) => any
|
|
29
|
+
export declare const useSuiteCRMSearchLeads: (query: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
26
30
|
/**
|
|
27
31
|
* Custom hook for creating SuiteCRM contacts
|
|
28
32
|
*/
|
|
29
|
-
export declare const useSuiteCRMCreateContact: () => any
|
|
33
|
+
export declare const useSuiteCRMCreateContact: () => import("@tanstack/react-query").UseMutationResult<import("../services").SuiteCRMAPIResponse<any>, Error, SuiteCRMContact, unknown>;
|
|
30
34
|
/**
|
|
31
35
|
* Custom hook for updating SuiteCRM contacts
|
|
32
36
|
*/
|
|
33
|
-
export declare const useSuiteCRMUpdateContact: () => any
|
|
37
|
+
export declare const useSuiteCRMUpdateContact: () => import("@tanstack/react-query").UseMutationResult<import("../services").SuiteCRMAPIResponse<any>, Error, {
|
|
38
|
+
contactId: string;
|
|
39
|
+
contact: Partial<SuiteCRMContact>;
|
|
40
|
+
}, unknown>;
|
|
34
41
|
/**
|
|
35
42
|
* Custom hook for fetching a specific SuiteCRM contact
|
|
36
43
|
*/
|
|
37
|
-
export declare const useSuiteCRMContact: (contactId: string) => any
|
|
44
|
+
export declare const useSuiteCRMContact: (contactId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
38
45
|
/**
|
|
39
46
|
* Custom hook for creating SuiteCRM accounts
|
|
40
47
|
*/
|
|
41
|
-
export declare const useSuiteCRMCreateAccount: () => any
|
|
48
|
+
export declare const useSuiteCRMCreateAccount: () => import("@tanstack/react-query").UseMutationResult<import("../services").SuiteCRMAPIResponse<any>, Error, SuiteCRMAccount, unknown>;
|
|
42
49
|
/**
|
|
43
50
|
* Custom hook for fetching a specific SuiteCRM account
|
|
44
51
|
*/
|
|
45
|
-
export declare const useSuiteCRMAccount: (accountId: string) => any
|
|
52
|
+
export declare const useSuiteCRMAccount: (accountId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
46
53
|
/**
|
|
47
54
|
* Custom hook for creating SuiteCRM opportunities
|
|
48
55
|
*/
|
|
49
|
-
export declare const useSuiteCRMCreateOpportunity: () => any
|
|
56
|
+
export declare const useSuiteCRMCreateOpportunity: () => import("@tanstack/react-query").UseMutationResult<import("../services").SuiteCRMAPIResponse<any>, Error, SuiteCRMOpportunity, unknown>;
|
|
50
57
|
/**
|
|
51
58
|
* Custom hook for fetching a specific SuiteCRM opportunity
|
|
52
59
|
*/
|
|
53
|
-
export declare const useSuiteCRMOpportunity: (opportunityId: string) => any
|
|
60
|
+
export declare const useSuiteCRMOpportunity: (opportunityId: string) => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
54
61
|
/**
|
|
55
62
|
* Custom hook for fetching SuiteCRM lead sources
|
|
56
63
|
*/
|
|
57
|
-
export declare const useSuiteCRMLeadSources: () => any
|
|
64
|
+
export declare const useSuiteCRMLeadSources: () => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
58
65
|
/**
|
|
59
66
|
* Custom hook for fetching SuiteCRM lead statuses
|
|
60
67
|
*/
|
|
61
|
-
export declare const useSuiteCRMLeadStatuses: () => any
|
|
68
|
+
export declare const useSuiteCRMLeadStatuses: () => import("@tanstack/react-query").UseQueryResult<NoInfer<import("../services").SuiteCRMAPIResponse<any>>, Error>;
|
|
62
69
|
/**
|
|
63
70
|
* Custom hook for testing SuiteCRM connection
|
|
64
71
|
*/
|
|
65
|
-
export declare const useSuiteCRMConnectionTest: () =>
|
|
72
|
+
export declare const useSuiteCRMConnectionTest: () => import("@tanstack/react-query").UseQueryResult<boolean, Error>;
|
package/dist/index.cjs
CHANGED
|
@@ -2435,6 +2435,316 @@ async function generateMauticData(options) {
|
|
|
2435
2435
|
}
|
|
2436
2436
|
}
|
|
2437
2437
|
|
|
2438
|
+
/** Normalize PHPList base URL to REST API v2 root. */
|
|
2439
|
+
function getApiBaseUrl(phplistUrl) {
|
|
2440
|
+
const trimmed = phplistUrl.replace(/\/$/, '');
|
|
2441
|
+
if (trimmed.endsWith('/api/v2'))
|
|
2442
|
+
return trimmed;
|
|
2443
|
+
if (trimmed.endsWith('/lists'))
|
|
2444
|
+
return `${trimmed}/api/v2`;
|
|
2445
|
+
return `${trimmed}/lists/api/v2`;
|
|
2446
|
+
}
|
|
2447
|
+
function buildViewProxyPath(messageId, uid) {
|
|
2448
|
+
const params = new URLSearchParams({ endpoint: 'view', mid: String(messageId) });
|
|
2449
|
+
return `/api/phplist-proxy?${params.toString()}`;
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
let cachedSession = null;
|
|
2453
|
+
function buildAccessHeaders(options) {
|
|
2454
|
+
const headers = {};
|
|
2455
|
+
if (options.cfAccessClientId && options.cfAccessClientSecret) {
|
|
2456
|
+
headers['CF-Access-Client-Id'] = options.cfAccessClientId;
|
|
2457
|
+
headers['CF-Access-Client-Secret'] = options.cfAccessClientSecret;
|
|
2458
|
+
}
|
|
2459
|
+
return headers;
|
|
2460
|
+
}
|
|
2461
|
+
async function createPhplistSession(options) {
|
|
2462
|
+
if (cachedSession && cachedSession.expiresAt > Date.now() + 300000) {
|
|
2463
|
+
return cachedSession.key;
|
|
2464
|
+
}
|
|
2465
|
+
const apiBase = getApiBaseUrl(options.apiUrl);
|
|
2466
|
+
const headers = {
|
|
2467
|
+
'Content-Type': 'application/json',
|
|
2468
|
+
...buildAccessHeaders(options),
|
|
2469
|
+
};
|
|
2470
|
+
const response = await fetch(`${apiBase}/sessions`, {
|
|
2471
|
+
method: 'POST',
|
|
2472
|
+
headers,
|
|
2473
|
+
body: JSON.stringify({
|
|
2474
|
+
login_name: options.apiUser,
|
|
2475
|
+
password: options.apiPassword,
|
|
2476
|
+
}),
|
|
2477
|
+
});
|
|
2478
|
+
if (!response.ok) {
|
|
2479
|
+
const errText = await response.text();
|
|
2480
|
+
throw new Error(`PHPList session failed: ${response.status} ${errText}`);
|
|
2481
|
+
}
|
|
2482
|
+
const data = (await response.json());
|
|
2483
|
+
const expiryMs = data.expiry ? Date.parse(data.expiry) : Date.now() + 3600000;
|
|
2484
|
+
cachedSession = { key: data.key, expiresAt: expiryMs };
|
|
2485
|
+
return data.key;
|
|
2486
|
+
}
|
|
2487
|
+
async function phplistApiFetch(options, path, init = {}) {
|
|
2488
|
+
const sessionKey = await createPhplistSession(options);
|
|
2489
|
+
const apiBase = getApiBaseUrl(options.apiUrl);
|
|
2490
|
+
const url = path.startsWith('http') ? path : `${apiBase}${path.startsWith('/') ? path : `/${path}`}`;
|
|
2491
|
+
const headers = {
|
|
2492
|
+
'Content-Type': 'application/json',
|
|
2493
|
+
'php-auth-pw': sessionKey,
|
|
2494
|
+
...init.headers,
|
|
2495
|
+
...buildAccessHeaders(options),
|
|
2496
|
+
};
|
|
2497
|
+
const response = await fetch(url, { ...init, headers });
|
|
2498
|
+
if (!response.ok) {
|
|
2499
|
+
const errText = await response.text();
|
|
2500
|
+
throw new Error(`PHPList API ${path} failed: ${response.status} ${errText}`);
|
|
2501
|
+
}
|
|
2502
|
+
if (response.status === 204) {
|
|
2503
|
+
return undefined;
|
|
2504
|
+
}
|
|
2505
|
+
const text = await response.text();
|
|
2506
|
+
if (!text)
|
|
2507
|
+
return undefined;
|
|
2508
|
+
return JSON.parse(text);
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
/** URL-safe slug from subject line. */
|
|
2512
|
+
function slugifySubject(subject) {
|
|
2513
|
+
return subject
|
|
2514
|
+
.toLowerCase()
|
|
2515
|
+
.normalize('NFKD')
|
|
2516
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
2517
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
2518
|
+
.replace(/^-+|-+$/g, '')
|
|
2519
|
+
.slice(0, 80) || 'newsletter';
|
|
2520
|
+
}
|
|
2521
|
+
function slugifyListName(name) {
|
|
2522
|
+
return slugifySubject(name);
|
|
2523
|
+
}
|
|
2524
|
+
function buildNewsletterSlug(listSlug, messageId, subject) {
|
|
2525
|
+
return `${listSlug}/${messageId}-${slugifySubject(subject)}`;
|
|
2526
|
+
}
|
|
2527
|
+
function stripHtmlExcerpt(html, maxLength = 200) {
|
|
2528
|
+
const text = html
|
|
2529
|
+
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
2530
|
+
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
2531
|
+
.replace(/<[^>]+>/g, ' ')
|
|
2532
|
+
.replace(/\s+/g, ' ')
|
|
2533
|
+
.trim();
|
|
2534
|
+
if (text.length <= maxLength)
|
|
2535
|
+
return text;
|
|
2536
|
+
return `${text.slice(0, maxLength).trim()}…`;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
async function generatePhplistData$1(config) {
|
|
2540
|
+
console.log('🔄 Generating PHPList newsletter JSON data...');
|
|
2541
|
+
try {
|
|
2542
|
+
const authOptions = {
|
|
2543
|
+
apiUrl: config.apiUrl,
|
|
2544
|
+
apiUser: config.apiUser,
|
|
2545
|
+
apiPassword: config.apiPassword,
|
|
2546
|
+
cfAccessClientId: config.cfAccessClientId,
|
|
2547
|
+
cfAccessClientSecret: config.cfAccessClientSecret,
|
|
2548
|
+
};
|
|
2549
|
+
const list = await phplistApiFetch(authOptions, `/lists/${config.listId}`);
|
|
2550
|
+
const listSlug = slugifyListName(list.name);
|
|
2551
|
+
const existing = readExistingData(config.outputPath);
|
|
2552
|
+
const knownIds = new Set(existing?.newsletters.map((n) => n.id) ?? []);
|
|
2553
|
+
const lastSentAt = existing?.newsletters.reduce((max, n) => {
|
|
2554
|
+
const t = Date.parse(n.sent_at);
|
|
2555
|
+
return Number.isNaN(t) ? max : Math.max(max, t);
|
|
2556
|
+
}, 0) ?? 0;
|
|
2557
|
+
const listMessageIds = await fetchListMessageIds(authOptions, config.listId);
|
|
2558
|
+
const listMessageIdSet = new Set(listMessageIds);
|
|
2559
|
+
const newItems = [];
|
|
2560
|
+
let afterId = 0;
|
|
2561
|
+
let hasMore = true;
|
|
2562
|
+
while (hasMore) {
|
|
2563
|
+
const page = await phplistApiFetch(authOptions, `/campaigns?limit=100&after_id=${afterId || 1}`);
|
|
2564
|
+
const items = page.items ?? [];
|
|
2565
|
+
if (items.length === 0)
|
|
2566
|
+
break;
|
|
2567
|
+
for (const summary of items) {
|
|
2568
|
+
const id = summary.id;
|
|
2569
|
+
if (!id || !listMessageIdSet.has(id) || knownIds.has(id))
|
|
2570
|
+
continue;
|
|
2571
|
+
if (summary.message_metadata?.status !== 'sent')
|
|
2572
|
+
continue;
|
|
2573
|
+
const sentAt = summary.message_metadata?.sent;
|
|
2574
|
+
if (sentAt && lastSentAt > 0 && Date.parse(sentAt) <= lastSentAt) {
|
|
2575
|
+
continue;
|
|
2576
|
+
}
|
|
2577
|
+
const detail = await phplistApiFetch(authOptions, `/campaigns/${id}`);
|
|
2578
|
+
const item = messageToNewsletter(detail, listSlug, config.newsletterRouteBase);
|
|
2579
|
+
if (item) {
|
|
2580
|
+
newItems.push(item);
|
|
2581
|
+
knownIds.add(id);
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
const lastId = items[items.length - 1]?.id;
|
|
2585
|
+
if (!lastId || items.length < 100) {
|
|
2586
|
+
hasMore = false;
|
|
2587
|
+
}
|
|
2588
|
+
else {
|
|
2589
|
+
afterId = lastId;
|
|
2590
|
+
const oldestSent = items
|
|
2591
|
+
.map((m) => m.message_metadata?.sent)
|
|
2592
|
+
.filter(Boolean)
|
|
2593
|
+
.map((s) => Date.parse(s))
|
|
2594
|
+
.filter((t) => !Number.isNaN(t));
|
|
2595
|
+
if (lastSentAt > 0 && oldestSent.length && Math.min(...oldestSent) <= lastSentAt) {
|
|
2596
|
+
hasMore = false;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
const mergedNewsletters = [
|
|
2601
|
+
...(existing?.newsletters ?? []),
|
|
2602
|
+
...newItems,
|
|
2603
|
+
].sort((a, b) => Date.parse(b.sent_at) - Date.parse(a.sent_at));
|
|
2604
|
+
const subscribePages = await fetchSubscribePages(authOptions, config.subscribePageIds);
|
|
2605
|
+
const data = {
|
|
2606
|
+
generated_at: new Date().toISOString(),
|
|
2607
|
+
list: {
|
|
2608
|
+
id: list.id,
|
|
2609
|
+
name: list.name,
|
|
2610
|
+
slug: listSlug,
|
|
2611
|
+
description: list.description,
|
|
2612
|
+
},
|
|
2613
|
+
subscribe_pages: subscribePages,
|
|
2614
|
+
newsletters: mergedNewsletters,
|
|
2615
|
+
};
|
|
2616
|
+
writeStaticData(config.outputPath, data);
|
|
2617
|
+
console.log(`✅ PHPList: ${mergedNewsletters.length} newsletters (${newItems.length} new)`);
|
|
2618
|
+
return { success: true, data };
|
|
2619
|
+
}
|
|
2620
|
+
catch (error) {
|
|
2621
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2622
|
+
console.warn(`⚠️ PHPList generation failed: ${message}`);
|
|
2623
|
+
return { success: false, error: message };
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
async function fetchListMessageIds(authOptions, listId) {
|
|
2627
|
+
const response = await phplistApiFetch(authOptions, `/list-messages/list/${listId}/messages`);
|
|
2628
|
+
return (response.items ?? []).map((row) => row.message_id).filter(Boolean);
|
|
2629
|
+
}
|
|
2630
|
+
async function fetchSubscribePages(authOptions, pageIds) {
|
|
2631
|
+
const pages = [];
|
|
2632
|
+
for (const pageId of pageIds) {
|
|
2633
|
+
try {
|
|
2634
|
+
const publicPage = await phplistApiFetch(authOptions, `/public/subscribe-pages/${pageId}`);
|
|
2635
|
+
pages.push({
|
|
2636
|
+
id: pageId,
|
|
2637
|
+
title: publicPage.title,
|
|
2638
|
+
intro: publicPage.intro,
|
|
2639
|
+
attributes: publicPage.attributes ?? [],
|
|
2640
|
+
list_ids: publicPage.list_ids,
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
catch (e) {
|
|
2644
|
+
console.warn(`⚠️ Could not load subscribe page ${pageId}:`, e.message);
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
return pages;
|
|
2648
|
+
}
|
|
2649
|
+
function messageToNewsletter(message, listSlug, _routeBase) {
|
|
2650
|
+
const subject = message.message_content?.subject?.trim();
|
|
2651
|
+
const html = message.message_content?.text ?? '';
|
|
2652
|
+
const sentAt = message.message_metadata?.sent;
|
|
2653
|
+
if (!subject || !sentAt || message.message_metadata?.status !== 'sent') {
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
const slug = buildNewsletterSlug(listSlug, message.id, subject);
|
|
2657
|
+
return {
|
|
2658
|
+
id: message.id,
|
|
2659
|
+
unique_id: message.unique_id,
|
|
2660
|
+
slug,
|
|
2661
|
+
subject,
|
|
2662
|
+
sent_at: sentAt,
|
|
2663
|
+
excerpt: stripHtmlExcerpt(html),
|
|
2664
|
+
html,
|
|
2665
|
+
plain_text: message.message_content?.text_message,
|
|
2666
|
+
view_url: buildViewProxyPath(message.id),
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
function readExistingData(outputPath) {
|
|
2670
|
+
try {
|
|
2671
|
+
if (!fs.existsSync(outputPath))
|
|
2672
|
+
return null;
|
|
2673
|
+
return JSON.parse(fs.readFileSync(outputPath, 'utf8'));
|
|
2674
|
+
}
|
|
2675
|
+
catch {
|
|
2676
|
+
return null;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
function writeStaticData(outputPath, data) {
|
|
2680
|
+
const dir = path.dirname(outputPath);
|
|
2681
|
+
if (!fs.existsSync(dir)) {
|
|
2682
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
2683
|
+
}
|
|
2684
|
+
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2), 'utf8');
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
function parseSubscribePageIds(raw) {
|
|
2688
|
+
if (!raw?.trim())
|
|
2689
|
+
return [];
|
|
2690
|
+
return raw
|
|
2691
|
+
.split(',')
|
|
2692
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
2693
|
+
.filter((n) => !Number.isNaN(n));
|
|
2694
|
+
}
|
|
2695
|
+
async function generatePhplistData(options) {
|
|
2696
|
+
const outputPath = options?.outputPath || './public/phplist-data.json';
|
|
2697
|
+
const phplistUrl = process.env.PHPLIST_URL || process.env.VITE_PHPLIST_URL;
|
|
2698
|
+
const listIdRaw = process.env.PHPLIST_LIST_ID || process.env.VITE_PHPLIST_LIST_ID;
|
|
2699
|
+
const apiUser = process.env.PHPLIST_API_USER || process.env.VITE_PHPLIST_API_USER;
|
|
2700
|
+
const apiPassword = process.env.PHPLIST_API_PASSWORD || process.env.VITE_PHPLIST_API_PASSWORD;
|
|
2701
|
+
const proxyUrl = process.env.PHPLIST_PROXY_URL || process.env.VITE_PHPLIST_PROXY_URL;
|
|
2702
|
+
const subscribePageIds = parseSubscribePageIds(process.env.PHPLIST_SUBSCRIBE_PAGE_IDS || process.env.VITE_PHPLIST_SUBSCRIBE_PAGE_IDS);
|
|
2703
|
+
const newsletterRouteBase = process.env.VITE_PHPLIST_NEWSLETTER_ROUTE_BASE || '/newsletters';
|
|
2704
|
+
const cfAccessClientId = process.env.CF_ACCESS_CLIENT_ID || process.env.VITE_CF_ACCESS_CLIENT_ID;
|
|
2705
|
+
const cfAccessClientSecret = process.env.CF_ACCESS_CLIENT_SECRET || process.env.VITE_CF_ACCESS_CLIENT_SECRET;
|
|
2706
|
+
if (!phplistUrl) {
|
|
2707
|
+
console.log('⚠️ PHPList URL not configured. Skipping PHPList data generation.');
|
|
2708
|
+
return true;
|
|
2709
|
+
}
|
|
2710
|
+
const listId = parseInt(listIdRaw || '0', 10);
|
|
2711
|
+
if (!listId || Number.isNaN(listId)) {
|
|
2712
|
+
console.warn('⚠️ PHPList list ID not configured. Skipping PHPList data generation.');
|
|
2713
|
+
return true;
|
|
2714
|
+
}
|
|
2715
|
+
if (!apiUser || !apiPassword) {
|
|
2716
|
+
console.warn('⚠️ PHPList API credentials missing. Skipping PHPList data generation.');
|
|
2717
|
+
return true;
|
|
2718
|
+
}
|
|
2719
|
+
try {
|
|
2720
|
+
const authMode = proxyUrl ? 'cloudflare_proxy' : 'direct';
|
|
2721
|
+
const result = await generatePhplistData$1({
|
|
2722
|
+
authMode,
|
|
2723
|
+
apiUrl: phplistUrl,
|
|
2724
|
+
cloudflareWorkerUrl: authMode === 'cloudflare_proxy' ? proxyUrl : undefined,
|
|
2725
|
+
apiUser,
|
|
2726
|
+
apiPassword,
|
|
2727
|
+
listId,
|
|
2728
|
+
subscribePageIds,
|
|
2729
|
+
outputPath,
|
|
2730
|
+
newsletterRouteBase,
|
|
2731
|
+
cfAccessClientId,
|
|
2732
|
+
cfAccessClientSecret,
|
|
2733
|
+
});
|
|
2734
|
+
if (result.success) {
|
|
2735
|
+
console.log(`📁 PHPList data written to: ${path.join(process.cwd(), outputPath)}`);
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
console.warn(`⚠️ PHPList generation completed with warnings: ${result.error ?? 'unknown'}`);
|
|
2739
|
+
}
|
|
2740
|
+
return true;
|
|
2741
|
+
}
|
|
2742
|
+
catch (e) {
|
|
2743
|
+
console.warn(`⚠️ PHPList generation failed: ${e.message}`);
|
|
2744
|
+
return true;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2438
2748
|
async function generateSuiteCRMData(options) {
|
|
2439
2749
|
const outputPath = options?.outputPath || './src/data/suitecrm-data.json';
|
|
2440
2750
|
const apiUrl = process.env.VITE_SUITECRM_URL;
|
|
@@ -2782,6 +3092,7 @@ async function generateAllData() {
|
|
|
2782
3092
|
const wpSuccess = await generateWordPressData();
|
|
2783
3093
|
const gfSuccess = await generateGravityFormsData();
|
|
2784
3094
|
const mauticSuccess = await generateMauticData();
|
|
3095
|
+
await generatePhplistData();
|
|
2785
3096
|
const suitecrmSuccess = await generateSuiteCRMData();
|
|
2786
3097
|
const googleReviewsSuccess = await generateGoogleReviewsData();
|
|
2787
3098
|
if (!wpSuccess || !gfSuccess || !mauticSuccess || !suitecrmSuccess || !googleReviewsSuccess) {
|
|
@@ -2799,6 +3110,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2799
3110
|
generateGoogleReviewsData: generateGoogleReviewsData,
|
|
2800
3111
|
generateGravityFormsData: generateGravityFormsData,
|
|
2801
3112
|
generateMauticData: generateMauticData,
|
|
3113
|
+
generatePhplistData: generatePhplistData,
|
|
2802
3114
|
generateSitemapPaths: generateSitemapPaths,
|
|
2803
3115
|
generateSitemapPathsSafe: generateSitemapPathsSafe,
|
|
2804
3116
|
generateSuiteCRMData: generateSuiteCRMData,
|
|
@@ -2821,6 +3133,7 @@ exports.generateAllData = generateAllData;
|
|
|
2821
3133
|
exports.generateGoogleReviewsData = generateGoogleReviewsData;
|
|
2822
3134
|
exports.generateGravityFormsData = generateGravityFormsData;
|
|
2823
3135
|
exports.generateMauticData = generateMauticData;
|
|
3136
|
+
exports.generatePhplistData = generatePhplistData;
|
|
2824
3137
|
exports.generateSitemapPaths = generateSitemapPaths;
|
|
2825
3138
|
exports.generateSitemapPathsSafe = generateSitemapPathsSafe;
|
|
2826
3139
|
exports.generateSuiteCRMData = generateSuiteCRMData;
|