perspectapi-ts-sdk 1.1.1 → 1.3.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/README.md +127 -0
- package/dist/index.d.mts +285 -10
- package/dist/index.d.ts +285 -10
- package/dist/index.js +226 -18
- package/dist/index.mjs +225 -18
- package/package.json +1 -1
- package/src/client/base-client.ts +11 -8
- package/src/client/contact-client.ts +14 -2
- package/src/client/newsletter-client.ts +381 -0
- package/src/index.ts +9 -0
- package/src/perspect-api-client.ts +3 -0
- package/src/types/index.ts +78 -0
- package/src/utils/http-client.ts +13 -8
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter subscription client for PerspectAPI SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseClient } from './base-client';
|
|
6
|
+
import type {
|
|
7
|
+
NewsletterSubscription,
|
|
8
|
+
CreateNewsletterSubscriptionRequest,
|
|
9
|
+
NewsletterList,
|
|
10
|
+
NewsletterPreferences,
|
|
11
|
+
NewsletterStatusResponse,
|
|
12
|
+
NewsletterSubscribeResponse,
|
|
13
|
+
NewsletterConfirmResponse,
|
|
14
|
+
NewsletterUnsubscribeRequest,
|
|
15
|
+
PaginatedResponse,
|
|
16
|
+
ApiResponse,
|
|
17
|
+
} from '../types';
|
|
18
|
+
|
|
19
|
+
export class NewsletterClient extends BaseClient {
|
|
20
|
+
constructor(http: any) {
|
|
21
|
+
super(http, '/api/v1');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build a newsletter endpoint scoped to a site (without /sites prefix)
|
|
26
|
+
*/
|
|
27
|
+
private newsletterEndpoint(siteName: string, endpoint: string): string {
|
|
28
|
+
return this.siteScopedEndpoint(siteName, endpoint, { includeSitesSegment: false });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Subscribe to newsletter
|
|
33
|
+
* @param siteName - The site to subscribe to
|
|
34
|
+
* @param data - Subscription data
|
|
35
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
36
|
+
*/
|
|
37
|
+
async subscribe(
|
|
38
|
+
siteName: string,
|
|
39
|
+
data: CreateNewsletterSubscriptionRequest,
|
|
40
|
+
csrfToken?: string
|
|
41
|
+
): Promise<ApiResponse<NewsletterSubscribeResponse>> {
|
|
42
|
+
// CSRF token is required for browser submissions
|
|
43
|
+
if (typeof window !== 'undefined' && !csrfToken && !data.turnstile_token) {
|
|
44
|
+
console.warn('CSRF token recommended for browser-based newsletter subscriptions');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.create<CreateNewsletterSubscriptionRequest, NewsletterSubscribeResponse>(
|
|
48
|
+
this.newsletterEndpoint(siteName, '/newsletter/subscribe'),
|
|
49
|
+
data,
|
|
50
|
+
csrfToken
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Confirm newsletter subscription via token
|
|
56
|
+
*/
|
|
57
|
+
async confirmSubscription(
|
|
58
|
+
siteName: string,
|
|
59
|
+
token: string
|
|
60
|
+
): Promise<ApiResponse<NewsletterConfirmResponse>> {
|
|
61
|
+
return this.getSingle(
|
|
62
|
+
this.newsletterEndpoint(siteName, `/newsletter/confirm/${encodeURIComponent(token)}`)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Unsubscribe from newsletter
|
|
68
|
+
* @param siteName - The site to unsubscribe from
|
|
69
|
+
* @param data - Unsubscribe data
|
|
70
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
71
|
+
*/
|
|
72
|
+
async unsubscribe(
|
|
73
|
+
siteName: string,
|
|
74
|
+
data: NewsletterUnsubscribeRequest,
|
|
75
|
+
csrfToken?: string
|
|
76
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
77
|
+
return this.create<NewsletterUnsubscribeRequest, { message: string }>(
|
|
78
|
+
this.newsletterEndpoint(siteName, '/newsletter/unsubscribe'),
|
|
79
|
+
data,
|
|
80
|
+
csrfToken
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* One-click unsubscribe via token (GET request)
|
|
86
|
+
*/
|
|
87
|
+
async unsubscribeByToken(
|
|
88
|
+
siteName: string,
|
|
89
|
+
token: string
|
|
90
|
+
): Promise<ApiResponse<string>> {
|
|
91
|
+
// This returns HTML, so we return the raw response
|
|
92
|
+
return this.http.get(
|
|
93
|
+
this.buildPath(this.newsletterEndpoint(siteName, `/newsletter/unsubscribe/${encodeURIComponent(token)}`))
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update subscription preferences
|
|
99
|
+
* @param siteName - The site name
|
|
100
|
+
* @param email - Subscriber email
|
|
101
|
+
* @param preferences - New preferences
|
|
102
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
103
|
+
*/
|
|
104
|
+
async updatePreferences(
|
|
105
|
+
siteName: string,
|
|
106
|
+
email: string,
|
|
107
|
+
preferences: NewsletterPreferences,
|
|
108
|
+
csrfToken?: string
|
|
109
|
+
): Promise<ApiResponse<{ message: string; preferences: NewsletterPreferences }>> {
|
|
110
|
+
return this.patch(
|
|
111
|
+
this.newsletterEndpoint(siteName, '/newsletter/preferences'),
|
|
112
|
+
{ email, ...preferences },
|
|
113
|
+
csrfToken
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get available newsletter lists
|
|
119
|
+
*/
|
|
120
|
+
async getLists(siteName: string): Promise<ApiResponse<{
|
|
121
|
+
lists: NewsletterList[];
|
|
122
|
+
total: number;
|
|
123
|
+
}>> {
|
|
124
|
+
return this.getSingle(
|
|
125
|
+
this.newsletterEndpoint(siteName, '/newsletter/lists')
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check subscription status by email
|
|
131
|
+
*/
|
|
132
|
+
async getStatus(
|
|
133
|
+
siteName: string,
|
|
134
|
+
email: string
|
|
135
|
+
): Promise<ApiResponse<NewsletterStatusResponse>> {
|
|
136
|
+
return this.http.get(
|
|
137
|
+
this.buildPath(this.newsletterEndpoint(siteName, '/newsletter/status')),
|
|
138
|
+
{ email }
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Admin methods (require authentication)
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get all newsletter subscriptions (admin only)
|
|
146
|
+
*/
|
|
147
|
+
async getSubscriptions(
|
|
148
|
+
siteName: string,
|
|
149
|
+
params?: {
|
|
150
|
+
page?: number;
|
|
151
|
+
limit?: number;
|
|
152
|
+
status?: string;
|
|
153
|
+
list_id?: string;
|
|
154
|
+
search?: string;
|
|
155
|
+
startDate?: string;
|
|
156
|
+
endDate?: string;
|
|
157
|
+
}
|
|
158
|
+
): Promise<PaginatedResponse<NewsletterSubscription>> {
|
|
159
|
+
return this.getPaginated<NewsletterSubscription>(
|
|
160
|
+
this.newsletterEndpoint(siteName, '/newsletter/subscriptions'),
|
|
161
|
+
params
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get subscription by ID (admin only)
|
|
167
|
+
*/
|
|
168
|
+
async getSubscriptionById(
|
|
169
|
+
siteName: string,
|
|
170
|
+
id: string
|
|
171
|
+
): Promise<ApiResponse<NewsletterSubscription>> {
|
|
172
|
+
return this.getSingle(
|
|
173
|
+
this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Update subscription status (admin only)
|
|
179
|
+
*/
|
|
180
|
+
async updateSubscriptionStatus(
|
|
181
|
+
siteName: string,
|
|
182
|
+
id: string,
|
|
183
|
+
status: 'confirmed' | 'unsubscribed' | 'bounced' | 'complained',
|
|
184
|
+
notes?: string
|
|
185
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
186
|
+
return this.patch(
|
|
187
|
+
this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`),
|
|
188
|
+
{ status, notes }
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Delete subscription (admin only)
|
|
194
|
+
*/
|
|
195
|
+
async deleteSubscription(
|
|
196
|
+
siteName: string,
|
|
197
|
+
id: string
|
|
198
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
199
|
+
return this.delete<{ message: string }>(
|
|
200
|
+
this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Bulk update subscriptions (admin only)
|
|
206
|
+
*/
|
|
207
|
+
async bulkUpdateSubscriptions(
|
|
208
|
+
siteName: string,
|
|
209
|
+
data: {
|
|
210
|
+
ids: string[];
|
|
211
|
+
action: 'confirm' | 'unsubscribe' | 'delete' | 'add_to_list' | 'remove_from_list';
|
|
212
|
+
list_id?: string;
|
|
213
|
+
}
|
|
214
|
+
): Promise<ApiResponse<{
|
|
215
|
+
success: boolean;
|
|
216
|
+
updatedCount: number;
|
|
217
|
+
failedCount: number;
|
|
218
|
+
}>> {
|
|
219
|
+
return this.create(
|
|
220
|
+
this.newsletterEndpoint(siteName, '/newsletter/subscriptions/bulk-update'),
|
|
221
|
+
data
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create newsletter list (admin only)
|
|
227
|
+
*/
|
|
228
|
+
async createList(
|
|
229
|
+
siteName: string,
|
|
230
|
+
data: {
|
|
231
|
+
list_name: string;
|
|
232
|
+
slug: string;
|
|
233
|
+
description?: string;
|
|
234
|
+
is_public?: boolean;
|
|
235
|
+
is_default?: boolean;
|
|
236
|
+
double_opt_in?: boolean;
|
|
237
|
+
welcome_email_enabled?: boolean;
|
|
238
|
+
}
|
|
239
|
+
): Promise<ApiResponse<NewsletterList>> {
|
|
240
|
+
return this.create<any, NewsletterList>(
|
|
241
|
+
this.newsletterEndpoint(siteName, '/newsletter/lists'),
|
|
242
|
+
data
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Update newsletter list (admin only)
|
|
248
|
+
*/
|
|
249
|
+
async updateList(
|
|
250
|
+
siteName: string,
|
|
251
|
+
listId: string,
|
|
252
|
+
data: Partial<{
|
|
253
|
+
list_name: string;
|
|
254
|
+
description: string;
|
|
255
|
+
is_public: boolean;
|
|
256
|
+
is_default: boolean;
|
|
257
|
+
double_opt_in: boolean;
|
|
258
|
+
welcome_email_enabled: boolean;
|
|
259
|
+
}>
|
|
260
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
261
|
+
return this.update(
|
|
262
|
+
this.newsletterEndpoint(siteName, `/newsletter/lists/${encodeURIComponent(listId)}`),
|
|
263
|
+
data
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Delete newsletter list (admin only)
|
|
269
|
+
*/
|
|
270
|
+
async deleteList(
|
|
271
|
+
siteName: string,
|
|
272
|
+
listId: string
|
|
273
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
274
|
+
return this.delete<{ message: string }>(
|
|
275
|
+
this.newsletterEndpoint(siteName, `/newsletter/lists/${encodeURIComponent(listId)}`)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get newsletter statistics (admin only)
|
|
281
|
+
*/
|
|
282
|
+
async getStatistics(
|
|
283
|
+
siteName: string,
|
|
284
|
+
params?: {
|
|
285
|
+
startDate?: string;
|
|
286
|
+
endDate?: string;
|
|
287
|
+
list_id?: string;
|
|
288
|
+
}
|
|
289
|
+
): Promise<ApiResponse<{
|
|
290
|
+
totalSubscribers: number;
|
|
291
|
+
confirmedSubscribers: number;
|
|
292
|
+
pendingSubscribers: number;
|
|
293
|
+
unsubscribedCount: number;
|
|
294
|
+
bouncedCount: number;
|
|
295
|
+
subscribersByDay: Array<{
|
|
296
|
+
date: string;
|
|
297
|
+
count: number;
|
|
298
|
+
}>;
|
|
299
|
+
subscribersByList: Array<{
|
|
300
|
+
list_id: string;
|
|
301
|
+
list_name: string;
|
|
302
|
+
count: number;
|
|
303
|
+
}>;
|
|
304
|
+
engagementMetrics: {
|
|
305
|
+
averageOpenRate: number;
|
|
306
|
+
averageClickRate: number;
|
|
307
|
+
};
|
|
308
|
+
}>> {
|
|
309
|
+
return this.http.get(
|
|
310
|
+
this.buildPath(this.newsletterEndpoint(siteName, '/newsletter/statistics')),
|
|
311
|
+
params
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Export newsletter subscriptions (admin only)
|
|
317
|
+
*/
|
|
318
|
+
async exportSubscriptions(
|
|
319
|
+
siteName: string,
|
|
320
|
+
params?: {
|
|
321
|
+
format?: 'csv' | 'json' | 'xlsx';
|
|
322
|
+
status?: string;
|
|
323
|
+
list_id?: string;
|
|
324
|
+
startDate?: string;
|
|
325
|
+
endDate?: string;
|
|
326
|
+
}
|
|
327
|
+
): Promise<ApiResponse<{
|
|
328
|
+
downloadUrl: string;
|
|
329
|
+
expiresAt: string;
|
|
330
|
+
}>> {
|
|
331
|
+
return this.create(
|
|
332
|
+
this.newsletterEndpoint(siteName, '/newsletter/export'),
|
|
333
|
+
params || {}
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Import newsletter subscriptions (admin only)
|
|
339
|
+
*/
|
|
340
|
+
async importSubscriptions(
|
|
341
|
+
siteName: string,
|
|
342
|
+
data: {
|
|
343
|
+
subscriptions: Array<{
|
|
344
|
+
email: string;
|
|
345
|
+
name?: string;
|
|
346
|
+
status?: string;
|
|
347
|
+
lists?: string[];
|
|
348
|
+
}>;
|
|
349
|
+
skip_confirmation?: boolean;
|
|
350
|
+
update_existing?: boolean;
|
|
351
|
+
}
|
|
352
|
+
): Promise<ApiResponse<{
|
|
353
|
+
imported: number;
|
|
354
|
+
updated: number;
|
|
355
|
+
failed: number;
|
|
356
|
+
errors?: Array<{ email: string; error: string }>;
|
|
357
|
+
}>> {
|
|
358
|
+
return this.create(
|
|
359
|
+
this.newsletterEndpoint(siteName, '/newsletter/import'),
|
|
360
|
+
data
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Send test newsletter (admin only)
|
|
366
|
+
*/
|
|
367
|
+
async sendTestNewsletter(
|
|
368
|
+
siteName: string,
|
|
369
|
+
data: {
|
|
370
|
+
to: string;
|
|
371
|
+
subject: string;
|
|
372
|
+
html_content: string;
|
|
373
|
+
text_content?: string;
|
|
374
|
+
}
|
|
375
|
+
): Promise<ApiResponse<{ message: string; sent: boolean }>> {
|
|
376
|
+
return this.create(
|
|
377
|
+
this.newsletterEndpoint(siteName, '/newsletter/test'),
|
|
378
|
+
data
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { CategoriesClient } from './client/categories-client';
|
|
|
18
18
|
export { WebhooksClient } from './client/webhooks-client';
|
|
19
19
|
export { CheckoutClient } from './client/checkout-client';
|
|
20
20
|
export { ContactClient } from './client/contact-client';
|
|
21
|
+
export { NewsletterClient } from './client/newsletter-client';
|
|
21
22
|
|
|
22
23
|
// Base classes
|
|
23
24
|
export { BaseClient } from './client/base-client';
|
|
@@ -71,4 +72,12 @@ export type {
|
|
|
71
72
|
ContactSubmission,
|
|
72
73
|
CheckoutSession,
|
|
73
74
|
PaymentGateway,
|
|
75
|
+
NewsletterSubscription,
|
|
76
|
+
CreateNewsletterSubscriptionRequest,
|
|
77
|
+
NewsletterList,
|
|
78
|
+
NewsletterPreferences,
|
|
79
|
+
NewsletterStatusResponse,
|
|
80
|
+
NewsletterSubscribeResponse,
|
|
81
|
+
NewsletterConfirmResponse,
|
|
82
|
+
NewsletterUnsubscribeRequest,
|
|
74
83
|
} from './types';
|
|
@@ -14,6 +14,7 @@ import { CategoriesClient } from './client/categories-client';
|
|
|
14
14
|
import { WebhooksClient } from './client/webhooks-client';
|
|
15
15
|
import { CheckoutClient } from './client/checkout-client';
|
|
16
16
|
import { ContactClient } from './client/contact-client';
|
|
17
|
+
import { NewsletterClient } from './client/newsletter-client';
|
|
17
18
|
|
|
18
19
|
import type { PerspectApiConfig, ApiResponse } from './types';
|
|
19
20
|
|
|
@@ -31,6 +32,7 @@ export class PerspectApiClient {
|
|
|
31
32
|
public readonly webhooks: WebhooksClient;
|
|
32
33
|
public readonly checkout: CheckoutClient;
|
|
33
34
|
public readonly contact: ContactClient;
|
|
35
|
+
public readonly newsletter: NewsletterClient;
|
|
34
36
|
|
|
35
37
|
constructor(config: PerspectApiConfig) {
|
|
36
38
|
// Validate required configuration
|
|
@@ -52,6 +54,7 @@ export class PerspectApiClient {
|
|
|
52
54
|
this.webhooks = new WebhooksClient(this.http);
|
|
53
55
|
this.checkout = new CheckoutClient(this.http);
|
|
54
56
|
this.contact = new ContactClient(this.http);
|
|
57
|
+
this.newsletter = new NewsletterClient(this.http);
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/**
|
package/src/types/index.ts
CHANGED
|
@@ -119,6 +119,83 @@ export interface CreateSiteRequest {
|
|
|
119
119
|
organizationId: number;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Newsletter Subscriptions
|
|
123
|
+
export interface NewsletterSubscription {
|
|
124
|
+
id: string;
|
|
125
|
+
email: string;
|
|
126
|
+
name?: string;
|
|
127
|
+
status: 'pending' | 'confirmed' | 'unsubscribed' | 'bounced' | 'complained';
|
|
128
|
+
frequency: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
129
|
+
topics?: string[];
|
|
130
|
+
language: string;
|
|
131
|
+
confirmedAt?: string;
|
|
132
|
+
unsubscribedAt?: string;
|
|
133
|
+
createdAt: string;
|
|
134
|
+
updatedAt: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface CreateNewsletterSubscriptionRequest {
|
|
138
|
+
email: string;
|
|
139
|
+
name?: string;
|
|
140
|
+
list_ids?: string[];
|
|
141
|
+
frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
142
|
+
topics?: string[];
|
|
143
|
+
language?: string;
|
|
144
|
+
source?: string;
|
|
145
|
+
source_url?: string;
|
|
146
|
+
double_opt_in?: boolean;
|
|
147
|
+
turnstile_token?: string;
|
|
148
|
+
metadata?: Record<string, any>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface NewsletterList {
|
|
152
|
+
id: string;
|
|
153
|
+
list_name: string;
|
|
154
|
+
slug: string;
|
|
155
|
+
description?: string;
|
|
156
|
+
is_default: boolean;
|
|
157
|
+
subscriber_count?: number;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface NewsletterPreferences {
|
|
161
|
+
frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
162
|
+
topics?: string[];
|
|
163
|
+
language?: string;
|
|
164
|
+
email_format?: 'html' | 'text' | 'both';
|
|
165
|
+
timezone?: string;
|
|
166
|
+
track_opens?: boolean;
|
|
167
|
+
track_clicks?: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface NewsletterStatusResponse {
|
|
171
|
+
subscribed: boolean;
|
|
172
|
+
status: string;
|
|
173
|
+
frequency?: string;
|
|
174
|
+
created_at?: string;
|
|
175
|
+
confirmed_at?: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface NewsletterSubscribeResponse {
|
|
179
|
+
message: string;
|
|
180
|
+
status: string;
|
|
181
|
+
subscription_id?: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface NewsletterConfirmResponse {
|
|
185
|
+
message: string;
|
|
186
|
+
subscription?: {
|
|
187
|
+
email: string;
|
|
188
|
+
name?: string;
|
|
189
|
+
frequency: string;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface NewsletterUnsubscribeRequest {
|
|
194
|
+
token?: string;
|
|
195
|
+
email?: string;
|
|
196
|
+
reason?: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
122
199
|
// Content Management
|
|
123
200
|
export type ContentStatus = 'draft' | 'publish' | 'private' | 'trash';
|
|
124
201
|
export type ContentType = 'post' | 'page';
|
|
@@ -396,4 +473,5 @@ export interface RequestOptions {
|
|
|
396
473
|
body?: any;
|
|
397
474
|
params?: Record<string, string | number | boolean>;
|
|
398
475
|
timeout?: number;
|
|
476
|
+
csrfToken?: string; // Optional CSRF token for protected endpoints
|
|
399
477
|
}
|
package/src/utils/http-client.ts
CHANGED
|
@@ -115,29 +115,29 @@ export class HttpClient {
|
|
|
115
115
|
/**
|
|
116
116
|
* POST request
|
|
117
117
|
*/
|
|
118
|
-
async post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
119
|
-
return this.request<T>(endpoint, { method: 'POST', body });
|
|
118
|
+
async post<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
119
|
+
return this.request<T>(endpoint, { method: 'POST', body, ...options });
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* PUT request
|
|
124
124
|
*/
|
|
125
|
-
async put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
126
|
-
return this.request<T>(endpoint, { method: 'PUT', body });
|
|
125
|
+
async put<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
126
|
+
return this.request<T>(endpoint, { method: 'PUT', body, ...options });
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
130
|
* DELETE request
|
|
131
131
|
*/
|
|
132
|
-
async delete<T = any>(endpoint: string): Promise<ApiResponse<T>> {
|
|
133
|
-
return this.request<T>(endpoint, { method: 'DELETE' });
|
|
132
|
+
async delete<T = any>(endpoint: string, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
133
|
+
return this.request<T>(endpoint, { method: 'DELETE', ...options });
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* PATCH request
|
|
138
138
|
*/
|
|
139
|
-
async patch<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
140
|
-
return this.request<T>(endpoint, { method: 'PATCH', body });
|
|
139
|
+
async patch<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
140
|
+
return this.request<T>(endpoint, { method: 'PATCH', body, ...options });
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
@@ -169,6 +169,11 @@ export class HttpClient {
|
|
|
169
169
|
...options.headers,
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
// Add CSRF token if provided
|
|
173
|
+
if (options.csrfToken) {
|
|
174
|
+
headers['X-CSRF-Token'] = options.csrfToken;
|
|
175
|
+
}
|
|
176
|
+
|
|
172
177
|
const requestOptions: RequestInit = {
|
|
173
178
|
method: options.method || 'GET',
|
|
174
179
|
headers,
|