@nlabs/reaktor 1.17.0 → 1.18.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 +182 -0
- package/docs/ASSISTANT.md +138 -0
- package/docs/GENERATION.md +15 -0
- package/docs/NATIVE_PUSH.md +56 -0
- package/lib/actions/alfredFaq.d.ts +80 -0
- package/lib/actions/alfredFaq.js +206 -0
- package/lib/actions/contentGeneration.d.ts +1 -0
- package/lib/actions/contentGeneration.js +25 -6
- package/lib/actions/conversations.d.ts +1 -1
- package/lib/actions/conversations.js +19 -9
- package/lib/actions/crud.js +26 -1
- package/lib/actions/index.d.ts +1 -0
- package/lib/actions/index.js +2 -1
- package/lib/actions/messages.js +23 -7
- package/lib/actions/users.js +57 -4
- package/lib/assistant/core.d.ts +53 -0
- package/lib/assistant/core.js +123 -0
- package/lib/assistant/faq.d.ts +26 -0
- package/lib/assistant/faq.js +238 -0
- package/lib/assistant/faqCore.d.ts +23 -0
- package/lib/assistant/faqCore.js +20 -0
- package/lib/assistant/index.d.ts +7 -0
- package/lib/assistant/index.js +5 -0
- package/lib/assistant/service.d.ts +35 -0
- package/lib/assistant/service.js +201 -0
- package/lib/durableChat/conversations.d.ts +62 -0
- package/lib/durableChat/conversations.js +729 -0
- package/lib/durableChat/errors.d.ts +6 -0
- package/lib/durableChat/errors.js +11 -0
- package/lib/durableChat/events.d.ts +22 -0
- package/lib/durableChat/events.js +50 -0
- package/lib/durableChat/index.d.ts +8 -0
- package/lib/durableChat/index.js +10 -0
- package/lib/durableChat/membership.d.ts +24 -0
- package/lib/durableChat/membership.js +85 -0
- package/lib/durableChat/messageTypes.d.ts +76 -0
- package/lib/durableChat/messageTypes.js +3 -0
- package/lib/durableChat/messages.d.ts +16 -0
- package/lib/durableChat/messages.js +257 -0
- package/lib/durableChat/transaction.d.ts +10 -0
- package/lib/durableChat/transaction.js +53 -0
- package/lib/durableChat/types.d.ts +81 -0
- package/lib/durableChat/types.js +3 -0
- package/lib/index.d.ts +4 -4
- package/lib/index.js +5 -3
- package/lib/lambdas/actions/websockets.js +4 -4
- package/lib/nativePush/graphql.d.ts +14 -0
- package/lib/nativePush/graphql.js +105 -0
- package/lib/nativePush/index.d.ts +7 -0
- package/lib/nativePush/index.js +9 -0
- package/lib/nativePush/migrations.d.ts +2 -0
- package/lib/nativePush/migrations.js +36 -0
- package/lib/nativePush/nativePushContract.fixture.d.ts +22 -0
- package/lib/nativePush/nativePushContract.fixture.js +27 -0
- package/lib/nativePush/payload.d.ts +2 -0
- package/lib/nativePush/payload.js +106 -0
- package/lib/nativePush/repository.d.ts +9 -0
- package/lib/nativePush/repository.js +120 -0
- package/lib/nativePush/service.d.ts +2 -0
- package/lib/nativePush/service.js +110 -0
- package/lib/nativePush/snsProvider.d.ts +17 -0
- package/lib/nativePush/snsProvider.js +165 -0
- package/lib/nativePush/types.d.ts +89 -0
- package/lib/nativePush/types.js +3 -0
- package/lib/organizations/accountLifecycle.d.ts +7 -0
- package/lib/organizations/accountLifecycle.js +106 -0
- package/lib/organizations/authorization.d.ts +9 -0
- package/lib/organizations/authorization.js +40 -0
- package/lib/organizations/billing.d.ts +38 -0
- package/lib/organizations/billing.js +277 -0
- package/lib/organizations/billingProvider.d.ts +4 -0
- package/lib/organizations/billingProvider.js +287 -0
- package/lib/organizations/billingUpdates.d.ts +9 -0
- package/lib/organizations/billingUpdates.js +388 -0
- package/lib/organizations/index.d.ts +10 -0
- package/lib/organizations/index.js +12 -0
- package/lib/organizations/mail.d.ts +10 -0
- package/lib/organizations/mail.js +60 -0
- package/lib/organizations/memberDirectory.d.ts +15 -0
- package/lib/organizations/memberDirectory.js +28 -0
- package/lib/organizations/membership.d.ts +19 -0
- package/lib/organizations/membership.js +248 -0
- package/lib/organizations/passwordRecovery.d.ts +9 -0
- package/lib/organizations/passwordRecovery.js +50 -0
- package/lib/organizations/plans.d.ts +6 -0
- package/lib/organizations/plans.js +40 -0
- package/lib/organizations/providerPagination.d.ts +6 -0
- package/lib/organizations/providerPagination.js +23 -0
- package/lib/organizations/reconciliation.d.ts +11 -0
- package/lib/organizations/reconciliation.js +400 -0
- package/lib/organizations/signup.d.ts +25 -0
- package/lib/organizations/signup.js +169 -0
- package/lib/organizations/store.d.ts +25 -0
- package/lib/organizations/store.js +167 -0
- package/lib/organizations/types.d.ts +138 -0
- package/lib/organizations/types.js +3 -0
- package/lib/organizations/verification.d.ts +3 -0
- package/lib/organizations/verification.js +51 -0
- package/package.json +16 -3
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { createHash, createPassword, parsePassword } from '@nlabs/utils/parsers/strings';
|
|
2
|
+
import { createHmac, randomBytes } from 'node:crypto';
|
|
3
|
+
import { organizationUserId } from './authorization.js';
|
|
4
|
+
import { encodeOrganizationMail, organizationMailKey } from './mail.js';
|
|
5
|
+
import { newOrganizationId, organizationKey, organizationStoreFor, requireIdentifier } from './store.js';
|
|
6
|
+
export const queueOrganizationVerification = async (tx, registration, previous)=>{
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
const token = randomBytes(32).toString('base64url');
|
|
9
|
+
const withinHour = previous && now - previous.resendWindow < 3600000;
|
|
10
|
+
const verification = {
|
|
11
|
+
attemptWindow: previous?.attemptWindow || now,
|
|
12
|
+
attempts: previous?.attempts || 0,
|
|
13
|
+
expiresAt: now + 24 * 3600000,
|
|
14
|
+
lastSent: now,
|
|
15
|
+
resendCount: withinHour ? previous.resendCount + 1 : 1,
|
|
16
|
+
resendWindow: withinHour ? previous.resendWindow : now,
|
|
17
|
+
tokenHash: organizationKey(token),
|
|
18
|
+
used: false,
|
|
19
|
+
userId: registration.userId
|
|
20
|
+
};
|
|
21
|
+
await tx.put('organizationVerificationTokens', registration.registrationId, verification, previous);
|
|
22
|
+
const jobId = newOrganizationId();
|
|
23
|
+
const job = {
|
|
24
|
+
attempts: 0,
|
|
25
|
+
desired: {
|
|
26
|
+
ciphertext: encodeOrganizationMail({
|
|
27
|
+
email: registration.email,
|
|
28
|
+
registrationId: registration.registrationId,
|
|
29
|
+
token
|
|
30
|
+
})
|
|
31
|
+
},
|
|
32
|
+
effectiveAt: now,
|
|
33
|
+
jobId,
|
|
34
|
+
kind: 'mail',
|
|
35
|
+
leaseExpires: 0,
|
|
36
|
+
organizationId: registration.organizationId,
|
|
37
|
+
owner: '',
|
|
38
|
+
priceVersion: '2026-10-v1',
|
|
39
|
+
sequence: -now,
|
|
40
|
+
status: 'pending'
|
|
41
|
+
};
|
|
42
|
+
await tx.put('organizationBillingJobs', jobId, job);
|
|
43
|
+
};
|
|
44
|
+
const insertOrganization = async (tx, name, userId)=>{
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const organization = {
|
|
47
|
+
createdAt: now,
|
|
48
|
+
name,
|
|
49
|
+
organizationId: newOrganizationId(),
|
|
50
|
+
ownerUserId: userId,
|
|
51
|
+
revision: 0,
|
|
52
|
+
updatedAt: now
|
|
53
|
+
};
|
|
54
|
+
const membership = {
|
|
55
|
+
createdAt: now,
|
|
56
|
+
organizationId: organization.organizationId,
|
|
57
|
+
revision: 0,
|
|
58
|
+
role: 'admin',
|
|
59
|
+
status: 'active',
|
|
60
|
+
updatedAt: now,
|
|
61
|
+
userId
|
|
62
|
+
};
|
|
63
|
+
await tx.put('organizations', organization.organizationId, organization);
|
|
64
|
+
await tx.put('organizationMemberships', organizationKey(organization.organizationId, userId), membership);
|
|
65
|
+
return organization;
|
|
66
|
+
};
|
|
67
|
+
export const registerOrganization = async (input, controlContext)=>{
|
|
68
|
+
if (!input || Object.keys(input).some((key)=>![
|
|
69
|
+
'email',
|
|
70
|
+
'organizationName',
|
|
71
|
+
'password',
|
|
72
|
+
'requestId'
|
|
73
|
+
].includes(key)) || typeof input.email !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.email) || input.email.length > 254 || typeof input.password !== 'string' || input.password.length < 12 || input.password.length > 128 || typeof input.organizationName !== 'string' || !input.organizationName.trim() || input.organizationName.length > 160) {
|
|
74
|
+
throw new Error('invalid_signup');
|
|
75
|
+
}
|
|
76
|
+
requireIdentifier(input.requestId);
|
|
77
|
+
const email = input.email.trim().toLowerCase();
|
|
78
|
+
const fingerprint = createHmac('sha256', organizationMailKey()).update(JSON.stringify({
|
|
79
|
+
...input,
|
|
80
|
+
email
|
|
81
|
+
})).digest('hex');
|
|
82
|
+
const registrationId = organizationKey('registration', email, input.requestId);
|
|
83
|
+
return organizationStoreFor(controlContext).transaction(async (tx)=>{
|
|
84
|
+
const previous = await tx.get('organizationRegistrations', registrationId);
|
|
85
|
+
if (previous) {
|
|
86
|
+
if (previous.fingerprint !== fingerprint) {
|
|
87
|
+
throw new Error('registration_conflict');
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
registrationId,
|
|
91
|
+
verificationRequired: true
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const users = await tx.find('users', {
|
|
95
|
+
email
|
|
96
|
+
});
|
|
97
|
+
const userId = createHash(email);
|
|
98
|
+
const collision = await tx.get('users', userId);
|
|
99
|
+
const existing = users.length > 0 || Boolean(collision);
|
|
100
|
+
let organizationId = '';
|
|
101
|
+
if (!existing) {
|
|
102
|
+
const salt = randomBytes(32).toString('hex');
|
|
103
|
+
await tx.put('users', userId, {
|
|
104
|
+
active: true,
|
|
105
|
+
added: Date.now(),
|
|
106
|
+
email,
|
|
107
|
+
locale: 'en',
|
|
108
|
+
organizationAccount: true,
|
|
109
|
+
password: createPassword(parsePassword(input.password), salt),
|
|
110
|
+
salt,
|
|
111
|
+
type: 'user',
|
|
112
|
+
userAccess: 0,
|
|
113
|
+
username: email,
|
|
114
|
+
verifiedEmail: false
|
|
115
|
+
});
|
|
116
|
+
({ organizationId } = await insertOrganization(tx, input.organizationName.trim(), userId));
|
|
117
|
+
}
|
|
118
|
+
const registration = {
|
|
119
|
+
email,
|
|
120
|
+
fingerprint,
|
|
121
|
+
organizationId,
|
|
122
|
+
registrationId,
|
|
123
|
+
requestId: input.requestId,
|
|
124
|
+
userId: existing ? '' : userId
|
|
125
|
+
};
|
|
126
|
+
await tx.put('organizationRegistrations', registrationId, registration);
|
|
127
|
+
if (!existing) {
|
|
128
|
+
await queueOrganizationVerification(tx, registration);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
registrationId,
|
|
132
|
+
verificationRequired: true
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
export const createOrganization = (context, name, requestId)=>organizationStoreFor(context).transaction(async (tx)=>{
|
|
137
|
+
const userId = organizationUserId(context);
|
|
138
|
+
const user = await tx.get('users', userId);
|
|
139
|
+
if (!user?.active || !user.verifiedEmail) {
|
|
140
|
+
throw new Error('email_verification_required');
|
|
141
|
+
}
|
|
142
|
+
if (typeof name !== 'string' || !name.trim() || name.length > 160) {
|
|
143
|
+
throw new Error('invalid_organization_name');
|
|
144
|
+
}
|
|
145
|
+
const key = organizationKey('authenticated-organization', userId, requireIdentifier(requestId));
|
|
146
|
+
const previous = await tx.get('organizationRegistrations', key);
|
|
147
|
+
if (previous) {
|
|
148
|
+
if (previous.name !== name.trim()) {
|
|
149
|
+
throw new Error('registration_conflict');
|
|
150
|
+
}
|
|
151
|
+
const existing = await tx.get('organizations', previous.organizationId);
|
|
152
|
+
const { _key, _rev, ...organization } = existing;
|
|
153
|
+
return organization;
|
|
154
|
+
}
|
|
155
|
+
const org = await insertOrganization(tx, name.trim(), userId);
|
|
156
|
+
await tx.put('organizationRegistrations', key, {
|
|
157
|
+
name: name.trim(),
|
|
158
|
+
organizationId: org.organizationId,
|
|
159
|
+
requestId: key,
|
|
160
|
+
userId
|
|
161
|
+
});
|
|
162
|
+
await tx.put('users', userId, {
|
|
163
|
+
...user,
|
|
164
|
+
organizationAccount: true
|
|
165
|
+
}, user);
|
|
166
|
+
return org;
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvb3JnYW5pemF0aW9ucy9zaWdudXAudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtjcmVhdGVIYXNoLCBjcmVhdGVQYXNzd29yZCwgcGFyc2VQYXNzd29yZH0gZnJvbSAnQG5sYWJzL3V0aWxzL3BhcnNlcnMvc3RyaW5ncyc7XG5pbXBvcnQge2NyZWF0ZUhtYWMsIHJhbmRvbUJ5dGVzfSBmcm9tICdub2RlOmNyeXB0byc7XG5cbmltcG9ydCB7b3JnYW5pemF0aW9uVXNlcklkfSBmcm9tICcuL2F1dGhvcml6YXRpb24uanMnO1xuaW1wb3J0IHtlbmNvZGVPcmdhbml6YXRpb25NYWlsLCBvcmdhbml6YXRpb25NYWlsS2V5fSBmcm9tICcuL21haWwuanMnO1xuaW1wb3J0IHtcbiAgbmV3T3JnYW5pemF0aW9uSWQsXG4gIG9yZ2FuaXphdGlvbktleSxcbiAgb3JnYW5pemF0aW9uU3RvcmVGb3IsXG4gIHJlcXVpcmVJZGVudGlmaWVyLFxuICB0eXBlIE9yZ2FuaXphdGlvblRyYW5zYWN0aW9uXG59IGZyb20gJy4vc3RvcmUuanMnO1xuXG5pbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7QmlsbGluZ0pvYiwgT3JnYW5pemF0aW9uLCBPcmdhbml6YXRpb25NZW1iZXJzaGlwLCBTaWdudXBJbnB1dCwgU2lnbnVwUmVzdWx0fSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IHR5cGUgUmVnaXN0cmF0aW9uID0ge1xuICBlbWFpbDogc3RyaW5nO1xuICBmaW5nZXJwcmludDogc3RyaW5nO1xuICBvcmdhbml6YXRpb25JZDogc3RyaW5nO1xuICByZWdpc3RyYXRpb25JZDogc3RyaW5nO1xuICByZXF1ZXN0SWQ6IHN0cmluZztcbiAgdXNlcklkOiBzdHJpbmc7XG59O1xuZXhwb3J0IHR5cGUgVmVyaWZpY2F0aW9uID0ge1xuICBhdHRlbXB0czogbnVtYmVyO1xuICBhdHRlbXB0V2luZG93OiBudW1iZXI7XG4gIGV4cGlyZXNBdDogbnVtYmVyO1xuICBsYXN0U2VudDogbnVtYmVyO1xuICByZXNlbmRDb3VudDogbnVtYmVyO1xuICByZXNlbmRXaW5kb3c6IG51bWJlcjtcbiAgdG9rZW5IYXNoOiBzdHJpbmc7XG4gIHVzZWQ6IGJvb2xlYW47XG4gIHVzZXJJZDogc3RyaW5nO1xufTtcbmV4cG9ydCBjb25zdCBxdWV1ZU9yZ2FuaXphdGlvblZlcmlmaWNhdGlvbiA9IGFzeW5jIChcbiAgdHg6IE9yZ2FuaXphdGlvblRyYW5zYWN0aW9uLFxuICByZWdpc3RyYXRpb246IFJlZ2lzdHJhdGlvbixcbiAgcHJldmlvdXM/OiBpbXBvcnQoJy4vc3RvcmUuanMnKS5TdG9yZWQ8VmVyaWZpY2F0aW9uPiB8IG51bGxcbik6IFByb21pc2U8dm9pZD4gPT4ge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBjb25zdCB0b2tlbiA9IHJhbmRvbUJ5dGVzKDMyKS50b1N0cmluZygnYmFzZTY0dXJsJyk7XG4gIGNvbnN0IHdpdGhpbkhvdXIgPSBwcmV2aW91cyAmJiBub3cgLSBwcmV2aW91cy5yZXNlbmRXaW5kb3cgPCAzNjAwMDAwO1xuICBjb25zdCB2ZXJpZmljYXRpb246IFZlcmlmaWNhdGlvbiA9IHtcbiAgICBhdHRlbXB0V2luZG93OiBwcmV2aW91cz8uYXR0ZW1wdFdpbmRvdyB8fCBub3csXG4gICAgYXR0ZW1wdHM6IHByZXZpb3VzPy5hdHRlbXB0cyB8fCAwLFxuICAgIGV4cGlyZXNBdDogbm93ICsgKDI0ICogMzYwMDAwMCksXG4gICAgbGFzdFNlbnQ6IG5vdyxcbiAgICByZXNlbmRDb3VudDogd2l0aGluSG91ciA/IHByZXZpb3VzLnJlc2VuZENvdW50ICsgMSA6IDEsXG4gICAgcmVzZW5kV2luZG93OiB3aXRoaW5Ib3VyID8gcHJldmlvdXMucmVzZW5kV2luZG93IDogbm93LFxuICAgIHRva2VuSGFzaDogb3JnYW5pemF0aW9uS2V5KHRva2VuKSxcbiAgICB1c2VkOiBmYWxzZSxcbiAgICB1c2VySWQ6IHJlZ2lzdHJhdGlvbi51c2VySWRcbiAgfTtcbiAgYXdhaXQgdHgucHV0KCdvcmdhbml6YXRpb25WZXJpZmljYXRpb25Ub2tlbnMnLCByZWdpc3RyYXRpb24ucmVnaXN0cmF0aW9uSWQsIHZlcmlmaWNhdGlvbiwgcHJldmlvdXMpO1xuICBjb25zdCBqb2JJZCA9IG5ld09yZ2FuaXphdGlvbklkKCk7XG4gIGNvbnN0IGpvYjogQmlsbGluZ0pvYiA9IHtcbiAgICBhdHRlbXB0czogMCxcbiAgICBkZXNpcmVkOiB7XG4gICAgICBjaXBoZXJ0ZXh0OiBlbmNvZGVPcmdhbml6YXRpb25NYWlsKHtcbiAgICAgICAgZW1haWw6IHJlZ2lzdHJhdGlvbi5lbWFpbCxcbiAgICAgICAgcmVnaXN0cmF0aW9uSWQ6IHJlZ2lzdHJhdGlvbi5yZWdpc3RyYXRpb25JZCxcbiAgICAgICAgdG9rZW5cbiAgICAgIH0pXG4gICAgfSxcbiAgICBlZmZlY3RpdmVBdDogbm93LFxuICAgIGpvYklkLFxuICAgIGtpbmQ6ICdtYWlsJyxcbiAgICBsZWFzZUV4cGlyZXM6IDAsXG4gICAgb3JnYW5pemF0aW9uSWQ6IHJlZ2lzdHJhdGlvbi5vcmdhbml6YXRpb25JZCxcbiAgICBvd25lcjogJycsXG4gICAgcHJpY2VWZXJzaW9uOiAnMjAyNi0xMC12MScsXG4gICAgc2VxdWVuY2U6IC1ub3csXG4gICAgc3RhdHVzOiAncGVuZGluZydcbiAgfTtcbiAgYXdhaXQgdHgucHV0KCdvcmdhbml6YXRpb25CaWxsaW5nSm9icycsIGpvYklkLCBqb2IpO1xufTtcblxuY29uc3QgaW5zZXJ0T3JnYW5pemF0aW9uID0gYXN5bmMgKFxuICB0eDogT3JnYW5pemF0aW9uVHJhbnNhY3Rpb24sXG4gIG5hbWU6IHN0cmluZyxcbiAgdXNlcklkOiBzdHJpbmdcbik6IFByb21pc2U8T3JnYW5pemF0aW9uPiA9PiB7XG4gIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gIGNvbnN0IG9yZ2FuaXphdGlvbjogT3JnYW5pemF0aW9uID0ge1xuICAgIGNyZWF0ZWRBdDogbm93LFxuICAgIG5hbWUsXG4gICAgb3JnYW5pemF0aW9uSWQ6IG5ld09yZ2FuaXphdGlvbklkKCksXG4gICAgb3duZXJVc2VySWQ6IHVzZXJJZCxcbiAgICByZXZpc2lvbjogMCxcbiAgICB1cGRhdGVkQXQ6IG5vd1xuICB9O1xuICBjb25zdCBtZW1iZXJzaGlwOiBPcmdhbml6YXRpb25NZW1iZXJzaGlwID0ge1xuICAgIGNyZWF0ZWRBdDogbm93LFxuICAgIG9yZ2FuaXphdGlvbklkOiBvcmdhbml6YXRpb24ub3JnYW5pemF0aW9uSWQsXG4gICAgcmV2aXNpb246IDAsXG4gICAgcm9sZTogJ2FkbWluJyxcbiAgICBzdGF0dXM6ICdhY3RpdmUnLFxuICAgIHVwZGF0ZWRBdDogbm93LFxuICAgIHVzZXJJZFxuICB9O1xuICBhd2FpdCB0eC5wdXQoJ29yZ2FuaXphdGlvbnMnLCBvcmdhbml6YXRpb24ub3JnYW5pemF0aW9uSWQsIG9yZ2FuaXphdGlvbik7XG4gIGF3YWl0IHR4LnB1dCgnb3JnYW5pemF0aW9uTWVtYmVyc2hpcHMnLCBvcmdhbml6YXRpb25LZXkob3JnYW5pemF0aW9uLm9yZ2FuaXphdGlvbklkLCB1c2VySWQpLCBtZW1iZXJzaGlwKTtcbiAgcmV0dXJuIG9yZ2FuaXphdGlvbjtcbn07XG5cbmV4cG9ydCBjb25zdCByZWdpc3Rlck9yZ2FuaXphdGlvbiA9IGFzeW5jIChcbiAgaW5wdXQ6IFNpZ251cElucHV0LFxuICBjb250cm9sQ29udGV4dDogQXBpQ29udGV4dFxuKTogUHJvbWlzZTxTaWdudXBSZXN1bHQ+ID0+IHtcbiAgaWYoXG4gICAgIWlucHV0IHx8XG4gICAgT2JqZWN0LmtleXMoaW5wdXQpLnNvbWUoKGtleSkgPT4gIVsnZW1haWwnLCAnb3JnYW5pemF0aW9uTmFtZScsICdwYXNzd29yZCcsICdyZXF1ZXN0SWQnXS5pbmNsdWRlcyhrZXkpKSB8fFxuICAgIHR5cGVvZiBpbnB1dC5lbWFpbCAhPT0gJ3N0cmluZycgfHxcbiAgICAhL15bXlxcc0BdK0BbXlxcc0BdK1xcLlteXFxzQF0rJC8udGVzdChpbnB1dC5lbWFpbCkgfHxcbiAgICBpbnB1dC5lbWFpbC5sZW5ndGggPiAyNTQgfHxcbiAgICB0eXBlb2YgaW5wdXQucGFzc3dvcmQgIT09ICdzdHJpbmcnIHx8XG4gICAgaW5wdXQucGFzc3dvcmQubGVuZ3RoIDwgMTIgfHxcbiAgICBpbnB1dC5wYXNzd29yZC5sZW5ndGggPiAxMjggfHxcbiAgICB0eXBlb2YgaW5wdXQub3JnYW5pemF0aW9uTmFtZSAhPT0gJ3N0cmluZycgfHxcbiAgICAhaW5wdXQub3JnYW5pemF0aW9uTmFtZS50cmltKCkgfHxcbiAgICBpbnB1dC5vcmdhbml6YXRpb25OYW1lLmxlbmd0aCA+IDE2MFxuICApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWRfc2lnbnVwJyk7XG4gIH1cbiAgcmVxdWlyZUlkZW50aWZpZXIoaW5wdXQucmVxdWVzdElkKTtcbiAgY29uc3QgZW1haWwgPSBpbnB1dC5lbWFpbC50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgY29uc3QgZmluZ2VycHJpbnQgPSBjcmVhdGVIbWFjKCdzaGEyNTYnLCBvcmdhbml6YXRpb25NYWlsS2V5KCkpXG4gICAgLnVwZGF0ZShKU09OLnN0cmluZ2lmeSh7Li4uaW5wdXQsIGVtYWlsfSkpXG4gICAgLmRpZ2VzdCgnaGV4Jyk7XG4gIGNvbnN0IHJlZ2lzdHJhdGlvbklkID0gb3JnYW5pemF0aW9uS2V5KCdyZWdpc3RyYXRpb24nLCBlbWFpbCwgaW5wdXQucmVxdWVzdElkKTtcbiAgcmV0dXJuIG9yZ2FuaXphdGlvblN0b3JlRm9yKGNvbnRyb2xDb250ZXh0KS50cmFuc2FjdGlvbihhc3luYyAodHgpID0+IHtcbiAgICBjb25zdCBwcmV2aW91cyA9IGF3YWl0IHR4LmdldDxSZWdpc3RyYXRpb24+KCdvcmdhbml6YXRpb25SZWdpc3RyYXRpb25zJywgcmVnaXN0cmF0aW9uSWQpO1xuICAgIGlmKHByZXZpb3VzKSB7XG4gICAgICBpZihwcmV2aW91cy5maW5nZXJwcmludCAhPT0gZmluZ2VycHJpbnQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZWdpc3RyYXRpb25fY29uZmxpY3QnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB7cmVnaXN0cmF0aW9uSWQsIHZlcmlmaWNhdGlvblJlcXVpcmVkOiB0cnVlfTtcbiAgICB9XG4gICAgY29uc3QgdXNlcnMgPSBhd2FpdCB0eC5maW5kKCd1c2VycycsIHtlbWFpbH0pO1xuICAgIGNvbnN0IHVzZXJJZCA9IGNyZWF0ZUhhc2goZW1haWwpO1xuICAgIGNvbnN0IGNvbGxpc2lvbiA9IGF3YWl0IHR4LmdldCgndXNlcnMnLCB1c2VySWQpO1xuICAgIGNvbnN0IGV4aXN0aW5nID0gdXNlcnMubGVuZ3RoID4gMCB8fCBCb29sZWFuKGNvbGxpc2lvbik7XG4gICAgbGV0IG9yZ2FuaXphdGlvbklkID0gJyc7XG4gICAgaWYoIWV4aXN0aW5nKSB7XG4gICAgICBjb25zdCBzYWx0ID0gcmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGF3YWl0IHR4LnB1dCgndXNlcnMnLCB1c2VySWQsIHtcbiAgICAgICAgYWN0aXZlOiB0cnVlLFxuICAgICAgICBhZGRlZDogRGF0ZS5ub3coKSxcbiAgICAgICAgZW1haWwsXG4gICAgICAgIGxvY2FsZTogJ2VuJyxcbiAgICAgICAgb3JnYW5pemF0aW9uQWNjb3VudDogdHJ1ZSxcbiAgICAgICAgcGFzc3dvcmQ6IGNyZWF0ZVBhc3N3b3JkKHBhcnNlUGFzc3dvcmQoaW5wdXQucGFzc3dvcmQpLCBzYWx0KSxcbiAgICAgICAgc2FsdCxcbiAgICAgICAgdHlwZTogJ3VzZXInLFxuICAgICAgICB1c2VyQWNjZXNzOiAwLFxuICAgICAgICB1c2VybmFtZTogZW1haWwsXG4gICAgICAgIHZlcmlmaWVkRW1haWw6IGZhbHNlXG4gICAgICB9KTtcbiAgICAgICh7b3JnYW5pemF0aW9uSWR9ID0gYXdhaXQgaW5zZXJ0T3JnYW5pemF0aW9uKHR4LCBpbnB1dC5vcmdhbml6YXRpb25OYW1lLnRyaW0oKSwgdXNlcklkKSk7XG4gICAgfVxuICAgIGNvbnN0IHJlZ2lzdHJhdGlvbjogUmVnaXN0cmF0aW9uID0ge1xuICAgICAgZW1haWwsXG4gICAgICBmaW5nZXJwcmludCxcbiAgICAgIG9yZ2FuaXphdGlvbklkLFxuICAgICAgcmVnaXN0cmF0aW9uSWQsXG4gICAgICByZXF1ZXN0SWQ6IGlucHV0LnJlcXVlc3RJZCxcbiAgICAgIHVzZXJJZDogZXhpc3RpbmcgPyAnJyA6IHVzZXJJZFxuICAgIH07XG4gICAgYXdhaXQgdHgucHV0KCdvcmdhbml6YXRpb25SZWdpc3RyYXRpb25zJywgcmVnaXN0cmF0aW9uSWQsIHJlZ2lzdHJhdGlvbik7XG4gICAgaWYoIWV4aXN0aW5nKSB7XG4gICAgICBhd2FpdCBxdWV1ZU9yZ2FuaXphdGlvblZlcmlmaWNhdGlvbih0eCwgcmVnaXN0cmF0aW9uKTtcbiAgICB9XG4gICAgcmV0dXJuIHtyZWdpc3RyYXRpb25JZCwgdmVyaWZpY2F0aW9uUmVxdWlyZWQ6IHRydWV9O1xuICB9KTtcbn07XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVPcmdhbml6YXRpb24gPSAoXG4gIGNvbnRleHQ6IEFwaUNvbnRleHQsXG4gIG5hbWU6IHN0cmluZyxcbiAgcmVxdWVzdElkOiBzdHJpbmdcbik6IFByb21pc2U8T3JnYW5pemF0aW9uPiA9PlxuICBvcmdhbml6YXRpb25TdG9yZUZvcihjb250ZXh0KS50cmFuc2FjdGlvbihhc3luYyAodHgpID0+IHtcbiAgICBjb25zdCB1c2VySWQgPSBvcmdhbml6YXRpb25Vc2VySWQoY29udGV4dCk7XG4gICAgY29uc3QgdXNlciA9IGF3YWl0IHR4LmdldDx7YWN0aXZlOiBib29sZWFuOyB2ZXJpZmllZEVtYWlsOiBib29sZWFufT4oJ3VzZXJzJywgdXNlcklkKTtcbiAgICBpZighdXNlcj8uYWN0aXZlIHx8ICF1c2VyLnZlcmlmaWVkRW1haWwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignZW1haWxfdmVyaWZpY2F0aW9uX3JlcXVpcmVkJyk7XG4gICAgfVxuICAgIGlmKHR5cGVvZiBuYW1lICE9PSAnc3RyaW5nJyB8fCAhbmFtZS50cmltKCkgfHwgbmFtZS5sZW5ndGggPiAxNjApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZF9vcmdhbml6YXRpb25fbmFtZScpO1xuICAgIH1cbiAgICBjb25zdCBrZXkgPSBvcmdhbml6YXRpb25LZXkoJ2F1dGhlbnRpY2F0ZWQtb3JnYW5pemF0aW9uJywgdXNlcklkLCByZXF1aXJlSWRlbnRpZmllcihyZXF1ZXN0SWQpKTtcbiAgICBjb25zdCBwcmV2aW91cyA9IGF3YWl0IHR4LmdldDx7bmFtZTogc3RyaW5nOyBvcmdhbml6YXRpb25JZDogc3RyaW5nfT4oJ29yZ2FuaXphdGlvblJlZ2lzdHJhdGlvbnMnLCBrZXkpO1xuICAgIGlmKHByZXZpb3VzKSB7XG4gICAgICBpZihwcmV2aW91cy5uYW1lICE9PSBuYW1lLnRyaW0oKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3JlZ2lzdHJhdGlvbl9jb25mbGljdCcpO1xuICAgICAgfVxuICAgICAgY29uc3QgZXhpc3RpbmcgPSAoYXdhaXQgdHguZ2V0PE9yZ2FuaXphdGlvbj4oJ29yZ2FuaXphdGlvbnMnLCBwcmV2aW91cy5vcmdhbml6YXRpb25JZCkpITtcbiAgICAgIGNvbnN0IHtfa2V5LCBfcmV2LCAuLi5vcmdhbml6YXRpb259ID0gZXhpc3Rpbmc7XG4gICAgICByZXR1cm4gb3JnYW5pemF0aW9uO1xuICAgIH1cbiAgICBjb25zdCBvcmcgPSBhd2FpdCBpbnNlcnRPcmdhbml6YXRpb24odHgsIG5hbWUudHJpbSgpLCB1c2VySWQpO1xuICAgIGF3YWl0IHR4LnB1dCgnb3JnYW5pemF0aW9uUmVnaXN0cmF0aW9ucycsIGtleSwge1xuICAgICAgbmFtZTogbmFtZS50cmltKCksXG4gICAgICBvcmdhbml6YXRpb25JZDogb3JnLm9yZ2FuaXphdGlvbklkLFxuICAgICAgcmVxdWVzdElkOiBrZXksXG4gICAgICB1c2VySWRcbiAgICB9KTtcbiAgICBhd2FpdCB0eC5wdXQoJ3VzZXJzJywgdXNlcklkLCB7Li4udXNlciwgb3JnYW5pemF0aW9uQWNjb3VudDogdHJ1ZX0sIHVzZXIpO1xuICAgIHJldHVybiBvcmc7XG4gIH0pO1xuIl0sIm5hbWVzIjpbImNyZWF0ZUhhc2giLCJjcmVhdGVQYXNzd29yZCIsInBhcnNlUGFzc3dvcmQiLCJjcmVhdGVIbWFjIiwicmFuZG9tQnl0ZXMiLCJvcmdhbml6YXRpb25Vc2VySWQiLCJlbmNvZGVPcmdhbml6YXRpb25NYWlsIiwib3JnYW5pemF0aW9uTWFpbEtleSIsIm5ld09yZ2FuaXphdGlvbklkIiwib3JnYW5pemF0aW9uS2V5Iiwib3JnYW5pemF0aW9uU3RvcmVGb3IiLCJyZXF1aXJlSWRlbnRpZmllciIsInF1ZXVlT3JnYW5pemF0aW9uVmVyaWZpY2F0aW9uIiwidHgiLCJyZWdpc3RyYXRpb24iLCJwcmV2aW91cyIsIm5vdyIsIkRhdGUiLCJ0b2tlbiIsInRvU3RyaW5nIiwid2l0aGluSG91ciIsInJlc2VuZFdpbmRvdyIsInZlcmlmaWNhdGlvbiIsImF0dGVtcHRXaW5kb3ciLCJhdHRlbXB0cyIsImV4cGlyZXNBdCIsImxhc3RTZW50IiwicmVzZW5kQ291bnQiLCJ0b2tlbkhhc2giLCJ1c2VkIiwidXNlcklkIiwicHV0IiwicmVnaXN0cmF0aW9uSWQiLCJqb2JJZCIsImpvYiIsImRlc2lyZWQiLCJjaXBoZXJ0ZXh0IiwiZW1haWwiLCJlZmZlY3RpdmVBdCIsImtpbmQiLCJsZWFzZUV4cGlyZXMiLCJvcmdhbml6YXRpb25JZCIsIm93bmVyIiwicHJpY2VWZXJzaW9uIiwic2VxdWVuY2UiLCJzdGF0dXMiLCJpbnNlcnRPcmdhbml6YXRpb24iLCJuYW1lIiwib3JnYW5pemF0aW9uIiwiY3JlYXRlZEF0Iiwib3duZXJVc2VySWQiLCJyZXZpc2lvbiIsInVwZGF0ZWRBdCIsIm1lbWJlcnNoaXAiLCJyb2xlIiwicmVnaXN0ZXJPcmdhbml6YXRpb24iLCJpbnB1dCIsImNvbnRyb2xDb250ZXh0IiwiT2JqZWN0Iiwia2V5cyIsInNvbWUiLCJrZXkiLCJpbmNsdWRlcyIsInRlc3QiLCJsZW5ndGgiLCJwYXNzd29yZCIsIm9yZ2FuaXphdGlvbk5hbWUiLCJ0cmltIiwiRXJyb3IiLCJyZXF1ZXN0SWQiLCJ0b0xvd2VyQ2FzZSIsImZpbmdlcnByaW50IiwidXBkYXRlIiwiSlNPTiIsInN0cmluZ2lmeSIsImRpZ2VzdCIsInRyYW5zYWN0aW9uIiwiZ2V0IiwidmVyaWZpY2F0aW9uUmVxdWlyZWQiLCJ1c2VycyIsImZpbmQiLCJjb2xsaXNpb24iLCJleGlzdGluZyIsIkJvb2xlYW4iLCJzYWx0IiwiYWN0aXZlIiwiYWRkZWQiLCJsb2NhbGUiLCJvcmdhbml6YXRpb25BY2NvdW50IiwidHlwZSIsInVzZXJBY2Nlc3MiLCJ1c2VybmFtZSIsInZlcmlmaWVkRW1haWwiLCJjcmVhdGVPcmdhbml6YXRpb24iLCJjb250ZXh0IiwidXNlciIsIl9rZXkiLCJfcmV2Iiwib3JnIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFRQSxVQUFVLEVBQUVDLGNBQWMsRUFBRUMsYUFBYSxRQUFPLCtCQUErQjtBQUN2RixTQUFRQyxVQUFVLEVBQUVDLFdBQVcsUUFBTyxjQUFjO0FBRXBELFNBQVFDLGtCQUFrQixRQUFPLHFCQUFxQjtBQUN0RCxTQUFRQyxzQkFBc0IsRUFBRUMsbUJBQW1CLFFBQU8sWUFBWTtBQUN0RSxTQUNFQyxpQkFBaUIsRUFDakJDLGVBQWUsRUFDZkMsb0JBQW9CLEVBQ3BCQyxpQkFBaUIsUUFFWixhQUFhO0FBd0JwQixPQUFPLE1BQU1DLGdDQUFnQyxPQUMzQ0MsSUFDQUMsY0FDQUM7SUFFQSxNQUFNQyxNQUFNQyxLQUFLRCxHQUFHO0lBQ3BCLE1BQU1FLFFBQVFkLFlBQVksSUFBSWUsUUFBUSxDQUFDO0lBQ3ZDLE1BQU1DLGFBQWFMLFlBQVlDLE1BQU1ELFNBQVNNLFlBQVksR0FBRztJQUM3RCxNQUFNQyxlQUE2QjtRQUNq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
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Database } from 'arangojs';
|
|
2
|
+
import type { ApiContext } from '../types/auth.types.js';
|
|
3
|
+
export declare const organizationCollections: readonly ['organizations', 'organizationMemberships', 'organizationApps', 'organizationRegistrations', 'organizationVerificationTokens', 'organizationInvitations', 'organizationOwnerTransfers', 'organizationBilling', 'organizationBillingJobs', 'organizationBillingEvents', 'organizationBillingPeriods', 'organizationBillingQuotes'];
|
|
4
|
+
export declare const organizationEdgeCollections: readonly ("hasConnection" | "hasConversation" | "hasCustomerNotification" | "hasCustomerOrder" | "hasFile" | "hasLocation" | "hasMailingList" | "hasMedia" | "hasPayment" | "hasPersona" | "hasPurchasedProduct" | "hasReaction" | "hasSupportTicket" | "hasSupportTicketEvent" | "hasSupportTicketMessage" | "isPosted" | "isTagged")[];
|
|
5
|
+
export type OrganizationCollection = (typeof organizationCollections)[number] | (typeof organizationEdgeCollections)[number] | 'users';
|
|
6
|
+
export type Stored<T> = T & {
|
|
7
|
+
_key: string;
|
|
8
|
+
_rev?: string;
|
|
9
|
+
};
|
|
10
|
+
export type OrganizationTransaction = Readonly<{
|
|
11
|
+
find: <T>(collection: OrganizationCollection, filter: Readonly<Record<string, unknown>>) => Promise<Stored<T>[]>;
|
|
12
|
+
get: <T>(collection: OrganizationCollection, key: string) => Promise<Stored<T> | null>;
|
|
13
|
+
put: <T extends object>(collection: OrganizationCollection, key: string, document: T, previous?: Stored<object> | null) => Promise<void>;
|
|
14
|
+
remove: (collection: OrganizationCollection, document: Stored<object>) => Promise<void>;
|
|
15
|
+
}>;
|
|
16
|
+
export type OrganizationStore = Readonly<{
|
|
17
|
+
initialize: () => Promise<void>;
|
|
18
|
+
transaction: <T>(action: (tx: OrganizationTransaction) => Promise<T>) => Promise<T>;
|
|
19
|
+
}>;
|
|
20
|
+
export declare const organizationKey: (...parts: string[]) => string;
|
|
21
|
+
export declare const newOrganizationId: () => string;
|
|
22
|
+
export declare const requireIdentifier: (value: unknown) => string;
|
|
23
|
+
export declare const controlDatabaseName: () => string;
|
|
24
|
+
export declare const organizationStoreFor: (_context: ApiContext) => OrganizationStore;
|
|
25
|
+
export declare const createOrganizationStore: (controlName: string, database?: Database) => OrganizationStore;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* Stream-transaction steps and ordered retry assertions must execute sequentially. */ /* eslint-disable no-await-in-loop */ import { aql } from 'arangojs';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { DEFAULT_EDGE_COLLECTIONS } from '../constants/collections.js';
|
|
4
|
+
import { useDb } from '../utils/arangodbUtils.js';
|
|
5
|
+
export const organizationCollections = [
|
|
6
|
+
'organizations',
|
|
7
|
+
'organizationMemberships',
|
|
8
|
+
'organizationApps',
|
|
9
|
+
'organizationRegistrations',
|
|
10
|
+
'organizationVerificationTokens',
|
|
11
|
+
'organizationInvitations',
|
|
12
|
+
'organizationOwnerTransfers',
|
|
13
|
+
'organizationBilling',
|
|
14
|
+
'organizationBillingJobs',
|
|
15
|
+
'organizationBillingEvents',
|
|
16
|
+
'organizationBillingPeriods',
|
|
17
|
+
'organizationBillingQuotes'
|
|
18
|
+
];
|
|
19
|
+
export const organizationEdgeCollections = DEFAULT_EDGE_COLLECTIONS;
|
|
20
|
+
export const organizationKey = (...parts)=>createHash('sha256').update(JSON.stringify(parts)).digest('hex');
|
|
21
|
+
export const newOrganizationId = ()=>randomUUID();
|
|
22
|
+
export const requireIdentifier = (value)=>{
|
|
23
|
+
if (typeof value !== 'string' || !/^[a-zA-Z0-9_-]{1,128}$/.test(value)) {
|
|
24
|
+
throw new Error('invalid_identifier');
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
export const controlDatabaseName = ()=>{
|
|
29
|
+
const name = process.env.ARANGODB_DATABASE;
|
|
30
|
+
if (!name || !/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(name) || name === '_system') {
|
|
31
|
+
throw new Error('organization_control_database_required');
|
|
32
|
+
}
|
|
33
|
+
return name;
|
|
34
|
+
};
|
|
35
|
+
export const organizationStoreFor = (_context)=>createOrganizationStore(controlDatabaseName());
|
|
36
|
+
const indexes = {
|
|
37
|
+
organizationApps: [
|
|
38
|
+
[
|
|
39
|
+
'appId'
|
|
40
|
+
]
|
|
41
|
+
],
|
|
42
|
+
organizationBilling: [
|
|
43
|
+
[
|
|
44
|
+
'organizationId'
|
|
45
|
+
]
|
|
46
|
+
],
|
|
47
|
+
organizationBillingEvents: [
|
|
48
|
+
[
|
|
49
|
+
'eventId'
|
|
50
|
+
]
|
|
51
|
+
],
|
|
52
|
+
organizationBillingJobs: [
|
|
53
|
+
[
|
|
54
|
+
'organizationId',
|
|
55
|
+
'sequence'
|
|
56
|
+
]
|
|
57
|
+
],
|
|
58
|
+
organizationMemberships: [
|
|
59
|
+
[
|
|
60
|
+
'organizationId',
|
|
61
|
+
'userId'
|
|
62
|
+
]
|
|
63
|
+
],
|
|
64
|
+
organizations: [
|
|
65
|
+
[
|
|
66
|
+
'organizationId'
|
|
67
|
+
]
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
export const createOrganizationStore = (controlName, database = useDb(controlName))=>{
|
|
71
|
+
if (!controlName || controlName === '_system' || !/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(controlName)) {
|
|
72
|
+
throw new Error('organization_control_database_required');
|
|
73
|
+
}
|
|
74
|
+
let initialized = false;
|
|
75
|
+
const initialize = async ()=>{
|
|
76
|
+
if (initialized) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
for (const name of [
|
|
80
|
+
...organizationCollections,
|
|
81
|
+
'users',
|
|
82
|
+
...organizationEdgeCollections
|
|
83
|
+
]){
|
|
84
|
+
const collection = database.collection(name);
|
|
85
|
+
if (!await collection.exists()) {
|
|
86
|
+
try {
|
|
87
|
+
await collection.create(organizationEdgeCollections.includes(name) ? {
|
|
88
|
+
type: 3
|
|
89
|
+
} : {});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error.errorNum !== 1207) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const fields of indexes[name] || []){
|
|
97
|
+
await collection.ensureIndex({
|
|
98
|
+
fields,
|
|
99
|
+
type: 'persistent',
|
|
100
|
+
unique: true
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
initialized = true;
|
|
105
|
+
};
|
|
106
|
+
const transaction = async (action)=>{
|
|
107
|
+
await initialize();
|
|
108
|
+
const trx = await database.beginTransaction({
|
|
109
|
+
exclusive: [
|
|
110
|
+
...organizationCollections,
|
|
111
|
+
'users',
|
|
112
|
+
...organizationEdgeCollections
|
|
113
|
+
]
|
|
114
|
+
}, {
|
|
115
|
+
lockTimeout: 10
|
|
116
|
+
});
|
|
117
|
+
const tx = {
|
|
118
|
+
find: async (name, filter)=>{
|
|
119
|
+
const cursor = await trx.step(()=>database.query(aql`FOR doc IN ${database.collection(name)} FILTER MATCHES(doc, ${filter}) RETURN doc`));
|
|
120
|
+
return await cursor.all();
|
|
121
|
+
},
|
|
122
|
+
get: async (name, key)=>{
|
|
123
|
+
const cursor = await trx.step(()=>database.query(aql`RETURN DOCUMENT(${name}, ${requireIdentifier(key)})`));
|
|
124
|
+
return await cursor.next();
|
|
125
|
+
},
|
|
126
|
+
put: async (name, key, document, previous)=>{
|
|
127
|
+
const collection = database.collection(name);
|
|
128
|
+
const { _id, _key, _rev, ...safe } = document;
|
|
129
|
+
if (previous) {
|
|
130
|
+
await trx.step(()=>collection.replace({
|
|
131
|
+
_key: requireIdentifier(key),
|
|
132
|
+
_rev: previous._rev
|
|
133
|
+
}, safe, {
|
|
134
|
+
ignoreRevs: false
|
|
135
|
+
}));
|
|
136
|
+
} else {
|
|
137
|
+
await trx.step(()=>collection.save({
|
|
138
|
+
...safe,
|
|
139
|
+
_key: requireIdentifier(key)
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
remove: async (name, document)=>{
|
|
144
|
+
await trx.step(()=>database.collection(name).remove({
|
|
145
|
+
_key: document._key,
|
|
146
|
+
_rev: document._rev
|
|
147
|
+
}, {
|
|
148
|
+
ignoreRevs: false
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
try {
|
|
153
|
+
const result = await action(tx);
|
|
154
|
+
await trx.commit();
|
|
155
|
+
return result;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
await trx.abort().catch(()=>undefined);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
initialize,
|
|
163
|
+
transaction
|
|
164
|
+
};
|
|
165
|
+
}; /* Stream-transaction steps and ordered retry assertions must execute sequentially. */ /* ArangoDB document metadata uses these reserved field names. */ /* eslint no-underscore-dangle: ["warn", {"allow": ["_key", "_rev", "_id"]}] */
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvb3JnYW5pemF0aW9ucy9zdG9yZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBTdHJlYW0tdHJhbnNhY3Rpb24gc3RlcHMgYW5kIG9yZGVyZWQgcmV0cnkgYXNzZXJ0aW9ucyBtdXN0IGV4ZWN1dGUgc2VxdWVudGlhbGx5LiAqL1xuLyogZXNsaW50LWRpc2FibGUgbm8tYXdhaXQtaW4tbG9vcCAqL1xuaW1wb3J0IHthcWwsIHR5cGUgRGF0YWJhc2V9IGZyb20gJ2FyYW5nb2pzJztcbmltcG9ydCB7Y3JlYXRlSGFzaCwgcmFuZG9tVVVJRH0gZnJvbSAnbm9kZTpjcnlwdG8nO1xuXG5pbXBvcnQge0RFRkFVTFRfRURHRV9DT0xMRUNUSU9OU30gZnJvbSAnLi4vY29uc3RhbnRzL2NvbGxlY3Rpb25zLmpzJztcbmltcG9ydCB7dXNlRGJ9IGZyb20gJy4uL3V0aWxzL2FyYW5nb2RiVXRpbHMuanMnO1xuXG5pbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5cbmV4cG9ydCBjb25zdCBvcmdhbml6YXRpb25Db2xsZWN0aW9ucyA9IFtcbiAgJ29yZ2FuaXphdGlvbnMnLFxuICAnb3JnYW5pemF0aW9uTWVtYmVyc2hpcHMnLFxuICAnb3JnYW5pemF0aW9uQXBwcycsXG4gICdvcmdhbml6YXRpb25SZWdpc3RyYXRpb25zJyxcbiAgJ29yZ2FuaXphdGlvblZlcmlmaWNhdGlvblRva2VucycsXG4gICdvcmdhbml6YXRpb25JbnZpdGF0aW9ucycsXG4gICdvcmdhbml6YXRpb25Pd25lclRyYW5zZmVycycsXG4gICdvcmdhbml6YXRpb25CaWxsaW5nJyxcbiAgJ29yZ2FuaXphdGlvbkJpbGxpbmdKb2JzJyxcbiAgJ29yZ2FuaXphdGlvbkJpbGxpbmdFdmVudHMnLFxuICAnb3JnYW5pemF0aW9uQmlsbGluZ1BlcmlvZHMnLFxuICAnb3JnYW5pemF0aW9uQmlsbGluZ1F1b3Rlcydcbl0gYXMgY29uc3Q7XG5leHBvcnQgY29uc3Qgb3JnYW5pemF0aW9uRWRnZUNvbGxlY3Rpb25zID0gREVGQVVMVF9FREdFX0NPTExFQ1RJT05TO1xuZXhwb3J0IHR5cGUgT3JnYW5pemF0aW9uQ29sbGVjdGlvbiA9XG4gICh0eXBlb2Ygb3JnYW5pemF0aW9uQ29sbGVjdGlvbnMpW251bWJlcl0gfCAodHlwZW9mIG9yZ2FuaXphdGlvbkVkZ2VDb2xsZWN0aW9ucylbbnVtYmVyXSB8ICd1c2Vycyc7XG5leHBvcnQgdHlwZSBTdG9yZWQ8VD4gPSBUICYge19rZXk6IHN0cmluZzsgX3Jldj86IHN0cmluZ307XG5leHBvcnQgdHlwZSBPcmdhbml6YXRpb25UcmFuc2FjdGlvbiA9IFJlYWRvbmx5PHtcbiAgZmluZDogPFQ+KFxuICAgIGNvbGxlY3Rpb246IE9yZ2FuaXphdGlvbkNvbGxlY3Rpb24sXG4gICAgZmlsdGVyOiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj5cbiAgKSA9PiBQcm9taXNlPFN0b3JlZDxUPltdPjtcbiAgZ2V0OiA8VD4oY29sbGVjdGlvbjogT3JnYW5pemF0aW9uQ29sbGVjdGlvbiwga2V5OiBzdHJpbmcpID0+IFByb21pc2U8U3RvcmVkPFQ+IHwgbnVsbD47XG4gIHB1dDogPFQgZXh0ZW5kcyBvYmplY3Q+KFxuICAgIGNvbGxlY3Rpb246IE9yZ2FuaXphdGlvbkNvbGxlY3Rpb24sXG4gICAga2V5OiBzdHJpbmcsXG4gICAgZG9jdW1lbnQ6IFQsXG4gICAgcHJldmlvdXM/OiBTdG9yZWQ8b2JqZWN0PiB8IG51bGxcbiAgKSA9PiBQcm9taXNlPHZvaWQ+O1xuICByZW1vdmU6IChjb2xsZWN0aW9uOiBPcmdhbml6YXRpb25Db2xsZWN0aW9uLCBkb2N1bWVudDogU3RvcmVkPG9iamVjdD4pID0+IFByb21pc2U8dm9pZD47XG59PjtcbmV4cG9ydCB0eXBlIE9yZ2FuaXphdGlvblN0b3JlID0gUmVhZG9ubHk8e1xuICBpbml0aWFsaXplOiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xuICB0cmFuc2FjdGlvbjogPFQ+KGFjdGlvbjogKHR4OiBPcmdhbml6YXRpb25UcmFuc2FjdGlvbikgPT4gUHJvbWlzZTxUPikgPT4gUHJvbWlzZTxUPjtcbn0+O1xuZXhwb3J0IGNvbnN0IG9yZ2FuaXphdGlvbktleSA9ICguLi5wYXJ0czogc3RyaW5nW10pOiBzdHJpbmcgPT5cbiAgY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKEpTT04uc3RyaW5naWZ5KHBhcnRzKSkuZGlnZXN0KCdoZXgnKTtcbmV4cG9ydCBjb25zdCBuZXdPcmdhbml6YXRpb25JZCA9ICgpOiBzdHJpbmcgPT4gcmFuZG9tVVVJRCgpO1xuZXhwb3J0IGNvbnN0IHJlcXVpcmVJZGVudGlmaWVyID0gKHZhbHVlOiB1bmtub3duKTogc3RyaW5nID0+IHtcbiAgaWYodHlwZW9mIHZhbHVlICE9PSAnc3RyaW5nJyB8fCAhL15bYS16QS1aMC05Xy1dezEsMTI4fSQvLnRlc3QodmFsdWUpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkX2lkZW50aWZpZXInKTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59O1xuZXhwb3J0IGNvbnN0IGNvbnRyb2xEYXRhYmFzZU5hbWUgPSAoKTogc3RyaW5nID0+IHtcbiAgY29uc3QgbmFtZSA9IHByb2Nlc3MuZW52LkFSQU5HT0RCX0RBVEFCQVNFO1xuICBpZighbmFtZSB8fCAhL15bYS16QS1aXVthLXpBLVowLTlfLV17MCw2M30kLy50ZXN0KG5hbWUpIHx8IG5hbWUgPT09ICdfc3lzdGVtJykge1xuICAgIHRocm93IG5ldyBFcnJvcignb3JnYW5pemF0aW9uX2NvbnRyb2xfZGF0YWJhc2VfcmVxdWlyZWQnKTtcbiAgfVxuICByZXR1cm4gbmFtZTtcbn07XG5leHBvcnQgY29uc3Qgb3JnYW5pemF0aW9uU3RvcmVGb3IgPSAoX2NvbnRleHQ6IEFwaUNvbnRleHQpOiBPcmdhbml6YXRpb25TdG9yZSA9PlxuICBjcmVhdGVPcmdhbml6YXRpb25TdG9yZShjb250cm9sRGF0YWJhc2VOYW1lKCkpO1xuXG5jb25zdCBpbmRleGVzOiBQYXJ0aWFsPFJlY29yZDxPcmdhbml6YXRpb25Db2xsZWN0aW9uLCBzdHJpbmdbXVtdPj4gPSB7XG4gIG9yZ2FuaXphdGlvbkFwcHM6IFtbJ2FwcElkJ11dLFxuICBvcmdhbml6YXRpb25CaWxsaW5nOiBbWydvcmdhbml6YXRpb25JZCddXSxcbiAgb3JnYW5pemF0aW9uQmlsbGluZ0V2ZW50czogW1snZXZlbnRJZCddXSxcbiAgb3JnYW5pemF0aW9uQmlsbGluZ0pvYnM6IFtbJ29yZ2FuaXphdGlvbklkJywgJ3NlcXVlbmNlJ11dLFxuICBvcmdhbml6YXRpb25NZW1iZXJzaGlwczogW1snb3JnYW5pemF0aW9uSWQnLCAndXNlcklkJ11dLFxuICBvcmdhbml6YXRpb25zOiBbWydvcmdhbml6YXRpb25JZCddXVxufTtcblxuZXhwb3J0IGNvbnN0IGNyZWF0ZU9yZ2FuaXphdGlvblN0b3JlID0gKFxuICBjb250cm9sTmFtZTogc3RyaW5nLFxuICBkYXRhYmFzZTogRGF0YWJhc2UgPSB1c2VEYihjb250cm9sTmFtZSlcbik6IE9yZ2FuaXphdGlvblN0b3JlID0+IHtcbiAgaWYoIWNvbnRyb2xOYW1lIHx8IGNvbnRyb2xOYW1lID09PSAnX3N5c3RlbScgfHwgIS9eW2EtekEtWl1bYS16QS1aMC05Xy1dezAsNjN9JC8udGVzdChjb250cm9sTmFtZSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ29yZ2FuaXphdGlvbl9jb250cm9sX2RhdGFiYXNlX3JlcXVpcmVkJyk7XG4gIH1cbiAgbGV0IGluaXRpYWxpemVkID0gZmFsc2U7XG4gIGNvbnN0IGluaXRpYWxpemUgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgaWYoaW5pdGlhbGl6ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZm9yKGNvbnN0IG5hbWUgb2YgW1xuICAgICAgLi4ub3JnYW5pemF0aW9uQ29sbGVjdGlvbnMsXG4gICAgICAndXNlcnMnLFxuICAgICAgLi4ub3JnYW5pemF0aW9uRWRnZUNvbGxlY3Rpb25zXG4gICAgXSBhcyBPcmdhbml6YXRpb25Db2xsZWN0aW9uW10pIHtcbiAgICAgIGNvbnN0IGNvbGxlY3Rpb24gPSBkYXRhYmFzZS5jb2xsZWN0aW9uKG5hbWUpO1xuICAgICAgaWYoIShhd2FpdCBjb2xsZWN0aW9uLmV4aXN0cygpKSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGNvbGxlY3Rpb24uY3JlYXRlKFxuICAgICAgICAgICAgb3JnYW5pemF0aW9uRWRnZUNvbGxlY3Rpb25zLmluY2x1ZGVzKG5hbWUgYXMgKHR5cGVvZiBvcmdhbml6YXRpb25FZGdlQ29sbGVjdGlvbnMpW251bWJlcl0pXG4gICAgICAgICAgICAgID8ge3R5cGU6IDN9XG4gICAgICAgICAgICAgIDoge31cbiAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XG4gICAgICAgICAgaWYoKGVycm9yIGFzIHtlcnJvck51bT86IG51bWJlcn0pLmVycm9yTnVtICE9PSAxMjA3KSB7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGZvcihjb25zdCBmaWVsZHMgb2YgaW5kZXhlc1tuYW1lXSB8fCBbXSkge1xuICAgICAgICBhd2FpdCBjb2xsZWN0aW9uLmVuc3VyZUluZGV4KHtmaWVsZHMsIHR5cGU6ICdwZXJzaXN0ZW50JywgdW5pcXVlOiB0cnVlfSk7XG4gICAgICB9XG4gICAgfVxuICAgIGluaXRpYWxpemVkID0gdHJ1ZTtcbiAgfTtcbiAgY29uc3QgdHJhbnNhY3Rpb24gPSBhc3luYyA8VD4oYWN0aW9uOiAodHg6IE9yZ2FuaXphdGlvblRyYW5zYWN0aW9uKSA9PiBQcm9taXNlPFQ+KTogUHJvbWlzZTxUPiA9PiB7XG4gICAgYXdhaXQgaW5pdGlhbGl6ZSgpO1xuICAgIGNvbnN0IHRyeCA9IGF3YWl0IGRhdGFiYXNlLmJlZ2luVHJhbnNhY3Rpb24oXG4gICAgICB7ZXhjbHVzaXZlOiBbLi4ub3JnYW5pemF0aW9uQ29sbGVjdGlvbnMsICd1c2VycycsIC4uLm9yZ2FuaXphdGlvbkVkZ2VDb2xsZWN0aW9uc119LFxuICAgICAge2xvY2tUaW1lb3V0OiAxMH1cbiAgICApO1xuICAgIGNvbnN0IHR4OiBPcmdhbml6YXRpb25UcmFuc2FjdGlvbiA9IHtcbiAgICAgIGZpbmQ6IGFzeW5jIDxEPihuYW1lOiBPcmdhbml6YXRpb25Db2xsZWN0aW9uLCBmaWx0ZXI6IFJlYWRvbmx5PFJlY29yZDxzdHJpbmcsIHVua25vd24+PikgPT4ge1xuICAgICAgICBjb25zdCBjdXJzb3IgPSBhd2FpdCB0cnguc3RlcCgoKSA9PlxuICAgICAgICAgIGRhdGFiYXNlLnF1ZXJ5KFxuICAgICAgICAgICAgYXFsYEZPUiBkb2MgSU4gJHtkYXRhYmFzZS5jb2xsZWN0aW9uKG5hbWUpfSBGSUxURVIgTUFUQ0hFUyhkb2MsICR7ZmlsdGVyfSkgUkVUVVJOIGRvY2BcbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybiAoYXdhaXQgY3Vyc29yLmFsbCgpKSBhcyBTdG9yZWQ8RD5bXTtcbiAgICAgIH0sXG4gICAgICBnZXQ6IGFzeW5jIDxEPihuYW1lOiBPcmdhbml6YXRpb25Db2xsZWN0aW9uLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICBjb25zdCBjdXJzb3IgPSBhd2FpdCB0cnguc3RlcCgoKSA9PlxuICAgICAgICAgIGRhdGFiYXNlLnF1ZXJ5KGFxbGBSRVRVUk4gRE9DVU1FTlQoJHtuYW1lfSwgJHtyZXF1aXJlSWRlbnRpZmllcihrZXkpfSlgKVxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gKGF3YWl0IGN1cnNvci5uZXh0KCkpIGFzIFN0b3JlZDxEPiB8IG51bGw7XG4gICAgICB9LFxuICAgICAgcHV0OiBhc3luYyA8RCBleHRlbmRzIG9iamVjdD4oXG4gICAgICAgIG5hbWU6IE9yZ2FuaXphdGlvbkNvbGxlY3Rpb24sXG4gICAgICAgIGtleTogc3RyaW5nLFxuICAgICAgICBkb2N1bWVudDogRCxcbiAgICAgICAgcHJldmlvdXM/OiBTdG9yZWQ8b2JqZWN0PiB8IG51bGxcbiAgICAgICkgPT4ge1xuICAgICAgICBjb25zdCBjb2xsZWN0aW9uID0gZGF0YWJhc2UuY29sbGVjdGlvbihuYW1lKTtcbiAgICAgICAgY29uc3Qge19pZCwgX2tleSwgX3JldiwgLi4uc2FmZX0gPSBkb2N1bWVudCBhcyBEICYge19pZD86IHN0cmluZzsgX2tleT86IHN0cmluZzsgX3Jldj86IHN0cmluZ307XG4gICAgICAgIGlmKHByZXZpb3VzKSB7XG4gICAgICAgICAgYXdhaXQgdHJ4LnN0ZXAoKCkgPT5cbiAgICAgICAgICAgIGNvbGxlY3Rpb24ucmVwbGFjZSh7X2tleTogcmVxdWlyZUlkZW50aWZpZXIoa2V5KSwgX3JldjogcHJldmlvdXMuX3Jldn0sIHNhZmUsIHtpZ25vcmVSZXZzOiBmYWxzZX0pXG4gICAgICAgICAgKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBhd2FpdCB0cnguc3RlcCgoKSA9PiBjb2xsZWN0aW9uLnNhdmUoey4uLnNhZmUsIF9rZXk6IHJlcXVpcmVJZGVudGlmaWVyKGtleSl9KSk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICByZW1vdmU6IGFzeW5jIChuYW1lLCBkb2N1bWVudCkgPT4ge1xuICAgICAgICBhd2FpdCB0cnguc3RlcCgoKSA9PlxuICAgICAgICAgIGRhdGFiYXNlLmNvbGxlY3Rpb24obmFtZSkucmVtb3ZlKHtfa2V5OiBkb2N1bWVudC5fa2V5LCBfcmV2OiBkb2N1bWVudC5fcmV2fSwge2lnbm9yZVJldnM6IGZhbHNlfSlcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBhY3Rpb24odHgpO1xuICAgICAgYXdhaXQgdHJ4LmNvbW1pdCgpO1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9IGNhdGNoKGVycm9yKSB7XG4gICAgICBhd2FpdCB0cnguYWJvcnQoKS5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9O1xuICByZXR1cm4ge2luaXRpYWxpemUsIHRyYW5zYWN0aW9ufTtcbn07XG4vKiBTdHJlYW0tdHJhbnNhY3Rpb24gc3RlcHMgYW5kIG9yZGVyZWQgcmV0cnkgYXNzZXJ0aW9ucyBtdXN0IGV4ZWN1dGUgc2VxdWVudGlhbGx5LiAqL1xuXG4vKiBBcmFuZ29EQiBkb2N1bWVudCBtZXRhZGF0YSB1c2VzIHRoZXNlIHJlc2VydmVkIGZpZWxkIG5hbWVzLiAqL1xuLyogZXNsaW50IG5vLXVuZGVyc2NvcmUtZGFuZ2xlOiBbXCJ3YXJuXCIsIHtcImFsbG93XCI6IFtcIl9rZXlcIiwgXCJfcmV2XCIsIFwiX2lkXCJdfV0gKi9cbiJdLCJuYW1lcyI6WyJhcWwiLCJjcmVhdGVIYXNoIiwicmFuZG9tVVVJRCIsIkRFRkFVTFRfRURHRV9DT0xMRUNUSU9OUyIsInVzZURiIiwib3JnYW5pemF0aW9uQ29sbGVjdGlvbnMiLCJvcmdhbml6YXRpb25FZGdlQ29sbGVjdGlvbnMiLCJvcmdhbml6YXRpb25LZXkiLCJwYXJ0cyIsInVwZGF0ZSIsIkpTT04iLCJzdHJpbmdpZnkiLCJkaWdlc3QiLCJuZXdPcmdhbml6YXRpb25JZCIsInJlcXVpcmVJZGVudGlmaWVyIiwidmFsdWUiLCJ0ZXN0IiwiRXJyb3IiLCJjb250cm9sRGF0YWJhc2VOYW1lIiwibmFtZSIsInByb2Nlc3MiLCJlbnYiLCJBUkFOR09EQl9EQVRBQkFTRSIsIm9yZ2FuaXphdGlvblN0b3JlRm9yIiwiX2NvbnRleHQiLCJjcmVhdGVPcmdhbml6YXRpb25TdG9yZSIsImluZGV4ZXMiLCJvcmdhbml6YXRpb25BcHBzIiwib3JnYW5pemF0aW9uQmlsbGluZyIsIm9yZ2FuaXphdGlvbkJpbGxpbmdFdmVudHMiLCJvcmdhbml6YXRpb25CaWxsaW5nSm9icyIsIm9yZ2FuaXphdGlvbk1lbWJlcnNoaXBzIiwib3JnYW5pemF0aW9ucyIsImNvbnRyb2xOYW1lIiwiZGF0YWJhc2UiLCJpbml0aWFsaXplZCIsImluaXRpYWxpemUiLCJjb2xsZWN0aW9uIiwiZXhpc3RzIiwiY3JlYXRlIiwiaW5jbHVkZXMiLCJ0eXBlIiwiZXJyb3IiLCJlcnJvck51bSIsImZpZWxkcyIsImVuc3VyZUluZGV4IiwidW5pcXVlIiwidHJhbnNhY3Rpb24iLCJhY3Rpb24iLCJ0cngiLCJiZWdpblRyYW5zYWN0aW9uIiwiZXhjbHVzaXZlIiwibG9ja1RpbWVvdXQiLCJ0eCIsImZpbmQiLCJmaWx0ZXIiLCJjdXJzb3IiLCJzdGVwIiwicXVlcnkiLCJhbGwiLCJnZXQiLCJrZXkiLCJuZXh0IiwicHV0IiwiZG9jdW1lbnQiLCJwcmV2aW91cyIsIl9pZCIsIl9rZXkiLCJfcmV2Iiwic2FmZSIsInJlcGxhY2UiLCJpZ25vcmVSZXZzIiwic2F2ZSIsInJlbW92ZSIsInJlc3VsdCIsImNvbW1pdCIsImFib3J0IiwiY2F0Y2giLCJ1bmRlZmluZWQiXSwibWFwcGluZ3MiOiJBQUFBLG9GQUFvRixHQUNwRixtQ0FBbUMsR0FDbkMsU0FBUUEsR0FBRyxRQUFzQixXQUFXO0FBQzVDLFNBQVFDLFVBQVUsRUFBRUMsVUFBVSxRQUFPLGNBQWM7QUFFbkQsU0FBUUMsd0JBQXdCLFFBQU8sOEJBQThCO0FBQ3JFLFNBQVFDLEtBQUssUUFBTyw0QkFBNEI7QUFJaEQsT0FBTyxNQUFNQywwQkFBMEI7SUFDckM7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0NBQ0QsQ0FBVTtBQUNYLE9BQU8sTUFBTUMsOEJBQThCSCx5QkFBeUI7QUFzQnBFLE9BQU8sTUFBTUksa0JBQWtCLENBQUMsR0FBR0MsUUFDakNQLFdBQVcsVUFBVVEsTUFBTSxDQUFDQyxLQUFLQyxTQUFTLENBQUNILFFBQVFJLE1BQU0sQ0FBQyxPQUFPO0FBQ25FLE9BQU8sTUFBTUMsb0JBQW9CLElBQWNYLGFBQWE7QUFDNUQsT0FBTyxNQUFNWSxvQkFBb0IsQ0FBQ0M7SUFD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
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { ApiContext } from '../types/auth.types.js';
|
|
2
|
+
export type PlanKey = 'starter' | 'pro';
|
|
3
|
+
export type BillingMode = 'disabled' | 'test';
|
|
4
|
+
export type MembershipRole = 'admin' | 'member';
|
|
5
|
+
export type MembershipStatus = 'pending' | 'active' | 'deactivated';
|
|
6
|
+
export type Organization = Readonly<{
|
|
7
|
+
createdAt: number;
|
|
8
|
+
name: string;
|
|
9
|
+
organizationId: string;
|
|
10
|
+
ownerUserId: string;
|
|
11
|
+
revision: number;
|
|
12
|
+
updatedAt: number;
|
|
13
|
+
}>;
|
|
14
|
+
export type OrganizationMembership = Readonly<{
|
|
15
|
+
createdAt: number;
|
|
16
|
+
organizationId: string;
|
|
17
|
+
revision: number;
|
|
18
|
+
role: MembershipRole;
|
|
19
|
+
status: MembershipStatus;
|
|
20
|
+
updatedAt: number;
|
|
21
|
+
userId: string;
|
|
22
|
+
}>;
|
|
23
|
+
export type OrganizationAccess = Readonly<{
|
|
24
|
+
membership: OrganizationMembership;
|
|
25
|
+
organization: Organization;
|
|
26
|
+
userId: string;
|
|
27
|
+
}>;
|
|
28
|
+
export type OrganizationPlan = Readonly<{
|
|
29
|
+
additionalSeatCents: number;
|
|
30
|
+
baseCents: number;
|
|
31
|
+
currency: 'usd';
|
|
32
|
+
includedSeats: 10;
|
|
33
|
+
interval: 'month';
|
|
34
|
+
key: PlanKey;
|
|
35
|
+
version: '2026-10-v1';
|
|
36
|
+
}>;
|
|
37
|
+
export type OveragePreference = Readonly<{
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
monthlyCapCents: number;
|
|
40
|
+
revision: number;
|
|
41
|
+
}>;
|
|
42
|
+
export type SubscriptionSummary = Readonly<{
|
|
43
|
+
activeMembers: number;
|
|
44
|
+
billingMode: BillingMode;
|
|
45
|
+
canManage: boolean;
|
|
46
|
+
estimatedMonthlyCents: number;
|
|
47
|
+
extraSeats: number;
|
|
48
|
+
metersEnabled: false;
|
|
49
|
+
organizationId: string;
|
|
50
|
+
overage: OveragePreference;
|
|
51
|
+
pendingChange: string | null;
|
|
52
|
+
periodEnd: number | null;
|
|
53
|
+
plan: PlanKey | null;
|
|
54
|
+
reconciliationPending: boolean;
|
|
55
|
+
status: string;
|
|
56
|
+
}>;
|
|
57
|
+
export type MembershipChange = Readonly<{
|
|
58
|
+
confirmedQuoteId?: string;
|
|
59
|
+
expectedRevision: number;
|
|
60
|
+
role: MembershipRole;
|
|
61
|
+
status: MembershipStatus;
|
|
62
|
+
userId: string;
|
|
63
|
+
}>;
|
|
64
|
+
export type SeatChangeQuote = Readonly<{
|
|
65
|
+
currency: 'usd';
|
|
66
|
+
expiresAt: number;
|
|
67
|
+
organizationRevision: number;
|
|
68
|
+
prorationCents: number;
|
|
69
|
+
quantity: number;
|
|
70
|
+
quoteId: string;
|
|
71
|
+
}>;
|
|
72
|
+
export type SignupInput = Readonly<{
|
|
73
|
+
email: string;
|
|
74
|
+
organizationName: string;
|
|
75
|
+
password: string;
|
|
76
|
+
requestId: string;
|
|
77
|
+
}>;
|
|
78
|
+
export type SignupResult = Readonly<{
|
|
79
|
+
registrationId: string;
|
|
80
|
+
verificationRequired: true;
|
|
81
|
+
}>;
|
|
82
|
+
export type BillingJob = Readonly<{
|
|
83
|
+
attempts: number;
|
|
84
|
+
desired: Record<string, unknown>;
|
|
85
|
+
effectiveAt: number;
|
|
86
|
+
jobId: string;
|
|
87
|
+
kind: 'cancel' | 'mail' | 'plan' | 'reconcile' | 'seats';
|
|
88
|
+
leaseExpires: number;
|
|
89
|
+
organizationId: string;
|
|
90
|
+
owner: string;
|
|
91
|
+
priceVersion: '2026-10-v1';
|
|
92
|
+
sequence: number;
|
|
93
|
+
status: 'complete' | 'pending';
|
|
94
|
+
}>;
|
|
95
|
+
export type ProviderSubscription = Readonly<{
|
|
96
|
+
cancelAtPeriodEnd?: boolean;
|
|
97
|
+
baseItemId: string;
|
|
98
|
+
customerId: string;
|
|
99
|
+
extraSeats: number;
|
|
100
|
+
id: string;
|
|
101
|
+
lastJobId?: string;
|
|
102
|
+
pendingPlan?: PlanKey | null;
|
|
103
|
+
periodEnd: number;
|
|
104
|
+
periodStart: number;
|
|
105
|
+
plan: PlanKey;
|
|
106
|
+
seatItemId: string;
|
|
107
|
+
status: string;
|
|
108
|
+
startedAt?: number;
|
|
109
|
+
}>;
|
|
110
|
+
export type CheckoutResult = Readonly<{
|
|
111
|
+
expiresAt: number;
|
|
112
|
+
id: string;
|
|
113
|
+
url: string;
|
|
114
|
+
}>;
|
|
115
|
+
export type VerifiedBillingEvent = Readonly<{
|
|
116
|
+
customerId: string;
|
|
117
|
+
eventId: string;
|
|
118
|
+
type: string;
|
|
119
|
+
}>;
|
|
120
|
+
export type BillingAdjustment = Readonly<{
|
|
121
|
+
amountCents: number;
|
|
122
|
+
periodEnd: number;
|
|
123
|
+
periodStart: number;
|
|
124
|
+
providerId: string;
|
|
125
|
+
}>;
|
|
126
|
+
export type BillingUpdateResult = Readonly<{
|
|
127
|
+
adjustments: readonly BillingAdjustment[];
|
|
128
|
+
}>;
|
|
129
|
+
export type BillingProvider = Readonly<{
|
|
130
|
+
checkout: (customerId: string, plan: PlanKey, extraSeats: number, key: string) => Promise<CheckoutResult>;
|
|
131
|
+
customer: (organizationId: string, email: string) => Promise<string>;
|
|
132
|
+
portal: (customerId: string) => Promise<string>;
|
|
133
|
+
preview: (subscription: ProviderSubscription, plan: PlanKey, seats: number, effectiveAt: number) => Promise<number>;
|
|
134
|
+
retrieve: (customerId: string, subscriptionId?: string) => Promise<ProviderSubscription | null>;
|
|
135
|
+
update: (subscription: ProviderSubscription, job: BillingJob, fence: () => Promise<void>) => Promise<BillingUpdateResult>;
|
|
136
|
+
verify: (body: string | Buffer, signature: string) => VerifiedBillingEvent;
|
|
137
|
+
}>;
|
|
138
|
+
export type OrganizationContext = ApiContext;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { };
|
|
2
|
+
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvb3JnYW5pemF0aW9ucy90eXBlcy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5cbmV4cG9ydCB0eXBlIFBsYW5LZXkgPSAnc3RhcnRlcicgfCAncHJvJztcbmV4cG9ydCB0eXBlIEJpbGxpbmdNb2RlID0gJ2Rpc2FibGVkJyB8ICd0ZXN0JztcbmV4cG9ydCB0eXBlIE1lbWJlcnNoaXBSb2xlID0gJ2FkbWluJyB8ICdtZW1iZXInO1xuZXhwb3J0IHR5cGUgTWVtYmVyc2hpcFN0YXR1cyA9ICdwZW5kaW5nJyB8ICdhY3RpdmUnIHwgJ2RlYWN0aXZhdGVkJztcbmV4cG9ydCB0eXBlIE9yZ2FuaXphdGlvbiA9IFJlYWRvbmx5PHtcbiAgY3JlYXRlZEF0OiBudW1iZXI7XG4gIG5hbWU6IHN0cmluZztcbiAgb3JnYW5pemF0aW9uSWQ6IHN0cmluZztcbiAgb3duZXJVc2VySWQ6IHN0cmluZztcbiAgcmV2aXNpb246IG51bWJlcjtcbiAgdXBkYXRlZEF0OiBudW1iZXI7XG59PjtcbmV4cG9ydCB0eXBlIE9yZ2FuaXphdGlvbk1lbWJlcnNoaXAgPSBSZWFkb25seTx7XG4gIGNyZWF0ZWRBdDogbnVtYmVyO1xuICBvcmdhbml6YXRpb25JZDogc3RyaW5nO1xuICByZXZpc2lvbjogbnVtYmVyO1xuICByb2xlOiBNZW1iZXJzaGlwUm9sZTtcbiAgc3RhdHVzOiBNZW1iZXJzaGlwU3RhdHVzO1xuICB1cGRhdGVkQXQ6IG51bWJlcjtcbiAgdXNlcklkOiBzdHJpbmc7XG59PjtcbmV4cG9ydCB0eXBlIE9yZ2FuaXphdGlvbkFjY2VzcyA9IFJlYWRvbmx5PHtcbiAgbWVtYmVyc2hpcDogT3JnYW5pemF0aW9uTWVtYmVyc2hpcDtcbiAgb3JnYW5pemF0aW9uOiBPcmdhbml6YXRpb247XG4gIHVzZXJJZDogc3RyaW5nO1xufT47XG5leHBvcnQgdHlwZSBPcmdhbml6YXRpb25QbGFuID0gUmVhZG9ubHk8e1xuICBhZGRpdGlvbmFsU2VhdENlbnRzOiBudW1iZXI7XG4gIGJhc2VDZW50czogbnVtYmVyO1xuICBjdXJyZW5jeTogJ3VzZCc7XG4gIGluY2x1ZGVkU2VhdHM6IDEwO1xuICBpbnRlcnZhbDogJ21vbnRoJztcbiAga2V5OiBQbGFuS2V5O1xuICB2ZXJzaW9uOiAnMjAyNi0xMC12MSc7XG59PjtcbmV4cG9ydCB0eXBlIE92ZXJhZ2VQcmVmZXJlbmNlID0gUmVhZG9ubHk8e2VuYWJsZWQ6IGJvb2xlYW47IG1vbnRobHlDYXBDZW50czogbnVtYmVyOyByZXZpc2lvbjogbnVtYmVyfT47XG5leHBvcnQgdHlwZSBTdWJzY3JpcHRpb25TdW1tYXJ5ID0gUmVhZG9ubHk8e1xuICBhY3RpdmVNZW1iZXJzOiBudW1iZXI7XG4gIGJpbGxpbmdNb2RlOiBCaWxsaW5nTW9kZTtcbiAgY2FuTWFuYWdlOiBib29sZWFuO1xuICBlc3RpbWF0ZWRNb250aGx5Q2VudHM6IG51bWJlcjtcbiAgZXh0cmFTZWF0czogbnVtYmVyO1xuICBtZXRlcnNFbmFibGVkOiBmYWxzZTtcbiAgb3JnYW5pemF0aW9uSWQ6IHN0cmluZztcbiAgb3ZlcmFnZTogT3ZlcmFnZVByZWZlcmVuY2U7XG4gIHBlbmRpbmdDaGFuZ2U6IHN0cmluZyB8IG51bGw7XG4gIHBlcmlvZEVuZDogbnVtYmVyIHwgbnVsbDtcbiAgcGxhbjogUGxhbktleSB8IG51bGw7XG4gIHJlY29uY2lsaWF0aW9uUGVuZGluZzogYm9vbGVhbjtcbiAgc3RhdHVzOiBzdHJpbmc7XG59PjtcbmV4cG9ydCB0eXBlIE1lbWJlcnNoaXBDaGFuZ2UgPSBSZWFkb25seTx7XG4gIGNvbmZpcm1lZFF1b3RlSWQ/OiBzdHJpbmc7XG4gIGV4cGVjdGVkUmV2aXNpb246IG51bWJlcjtcbiAgcm9sZTogTWVtYmVyc2hpcFJvbGU7XG4gIHN0YXR1czogTWVtYmVyc2hpcFN0YXR1cztcbiAgdXNlcklkOiBzdHJpbmc7XG59PjtcbmV4cG9ydCB0eXBlIFNlYXRDaGFuZ2VRdW90ZSA9IFJlYWRvbmx5PHtcbiAgY3VycmVuY3k6ICd1c2QnO1xuICBleHBpcmVzQXQ6IG51bWJlcjtcbiAgb3JnYW5pemF0aW9uUmV2aXNpb246IG51bWJlcjtcbiAgcHJvcmF0aW9uQ2VudHM6IG51bWJlcjtcbiAgcXVhbnRpdHk6IG51bWJlcjtcbiAgcXVvdGVJZDogc3RyaW5nO1xufT47XG5leHBvcnQgdHlwZSBTaWdudXBJbnB1dCA9IFJlYWRvbmx5PHtcbiAgZW1haWw6IHN0cmluZztcbiAgb3JnYW5pemF0aW9uTmFtZTogc3RyaW5nO1xuICBwYXNzd29yZDogc3RyaW5nO1xuICByZXF1ZXN0SWQ6IHN0cmluZztcbn0+O1xuZXhwb3J0IHR5cGUgU2lnbnVwUmVzdWx0ID0gUmVhZG9ubHk8e3JlZ2lzdHJhdGlvbklkOiBzdHJpbmc7IHZlcmlmaWNhdGlvblJlcXVpcmVkOiB0cnVlfT47XG5leHBvcnQgdHlwZSBCaWxsaW5nSm9iID0gUmVhZG9ubHk8e1xuICBhdHRlbXB0czogbnVtYmVyO1xuICBkZXNpcmVkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgZWZmZWN0aXZlQXQ6IG51bWJlcjtcbiAgam9iSWQ6IHN0cmluZztcbiAga2luZDogJ2NhbmNlbCcgfCAnbWFpbCcgfCAncGxhbicgfCAncmVjb25jaWxlJyB8ICdzZWF0cyc7XG4gIGxlYXNlRXhwaXJlczogbnVtYmVyO1xuICBvcmdhbml6YXRpb25JZDogc3RyaW5nO1xuICBvd25lcjogc3RyaW5nO1xuICBwcmljZVZlcnNpb246ICcyMDI2LTEwLXYxJztcbiAgc2VxdWVuY2U6IG51bWJlcjtcbiAgc3RhdHVzOiAnY29tcGxldGUnIHwgJ3BlbmRpbmcnO1xufT47XG5leHBvcnQgdHlwZSBQcm92aWRlclN1YnNjcmlwdGlvbiA9IFJlYWRvbmx5PHtcbiAgY2FuY2VsQXRQZXJpb2RFbmQ/OiBib29sZWFuO1xuICBiYXNlSXRlbUlkOiBzdHJpbmc7XG4gIGN1c3RvbWVySWQ6IHN0cmluZztcbiAgZXh0cmFTZWF0czogbnVtYmVyO1xuICBpZDogc3RyaW5nO1xuICBsYXN0Sm9iSWQ/OiBzdHJpbmc7XG4gIHBlbmRpbmdQbGFuPzogUGxhbktleSB8IG51bGw7XG4gIHBlcmlvZEVuZDogbnVtYmVyO1xuICBwZXJpb2RTdGFydDogbnVtYmVyO1xuICBwbGFuOiBQbGFuS2V5O1xuICBzZWF0SXRlbUlkOiBzdHJpbmc7XG4gIHN0YXR1czogc3RyaW5nO1xuICBzdGFydGVkQXQ/OiBudW1iZXI7XG59PjtcbmV4cG9ydCB0eXBlIENoZWNrb3V0UmVzdWx0ID0gUmVhZG9ubHk8e2V4cGlyZXNBdDogbnVtYmVyOyBpZDogc3RyaW5nOyB1cmw6IHN0cmluZ30+O1xuZXhwb3J0IHR5cGUgVmVyaWZpZWRCaWxsaW5nRXZlbnQgPSBSZWFkb25seTx7Y3VzdG9tZXJJZDogc3RyaW5nOyBldmVudElkOiBzdHJpbmc7IHR5cGU6IHN0cmluZ30+O1xuZXhwb3J0IHR5cGUgQmlsbGluZ0FkanVzdG1lbnQgPSBSZWFkb25seTx7XG4gIGFtb3VudENlbnRzOiBudW1iZXI7XG4gIHBlcmlvZEVuZDogbnVtYmVyO1xuICBwZXJpb2RTdGFydDogbnVtYmVyO1xuICBwcm92aWRlcklkOiBzdHJpbmc7XG59PjtcbmV4cG9ydCB0eXBlIEJpbGxpbmdVcGRhdGVSZXN1bHQgPSBSZWFkb25seTx7YWRqdXN0bWVudHM6IHJlYWRvbmx5IEJpbGxpbmdBZGp1c3RtZW50W119PjtcbmV4cG9ydCB0eXBlIEJpbGxpbmdQcm92aWRlciA9IFJlYWRvbmx5PHtcbiAgY2hlY2tvdXQ6IChjdXN0b21lcklkOiBzdHJpbmcsIHBsYW46IFBsYW5LZXksIGV4dHJhU2VhdHM6IG51bWJlciwga2V5OiBzdHJpbmcpID0+IFByb21pc2U8Q2hlY2tvdXRSZXN1bHQ+O1xuICBjdXN0b21lcjogKG9yZ2FuaXphdGlvbklkOiBzdHJpbmcsIGVtYWlsOiBzdHJpbmcpID0+IFByb21pc2U8c3RyaW5nPjtcbiAgcG9ydGFsOiAoY3VzdG9tZXJJZDogc3RyaW5nKSA9PiBQcm9taXNlPHN0cmluZz47XG4gIHByZXZpZXc6IChcbiAgICBzdWJzY3JpcHRpb246IFByb3ZpZGVyU3Vic2NyaXB0aW9uLFxuICAgIHBsYW46IFBsYW5LZXksXG4gICAgc2VhdHM6IG51bWJlcixcbiAgICBlZmZlY3RpdmVBdDogbnVtYmVyXG4gICkgPT4gUHJvbWlzZTxudW1iZXI+O1xuICByZXRyaWV2ZTogKGN1c3RvbWVySWQ6IHN0cmluZywgc3Vic2NyaXB0aW9uSWQ/OiBzdHJpbmcpID0+IFByb21pc2U8UHJvdmlkZXJTdWJzY3JpcHRpb24gfCBudWxsPjtcbiAgdXBkYXRlOiAoXG4gICAgc3Vic2NyaXB0aW9uOiBQcm92aWRlclN1YnNjcmlwdGlvbixcbiAgICBqb2I6IEJpbGxpbmdKb2IsXG4gICAgZmVuY2U6ICgpID0+IFByb21pc2U8dm9pZD5cbiAgKSA9PiBQcm9taXNlPEJpbGxpbmdVcGRhdGVSZXN1bHQ+O1xuICB2ZXJpZnk6IChib2R5OiBzdHJpbmcgfCBCdWZmZXIsIHNpZ25hdHVyZTogc3RyaW5nKSA9PiBWZXJpZmllZEJpbGxpbmdFdmVudDtcbn0+O1xuZXhwb3J0IHR5cGUgT3JnYW5pemF0aW9uQ29udGV4dCA9IEFwaUNvbnRleHQ7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBa0lBLFdBQTZDIn0=
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ApiContext } from '../types/auth.types.js';
|
|
2
|
+
export declare const verifyOrganizationEmail: (registrationId: string, token: string, context: ApiContext) => Promise<void>;
|
|
3
|
+
export declare const resendOrganizationVerification: (registrationId: string, context: ApiContext) => Promise<void>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { queueOrganizationVerification } from './signup.js';
|
|
2
|
+
import { organizationKey, organizationStoreFor, requireIdentifier } from './store.js';
|
|
3
|
+
export const verifyOrganizationEmail = async (registrationId, token, context)=>{
|
|
4
|
+
const error = await organizationStoreFor(context).transaction(async (tx)=>{
|
|
5
|
+
const previous = await tx.get('organizationVerificationTokens', requireIdentifier(registrationId));
|
|
6
|
+
if (!previous || previous.used || previous.expiresAt <= Date.now()) {
|
|
7
|
+
return 'invalid_verification';
|
|
8
|
+
}
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
const attempts = now - previous.attemptWindow < 3600000 ? previous.attempts : 0;
|
|
11
|
+
if (attempts >= 10) {
|
|
12
|
+
return 'verification_rate_limited';
|
|
13
|
+
}
|
|
14
|
+
const valid = typeof token === 'string' && token.length <= 128 && previous.tokenHash === organizationKey(token);
|
|
15
|
+
await tx.put('organizationVerificationTokens', registrationId, {
|
|
16
|
+
...previous,
|
|
17
|
+
attemptWindow: attempts ? previous.attemptWindow : now,
|
|
18
|
+
attempts: attempts + 1,
|
|
19
|
+
used: valid
|
|
20
|
+
}, previous);
|
|
21
|
+
if (!valid) {
|
|
22
|
+
return 'invalid_verification';
|
|
23
|
+
}
|
|
24
|
+
const user = await tx.get('users', previous.userId);
|
|
25
|
+
if (!user) {
|
|
26
|
+
throw new Error('invalid_verification');
|
|
27
|
+
}
|
|
28
|
+
await tx.put('users', previous.userId, {
|
|
29
|
+
...user,
|
|
30
|
+
verifiedEmail: true
|
|
31
|
+
}, user);
|
|
32
|
+
return '';
|
|
33
|
+
});
|
|
34
|
+
if (error) {
|
|
35
|
+
throw new Error(error);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export const resendOrganizationVerification = (registrationId, context)=>organizationStoreFor(context).transaction(async (tx)=>{
|
|
39
|
+
const registration = await tx.get('organizationRegistrations', requireIdentifier(registrationId));
|
|
40
|
+
const previous = await tx.get('organizationVerificationTokens', registrationId);
|
|
41
|
+
if (!registration?.userId || !previous || previous.used) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
if (now - previous.lastSent < 60000 || now - previous.resendWindow < 3600000 && previous.resendCount >= 5) {
|
|
46
|
+
throw new Error('verification_rate_limited');
|
|
47
|
+
}
|
|
48
|
+
await queueOrganizationVerification(tx, registration, previous);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvb3JnYW5pemF0aW9ucy92ZXJpZmljYXRpb24udHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtxdWV1ZU9yZ2FuaXphdGlvblZlcmlmaWNhdGlvbiwgdHlwZSBSZWdpc3RyYXRpb24sIHR5cGUgVmVyaWZpY2F0aW9ufSBmcm9tICcuL3NpZ251cC5qcyc7XG5pbXBvcnQge29yZ2FuaXphdGlvbktleSwgb3JnYW5pemF0aW9uU3RvcmVGb3IsIHJlcXVpcmVJZGVudGlmaWVyfSBmcm9tICcuL3N0b3JlLmpzJztcblxuaW1wb3J0IHR5cGUge0FwaUNvbnRleHR9IGZyb20gJy4uL3R5cGVzL2F1dGgudHlwZXMuanMnO1xuXG5leHBvcnQgY29uc3QgdmVyaWZ5T3JnYW5pemF0aW9uRW1haWwgPSBhc3luYyAoXG4gIHJlZ2lzdHJhdGlvbklkOiBzdHJpbmcsXG4gIHRva2VuOiBzdHJpbmcsXG4gIGNvbnRleHQ6IEFwaUNvbnRleHRcbik6IFByb21pc2U8dm9pZD4gPT4ge1xuICBjb25zdCBlcnJvciA9IGF3YWl0IG9yZ2FuaXphdGlvblN0b3JlRm9yKGNvbnRleHQpLnRyYW5zYWN0aW9uKGFzeW5jICh0eCkgPT4ge1xuICAgIGNvbnN0IHByZXZpb3VzID0gYXdhaXQgdHguZ2V0PFZlcmlmaWNhdGlvbj4oXG4gICAgICAnb3JnYW5pemF0aW9uVmVyaWZpY2F0aW9uVG9rZW5zJyxcbiAgICAgIHJlcXVpcmVJZGVudGlmaWVyKHJlZ2lzdHJhdGlvbklkKVxuICAgICk7XG4gICAgaWYoIXByZXZpb3VzIHx8IHByZXZpb3VzLnVzZWQgfHwgcHJldmlvdXMuZXhwaXJlc0F0IDw9IERhdGUubm93KCkpIHtcbiAgICAgIHJldHVybiAnaW52YWxpZF92ZXJpZmljYXRpb24nO1xuICAgIH1cbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IGF0dGVtcHRzID0gbm93IC0gcHJldmlvdXMuYXR0ZW1wdFdpbmRvdyA8IDM2MDAwMDAgPyBwcmV2aW91cy5hdHRlbXB0cyA6IDA7XG4gICAgaWYoYXR0ZW1wdHMgPj0gMTApIHtcbiAgICAgIHJldHVybiAndmVyaWZpY2F0aW9uX3JhdGVfbGltaXRlZCc7XG4gICAgfVxuICAgIGNvbnN0IHZhbGlkID1cbiAgICAgIHR5cGVvZiB0b2tlbiA9PT0gJ3N0cmluZycgJiYgdG9rZW4ubGVuZ3RoIDw9IDEyOCAmJiBwcmV2aW91cy50b2tlbkhhc2ggPT09IG9yZ2FuaXphdGlvbktleSh0b2tlbik7XG4gICAgYXdhaXQgdHgucHV0KFxuICAgICAgJ29yZ2FuaXphdGlvblZlcmlmaWNhdGlvblRva2VucycsXG4gICAgICByZWdpc3RyYXRpb25JZCxcbiAgICAgIHtcbiAgICAgICAgLi4ucHJldmlvdXMsXG4gICAgICAgIGF0dGVtcHRXaW5kb3c6IGF0dGVtcHRzID8gcHJldmlvdXMuYXR0ZW1wdFdpbmRvdyA6IG5vdyxcbiAgICAgICAgYXR0ZW1wdHM6IGF0dGVtcHRzICsgMSxcbiAgICAgICAgdXNlZDogdmFsaWRcbiAgICAgIH0sXG4gICAgICBwcmV2aW91c1xuICAgICk7XG4gICAgaWYoIXZhbGlkKSB7XG4gICAgICByZXR1cm4gJ2ludmFsaWRfdmVyaWZpY2F0aW9uJztcbiAgICB9XG4gICAgY29uc3QgdXNlciA9IGF3YWl0IHR4LmdldCgndXNlcnMnLCBwcmV2aW91cy51c2VySWQpO1xuICAgIGlmKCF1c2VyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWRfdmVyaWZpY2F0aW9uJyk7XG4gICAgfVxuICAgIGF3YWl0IHR4LnB1dCgndXNlcnMnLCBwcmV2aW91cy51c2VySWQsIHsuLi51c2VyLCB2ZXJpZmllZEVtYWlsOiB0cnVlfSwgdXNlcik7XG4gICAgcmV0dXJuICcnO1xuICB9KTtcbiAgaWYoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoZXJyb3IpO1xuICB9XG59O1xuXG5leHBvcnQgY29uc3QgcmVzZW5kT3JnYW5pemF0aW9uVmVyaWZpY2F0aW9uID0gKHJlZ2lzdHJhdGlvbklkOiBzdHJpbmcsIGNvbnRleHQ6IEFwaUNvbnRleHQpOiBQcm9taXNlPHZvaWQ+ID0+XG4gIG9yZ2FuaXphdGlvblN0b3JlRm9yKGNvbnRleHQpLnRyYW5zYWN0aW9uKGFzeW5jICh0eCkgPT4ge1xuICAgIGNvbnN0IHJlZ2lzdHJhdGlvbiA9IGF3YWl0IHR4LmdldDxSZWdpc3RyYXRpb24+KFxuICAgICAgJ29yZ2FuaXphdGlvblJlZ2lzdHJhdGlvbnMnLFxuICAgICAgcmVxdWlyZUlkZW50aWZpZXIocmVnaXN0cmF0aW9uSWQpXG4gICAgKTtcbiAgICBjb25zdCBwcmV2aW91cyA9IGF3YWl0IHR4LmdldDxWZXJpZmljYXRpb24+KCdvcmdhbml6YXRpb25WZXJpZmljYXRpb25Ub2tlbnMnLCByZWdpc3RyYXRpb25JZCk7XG4gICAgaWYoIXJlZ2lzdHJhdGlvbj8udXNlcklkIHx8ICFwcmV2aW91cyB8fCBwcmV2aW91cy51c2VkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgaWYoXG4gICAgICBub3cgLSBwcmV2aW91cy5sYXN0U2VudCA8IDYwMDAwIHx8XG4gICAgICAobm93IC0gcHJldmlvdXMucmVzZW5kV2luZG93IDwgMzYwMDAwMCAmJiBwcmV2aW91cy5yZXNlbmRDb3VudCA+PSA1KVxuICAgICkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd2ZXJpZmljYXRpb25fcmF0ZV9saW1pdGVkJyk7XG4gICAgfVxuICAgIGF3YWl0IHF1ZXVlT3JnYW5pemF0aW9uVmVyaWZpY2F0aW9uKHR4LCByZWdpc3RyYXRpb24sIHByZXZpb3VzKTtcbiAgfSk7XG4iXSwibmFtZXMiOlsicXVldWVPcmdhbml6YXRpb25WZXJpZmljYXRpb24iLCJvcmdhbml6YXRpb25LZXkiLCJvcmdhbml6YXRpb25TdG9yZUZvciIsInJlcXVpcmVJZGVudGlmaWVyIiwidmVyaWZ5T3JnYW5pemF0aW9uRW1haWwiLCJyZWdpc3RyYXRpb25JZCIsInRva2VuIiwiY29udGV4dCIsImVycm9yIiwidHJhbnNhY3Rpb24iLCJ0eCIsInByZXZpb3VzIiwiZ2V0IiwidXNlZCIsImV4cGlyZXNBdCIsIkRhdGUiLCJub3ciLCJhdHRlbXB0cyIsImF0dGVtcHRXaW5kb3ciLCJ2YWxpZCIsImxlbmd0aCIsInRva2VuSGFzaCIsInB1dCIsInVzZXIiLCJ1c2VySWQiLCJFcnJvciIsInZlcmlmaWVkRW1haWwiLCJyZXNlbmRPcmdhbml6YXRpb25WZXJpZmljYXRpb24iLCJyZWdpc3RyYXRpb24iLCJsYXN0U2VudCIsInJlc2VuZFdpbmRvdyIsInJlc2VuZENvdW50Il0sIm1hcHBpbmdzIjoiQUFBQSxTQUFRQSw2QkFBNkIsUUFBNkMsY0FBYztBQUNoRyxTQUFRQyxlQUFlLEVBQUVDLG9CQUFvQixFQUFFQyxpQkFBaUIsUUFBTyxhQUFhO0FBSXBGLE9BQU8sTUFBTUMsMEJBQTBCLE9BQ3JDQyxnQkFDQUMsT0FDQUM7SUFFQSxNQUFNQyxRQUFRLE1BQU1OLHFCQUFxQkssU0FBU0UsV0FBVyxDQUFDLE9BQU9DO1FBQ25FLE1BQU1DLFdBQVcsTUFBTUQsR0FBR0UsR0FBRyxDQUMzQixrQ0FDQVQsa0JBQWtCRTtRQUVwQixJQUFHLENBQUNNLFlBQVlBLFNBQVNFLElBQUksSUFBSUYsU0FBU0csU0FBUyxJQUFJQyxLQUFLQyxHQUFHLElBQUk7WUFDakUsT0FBTztRQUNUO1FBQ0EsTUFBTUEsTUFBTUQsS0FBS0MsR0FBRztRQUNwQixNQUFNQyxXQUFXRCxNQUFNTCxTQUFTTyxhQUFhLEdBQUcsVUFBVVAsU0FBU00sUUFBUSxHQUFHO1FBQzlFLElBQUdBLFlBQVksSUFBSTtZQUNqQixPQUFPO1FBQ1Q7UUFDQSxNQUFNRSxRQUNKLE9BQU9iLFVBQVUsWUFBWUEsTUFBTWMsTUFBTSxJQUFJLE9BQU9ULFNBQVNVLFNBQVMsS0FBS3BCLGdCQUFnQks7UUFDN0YsTUFBTUksR0FBR1ksR0FBRyxDQUNWLGtDQUNBakIsZ0JBQ0E7WUFDRSxHQUFHTSxRQUFRO1lBQ1hPLGVBQWVELFdBQVdOLFNBQVNPLGFBQWEsR0FBR0Y7WUFDbkRDLFVBQVVBLFdBQVc7WUFDckJKLE1BQU1NO1FBQ1IsR0FDQVI7UUFFRixJQUFHLENBQUNRLE9BQU87WUFDVCxPQUFPO1FBQ1Q7UUFDQSxNQUFNSSxPQUFPLE1BQU1iLEdBQUdFLEdBQUcsQ0FBQyxTQUFTRCxTQUFTYSxNQUFNO1FBQ2xELElBQUcsQ0FBQ0QsTUFBTTtZQUNSLE1BQU0sSUFBSUUsTUFBTTtRQUNsQjtRQUNBLE1BQU1mLEdBQUdZLEdBQUcsQ0FBQyxTQUFTWCxTQUFTYSxNQUFNLEVBQUU7WUFBQyxHQUFHRCxJQUFJO1lBQUVHLGVBQWU7UUFBSSxHQUFHSDtRQUN2RSxPQUFPO0lBQ1Q7SUFDQSxJQUFHZixPQUFPO1FBQ1IsTUFBTSxJQUFJaUIsTUFBTWpCO0lBQ2xCO0FBQ0YsRUFBRTtBQUVGLE9BQU8sTUFBTW1CLGlDQUFpQyxDQUFDdEIsZ0JBQXdCRSxVQUNyRUwscUJBQXFCSyxTQUFTRSxXQUFXLENBQUMsT0FBT0M7UUFDL0MsTUFBTWtCLGVBQWUsTUFBTWxCLEdBQUdFLEdBQUcsQ0FDL0IsNkJBQ0FULGtCQUFrQkU7UUFFcEIsTUFBTU0sV0FBVyxNQUFNRCxHQUFHRSxHQUFHLENBQWUsa0NBQWtDUDtRQUM5RSxJQUFHLENBQUN1QixjQUFjSixVQUFVLENBQUNiLFlBQVlBLFNBQVNFLElBQUksRUFBRTtZQUN0RDtRQUNGO1FBQ0EsTUFBTUcsTUFBTUQsS0FBS0MsR0FBRztRQUNwQixJQUNFQSxNQUFNTCxTQUFTa0IsUUFBUSxHQUFHLFNBQ3pCYixNQUFNTCxTQUFTbUIsWUFBWSxHQUFHLFdBQVduQixTQUFTb0IsV0FBVyxJQUFJLEdBQ2xFO1lBQ0EsTUFBTSxJQUFJTixNQUFNO1FBQ2xCO1FBQ0EsTUFBTXpCLDhCQUE4QlUsSUFBSWtCLGNBQWNqQjtJQUN4RCxHQUFHIn0=
|