@lilaquadrat/studio 11.2.1 → 11.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/lib/src/models/permissions.model.js +9 -10
- package/lib/src/models/permissions.model.js.map +1 -1
- package/lib/src/services/jetstream.service.js +24 -7
- package/lib/src/services/jetstream.service.js.map +1 -1
- package/lib/src/services/participants.service.js +2 -2
- package/lib/src/services/support-access.service.d.ts +10 -4
- package/lib/src/services/support-access.service.js +46 -15
- package/lib/src/services/support-access.service.js.map +1 -1
- package/lib/src/services/support.service.d.ts +15 -2
- package/lib/src/services/support.service.js +53 -2
- package/lib/src/services/support.service.js.map +1 -1
- package/lib/tests/supportCompany.spec.d.ts +1 -0
- package/lib/tests/supportCompany.spec.js +71 -0
- package/lib/tests/supportCompany.spec.js.map +1 -0
- package/package.json +1 -1
|
@@ -95,16 +95,6 @@ const CompanyScope = {
|
|
|
95
95
|
'remove',
|
|
96
96
|
'history',
|
|
97
97
|
],
|
|
98
|
-
support: [
|
|
99
|
-
'read',
|
|
100
|
-
'create',
|
|
101
|
-
'edit',
|
|
102
|
-
'history',
|
|
103
|
-
'resolve',
|
|
104
|
-
'assign',
|
|
105
|
-
'member',
|
|
106
|
-
'oversee',
|
|
107
|
-
],
|
|
108
98
|
sla: [
|
|
109
99
|
'read',
|
|
110
100
|
'create',
|
|
@@ -155,6 +145,15 @@ const CompanyScopeAdmin = {
|
|
|
155
145
|
license: [
|
|
156
146
|
'admin',
|
|
157
147
|
],
|
|
148
|
+
support: [
|
|
149
|
+
'read',
|
|
150
|
+
'write',
|
|
151
|
+
'create',
|
|
152
|
+
'participants',
|
|
153
|
+
'resolve',
|
|
154
|
+
'assign',
|
|
155
|
+
'oversee',
|
|
156
|
+
],
|
|
158
157
|
};
|
|
159
158
|
const ProjectScope = {
|
|
160
159
|
editor: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.model.js","sourceRoot":"","sources":["../../../src/models/permissions.model.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,6BAA6B,CAAC;AAElD,MAAM,KAAK,GAAG,IAAI,OAAO,CAAuB,aAAa,EAAE,aAAa,CAAC,CAAC;AAE9E,KAAK,CAAC,SAAS,CACb;IACE,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACxB,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACvB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC3B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACpD;IACD,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;IAC9B,oBAAoB,EAAE,KAAK;CAC5B,EACD,QAAQ,CACT,CAAC;AAEF,KAAK,CAAC,SAAS,CACb;IACE,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACxB,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACvB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC3B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACpD;IACD,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;IAC9B,oBAAoB,EAAE,KAAK;CAC5B,EACD,QAAQ,CACT,CAAC;AAEF,KAAK,CAAC,QAAQ,CACZ;IACE,GAAG,EAAE;QACH,IAAI,EAAE,CAAC;QACP,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;KACX;IACD,MAAM,EAAE,IAAI;IACZ,gBAAgB;IAChB,IAAI,EAAE,aAAa;CACpB,EACD,CAAC,CACF,CAAC;AAEF,kBAAkB;AAClB,MAAM;AACN,aAAa;AACb,gBAAgB;AAChB,oBAAoB;AACpB,oBAAoB;AACpB,SAAS;AACT,oBAAoB;AACpB,oBAAoB;AACpB,0BAA0B;AAC1B,OAAO;AACP,OAAO;AACP,KAAK;AAEL,MAAM,YAAY,GAAgB;IAChC,OAAO,EAAE;QACP,MAAM;QACN,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,MAAM;QACN,SAAS;KACV;IACD,GAAG,EAAE;QACH,MAAM;QACN,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,SAAS;KACV;IACD,SAAS,EAAE;QACT,MAAM;QACN,MAAM;QACN,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,gBAAgB;QAChB,kBAAkB;QAClB,gBAAgB;QAChB,kBAAkB;QAClB,aAAa;QACb,eAAe;QACf,aAAa;QACb,eAAe;QACf,OAAO;KACR;IACD,QAAQ,EAAE;QACR,MAAM;QACN,MAAM;QACN,SAAS;KACV;IACD,WAAW,EAAE;QACX,MAAM;QACN,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,SAAS;KACV;IACD,
|
|
1
|
+
{"version":3,"file":"permissions.model.js","sourceRoot":"","sources":["../../../src/models/permissions.model.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { connect, credsAuthenticator } from '@nats-io/transport-node';
|
|
2
|
-
import { jetstream, jetstreamManager, RetentionPolicy, } from '@nats-io/jetstream';
|
|
2
|
+
import { jetstream, jetstreamManager, RetentionPolicy, JetStreamApiError, JetStreamApiCodes, } from '@nats-io/jetstream';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import logger from '../logger.js';
|
|
5
5
|
export class JetStreamService {
|
|
@@ -61,12 +61,29 @@ export class JetStreamService {
|
|
|
61
61
|
async createStream(streamName, subjects, retention = RetentionPolicy.Limits, options) {
|
|
62
62
|
if (!this.jsm)
|
|
63
63
|
throw new Error('Not connected to JetStream');
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
let existing;
|
|
65
|
+
try {
|
|
66
|
+
existing = await this.jsm.streams.info(streamName);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!(error instanceof JetStreamApiError) || error.code !== JetStreamApiCodes.StreamNotFound)
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
if (!existing) {
|
|
73
|
+
await this.jsm.streams.add({
|
|
74
|
+
name: streamName,
|
|
75
|
+
subjects,
|
|
76
|
+
retention,
|
|
77
|
+
max_age: options?.maxAge || 0
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const missingSubjects = subjects.filter(subject => !existing.config.subjects.includes(subject));
|
|
82
|
+
if (missingSubjects.length > 0) {
|
|
83
|
+
await this.jsm.streams.update(streamName, {
|
|
84
|
+
subjects: [...existing.config.subjects, ...missingSubjects]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
70
87
|
}
|
|
71
88
|
publish(subject, data) {
|
|
72
89
|
if (!this.js)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jetstream.service.js","sourceRoot":"","sources":["../../../src/services/jetstream.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAyC,MAAM,yBAAyB,CAAC;AAC7G,OAAO,EACL,SAAS,EACT,gBAAgB,EAChB,eAAe,
|
|
1
|
+
{"version":3,"file":"jetstream.service.js","sourceRoot":"","sources":["../../../src/services/jetstream.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -121,8 +121,8 @@ export class ParticipantsService extends Immutable {
|
|
|
121
121
|
*
|
|
122
122
|
* The team reconcile is add-only by design (addMissingSupportMembers): nothing on a support
|
|
123
123
|
* conversation records who the customer is, so "remove everyone not on the team" could eject
|
|
124
|
-
* the customer themselves. The consequence is that
|
|
125
|
-
*
|
|
124
|
+
* the customer themselves. The consequence is that revoking someone's support scopes never
|
|
125
|
+
* removed their participant docs — without this check they would keep reading
|
|
126
126
|
* every support conversation they were ever added to, forever.
|
|
127
127
|
*
|
|
128
128
|
* `role: 'agent'` is only ever set on support participants (participantsService
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare class SupportAccessService {
|
|
2
|
-
/** last successfully fetched
|
|
2
|
+
/** last successfully fetched members and their scopes, or null if we never got one */
|
|
3
3
|
private cached;
|
|
4
4
|
/** epoch ms after which `cached` is considered due for a refresh */
|
|
5
5
|
private nextRefreshAt;
|
|
@@ -10,11 +10,17 @@ declare class SupportAccessService {
|
|
|
10
10
|
*/
|
|
11
11
|
private inFlight;
|
|
12
12
|
private static ttl;
|
|
13
|
-
/** every
|
|
13
|
+
/** every support member and the `support:*` scopes they hold (names without the prefix) */
|
|
14
|
+
members(): Promise<Map<string, Set<string>>>;
|
|
15
|
+
/** every user id with at least one support scope */
|
|
14
16
|
team(): Promise<Set<string>>;
|
|
15
17
|
isStaff(user: string): Promise<boolean>;
|
|
18
|
+
/** whether the user holds a named support scope, e.g. `resolve` */
|
|
19
|
+
has(user: string, scope: string): Promise<boolean>;
|
|
20
|
+
/** the support scopes of one user; empty when they are not staff */
|
|
21
|
+
scopes(user: string): Promise<Set<string>>;
|
|
16
22
|
/**
|
|
17
|
-
* Drop the cached
|
|
23
|
+
* Drop the cached members so the next read goes back to mongo.
|
|
18
24
|
*
|
|
19
25
|
* The value is discarded outright rather than merely marked stale: an explicit invalidation
|
|
20
26
|
* means "what I am holding is wrong", and serving it as the stale-while-error fallback would
|
|
@@ -26,7 +32,7 @@ declare class SupportAccessService {
|
|
|
26
32
|
invalidate(): void;
|
|
27
33
|
/**
|
|
28
34
|
* Stale-while-error: a mongo blip must not eject the entire support team mid-shift. A failed
|
|
29
|
-
* refresh keeps serving the last known good
|
|
35
|
+
* refresh keeps serving the last known good members; only a failure with nothing cached at all
|
|
30
36
|
* throws, so an empty cache fails closed rather than silently answering "nobody is staff".
|
|
31
37
|
*/
|
|
32
38
|
private refresh;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import logger from '../logger.js';
|
|
2
2
|
import supportService from './support.service.js';
|
|
3
3
|
/**
|
|
4
|
-
* Who is support staff.
|
|
4
|
+
* Who is support staff, and what each member may do.
|
|
5
5
|
*
|
|
6
6
|
* Staff-ness is a **database fact, not a token claim**. `authPlugin` sets
|
|
7
7
|
* `request.auth = request.user`, which is the raw set of verified Auth0 claims — no `scope`,
|
|
8
8
|
* no `permissions`. The authority is the `permissions` collection: a document for the support
|
|
9
|
-
* company
|
|
10
|
-
*
|
|
9
|
+
* company — the admin company, resolved by `SupportService.getSupportCompany()` — whose `scope`
|
|
10
|
+
* array holds `support:*` entries (`read`, `write`, `create`, `participants`, `resolve`, `assign`,
|
|
11
|
+
* `oversee`). A user is staff when they hold at least one of them; the individual scopes name the
|
|
12
|
+
* capability. `SupportService` is the only reader of it, so this service wraps that and nothing
|
|
13
|
+
* else.
|
|
11
14
|
*
|
|
12
15
|
* It deliberately imports **only** the support service. `participants.service.ts` needs it
|
|
13
16
|
* inside `checkAccess`, and any local import here would close a cycle back through it.
|
|
@@ -23,8 +26,8 @@ import supportService from './support.service.js';
|
|
|
23
26
|
* The write side lives in studio-api: a different process, a different deployment. Nothing can
|
|
24
27
|
* notify this one when a permission document changes, and there is no shared bus to listen on.
|
|
25
28
|
* `invalidate()` therefore only helps callers inside *this* process. In practice the revocation
|
|
26
|
-
* window
|
|
27
|
-
*
|
|
29
|
+
* window is one TTL (default 60s), and that is the honest guarantee. Do not describe it as
|
|
30
|
+
* immediate anywhere.
|
|
28
31
|
*/
|
|
29
32
|
/** default refresh interval; overridable with SUPPORT_TEAM_TTL_MS */
|
|
30
33
|
const DEFAULT_TTL_MS = 60000;
|
|
@@ -36,7 +39,7 @@ const DEFAULT_TTL_MS = 60000;
|
|
|
36
39
|
const ERROR_RETRY_MS = 5000;
|
|
37
40
|
class SupportAccessService {
|
|
38
41
|
constructor() {
|
|
39
|
-
/** last successfully fetched
|
|
42
|
+
/** last successfully fetched members and their scopes, or null if we never got one */
|
|
40
43
|
this.cached = null;
|
|
41
44
|
/** epoch ms after which `cached` is considered due for a refresh */
|
|
42
45
|
this.nextRefreshAt = 0;
|
|
@@ -51,8 +54,8 @@ class SupportAccessService {
|
|
|
51
54
|
const configured = Number(process.env.SUPPORT_TEAM_TTL_MS);
|
|
52
55
|
return Number.isFinite(configured) && configured >= 0 ? configured : DEFAULT_TTL_MS;
|
|
53
56
|
}
|
|
54
|
-
/** every
|
|
55
|
-
async
|
|
57
|
+
/** every support member and the `support:*` scopes they hold (names without the prefix) */
|
|
58
|
+
async members() {
|
|
56
59
|
if (this.cached && Date.now() < this.nextRefreshAt)
|
|
57
60
|
return this.cached;
|
|
58
61
|
if (this.inFlight)
|
|
@@ -65,13 +68,29 @@ class SupportAccessService {
|
|
|
65
68
|
this.inFlight = null;
|
|
66
69
|
}
|
|
67
70
|
}
|
|
71
|
+
/** every user id with at least one support scope */
|
|
72
|
+
async team() {
|
|
73
|
+
return new Set((await this.members()).keys());
|
|
74
|
+
}
|
|
68
75
|
async isStaff(user) {
|
|
69
76
|
if (!user)
|
|
70
77
|
return false;
|
|
71
|
-
return (await this.
|
|
78
|
+
return (await this.members()).has(user);
|
|
79
|
+
}
|
|
80
|
+
/** whether the user holds a named support scope, e.g. `resolve` */
|
|
81
|
+
async has(user, scope) {
|
|
82
|
+
if (!user)
|
|
83
|
+
return false;
|
|
84
|
+
return (await this.members()).get(user)?.has(scope) ?? false;
|
|
85
|
+
}
|
|
86
|
+
/** the support scopes of one user; empty when they are not staff */
|
|
87
|
+
async scopes(user) {
|
|
88
|
+
if (!user)
|
|
89
|
+
return new Set();
|
|
90
|
+
return (await this.members()).get(user) ?? new Set();
|
|
72
91
|
}
|
|
73
92
|
/**
|
|
74
|
-
* Drop the cached
|
|
93
|
+
* Drop the cached members so the next read goes back to mongo.
|
|
75
94
|
*
|
|
76
95
|
* The value is discarded outright rather than merely marked stale: an explicit invalidation
|
|
77
96
|
* means "what I am holding is wrong", and serving it as the stale-while-error fallback would
|
|
@@ -86,16 +105,28 @@ class SupportAccessService {
|
|
|
86
105
|
}
|
|
87
106
|
/**
|
|
88
107
|
* Stale-while-error: a mongo blip must not eject the entire support team mid-shift. A failed
|
|
89
|
-
* refresh keeps serving the last known good
|
|
108
|
+
* refresh keeps serving the last known good members; only a failure with nothing cached at all
|
|
90
109
|
* throws, so an empty cache fails closed rather than silently answering "nobody is staff".
|
|
91
110
|
*/
|
|
92
111
|
async refresh() {
|
|
93
112
|
try {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
const company = await supportService.getSupportCompany();
|
|
114
|
+
/**
|
|
115
|
+
* No admin company means no company provides support. Treated as an empty team rather than
|
|
116
|
+
* a thrown error: it is a persistent configuration state, not a transient mongo failure, so
|
|
117
|
+
* every support call failing closed (403 / SUPPORT_UNAVAILABLE) is the honest answer and a
|
|
118
|
+
* 500 would only mask the cause. The set is cached and rechecked after one TTL.
|
|
119
|
+
*/
|
|
120
|
+
if (!company) {
|
|
121
|
+
logger.warn('support.team.company.missing');
|
|
122
|
+
this.cached = new Map();
|
|
123
|
+
this.nextRefreshAt = Date.now() + SupportAccessService.ttl();
|
|
124
|
+
return this.cached;
|
|
125
|
+
}
|
|
126
|
+
const members = await supportService.getSupportPermissions(company);
|
|
127
|
+
this.cached = members;
|
|
97
128
|
this.nextRefreshAt = Date.now() + SupportAccessService.ttl();
|
|
98
|
-
return
|
|
129
|
+
return members;
|
|
99
130
|
}
|
|
100
131
|
catch (error) {
|
|
101
132
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"support-access.service.js","sourceRoot":"","sources":["../../../src/services/support-access.service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,cAAc,CAAC;AAClC,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAElD
|
|
1
|
+
{"version":3,"file":"support-access.service.js","sourceRoot":"","sources":["../../../src/services/support-access.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
export declare class SupportService {
|
|
2
|
-
|
|
2
|
+
private cachedCompany;
|
|
3
|
+
private nextCompanyRefreshAt;
|
|
4
|
+
/** id of the admin company, or null when none is configured */
|
|
5
|
+
getSupportCompany(): Promise<string | null>;
|
|
6
|
+
/** every user id with at least one `support:*` scope in the company */
|
|
7
|
+
getSupportMembers(company: string): Promise<string[]>;
|
|
8
|
+
/**
|
|
9
|
+
* The support scopes each user holds — scope names without the `support:` prefix.
|
|
10
|
+
*
|
|
11
|
+
* This is the authority for every support capability: `read`/`write`/`participants` become
|
|
12
|
+
* participant rights on the conversation, `resolve` gates the status route, and `create`/
|
|
13
|
+
* `assign`/`oversee` are still landing. Appearing here at all is what makes someone staff.
|
|
14
|
+
*/
|
|
15
|
+
getSupportPermissions(company: string): Promise<Map<string, Set<string>>>;
|
|
3
16
|
getSupportConversations(): Promise<import("mongodb").WithId<import("@lilaquadrat/interfaces").Conversation>[]>;
|
|
4
|
-
getNextSupportMember(company: string): Promise<string | null
|
|
17
|
+
getNextSupportMember(company: string): Promise<string | null>;
|
|
5
18
|
}
|
|
6
19
|
declare const _default: SupportService;
|
|
7
20
|
export default _default;
|
|
@@ -1,12 +1,63 @@
|
|
|
1
1
|
import PermissionsModel from '../models/permissions.model.js';
|
|
2
2
|
import ConversationModel from '../models/conversation.model.js';
|
|
3
|
+
import CompanyModel from '../models/company.model.js';
|
|
4
|
+
/**
|
|
5
|
+
* The company that provides support is the admin company — the one `companies` document whose
|
|
6
|
+
* `attributes` include `'admin'` — not a deployed env value. The same attribute backs
|
|
7
|
+
* `checkRights('admin')`, so there is exactly one source of truth for what "admin company" means.
|
|
8
|
+
*
|
|
9
|
+
* Memoized with a TTL because the chat conversation list resolves it on every request and
|
|
10
|
+
* `companies.attributes` is not indexed; the value is effectively static, so a short cache keeps
|
|
11
|
+
* the lookup off the hot path without serving a stale company for long after an admin change.
|
|
12
|
+
*/
|
|
13
|
+
const COMPANY_TTL_MS = 60000;
|
|
14
|
+
/** granted scopes are stored with the model prefix, e.g. `support:read` */
|
|
15
|
+
const SUPPORT_SCOPE_PREFIX = 'support:';
|
|
16
|
+
const SUPPORT_SCOPE_PATTERN = /^support:/;
|
|
3
17
|
export class SupportService {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.cachedCompany = null;
|
|
20
|
+
this.nextCompanyRefreshAt = 0;
|
|
21
|
+
}
|
|
22
|
+
/** id of the admin company, or null when none is configured */
|
|
23
|
+
async getSupportCompany() {
|
|
24
|
+
if (Date.now() < this.nextCompanyRefreshAt)
|
|
25
|
+
return this.cachedCompany;
|
|
26
|
+
const company = await CompanyModel.db.findOne({ attributes: 'admin' }, { projection: { id: 1 } });
|
|
27
|
+
this.cachedCompany = company?.id ?? null;
|
|
28
|
+
this.nextCompanyRefreshAt = Date.now() + COMPANY_TTL_MS;
|
|
29
|
+
return this.cachedCompany;
|
|
30
|
+
}
|
|
31
|
+
/** every user id with at least one `support:*` scope in the company */
|
|
4
32
|
async getSupportMembers(company) {
|
|
5
33
|
const permissions = await PermissionsModel.db.find({
|
|
6
34
|
company,
|
|
7
|
-
scope:
|
|
35
|
+
scope: SUPPORT_SCOPE_PATTERN,
|
|
8
36
|
}).toArray();
|
|
9
|
-
return permissions.map((permission) => permission.user);
|
|
37
|
+
return [...new Set(permissions.map((permission) => permission.user).filter((user) => !!user))];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The support scopes each user holds — scope names without the `support:` prefix.
|
|
41
|
+
*
|
|
42
|
+
* This is the authority for every support capability: `read`/`write`/`participants` become
|
|
43
|
+
* participant rights on the conversation, `resolve` gates the status route, and `create`/
|
|
44
|
+
* `assign`/`oversee` are still landing. Appearing here at all is what makes someone staff.
|
|
45
|
+
*/
|
|
46
|
+
async getSupportPermissions(company) {
|
|
47
|
+
const permissions = await PermissionsModel.db.find({
|
|
48
|
+
company,
|
|
49
|
+
scope: SUPPORT_SCOPE_PATTERN,
|
|
50
|
+
}).toArray();
|
|
51
|
+
const result = new Map();
|
|
52
|
+
for (const permission of permissions) {
|
|
53
|
+
if (!permission.user)
|
|
54
|
+
continue;
|
|
55
|
+
const scopes = (permission.scope ?? [])
|
|
56
|
+
.filter((single) => single.startsWith(SUPPORT_SCOPE_PREFIX))
|
|
57
|
+
.map((single) => single.slice(SUPPORT_SCOPE_PREFIX.length));
|
|
58
|
+
result.set(permission.user, new Set(scopes));
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
10
61
|
}
|
|
11
62
|
async getSupportConversations() {
|
|
12
63
|
return ConversationModel.db.find({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"support.service.js","sourceRoot":"","sources":["../../../src/services/support.service.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,gCAAgC,CAAC;AAC9D,OAAO,iBAAiB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"support.service.js","sourceRoot":"","sources":["../../../src/services/support.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals';
|
|
2
|
+
import CompanyModel from '../src/models/company.model';
|
|
3
|
+
import SupportService from '../src/services/support.service';
|
|
4
|
+
import supportAccess from '../src/services/support-access.service';
|
|
5
|
+
function expireCompanyCache() {
|
|
6
|
+
const service = SupportService;
|
|
7
|
+
service.cachedCompany = null;
|
|
8
|
+
service.nextCompanyRefreshAt = 0;
|
|
9
|
+
}
|
|
10
|
+
describe('SupportService.getSupportCompany', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
jest.useRealTimers();
|
|
13
|
+
expireCompanyCache();
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
jest.restoreAllMocks();
|
|
17
|
+
});
|
|
18
|
+
it('resolves the admin company id', async () => {
|
|
19
|
+
const findOne = jest.fn().mockResolvedValue({ id: 'lilaquadrat' });
|
|
20
|
+
jest.spyOn(CompanyModel, 'db', 'get').mockReturnValue({ findOne });
|
|
21
|
+
await expect(SupportService.getSupportCompany()).resolves.toBe('lilaquadrat');
|
|
22
|
+
expect(findOne).toHaveBeenCalledWith({ attributes: 'admin' }, { projection: { id: 1 } });
|
|
23
|
+
});
|
|
24
|
+
it('resolves null when no admin company exists', async () => {
|
|
25
|
+
const findOne = jest.fn().mockResolvedValue(null);
|
|
26
|
+
jest.spyOn(CompanyModel, 'db', 'get').mockReturnValue({ findOne });
|
|
27
|
+
await expect(SupportService.getSupportCompany()).resolves.toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it('serves the memoized company within the TTL and refetches after it', async () => {
|
|
30
|
+
jest.useFakeTimers();
|
|
31
|
+
jest.setSystemTime(new Date('2026-01-01T00:00:00.000Z'));
|
|
32
|
+
const findOne = jest.fn().mockResolvedValue({ id: 'lilaquadrat' });
|
|
33
|
+
jest.spyOn(CompanyModel, 'db', 'get').mockReturnValue({ findOne });
|
|
34
|
+
expect(await SupportService.getSupportCompany()).toBe('lilaquadrat');
|
|
35
|
+
expect(await SupportService.getSupportCompany()).toBe('lilaquadrat');
|
|
36
|
+
expect(findOne).toHaveBeenCalledTimes(1);
|
|
37
|
+
jest.setSystemTime(new Date('2026-01-01T00:01:01.000Z'));
|
|
38
|
+
findOne.mockResolvedValue({ id: 'partner' });
|
|
39
|
+
expect(await SupportService.getSupportCompany()).toBe('partner');
|
|
40
|
+
expect(findOne).toHaveBeenCalledTimes(2);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('SupportAccessService with the admin company', () => {
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
jest.restoreAllMocks();
|
|
46
|
+
});
|
|
47
|
+
it('reads the team of the resolved admin company', async () => {
|
|
48
|
+
jest.spyOn(SupportService, 'getSupportCompany').mockResolvedValue('acme');
|
|
49
|
+
const getSupportPermissions = jest.spyOn(SupportService, 'getSupportPermissions').mockResolvedValue(new Map([['auth0|a', new Set(['read'])]]));
|
|
50
|
+
supportAccess.invalidate();
|
|
51
|
+
await expect(supportAccess.team()).resolves.toEqual(new Set(['auth0|a']));
|
|
52
|
+
expect(getSupportPermissions).toHaveBeenCalledWith('acme');
|
|
53
|
+
});
|
|
54
|
+
it('fails closed to an empty team when no admin company exists', async () => {
|
|
55
|
+
jest.spyOn(SupportService, 'getSupportCompany').mockResolvedValue(null);
|
|
56
|
+
const getSupportPermissions = jest.spyOn(SupportService, 'getSupportPermissions');
|
|
57
|
+
supportAccess.invalidate();
|
|
58
|
+
await expect(supportAccess.team()).resolves.toEqual(new Set());
|
|
59
|
+
expect(getSupportPermissions).not.toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
it('exposes the individual scopes of a member', async () => {
|
|
62
|
+
jest.spyOn(SupportService, 'getSupportCompany').mockResolvedValue('acme');
|
|
63
|
+
jest.spyOn(SupportService, 'getSupportPermissions').mockResolvedValue(new Map([['auth0|a', new Set(['read', 'resolve'])]]));
|
|
64
|
+
supportAccess.invalidate();
|
|
65
|
+
await expect(supportAccess.has('auth0|a', 'resolve')).resolves.toBe(true);
|
|
66
|
+
await expect(supportAccess.has('auth0|a', 'write')).resolves.toBe(false);
|
|
67
|
+
await expect(supportAccess.scopes('auth0|a')).resolves.toEqual(new Set(['read', 'resolve']));
|
|
68
|
+
await expect(supportAccess.scopes('auth0|b')).resolves.toEqual(new Set());
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=supportCompany.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supportCompany.spec.js","sourceRoot":"","sources":["../../tests/supportCompany.spec.ts"],"names":[],"mappings":"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"}
|