@consilioweb/payload-support 3.0.0 → 5.0.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 +58 -14
- package/dist/index.cjs +673 -107
- package/dist/index.d.cts +40 -5
- package/dist/index.d.ts +40 -5
- package/dist/index.js +673 -107
- package/dist/utils/db.d.ts +34 -0
- package/dist/utils/readSettings.d.ts +90 -0
- package/dist/views/BillingView/index.js +4 -4
- package/dist/views/ChatView/index.js +4 -4
- package/dist/views/CrmView/index.js +4 -4
- package/dist/views/EmailTrackingView/index.js +4 -4
- package/dist/views/ImportConversationView/index.js +4 -4
- package/dist/views/LogsView/index.js +4 -2
- package/dist/views/NewTicketView/index.js +4 -2
- package/dist/views/PendingEmailsView/index.js +4 -4
- package/dist/views/SupportDashboardView/index.js +4 -4
- package/dist/views/TicketDetailView/index.js +4 -4
- package/dist/views/TicketInboxView/index.js +4 -2
- package/dist/views/TicketingSettingsView/index.js +4 -4
- package/dist/views/TimeDashboardView/index.js +4 -4
- package/dist/views/shared/viewAccess.d.ts +29 -0
- package/dist/views/shared/viewAccess.js +24 -0
- package/package.json +26 -20
- package/src/collections/ChatMessages.ts +59 -2
- package/src/collections/ClientSummaries.ts +10 -4
- package/src/collections/TicketMessages.ts +4 -1
- package/src/collections/WebhookEndpoints.ts +44 -2
- package/src/endpoints/admin-chat.ts +3 -3
- package/src/endpoints/ai-agent.ts +3 -3
- package/src/endpoints/ai.ts +3 -3
- package/src/endpoints/auth-2fa.ts +68 -11
- package/src/endpoints/capabilities.ts +7 -9
- package/src/endpoints/chat.ts +7 -5
- package/src/endpoints/chatbot.ts +50 -4
- package/src/endpoints/client-intelligence.ts +4 -4
- package/src/endpoints/email-stats.ts +19 -3
- package/src/endpoints/import-conversation.ts +3 -3
- package/src/endpoints/index.ts +1 -1
- package/src/endpoints/invite-collaborator.ts +29 -3
- package/src/endpoints/login.ts +28 -5
- package/src/endpoints/oauth-google.ts +130 -8
- package/src/endpoints/push.ts +14 -1
- package/src/endpoints/resend-notification.ts +3 -3
- package/src/endpoints/send-reminder.ts +3 -3
- package/src/endpoints/signature.ts +9 -2
- package/src/endpoints/statuses.ts +17 -0
- package/src/endpoints/ticket-synthesis.ts +3 -3
- package/src/endpoints/transfer-ticket.ts +28 -3
- package/src/endpoints/typing.ts +117 -14
- package/src/endpoints/user-prefs.ts +5 -2
- package/src/plugin.ts +12 -0
- package/src/portal/auth/layout.tsx +19 -1
- package/src/portal/auth/tickets/detail/MessageBody.tsx +88 -0
- package/src/portal/auth/tickets/detail/page.tsx +2 -6
- package/src/portal/login/page.tsx +23 -5
- package/src/utils/fireWebhooks.ts +4 -1
- package/src/utils/push.ts +22 -0
- package/src/utils/rateLimiter.ts +136 -4
- package/src/utils/readSettings.ts +124 -14
- package/src/utils/ticketAccess.ts +16 -1
- package/src/utils/twoFactorChallenge.ts +85 -0
- package/src/utils/urlSafety.ts +265 -0
- package/src/utils/webhookDispatcher.ts +5 -1
- package/src/views/BillingView/index.tsx +4 -4
- package/src/views/ChatView/index.tsx +4 -4
- package/src/views/CrmView/index.tsx +4 -4
- package/src/views/EmailTrackingView/index.tsx +4 -4
- package/src/views/ImportConversationView/index.tsx +4 -4
- package/src/views/LogsView/index.tsx +4 -2
- package/src/views/NewTicketView/index.tsx +4 -2
- package/src/views/PendingEmailsView/index.tsx +4 -4
- package/src/views/SupportDashboardView/index.tsx +4 -4
- package/src/views/TicketDetailView/index.tsx +4 -4
- package/src/views/TicketInboxView/index.tsx +4 -2
- package/src/views/TicketingSettingsView/index.tsx +4 -4
- package/src/views/TimeDashboardView/index.tsx +4 -4
- package/src/views/shared/viewAccess.ts +73 -0
package/dist/index.d.cts
CHANGED
|
@@ -11,9 +11,29 @@ interface RateLimitStore {
|
|
|
11
11
|
/**
|
|
12
12
|
* Process-local fallback store. Applications running more than one process
|
|
13
13
|
* should provide a persistent RateLimitStore through the plugin options.
|
|
14
|
+
*
|
|
15
|
+
* BOUNDED, because the keys come from anonymous HTTP traffic: `/support/login`
|
|
16
|
+
* and `/support/chatbot` key their limiter on the client IP, and the IP is read
|
|
17
|
+
* from `x-forwarded-for`. A caller rotating that header wrote one PERMANENT
|
|
18
|
+
* entry per value into a map that had no ceiling, no expiry sweep and no
|
|
19
|
+
* eviction — only `reset()` ever removed anything, and the login path never
|
|
20
|
+
* calls it. Worse, every fresh key opened a fresh window, so the limiter did
|
|
21
|
+
* not even slow down the flood that was exhausting it.
|
|
22
|
+
*
|
|
23
|
+
* Two independent bounds, the same pair `endpoints/typing.ts` already applies
|
|
24
|
+
* to its own module-level map: `clientIpRateKey` caps the SIZE of a key, the
|
|
25
|
+
* ceiling below caps their NUMBER.
|
|
14
26
|
*/
|
|
15
27
|
declare class MemoryRateLimitStore implements RateLimitStore {
|
|
28
|
+
private readonly maxKeys;
|
|
16
29
|
private readonly entries;
|
|
30
|
+
constructor(maxKeys?: number);
|
|
31
|
+
/** Distinct keys currently held. Exposed so the ceiling can be asserted. */
|
|
32
|
+
get size(): number;
|
|
33
|
+
/** Reclaims every window that has already closed. */
|
|
34
|
+
private sweepExpired;
|
|
35
|
+
/** Soonest-closing window first, so the ceiling drops the least useful entry. */
|
|
36
|
+
private evictOldest;
|
|
17
37
|
increment(key: string, windowMs: number): Promise<RateLimitEntry>;
|
|
18
38
|
reset(key: string): Promise<void>;
|
|
19
39
|
}
|
|
@@ -28,7 +48,19 @@ declare class RateLimiter {
|
|
|
28
48
|
private readonly windowMs;
|
|
29
49
|
private readonly maxRequests;
|
|
30
50
|
private readonly store;
|
|
31
|
-
|
|
51
|
+
private readonly prefix;
|
|
52
|
+
/**
|
|
53
|
+
* @param namespace Endpoint-scoped prefix for every key this limiter writes.
|
|
54
|
+
* The store is SHARED (`rateLimitStore: 'payload'` builds one instance for
|
|
55
|
+
* all endpoints), and the raw keys collide across endpoints: `ip` was used
|
|
56
|
+
* by both the login and the chatbot limiter — 10 forged chatbot requests
|
|
57
|
+
* locked a victim out of the portal for 15 minutes — and `String(user.id)`
|
|
58
|
+
* by five different endpoints. Always pass one; it is optional only because
|
|
59
|
+
* `RateLimiter` is part of the published API surface.
|
|
60
|
+
*/
|
|
61
|
+
constructor(windowMs: number, maxRequests: number, store?: RateLimitStore, namespace?: string);
|
|
62
|
+
/** The key actually written to the store. Exposed for assertions in tests. */
|
|
63
|
+
scopedKey(key: string): string;
|
|
32
64
|
check(key: string, context?: unknown): Promise<boolean>;
|
|
33
65
|
reset(key: string, context?: unknown): Promise<void>;
|
|
34
66
|
}
|
|
@@ -505,14 +537,17 @@ interface SupportSettingsState {
|
|
|
505
537
|
*/
|
|
506
538
|
featuresConfigured: boolean;
|
|
507
539
|
}
|
|
508
|
-
declare function readSupportSettingsState(payload: Payload): Promise<SupportSettingsState>;
|
|
509
|
-
declare function readSupportSettings(payload: Payload): Promise<SupportSettings>;
|
|
510
|
-
declare function readUserPrefs(payload: Payload, userId: string | number): Promise<UserPrefs>;
|
|
540
|
+
declare function readSupportSettingsState(payload: Payload, staffSlug?: string): Promise<SupportSettingsState>;
|
|
541
|
+
declare function readSupportSettings(payload: Payload, staffSlug?: string): Promise<SupportSettings>;
|
|
542
|
+
declare function readUserPrefs(payload: Payload, userId: string | number, staffSlug?: string): Promise<UserPrefs>;
|
|
511
543
|
|
|
512
544
|
/**
|
|
513
545
|
* POST /api/support/oauth/google
|
|
514
546
|
* Google OAuth — handles both login redirect and callback.
|
|
515
|
-
* Body: { action: 'login' } or { code: string, state: string
|
|
547
|
+
* Body: { action: 'login' } or { code: string, state: string }
|
|
548
|
+
*
|
|
549
|
+
* The CSRF state is NOT taken from the body: `action: 'login'` issues it as an
|
|
550
|
+
* HttpOnly cookie and the callback reads it back from the `Cookie` header.
|
|
516
551
|
*/
|
|
517
552
|
interface OAuthGoogleOptions {
|
|
518
553
|
allowedEmailDomains?: string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -11,9 +11,29 @@ interface RateLimitStore {
|
|
|
11
11
|
/**
|
|
12
12
|
* Process-local fallback store. Applications running more than one process
|
|
13
13
|
* should provide a persistent RateLimitStore through the plugin options.
|
|
14
|
+
*
|
|
15
|
+
* BOUNDED, because the keys come from anonymous HTTP traffic: `/support/login`
|
|
16
|
+
* and `/support/chatbot` key their limiter on the client IP, and the IP is read
|
|
17
|
+
* from `x-forwarded-for`. A caller rotating that header wrote one PERMANENT
|
|
18
|
+
* entry per value into a map that had no ceiling, no expiry sweep and no
|
|
19
|
+
* eviction — only `reset()` ever removed anything, and the login path never
|
|
20
|
+
* calls it. Worse, every fresh key opened a fresh window, so the limiter did
|
|
21
|
+
* not even slow down the flood that was exhausting it.
|
|
22
|
+
*
|
|
23
|
+
* Two independent bounds, the same pair `endpoints/typing.ts` already applies
|
|
24
|
+
* to its own module-level map: `clientIpRateKey` caps the SIZE of a key, the
|
|
25
|
+
* ceiling below caps their NUMBER.
|
|
14
26
|
*/
|
|
15
27
|
declare class MemoryRateLimitStore implements RateLimitStore {
|
|
28
|
+
private readonly maxKeys;
|
|
16
29
|
private readonly entries;
|
|
30
|
+
constructor(maxKeys?: number);
|
|
31
|
+
/** Distinct keys currently held. Exposed so the ceiling can be asserted. */
|
|
32
|
+
get size(): number;
|
|
33
|
+
/** Reclaims every window that has already closed. */
|
|
34
|
+
private sweepExpired;
|
|
35
|
+
/** Soonest-closing window first, so the ceiling drops the least useful entry. */
|
|
36
|
+
private evictOldest;
|
|
17
37
|
increment(key: string, windowMs: number): Promise<RateLimitEntry>;
|
|
18
38
|
reset(key: string): Promise<void>;
|
|
19
39
|
}
|
|
@@ -28,7 +48,19 @@ declare class RateLimiter {
|
|
|
28
48
|
private readonly windowMs;
|
|
29
49
|
private readonly maxRequests;
|
|
30
50
|
private readonly store;
|
|
31
|
-
|
|
51
|
+
private readonly prefix;
|
|
52
|
+
/**
|
|
53
|
+
* @param namespace Endpoint-scoped prefix for every key this limiter writes.
|
|
54
|
+
* The store is SHARED (`rateLimitStore: 'payload'` builds one instance for
|
|
55
|
+
* all endpoints), and the raw keys collide across endpoints: `ip` was used
|
|
56
|
+
* by both the login and the chatbot limiter — 10 forged chatbot requests
|
|
57
|
+
* locked a victim out of the portal for 15 minutes — and `String(user.id)`
|
|
58
|
+
* by five different endpoints. Always pass one; it is optional only because
|
|
59
|
+
* `RateLimiter` is part of the published API surface.
|
|
60
|
+
*/
|
|
61
|
+
constructor(windowMs: number, maxRequests: number, store?: RateLimitStore, namespace?: string);
|
|
62
|
+
/** The key actually written to the store. Exposed for assertions in tests. */
|
|
63
|
+
scopedKey(key: string): string;
|
|
32
64
|
check(key: string, context?: unknown): Promise<boolean>;
|
|
33
65
|
reset(key: string, context?: unknown): Promise<void>;
|
|
34
66
|
}
|
|
@@ -505,14 +537,17 @@ interface SupportSettingsState {
|
|
|
505
537
|
*/
|
|
506
538
|
featuresConfigured: boolean;
|
|
507
539
|
}
|
|
508
|
-
declare function readSupportSettingsState(payload: Payload): Promise<SupportSettingsState>;
|
|
509
|
-
declare function readSupportSettings(payload: Payload): Promise<SupportSettings>;
|
|
510
|
-
declare function readUserPrefs(payload: Payload, userId: string | number): Promise<UserPrefs>;
|
|
540
|
+
declare function readSupportSettingsState(payload: Payload, staffSlug?: string): Promise<SupportSettingsState>;
|
|
541
|
+
declare function readSupportSettings(payload: Payload, staffSlug?: string): Promise<SupportSettings>;
|
|
542
|
+
declare function readUserPrefs(payload: Payload, userId: string | number, staffSlug?: string): Promise<UserPrefs>;
|
|
511
543
|
|
|
512
544
|
/**
|
|
513
545
|
* POST /api/support/oauth/google
|
|
514
546
|
* Google OAuth — handles both login redirect and callback.
|
|
515
|
-
* Body: { action: 'login' } or { code: string, state: string
|
|
547
|
+
* Body: { action: 'login' } or { code: string, state: string }
|
|
548
|
+
*
|
|
549
|
+
* The CSRF state is NOT taken from the body: `action: 'login'` issues it as an
|
|
550
|
+
* HttpOnly cookie and the callback reads it back from the `Cookie` header.
|
|
516
551
|
*/
|
|
517
552
|
interface OAuthGoogleOptions {
|
|
518
553
|
allowedEmailDomains?: string[];
|