@omnibase/core-js 0.7.4 → 0.7.6
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/dist/chunk-TFAV5P6I.js +869 -0
- package/dist/chunk-V6HVRJCU.js +693 -0
- package/dist/index.cjs +228 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/payments/index.d.cts +323 -1
- package/dist/payments/index.d.ts +323 -1
- package/dist/tenants/index.cjs +228 -0
- package/dist/tenants/index.d.cts +1 -1
- package/dist/tenants/index.d.ts +1 -1
- package/dist/tenants/index.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
// src/tenants/invites.ts
|
|
2
|
+
var TenantInviteManager = class {
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new TenantInviteManager instance
|
|
5
|
+
*
|
|
6
|
+
* Initializes the manager with the provided Omnibase client for making
|
|
7
|
+
* authenticated API requests to tenant invitation endpoints.
|
|
8
|
+
*
|
|
9
|
+
* @param omnibaseClient - Configured Omnibase client instance
|
|
10
|
+
*
|
|
11
|
+
* @group Tenant Invitations
|
|
12
|
+
*/
|
|
13
|
+
constructor(omnibaseClient) {
|
|
14
|
+
this.omnibaseClient = omnibaseClient;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Accepts a tenant invitation using a secure token
|
|
18
|
+
*
|
|
19
|
+
* Processes a tenant invitation by validating the provided token and
|
|
20
|
+
* adding the authenticated user to the specified tenant. The invitation
|
|
21
|
+
* token is consumed during this process and cannot be used again.
|
|
22
|
+
*
|
|
23
|
+
* The function performs several validations:
|
|
24
|
+
* - Verifies the token exists and is valid
|
|
25
|
+
* - Checks that the invitation hasn't expired
|
|
26
|
+
* - Ensures the invitation hasn't already been used
|
|
27
|
+
* - Confirms the user is authenticated via session cookies
|
|
28
|
+
*
|
|
29
|
+
* Upon successful acceptance, the user is granted access to the tenant
|
|
30
|
+
* with the role specified in the original invitation. The invitation
|
|
31
|
+
* record is marked as used and cannot be accepted again.
|
|
32
|
+
*
|
|
33
|
+
* @param token - The secure invitation token from the email invitation
|
|
34
|
+
*
|
|
35
|
+
* @returns Promise resolving to the tenant ID and success confirmation
|
|
36
|
+
*
|
|
37
|
+
* @throws {Error} When the token parameter is missing or empty
|
|
38
|
+
* @throws {Error} When the invitation token is invalid or expired
|
|
39
|
+
* @throws {Error} When the invitation has already been accepted
|
|
40
|
+
* @throws {Error} When the user is not authenticated
|
|
41
|
+
* @throws {Error} When the API request fails due to network issues
|
|
42
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx status codes)
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Typically called from an invitation link like:
|
|
47
|
+
* // https://app.com/accept-invite?token=inv_secure_token_abc123
|
|
48
|
+
*
|
|
49
|
+
* const urlParams = new URLSearchParams(window.location.search);
|
|
50
|
+
* const inviteToken = urlParams.get('token');
|
|
51
|
+
*
|
|
52
|
+
* if (inviteToken) {
|
|
53
|
+
* try {
|
|
54
|
+
* const result = await inviteManager.accept(inviteToken);
|
|
55
|
+
*
|
|
56
|
+
* // Success - redirect to tenant dashboard
|
|
57
|
+
* console.log(`Successfully joined tenant: ${result.data.tenant_id}`);
|
|
58
|
+
* window.location.href = `/dashboard?tenant=${result.data.tenant_id}`;
|
|
59
|
+
* } catch (error) {
|
|
60
|
+
* console.error('Failed to accept invitation:', error.message);
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @since 0.6.0
|
|
66
|
+
* @public
|
|
67
|
+
* @group Tenant Invitations
|
|
68
|
+
*/
|
|
69
|
+
async accept(token) {
|
|
70
|
+
if (!token) {
|
|
71
|
+
throw new Error("Invite token is required");
|
|
72
|
+
}
|
|
73
|
+
const requestBody = {
|
|
74
|
+
token
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
const response = await this.omnibaseClient.fetch(
|
|
78
|
+
`/api/v1/tenants/invites/accept`,
|
|
79
|
+
{
|
|
80
|
+
method: "PUT",
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json"
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify(requestBody),
|
|
85
|
+
credentials: "include"
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const errorData = await response.text();
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Failed to accept invite: ${response.status} - ${errorData}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const data = await response.json();
|
|
95
|
+
return data;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Error accepting tenant invite:", error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Creates a new user invitation for the active tenant
|
|
103
|
+
*
|
|
104
|
+
* Generates a secure invitation that allows a user to join the currently active
|
|
105
|
+
* tenant with the defined role. The invitation is sent to the provided email address
|
|
106
|
+
* and includes a time-limited token for security. The invite URL will be automatically
|
|
107
|
+
* appended with ?token=XYZ when sent to the user.
|
|
108
|
+
*
|
|
109
|
+
* The function creates the invitation record in the database and triggers an email
|
|
110
|
+
* notification to the invited user. The invitation expires after 7 days and can only
|
|
111
|
+
* be used once.
|
|
112
|
+
*
|
|
113
|
+
* Only existing tenant members with appropriate permissions (invite_user permission)
|
|
114
|
+
* can create invitations. The inviter's authentication and tenant context are validated
|
|
115
|
+
* via HTTP-only cookies sent with the request.
|
|
116
|
+
*
|
|
117
|
+
* @param inviteData - Configuration object for the invitation
|
|
118
|
+
* @param inviteData.email - Email address of the user to invite
|
|
119
|
+
* @param inviteData.role - Role the user will have after joining (e.g., 'member', 'admin')
|
|
120
|
+
* @param inviteData.invite_url - Base URL for the invitation link (will be appended with ?token=XYZ)
|
|
121
|
+
*
|
|
122
|
+
* @returns Promise resolving to the created invitation with secure token
|
|
123
|
+
*
|
|
124
|
+
* @throws {Error} When required fields (email, role, invite_url) are missing or empty
|
|
125
|
+
* @throws {Error} When the user doesn't have permission to invite users to the tenant
|
|
126
|
+
* @throws {Error} When the API request fails due to network issues
|
|
127
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx status codes)
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const invite = await inviteManager.create({
|
|
132
|
+
* email: 'colleague@company.com',
|
|
133
|
+
* role: 'member',
|
|
134
|
+
* invite_url: 'https://yourapp.com/accept-invite'
|
|
135
|
+
* });
|
|
136
|
+
*
|
|
137
|
+
* console.log(`Invite sent to: ${invite.data.invite.email}`);
|
|
138
|
+
* console.log(`Invite token: ${invite.data.invite.token}`);
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @since 0.6.0
|
|
142
|
+
* @public
|
|
143
|
+
* @group Tenant Invitations
|
|
144
|
+
*/
|
|
145
|
+
async create(inviteData) {
|
|
146
|
+
if (!inviteData.email || !inviteData.role || !inviteData.invite_url) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
"Missing data in `create` - email, role, and invite_url are required"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const response = await this.omnibaseClient.fetch(
|
|
153
|
+
`/api/v1/tenants/invites`,
|
|
154
|
+
{
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: {
|
|
157
|
+
"Content-Type": "application/json"
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify(inviteData),
|
|
160
|
+
credentials: "include"
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
const errorData = await response.text();
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Failed to create invite: ${response.status} - ${errorData}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const data = await response.json();
|
|
170
|
+
return data;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error("Error creating tenant user invite:", error);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/tenants/management.ts
|
|
179
|
+
var TenantManger = class {
|
|
180
|
+
/**
|
|
181
|
+
* Creates a new TenantManger instance
|
|
182
|
+
*
|
|
183
|
+
* Initializes the manager with the provided Omnibase client for making
|
|
184
|
+
* authenticated API requests to tenant management endpoints.
|
|
185
|
+
*
|
|
186
|
+
* @param omnibaseClient - Configured Omnibase client instance
|
|
187
|
+
*
|
|
188
|
+
* @group Tenant Management
|
|
189
|
+
*/
|
|
190
|
+
constructor(omnibaseClient) {
|
|
191
|
+
this.omnibaseClient = omnibaseClient;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Creates a new tenant in the multi-tenant system
|
|
195
|
+
*
|
|
196
|
+
* Establishes a new tenant with integrated Stripe billing setup and assigns
|
|
197
|
+
* the specified user as the tenant owner. The operation creates the necessary
|
|
198
|
+
* database records and returns a JWT token that enables Row-Level Security
|
|
199
|
+
* access to the tenant's isolated data.
|
|
200
|
+
*
|
|
201
|
+
* The function automatically handles Stripe customer creation for billing
|
|
202
|
+
* integration and sets up the initial tenant configuration. The returned
|
|
203
|
+
* token should be stored securely for subsequent API calls.
|
|
204
|
+
*
|
|
205
|
+
* @param tenantData - Configuration object for the new tenant
|
|
206
|
+
* @param tenantData.name - Display name for the tenant organization
|
|
207
|
+
* @param tenantData.billing_email - Email address for Stripe billing notifications
|
|
208
|
+
* @param tenantData.user_id - Unique identifier of the user who will own this tenant
|
|
209
|
+
*
|
|
210
|
+
* @returns Promise resolving to the created tenant with authentication token
|
|
211
|
+
*
|
|
212
|
+
* @throws {Error} When required fields (name, user_id) are missing or empty
|
|
213
|
+
* @throws {Error} When the API request fails due to network issues
|
|
214
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx status codes)
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const newTenant = await tenantManager.createTenant({
|
|
219
|
+
* name: 'Acme Corporation',
|
|
220
|
+
* billing_email: 'billing@acme.com',
|
|
221
|
+
* user_id: 'user_123'
|
|
222
|
+
* });
|
|
223
|
+
*
|
|
224
|
+
* console.log(`Tenant created: ${newTenant.data.tenant.id}`);
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* @since 0.6.0
|
|
228
|
+
* @public
|
|
229
|
+
* @group Tenant Management
|
|
230
|
+
*/
|
|
231
|
+
async createTenant(tenantData) {
|
|
232
|
+
if (!tenantData.name || !tenantData.user_id) {
|
|
233
|
+
throw new Error("Name and user_id are required");
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
const response = await this.omnibaseClient.fetch(`/api/v1/tenants`, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: {
|
|
239
|
+
"Content-Type": "application/json"
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify(tenantData),
|
|
242
|
+
credentials: "include"
|
|
243
|
+
});
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
const errorData = await response.text();
|
|
246
|
+
throw new Error(
|
|
247
|
+
`Failed to create tenant: ${response.status} - ${errorData}`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const data = await response.json();
|
|
251
|
+
return data;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error("Error creating tenant:", error);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Permanently deletes a tenant and all associated data
|
|
259
|
+
*
|
|
260
|
+
* ⚠️ **WARNING: This operation is irreversible and will permanently delete:**
|
|
261
|
+
* - The tenant record and all metadata
|
|
262
|
+
* - All user memberships and invitations for this tenant
|
|
263
|
+
* - All tenant-specific data protected by row-level security
|
|
264
|
+
* - Any tenant-related billing information
|
|
265
|
+
* - All tenant configuration and settings
|
|
266
|
+
*
|
|
267
|
+
* **Access Control:**
|
|
268
|
+
* Only tenant owners can delete a tenant. This operation requires:
|
|
269
|
+
* - User must be authenticated
|
|
270
|
+
* - User must have 'owner' role for the specified tenant
|
|
271
|
+
* - Tenant must exist and be accessible to the user
|
|
272
|
+
*
|
|
273
|
+
* **Security Considerations:**
|
|
274
|
+
* - All tenant data is immediately and permanently removed
|
|
275
|
+
* - Other tenant members lose access immediately
|
|
276
|
+
* - Any active sessions for this tenant are invalidated
|
|
277
|
+
* - Billing subscriptions are cancelled (if applicable)
|
|
278
|
+
* - Audit logs for deletion are maintained for compliance
|
|
279
|
+
*
|
|
280
|
+
* @returns Promise resolving to a confirmation message
|
|
281
|
+
*
|
|
282
|
+
* @throws {Error} When the tenantId parameter is missing or empty
|
|
283
|
+
* @throws {Error} When the user is not authenticated
|
|
284
|
+
* @throws {Error} When the user is not an owner of the specified tenant
|
|
285
|
+
* @throws {Error} When the tenant doesn't exist or is not accessible
|
|
286
|
+
* @throws {Error} When the API request fails due to network issues
|
|
287
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx status codes)
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* // Always confirm before deleting
|
|
292
|
+
* const userConfirmed = confirm(
|
|
293
|
+
* 'Are you sure you want to delete this tenant? This action cannot be undone.'
|
|
294
|
+
* );
|
|
295
|
+
*
|
|
296
|
+
* if (userConfirmed) {
|
|
297
|
+
* try {
|
|
298
|
+
* const result = await tenantManager.deleteTenant();
|
|
299
|
+
* console.log(result.data.message);
|
|
300
|
+
*
|
|
301
|
+
* // Redirect user away from deleted tenant
|
|
302
|
+
* window.location.href = '/dashboard';
|
|
303
|
+
* } catch (error) {
|
|
304
|
+
* console.error('Failed to delete tenant:', error);
|
|
305
|
+
* }
|
|
306
|
+
* }
|
|
307
|
+
* ```
|
|
308
|
+
*
|
|
309
|
+
* @since 0.6.0
|
|
310
|
+
* @public
|
|
311
|
+
* @group Tenant Management
|
|
312
|
+
*/
|
|
313
|
+
async deleteTenant() {
|
|
314
|
+
try {
|
|
315
|
+
const response = await this.omnibaseClient.fetch(`/api/v1/tenants`, {
|
|
316
|
+
method: "DELETE",
|
|
317
|
+
headers: {
|
|
318
|
+
"Content-Type": "application/json"
|
|
319
|
+
},
|
|
320
|
+
credentials: "include"
|
|
321
|
+
});
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
const errorData = await response.text();
|
|
324
|
+
throw new Error(
|
|
325
|
+
`Failed to delete tenant: ${response.status} - ${errorData}`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
const data = await response.json();
|
|
329
|
+
return data;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error("Error deleting tenant:", error);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Switches the user's active tenant context
|
|
337
|
+
*
|
|
338
|
+
* Changes the user's active tenant to the specified tenant ID, updating
|
|
339
|
+
* their authentication context and permissions. This function is essential
|
|
340
|
+
* for multi-tenant applications where users belong to multiple tenants
|
|
341
|
+
* and need to switch between them.
|
|
342
|
+
*
|
|
343
|
+
* The function performs several operations:
|
|
344
|
+
* - Validates that the user has access to the specified tenant
|
|
345
|
+
* - Updates the user's active tenant in their session
|
|
346
|
+
* - Generates a new JWT token with updated tenant claims
|
|
347
|
+
* - Updates any cached tenant-specific data
|
|
348
|
+
*
|
|
349
|
+
* After switching tenants, all subsequent API calls will be made within
|
|
350
|
+
* the context of the new active tenant, with row-level security policies
|
|
351
|
+
* applied accordingly. The new JWT token should be used for all future
|
|
352
|
+
* authenticated requests.
|
|
353
|
+
*
|
|
354
|
+
* @param tenantId - The ID of the tenant to switch to (must be a tenant the user belongs to)
|
|
355
|
+
*
|
|
356
|
+
* @returns Promise resolving to a new JWT token and success confirmation
|
|
357
|
+
*
|
|
358
|
+
* @throws {Error} When the tenantId parameter is missing or empty
|
|
359
|
+
* @throws {Error} When the user doesn't have access to the specified tenant
|
|
360
|
+
* @throws {Error} When the user is not authenticated
|
|
361
|
+
* @throws {Error} When the specified tenant doesn't exist
|
|
362
|
+
* @throws {Error} When the API request fails due to network issues
|
|
363
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx status codes)
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```typescript
|
|
367
|
+
* const result = await tenantManager.switchActiveTenant('tenant_xyz789');
|
|
368
|
+
*
|
|
369
|
+
* // Store the new token for future requests
|
|
370
|
+
* console.log(`Switched to tenant. New token: ${result.data.token}`);
|
|
371
|
+
*
|
|
372
|
+
* // Now all API calls will be in the context of tenant_xyz789
|
|
373
|
+
* const tenantData = await getCurrentTenantData();
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* @since 0.6.0
|
|
377
|
+
* @public
|
|
378
|
+
* @group Tenant Management
|
|
379
|
+
*/
|
|
380
|
+
async switchActiveTenant(tenantId) {
|
|
381
|
+
if (!tenantId) {
|
|
382
|
+
throw new Error("Tenant ID is required");
|
|
383
|
+
}
|
|
384
|
+
const requestBody = {
|
|
385
|
+
tenant_id: tenantId
|
|
386
|
+
};
|
|
387
|
+
try {
|
|
388
|
+
const response = await this.omnibaseClient.fetch(
|
|
389
|
+
`/api/v1/tenants/switch-active`,
|
|
390
|
+
{
|
|
391
|
+
method: "PUT",
|
|
392
|
+
headers: {
|
|
393
|
+
"Content-Type": "application/json"
|
|
394
|
+
},
|
|
395
|
+
body: JSON.stringify(requestBody),
|
|
396
|
+
credentials: "include"
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
const errorData = await response.text();
|
|
401
|
+
throw new Error(
|
|
402
|
+
`Failed to switch tenant: ${response.status} - ${errorData}`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const data = await response.json();
|
|
406
|
+
return data;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error("Error switching active tenant:", error);
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// src/tenants/subscriptions.ts
|
|
415
|
+
var TenantSubscriptionManager = class {
|
|
416
|
+
/**
|
|
417
|
+
* Creates a new TenantSubscriptionManager instance
|
|
418
|
+
*
|
|
419
|
+
* @param omnibaseClient - Configured Omnibase client instance
|
|
420
|
+
*
|
|
421
|
+
* @group Tenant Subscriptions
|
|
422
|
+
*/
|
|
423
|
+
constructor(omnibaseClient) {
|
|
424
|
+
this.omnibaseClient = omnibaseClient;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get all active subscriptions for the current tenant
|
|
428
|
+
*
|
|
429
|
+
* Retrieves all active Stripe subscriptions associated with the user's
|
|
430
|
+
* currently active tenant. Returns subscriptions with config-based price IDs
|
|
431
|
+
* instead of raw Stripe IDs, making it easier to match against your billing
|
|
432
|
+
* configuration.
|
|
433
|
+
*
|
|
434
|
+
* The endpoint automatically:
|
|
435
|
+
* - Fetches subscriptions from Stripe API
|
|
436
|
+
* - Maps Stripe price IDs to your config price IDs
|
|
437
|
+
* - Checks both current and historical price mappings
|
|
438
|
+
* - Flags legacy prices from old billing configurations
|
|
439
|
+
* - Filters to only active/trialing/past_due subscriptions
|
|
440
|
+
*
|
|
441
|
+
* Returns an empty array if:
|
|
442
|
+
* - Tenant has no Stripe customer ID configured
|
|
443
|
+
* - Tenant has no active subscriptions
|
|
444
|
+
* - User is not authenticated
|
|
445
|
+
*
|
|
446
|
+
* @returns Promise resolving to array of active subscriptions
|
|
447
|
+
*
|
|
448
|
+
* @throws {Error} When the user is not authenticated
|
|
449
|
+
* @throws {Error} When the API request fails due to network issues
|
|
450
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx)
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```typescript
|
|
454
|
+
* const response = await subscriptionManager.getActive();
|
|
455
|
+
*
|
|
456
|
+
* if (response.data.length === 0) {
|
|
457
|
+
* console.log('No active subscriptions');
|
|
458
|
+
* } else {
|
|
459
|
+
* response.data.forEach(sub => {
|
|
460
|
+
* console.log(`Plan: ${sub.config_price_id}`);
|
|
461
|
+
* console.log(`Status: ${sub.status}`);
|
|
462
|
+
* if (sub.is_legacy_price) {
|
|
463
|
+
* console.log('⚠️ Using legacy pricing');
|
|
464
|
+
* }
|
|
465
|
+
* });
|
|
466
|
+
* }
|
|
467
|
+
* ```
|
|
468
|
+
*
|
|
469
|
+
* @since 0.6.0
|
|
470
|
+
* @public
|
|
471
|
+
* @group Tenant Subscriptions
|
|
472
|
+
*/
|
|
473
|
+
async getActive() {
|
|
474
|
+
try {
|
|
475
|
+
const response = await this.omnibaseClient.fetch(
|
|
476
|
+
`/api/v1/tenants/subscriptions`,
|
|
477
|
+
{
|
|
478
|
+
method: "GET",
|
|
479
|
+
credentials: "include"
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
if (!response.ok) {
|
|
483
|
+
const errorData = await response.text();
|
|
484
|
+
throw new Error(
|
|
485
|
+
`Failed to fetch subscriptions: ${response.status} - ${errorData}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
const data = await response.json();
|
|
489
|
+
return data;
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error("Error fetching tenant subscriptions:", error);
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Check if the current tenant has billing information configured
|
|
497
|
+
*
|
|
498
|
+
* Verifies whether the tenant has valid payment methods attached to their
|
|
499
|
+
* Stripe customer account. This is useful for:
|
|
500
|
+
* - Showing billing setup prompts
|
|
501
|
+
* - Gating premium features behind payment method requirement
|
|
502
|
+
* - Displaying billing status indicators in UI
|
|
503
|
+
* - Determining if customer portal access should be shown
|
|
504
|
+
*
|
|
505
|
+
* The check verifies:
|
|
506
|
+
* - Default payment source (card, bank account, etc.)
|
|
507
|
+
* - Default payment method in invoice settings
|
|
508
|
+
* - Whether the payment method is valid and active
|
|
509
|
+
*
|
|
510
|
+
* Returns `false` if:
|
|
511
|
+
* - Tenant has no Stripe customer ID
|
|
512
|
+
* - No payment methods are configured
|
|
513
|
+
* - User is not authenticated
|
|
514
|
+
*
|
|
515
|
+
* @returns Promise resolving to billing status information
|
|
516
|
+
*
|
|
517
|
+
* @throws {Error} When the user is not authenticated
|
|
518
|
+
* @throws {Error} When the API request fails due to network issues
|
|
519
|
+
* @throws {Error} When the server returns an error response (4xx, 5xx)
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```typescript
|
|
523
|
+
* const response = await subscriptionManager.getBillingStatus();
|
|
524
|
+
*
|
|
525
|
+
* if (!response.data.has_billing_info) {
|
|
526
|
+
* // Show billing setup prompt
|
|
527
|
+
* showBillingSetupModal();
|
|
528
|
+
* } else if (!response.data.is_active) {
|
|
529
|
+
* // Payment method exists but may be expired/invalid
|
|
530
|
+
* showPaymentMethodUpdatePrompt();
|
|
531
|
+
* } else {
|
|
532
|
+
* // All good - show customer portal link
|
|
533
|
+
* showManageBillingButton();
|
|
534
|
+
* }
|
|
535
|
+
* ```
|
|
536
|
+
*
|
|
537
|
+
* @since 0.6.0
|
|
538
|
+
* @public
|
|
539
|
+
* @group Tenant Subscriptions
|
|
540
|
+
*/
|
|
541
|
+
async getBillingStatus() {
|
|
542
|
+
try {
|
|
543
|
+
const response = await this.omnibaseClient.fetch(
|
|
544
|
+
`/api/v1/tenants/billing-status`,
|
|
545
|
+
{
|
|
546
|
+
method: "GET",
|
|
547
|
+
credentials: "include"
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
if (!response.ok) {
|
|
551
|
+
const errorData = await response.text();
|
|
552
|
+
throw new Error(
|
|
553
|
+
`Failed to fetch billing status: ${response.status} - ${errorData}`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
const data = await response.json();
|
|
557
|
+
return data;
|
|
558
|
+
} catch (error) {
|
|
559
|
+
console.error("Error fetching billing status:", error);
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// src/tenants/user.ts
|
|
566
|
+
var TenantUserManager = class {
|
|
567
|
+
/**
|
|
568
|
+
* Creates a new tenant user manager
|
|
569
|
+
*
|
|
570
|
+
* @param omnibaseClient - Configured OmnibaseClient instance for API communication
|
|
571
|
+
*
|
|
572
|
+
* @group Tenant User Management
|
|
573
|
+
*/
|
|
574
|
+
constructor(omnibaseClient) {
|
|
575
|
+
this.omnibaseClient = omnibaseClient;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Retrieves all users in the active tenant
|
|
579
|
+
*
|
|
580
|
+
* This method fetches a list of all users who are members of the current active tenant,
|
|
581
|
+
* including their basic information (name, email) and assigned role. The operation
|
|
582
|
+
* requires the requesting user to have appropriate permissions to view tenant users
|
|
583
|
+
* (typically requires `view_users` permission).
|
|
584
|
+
*
|
|
585
|
+
* The returned list includes all tenant members regardless of their role, ordered by
|
|
586
|
+
* when they joined the tenant (newest first).
|
|
587
|
+
*
|
|
588
|
+
* @returns Promise resolving to an array of tenant users with their details
|
|
589
|
+
*
|
|
590
|
+
* @throws {Error} When the API request fails (includes status code and error details)
|
|
591
|
+
* @throws {Error} When the user doesn't have permission to view users
|
|
592
|
+
* @throws {Error} When the user is not authenticated or no active tenant is set
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```typescript
|
|
596
|
+
* // Fetch all users in the active tenant
|
|
597
|
+
* try {
|
|
598
|
+
* const result = await userManager.getAll();
|
|
599
|
+
* console.log(`Found ${result.data.length} users`);
|
|
600
|
+
*
|
|
601
|
+
* result.data.forEach(user => {
|
|
602
|
+
* console.log(`${user.first_name} ${user.last_name} (${user.email}) - ${user.role}`);
|
|
603
|
+
* });
|
|
604
|
+
* } catch (error) {
|
|
605
|
+
* if (error.message.includes('403')) {
|
|
606
|
+
* console.error('Insufficient permissions to view users');
|
|
607
|
+
* } else {
|
|
608
|
+
* console.error('Failed to fetch users:', error);
|
|
609
|
+
* }
|
|
610
|
+
* }
|
|
611
|
+
* ```
|
|
612
|
+
*
|
|
613
|
+
* @since 1.0.0
|
|
614
|
+
* @public
|
|
615
|
+
* @group Tenant User Management
|
|
616
|
+
*/
|
|
617
|
+
async getAll() {
|
|
618
|
+
const response = await this.omnibaseClient.fetch("/api/v1/tenants/users", {
|
|
619
|
+
method: "GET"
|
|
620
|
+
});
|
|
621
|
+
if (!response.ok) {
|
|
622
|
+
const errorData = await response.text();
|
|
623
|
+
throw new Error(
|
|
624
|
+
`Failed to fetch tenant users: ${response.status} - ${errorData}`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
return await response.json();
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Removes a user from the active tenant
|
|
631
|
+
*
|
|
632
|
+
* This method removes a specified user from the current active tenant. The operation
|
|
633
|
+
* requires the requesting user to have appropriate permissions (admin or owner role).
|
|
634
|
+
* The user being removed will lose access to the tenant and all its resources.
|
|
635
|
+
*
|
|
636
|
+
* Note: You cannot remove yourself from a tenant using this method. To leave a tenant,
|
|
637
|
+
* use the appropriate leave or delete tenant operations instead.
|
|
638
|
+
*
|
|
639
|
+
* @param data - Request data containing the user ID to remove
|
|
640
|
+
* @param data.user_id - ID of the user to remove from the tenant
|
|
641
|
+
*
|
|
642
|
+
* @returns Promise resolving to an API response confirming the removal
|
|
643
|
+
*
|
|
644
|
+
* @throws {Error} When user_id is not provided
|
|
645
|
+
* @throws {Error} When the API request fails (includes status code and error details)
|
|
646
|
+
* @throws {Error} When the user doesn't have permission to remove users
|
|
647
|
+
* @throws {Error} When the specified user is not a member of the tenant
|
|
648
|
+
*
|
|
649
|
+
* @example
|
|
650
|
+
* ```typescript
|
|
651
|
+
* // Remove a user from the active tenant
|
|
652
|
+
* try {
|
|
653
|
+
* await userManager.remove({ user_id: 'user_abc123' });
|
|
654
|
+
* console.log('User removed successfully');
|
|
655
|
+
* } catch (error) {
|
|
656
|
+
* if (error.message.includes('403')) {
|
|
657
|
+
* console.error('Insufficient permissions to remove user');
|
|
658
|
+
* } else if (error.message.includes('404')) {
|
|
659
|
+
* console.error('User not found in tenant');
|
|
660
|
+
* } else {
|
|
661
|
+
* console.error('Failed to remove user:', error);
|
|
662
|
+
* }
|
|
663
|
+
* }
|
|
664
|
+
* ```
|
|
665
|
+
*
|
|
666
|
+
* @since 0.6.0
|
|
667
|
+
* @public
|
|
668
|
+
* @group Tenant User Management
|
|
669
|
+
*/
|
|
670
|
+
async remove(data) {
|
|
671
|
+
if (!data.user_id) {
|
|
672
|
+
throw new Error("user_id is required");
|
|
673
|
+
}
|
|
674
|
+
const response = await this.omnibaseClient.fetch("/api/v1/tenants/users", {
|
|
675
|
+
method: "DELETE",
|
|
676
|
+
body: JSON.stringify(data)
|
|
677
|
+
});
|
|
678
|
+
if (!response.ok) {
|
|
679
|
+
const errorData = await response.text();
|
|
680
|
+
throw new Error(
|
|
681
|
+
`Failed to delete user from tenant: ${response.status} - ${errorData}`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
return await response.json();
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Updates a user's role within the active tenant
|
|
688
|
+
*
|
|
689
|
+
* This method changes the role of a specified user in the current active tenant. The operation
|
|
690
|
+
* requires the requesting user to have appropriate permissions (typically admin or owner role).
|
|
691
|
+
* Role updates take effect immediately and affect the user's permissions and access rights
|
|
692
|
+
* within the tenant.
|
|
693
|
+
*
|
|
694
|
+
* Common roles include 'admin', 'member', and 'viewer', but the exact roles available depend
|
|
695
|
+
* on your tenant's configuration. Changing a user's role will modify their ability to perform
|
|
696
|
+
* various operations within the tenant.
|
|
697
|
+
*
|
|
698
|
+
* @param data - Request data containing the user ID and new role
|
|
699
|
+
* @param data.user_id - ID of the user whose role is being updated
|
|
700
|
+
* @param data.role - New role to assign to the user
|
|
701
|
+
*
|
|
702
|
+
* @returns Promise resolving to an API response confirming the role update
|
|
703
|
+
*
|
|
704
|
+
* @throws {Error} When user_id or role is not provided
|
|
705
|
+
* @throws {Error} When the API request fails (includes status code and error details)
|
|
706
|
+
* @throws {Error} When the user doesn't have permission to update roles
|
|
707
|
+
* @throws {Error} When the specified user is not a member of the tenant
|
|
708
|
+
* @throws {Error} When the specified role is invalid or not allowed
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* ```typescript
|
|
712
|
+
* // Update a user's role to admin
|
|
713
|
+
* try {
|
|
714
|
+
* const result = await userManager.updateRole({
|
|
715
|
+
* user_id: 'user_abc123',
|
|
716
|
+
* role: 'admin'
|
|
717
|
+
* });
|
|
718
|
+
* console.log('Role updated successfully:', result.data.message);
|
|
719
|
+
* } catch (error) {
|
|
720
|
+
* if (error.message.includes('403')) {
|
|
721
|
+
* console.error('Insufficient permissions to update roles');
|
|
722
|
+
* } else if (error.message.includes('404')) {
|
|
723
|
+
* console.error('User not found in tenant');
|
|
724
|
+
* } else {
|
|
725
|
+
* console.error('Failed to update role:', error);
|
|
726
|
+
* }
|
|
727
|
+
* }
|
|
728
|
+
* ```
|
|
729
|
+
*
|
|
730
|
+
* @since 0.6.0
|
|
731
|
+
* @public
|
|
732
|
+
* @group Tenant User Management
|
|
733
|
+
*/
|
|
734
|
+
async updateRole(data) {
|
|
735
|
+
if (!data.role || !data.user_id)
|
|
736
|
+
throw new Error("user_id and role is required");
|
|
737
|
+
const response = await this.omnibaseClient.fetch("/api/v1/tenants/users", {
|
|
738
|
+
method: "PUT",
|
|
739
|
+
body: JSON.stringify(data)
|
|
740
|
+
});
|
|
741
|
+
if (!response.ok) {
|
|
742
|
+
const errorData = await response.text();
|
|
743
|
+
throw new Error(
|
|
744
|
+
`Failed to update users role: ${response.status} - ${errorData}`
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
return await response.json();
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
// src/tenants/handler.ts
|
|
752
|
+
var TenantHandler = class {
|
|
753
|
+
/**
|
|
754
|
+
* Creates a new TenantHandler instance
|
|
755
|
+
*
|
|
756
|
+
* Initializes the handler with the provided Omnibase client and sets up
|
|
757
|
+
* the specialized manager instances for tenant and invitation operations.
|
|
758
|
+
* The client is used for all underlying HTTP requests and authentication.
|
|
759
|
+
*
|
|
760
|
+
* @param omnibaseClient - Configured Omnibase client instance
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```typescript
|
|
764
|
+
* const client = new OmnibaseClient({
|
|
765
|
+
* apiKey: 'your-api-key',
|
|
766
|
+
* baseURL: 'https://api.yourapp.com'
|
|
767
|
+
* });
|
|
768
|
+
* const tenantHandler = new TenantHandler(client);
|
|
769
|
+
* ```
|
|
770
|
+
*
|
|
771
|
+
* @group Tenant Management
|
|
772
|
+
*/
|
|
773
|
+
constructor(omnibaseClient) {
|
|
774
|
+
this.invites = new TenantInviteManager(omnibaseClient);
|
|
775
|
+
this.manage = new TenantManger(omnibaseClient);
|
|
776
|
+
this.subscriptions = new TenantSubscriptionManager(omnibaseClient);
|
|
777
|
+
this.user = new TenantUserManager(omnibaseClient);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Tenant user management operations
|
|
781
|
+
*
|
|
782
|
+
* Provides access to operations for managing users within tenants, including
|
|
783
|
+
* removing users from the active tenant. All operations respect user permissions
|
|
784
|
+
* and tenant ownership rules.
|
|
785
|
+
*
|
|
786
|
+
* @example
|
|
787
|
+
* ```typescript
|
|
788
|
+
* // Remove a user from the active tenant
|
|
789
|
+
* await tenantHandler.user.remove({ user_id: 'user_123' });
|
|
790
|
+
* ```
|
|
791
|
+
*
|
|
792
|
+
* @since 0.6.0
|
|
793
|
+
* @group Tenant Management
|
|
794
|
+
*/
|
|
795
|
+
user;
|
|
796
|
+
/**
|
|
797
|
+
* Core tenant management operations
|
|
798
|
+
*
|
|
799
|
+
* Provides access to tenant lifecycle operations including creation,
|
|
800
|
+
* deletion, and active tenant switching. All operations respect user
|
|
801
|
+
* permissions and tenant ownership rules.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```typescript
|
|
805
|
+
* // Create a new tenant
|
|
806
|
+
* const tenant = await tenantHandler.manage.createTenant({
|
|
807
|
+
* name: 'New Company',
|
|
808
|
+
* billing_email: 'billing@newcompany.com',
|
|
809
|
+
* user_id: 'user_456'
|
|
810
|
+
* });
|
|
811
|
+
*
|
|
812
|
+
* // Switch to the tenant
|
|
813
|
+
* await tenantHandler.manage.switchActiveTenant(tenant.data.tenant.id);
|
|
814
|
+
*
|
|
815
|
+
* // Delete the tenant (owner only)
|
|
816
|
+
* await tenantHandler.manage.deleteTenant(tenant.data.tenant.id);
|
|
817
|
+
* ```
|
|
818
|
+
*/
|
|
819
|
+
manage;
|
|
820
|
+
/**
|
|
821
|
+
* Tenant invitation management operations
|
|
822
|
+
*
|
|
823
|
+
* Provides access to user invitation functionality including creating
|
|
824
|
+
* invitations for new users and accepting existing invitations.
|
|
825
|
+
* Supports role-based access control and secure token-based workflows.
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* ```typescript
|
|
829
|
+
* // Create an invitation
|
|
830
|
+
* const invite = await tenantHandler.invites.create({
|
|
831
|
+
* email: 'newuser@company.com',
|
|
832
|
+
* role: 'admin',
|
|
833
|
+
* invite_url: 'https://yourapp.com/accept-invite'
|
|
834
|
+
* });
|
|
835
|
+
*
|
|
836
|
+
* // Accept an invitation (from the invited user's session)
|
|
837
|
+
* const result = await tenantHandler.invites.accept('invite_token_xyz');
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
840
|
+
invites;
|
|
841
|
+
/**
|
|
842
|
+
* Tenant subscription and billing management
|
|
843
|
+
*
|
|
844
|
+
* Provides access to subscription data and billing status for the
|
|
845
|
+
* active tenant, including legacy price detection and payment method
|
|
846
|
+
* verification. All operations are automatically scoped to the user's
|
|
847
|
+
* currently active tenant.
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* ```typescript
|
|
851
|
+
* // Get active subscriptions
|
|
852
|
+
* const subs = await tenantHandler.subscriptions.getActive();
|
|
853
|
+
*
|
|
854
|
+
* // Check billing status
|
|
855
|
+
* const status = await tenantHandler.subscriptions.getBillingStatus();
|
|
856
|
+
* if (!status.data.has_billing_info) {
|
|
857
|
+
* console.log('No payment method configured');
|
|
858
|
+
* }
|
|
859
|
+
* ```
|
|
860
|
+
*
|
|
861
|
+
* @since 0.6.0
|
|
862
|
+
* @group Tenant Management
|
|
863
|
+
*/
|
|
864
|
+
subscriptions;
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
export {
|
|
868
|
+
TenantHandler
|
|
869
|
+
};
|