@vritti/api-sdk 0.0.1 → 0.0.3
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 +504 -43
- package/dist/index.cjs +1242 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +755 -2
- package/dist/index.d.ts +755 -2
- package/dist/index.js +1222 -5
- package/dist/index.js.map +1 -1
- package/package.json +23 -3
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
var __export = (target, all) => {
|
|
@@ -16,19 +18,1254 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/index.ts
|
|
22
32
|
var index_exports = {};
|
|
23
33
|
__export(index_exports, {
|
|
24
|
-
|
|
34
|
+
AuthConfigModule: () => AuthConfigModule,
|
|
35
|
+
DatabaseModule: () => DatabaseModule,
|
|
36
|
+
Onboarding: () => Onboarding,
|
|
37
|
+
PrimaryDatabaseService: () => PrimaryDatabaseService,
|
|
38
|
+
Public: () => Public,
|
|
39
|
+
Tenant: () => Tenant,
|
|
40
|
+
TenantContextService: () => TenantContextService,
|
|
41
|
+
TenantDatabaseService: () => TenantDatabaseService,
|
|
42
|
+
VrittiAuthGuard: () => VrittiAuthGuard
|
|
25
43
|
});
|
|
26
44
|
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
|
|
46
|
+
// src/auth/auth-config.module.ts
|
|
47
|
+
var import_common5 = require("@nestjs/common");
|
|
48
|
+
var import_config2 = require("@nestjs/config");
|
|
49
|
+
var import_core3 = require("@nestjs/core");
|
|
50
|
+
var import_jwt2 = require("@nestjs/jwt");
|
|
51
|
+
|
|
52
|
+
// src/request/request.module.ts
|
|
53
|
+
var import_common2 = require("@nestjs/common");
|
|
54
|
+
|
|
55
|
+
// src/request/services/request.service.ts
|
|
56
|
+
var import_common = require("@nestjs/common");
|
|
57
|
+
var import_core = require("@nestjs/core");
|
|
58
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
59
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
60
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
61
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
62
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
63
|
+
}
|
|
64
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
65
|
+
function _ts_metadata(k, v) {
|
|
66
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
67
|
+
}
|
|
68
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
69
|
+
function _ts_param(paramIndex, decorator) {
|
|
70
|
+
return function(target, key) {
|
|
71
|
+
decorator(target, key, paramIndex);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
__name(_ts_param, "_ts_param");
|
|
75
|
+
var RequestService = class {
|
|
76
|
+
static {
|
|
77
|
+
__name(this, "RequestService");
|
|
78
|
+
}
|
|
79
|
+
request;
|
|
80
|
+
constructor(request) {
|
|
81
|
+
this.request = request;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extract tenant identifier from request headers
|
|
85
|
+
* Priority: x-tenant-id > x-subdomain
|
|
86
|
+
* @returns Tenant identifier or null if not found
|
|
87
|
+
*/
|
|
88
|
+
getTenantIdentifier() {
|
|
89
|
+
const getHeader = /* @__PURE__ */ __name((key) => {
|
|
90
|
+
const value = this.request.headers?.[key];
|
|
91
|
+
return Array.isArray(value) ? value[0] : value;
|
|
92
|
+
}, "getHeader");
|
|
93
|
+
return getHeader("x-tenant-id") || getHeader("x-subdomain") || null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract access token from Authorization header
|
|
97
|
+
* Format: "Bearer <token>"
|
|
98
|
+
* @returns Access token or null if not found
|
|
99
|
+
*/
|
|
100
|
+
getAccessToken() {
|
|
101
|
+
const authHeader = this.request.headers?.authorization;
|
|
102
|
+
if (!authHeader) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const [type, token] = authHeader.split(" ") ?? [];
|
|
106
|
+
return type === "Bearer" && token ? token : null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Extract refresh token from session-id cookie
|
|
110
|
+
* Cookie name: session-id
|
|
111
|
+
* @returns Refresh token or null if not found
|
|
112
|
+
*/
|
|
113
|
+
getRefreshToken() {
|
|
114
|
+
try {
|
|
115
|
+
const cookies = this.request.cookies;
|
|
116
|
+
if (cookies && typeof cookies === "object") {
|
|
117
|
+
const sessionId = cookies["session-id"];
|
|
118
|
+
if (sessionId) {
|
|
119
|
+
return sessionId;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get a specific header value
|
|
129
|
+
* @param key Header key
|
|
130
|
+
* @returns Header value (string, array, or undefined)
|
|
131
|
+
*/
|
|
132
|
+
getHeader(key) {
|
|
133
|
+
return this.request.headers?.[key];
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get all headers
|
|
137
|
+
* @returns Record of all headers
|
|
138
|
+
*/
|
|
139
|
+
getAllHeaders() {
|
|
140
|
+
return this.request.headers || {};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
RequestService = _ts_decorate([
|
|
144
|
+
(0, import_common.Injectable)({
|
|
145
|
+
scope: import_common.Scope.REQUEST
|
|
146
|
+
}),
|
|
147
|
+
_ts_param(0, (0, import_common.Inject)(import_core.REQUEST)),
|
|
148
|
+
_ts_metadata("design:type", Function),
|
|
149
|
+
_ts_metadata("design:paramtypes", [
|
|
150
|
+
typeof FastifyRequest === "undefined" ? Object : FastifyRequest
|
|
151
|
+
])
|
|
152
|
+
], RequestService);
|
|
153
|
+
|
|
154
|
+
// src/request/request.module.ts
|
|
155
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
156
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
157
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
158
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
159
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
160
|
+
}
|
|
161
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
162
|
+
var RequestModule = class {
|
|
163
|
+
static {
|
|
164
|
+
__name(this, "RequestModule");
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
RequestModule = _ts_decorate2([
|
|
168
|
+
(0, import_common2.Global)(),
|
|
169
|
+
(0, import_common2.Module)({
|
|
170
|
+
providers: [
|
|
171
|
+
RequestService
|
|
172
|
+
],
|
|
173
|
+
exports: [
|
|
174
|
+
RequestService
|
|
175
|
+
]
|
|
176
|
+
})
|
|
177
|
+
], RequestModule);
|
|
178
|
+
|
|
179
|
+
// src/auth/guards/vritti-auth.guard.ts
|
|
180
|
+
var import_common4 = require("@nestjs/common");
|
|
181
|
+
var import_config = require("@nestjs/config");
|
|
182
|
+
var import_core2 = require("@nestjs/core");
|
|
183
|
+
var import_jwt = require("@nestjs/jwt");
|
|
184
|
+
var jwt = __toESM(require("jsonwebtoken"), 1);
|
|
185
|
+
|
|
186
|
+
// src/database/services/primary-database.service.ts
|
|
187
|
+
var import_common3 = require("@nestjs/common");
|
|
188
|
+
|
|
189
|
+
// src/database/constants.ts
|
|
190
|
+
var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
|
|
191
|
+
|
|
192
|
+
// src/database/services/primary-database.service.ts
|
|
193
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
194
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
195
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
196
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
197
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
198
|
+
}
|
|
199
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
200
|
+
function _ts_metadata2(k, v) {
|
|
201
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
202
|
+
}
|
|
203
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
204
|
+
function _ts_param2(paramIndex, decorator) {
|
|
205
|
+
return function(target, key) {
|
|
206
|
+
decorator(target, key, paramIndex);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
__name(_ts_param2, "_ts_param");
|
|
210
|
+
var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
211
|
+
static {
|
|
212
|
+
__name(this, "PrimaryDatabaseService");
|
|
213
|
+
}
|
|
214
|
+
options;
|
|
215
|
+
logger = new import_common3.Logger(_PrimaryDatabaseService.name);
|
|
216
|
+
/** Primary database client for querying tenant registry */
|
|
217
|
+
primaryDbClient;
|
|
218
|
+
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
219
|
+
tenantConfigCache = /* @__PURE__ */ new Map();
|
|
220
|
+
/** Cache TTL in milliseconds */
|
|
221
|
+
cacheTTL;
|
|
222
|
+
constructor(options) {
|
|
223
|
+
this.options = options;
|
|
224
|
+
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
225
|
+
}
|
|
226
|
+
async onModuleInit() {
|
|
227
|
+
if (this.options.primaryDb) {
|
|
228
|
+
await this.initializePrimaryDbClient();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Initialize connection to primary database
|
|
233
|
+
*/
|
|
234
|
+
async initializePrimaryDbClient() {
|
|
235
|
+
try {
|
|
236
|
+
const PrimaryDbClient = this.options.prismaClientConstructor;
|
|
237
|
+
const databaseUrl = this.buildPrimaryDbUrl();
|
|
238
|
+
this.primaryDbClient = new PrimaryDbClient({
|
|
239
|
+
datasources: {
|
|
240
|
+
db: {
|
|
241
|
+
url: databaseUrl
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
log: [
|
|
245
|
+
"error",
|
|
246
|
+
"warn"
|
|
247
|
+
]
|
|
248
|
+
});
|
|
249
|
+
await this.primaryDbClient.$connect();
|
|
250
|
+
this.logger.log("Connected to primary database (tenant registry)");
|
|
251
|
+
} catch (error) {
|
|
252
|
+
this.logger.error("Failed to connect to primary database", error);
|
|
253
|
+
throw new import_common3.InternalServerErrorException("Failed to initialize tenant registry");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Build connection URL from primary database properties
|
|
258
|
+
*/
|
|
259
|
+
buildPrimaryDbUrl() {
|
|
260
|
+
if (!this.options.primaryDb) {
|
|
261
|
+
throw new Error("Primary database configuration not provided");
|
|
262
|
+
}
|
|
263
|
+
const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
|
|
264
|
+
let url = `postgresql://${username}:${password}@${host}:${port}/${database}`;
|
|
265
|
+
const params = new URLSearchParams();
|
|
266
|
+
if (schema) {
|
|
267
|
+
params.set("schema", schema);
|
|
268
|
+
}
|
|
269
|
+
params.set("sslmode", sslMode);
|
|
270
|
+
const queryString = params.toString();
|
|
271
|
+
if (queryString) {
|
|
272
|
+
url += `?${queryString}`;
|
|
273
|
+
}
|
|
274
|
+
this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
|
|
275
|
+
return url;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Mask password in connection URL for logging
|
|
279
|
+
*/
|
|
280
|
+
maskPassword(url) {
|
|
281
|
+
return url.replace(/:([^@]+)@/, ":****@");
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get tenant configuration by identifier (ID or slug)
|
|
285
|
+
*
|
|
286
|
+
* @param tenantIdentifier Tenant ID or slug
|
|
287
|
+
* @returns Tenant configuration or null if not found
|
|
288
|
+
*/
|
|
289
|
+
async getTenantInfo(tenantIdentifier) {
|
|
290
|
+
const cached = this.tenantConfigCache.get(tenantIdentifier);
|
|
291
|
+
if (cached) {
|
|
292
|
+
this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
|
|
293
|
+
return cached;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
if (!this.primaryDbClient) {
|
|
297
|
+
throw new Error("Primary database client not initialized");
|
|
298
|
+
}
|
|
299
|
+
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
300
|
+
const tenant = await this.primaryDbClient.tenant.findFirst({
|
|
301
|
+
where: {
|
|
302
|
+
OR: [
|
|
303
|
+
{
|
|
304
|
+
id: tenantIdentifier
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
subdomain: tenantIdentifier
|
|
308
|
+
}
|
|
309
|
+
],
|
|
310
|
+
status: "ACTIVE"
|
|
311
|
+
},
|
|
312
|
+
include: {
|
|
313
|
+
databaseConfig: true
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
if (!tenant) {
|
|
317
|
+
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const info = {
|
|
321
|
+
id: tenant.id,
|
|
322
|
+
subdomain: tenant.subdomain,
|
|
323
|
+
type: tenant.dbType,
|
|
324
|
+
status: tenant.status,
|
|
325
|
+
// For SHARED tenants: schema name
|
|
326
|
+
schemaName: tenant.databaseConfig?.dbSchema || void 0,
|
|
327
|
+
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
328
|
+
databaseName: tenant.databaseConfig?.dbName || void 0,
|
|
329
|
+
databaseHost: tenant.databaseConfig?.dbHost || void 0,
|
|
330
|
+
databasePort: tenant.databaseConfig?.dbPort || void 0,
|
|
331
|
+
databaseUsername: tenant.databaseConfig?.dbUsername ? this.decrypt(tenant.databaseConfig.dbUsername) : void 0,
|
|
332
|
+
databasePassword: tenant.databaseConfig?.dbPassword ? this.decrypt(tenant.databaseConfig.dbPassword) : void 0,
|
|
333
|
+
databaseSslMode: tenant.databaseConfig?.dbSslMode || void 0,
|
|
334
|
+
connectionPoolSize: tenant.databaseConfig?.connectionPoolSize || void 0
|
|
335
|
+
};
|
|
336
|
+
this.cacheInfo(info);
|
|
337
|
+
return info;
|
|
338
|
+
} catch (error) {
|
|
339
|
+
this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
|
|
340
|
+
throw new import_common3.InternalServerErrorException("Failed to resolve tenant");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Cache tenant information with TTL
|
|
345
|
+
*/
|
|
346
|
+
cacheInfo(info) {
|
|
347
|
+
this.tenantConfigCache.set(info.id, info);
|
|
348
|
+
this.tenantConfigCache.set(info.subdomain, info);
|
|
349
|
+
setTimeout(() => {
|
|
350
|
+
this.tenantConfigCache.delete(info.id);
|
|
351
|
+
this.tenantConfigCache.delete(info.subdomain);
|
|
352
|
+
this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
|
|
353
|
+
}, this.cacheTTL);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Clear cached tenant information
|
|
357
|
+
*
|
|
358
|
+
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
359
|
+
*
|
|
360
|
+
* @param tenantIdentifier Tenant ID or slug
|
|
361
|
+
*/
|
|
362
|
+
clearTenantCache(tenantIdentifier) {
|
|
363
|
+
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
364
|
+
if (config) {
|
|
365
|
+
this.tenantConfigCache.delete(config.id);
|
|
366
|
+
this.tenantConfigCache.delete(config.subdomain);
|
|
367
|
+
this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Clear all cached tenant configurations
|
|
372
|
+
*/
|
|
373
|
+
clearAllCaches() {
|
|
374
|
+
const size = this.tenantConfigCache.size;
|
|
375
|
+
this.tenantConfigCache.clear();
|
|
376
|
+
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get primary database client for direct database access
|
|
380
|
+
*
|
|
381
|
+
* This is useful for platform admin operations (creating tenants, billing, etc.)
|
|
382
|
+
*
|
|
383
|
+
* @returns Primary database client instance
|
|
384
|
+
* @throws Error if primary database client is not initialized
|
|
385
|
+
*/
|
|
386
|
+
getPrimaryDbClient() {
|
|
387
|
+
if (!this.primaryDbClient) {
|
|
388
|
+
throw new Error("Primary database client not initialized. Are you in gateway mode?");
|
|
389
|
+
}
|
|
390
|
+
return this.primaryDbClient;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Decrypt database credentials
|
|
394
|
+
*
|
|
395
|
+
* Override this method to implement your encryption strategy
|
|
396
|
+
*
|
|
397
|
+
* @param encrypted Encrypted value
|
|
398
|
+
* @returns Decrypted value
|
|
399
|
+
*/
|
|
400
|
+
decrypt(encrypted) {
|
|
401
|
+
return encrypted;
|
|
402
|
+
}
|
|
403
|
+
async onModuleDestroy() {
|
|
404
|
+
if (this.primaryDbClient) {
|
|
405
|
+
await this.primaryDbClient.$disconnect();
|
|
406
|
+
this.logger.log("Disconnected from primary database");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
PrimaryDatabaseService = _ts_decorate3([
|
|
411
|
+
(0, import_common3.Injectable)(),
|
|
412
|
+
_ts_param2(0, (0, import_common3.Inject)(DATABASE_MODULE_OPTIONS)),
|
|
413
|
+
_ts_metadata2("design:type", Function),
|
|
414
|
+
_ts_metadata2("design:paramtypes", [
|
|
415
|
+
typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
|
|
416
|
+
])
|
|
417
|
+
], PrimaryDatabaseService);
|
|
418
|
+
|
|
419
|
+
// src/auth/guards/vritti-auth.guard.ts
|
|
420
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
421
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
422
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
423
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
424
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
425
|
+
}
|
|
426
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
427
|
+
function _ts_metadata3(k, v) {
|
|
428
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
429
|
+
}
|
|
430
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
431
|
+
var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
432
|
+
static {
|
|
433
|
+
__name(this, "VrittiAuthGuard");
|
|
434
|
+
}
|
|
435
|
+
reflector;
|
|
436
|
+
configService;
|
|
437
|
+
jwtService;
|
|
438
|
+
primaryDatabase;
|
|
439
|
+
requestService;
|
|
440
|
+
logger = new import_common4.Logger(_VrittiAuthGuard.name);
|
|
441
|
+
constructor(reflector, configService, jwtService, primaryDatabase, requestService) {
|
|
442
|
+
this.reflector = reflector;
|
|
443
|
+
this.configService = configService;
|
|
444
|
+
this.jwtService = jwtService;
|
|
445
|
+
this.primaryDatabase = primaryDatabase;
|
|
446
|
+
this.requestService = requestService;
|
|
447
|
+
}
|
|
448
|
+
async canActivate(context) {
|
|
449
|
+
const request = context.switchToHttp().getRequest();
|
|
450
|
+
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
451
|
+
context.getHandler(),
|
|
452
|
+
context.getClass()
|
|
453
|
+
]);
|
|
454
|
+
if (isPublic) {
|
|
455
|
+
this.logger.debug("Public endpoint detected, skipping authentication");
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
const isOnboarding = this.reflector.getAllAndOverride("isOnboarding", [
|
|
459
|
+
context.getHandler(),
|
|
460
|
+
context.getClass()
|
|
461
|
+
]);
|
|
462
|
+
try {
|
|
463
|
+
const accessToken = this.requestService.getAccessToken();
|
|
464
|
+
if (!accessToken) {
|
|
465
|
+
this.logger.warn("Access token not found in Authorization header");
|
|
466
|
+
throw new import_common4.UnauthorizedException("Access token not found");
|
|
467
|
+
}
|
|
468
|
+
const decodedToken = this.jwtService.decode(accessToken);
|
|
469
|
+
if (!decodedToken) {
|
|
470
|
+
this.logger.warn("Failed to decode access token");
|
|
471
|
+
throw new import_common4.UnauthorizedException("Invalid token format");
|
|
472
|
+
}
|
|
473
|
+
if (isOnboarding) {
|
|
474
|
+
if (decodedToken.type !== "onboarding") {
|
|
475
|
+
this.logger.warn("Onboarding endpoint requires onboarding token");
|
|
476
|
+
throw new import_common4.UnauthorizedException("This endpoint requires an onboarding token");
|
|
477
|
+
}
|
|
478
|
+
const validatedToken2 = this.validateAccessToken(accessToken);
|
|
479
|
+
this.logger.debug("Onboarding token validated successfully");
|
|
480
|
+
const userId2 = validatedToken2.userId;
|
|
481
|
+
request.user = {
|
|
482
|
+
id: userId2
|
|
483
|
+
};
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
if (decodedToken.type === "onboarding") {
|
|
487
|
+
this.logger.warn("Regular endpoint accessed with onboarding token");
|
|
488
|
+
throw new import_common4.UnauthorizedException("Onboarding tokens cannot access this endpoint");
|
|
489
|
+
}
|
|
490
|
+
const validatedToken = this.validateAccessToken(accessToken);
|
|
491
|
+
this.logger.debug("Access token validated successfully");
|
|
492
|
+
const refreshToken = this.requestService.getRefreshToken();
|
|
493
|
+
if (!refreshToken) {
|
|
494
|
+
this.logger.warn("Refresh token (session-id) not found in cookies");
|
|
495
|
+
throw new import_common4.UnauthorizedException("Refresh token not found");
|
|
496
|
+
}
|
|
497
|
+
this.validateRefreshToken(refreshToken);
|
|
498
|
+
this.logger.debug("Refresh token validated successfully");
|
|
499
|
+
const userId = validatedToken.userId;
|
|
500
|
+
request.user = {
|
|
501
|
+
id: userId
|
|
502
|
+
};
|
|
503
|
+
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
504
|
+
if (!tenantIdentifier) {
|
|
505
|
+
this.logger.warn("Tenant identifier not found in request");
|
|
506
|
+
throw new import_common4.UnauthorizedException("Tenant identifier not found");
|
|
507
|
+
}
|
|
508
|
+
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
509
|
+
if (tenantIdentifier === "cloud") {
|
|
510
|
+
this.logger.debug("Platform admin access detected, skipping tenant database validation");
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
514
|
+
if (!tenantInfo) {
|
|
515
|
+
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
516
|
+
throw new import_common4.UnauthorizedException("Invalid tenant");
|
|
517
|
+
}
|
|
518
|
+
if (tenantInfo.status !== "ACTIVE") {
|
|
519
|
+
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
520
|
+
throw new import_common4.UnauthorizedException(`Tenant is ${tenantInfo.status}`);
|
|
521
|
+
}
|
|
522
|
+
this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
523
|
+
return true;
|
|
524
|
+
} catch (error) {
|
|
525
|
+
if (error instanceof import_common4.UnauthorizedException) {
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
this.logger.error("Unexpected error in auth guard", error);
|
|
529
|
+
throw new import_common4.UnauthorizedException("Authentication failed");
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Validate access token with proper expiry checks
|
|
534
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
535
|
+
*/
|
|
536
|
+
validateAccessToken(token) {
|
|
537
|
+
try {
|
|
538
|
+
const decoded = this.jwtService.verify(token);
|
|
539
|
+
this.logger.debug(`Access token decoded for user: ${decoded.userId}`);
|
|
540
|
+
if (decoded.exp) {
|
|
541
|
+
const expiryTime = decoded.exp * 1e3;
|
|
542
|
+
const currentTime = Date.now();
|
|
543
|
+
const timeRemaining = expiryTime - currentTime;
|
|
544
|
+
this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
545
|
+
}
|
|
546
|
+
return decoded;
|
|
547
|
+
} catch (error) {
|
|
548
|
+
if (error instanceof import_common4.UnauthorizedException) {
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
551
|
+
const jwtError = error;
|
|
552
|
+
if (jwtError?.name === "TokenExpiredError") {
|
|
553
|
+
this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);
|
|
554
|
+
throw new import_common4.UnauthorizedException("Access token has expired");
|
|
555
|
+
}
|
|
556
|
+
if (jwtError?.name === "JsonWebTokenError") {
|
|
557
|
+
this.logger.warn(`Access token verification failed: ${jwtError?.message}`);
|
|
558
|
+
throw new import_common4.UnauthorizedException("Invalid access token");
|
|
559
|
+
}
|
|
560
|
+
if (jwtError?.name === "NotBeforeError") {
|
|
561
|
+
this.logger.warn("Access token used before valid (nbf claim)");
|
|
562
|
+
throw new import_common4.UnauthorizedException("Access token not yet valid");
|
|
563
|
+
}
|
|
564
|
+
this.logger.error("Unexpected error validating access token", error);
|
|
565
|
+
throw new import_common4.UnauthorizedException("Access token validation failed");
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Validate refresh token with proper expiry checks
|
|
570
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
571
|
+
*/
|
|
572
|
+
validateRefreshToken(token) {
|
|
573
|
+
const jwtSecret = this.configService.get("JWT_REFRESH_SECRET") || this.configService.get("JWT_SECRET");
|
|
574
|
+
this.validateRefreshTokenWithSecret(token, jwtSecret);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Helper to validate refresh token with specific secret
|
|
578
|
+
*/
|
|
579
|
+
validateRefreshTokenWithSecret(token, secret) {
|
|
580
|
+
if (!secret) {
|
|
581
|
+
this.logger.error("JWT secret not configured for refresh token validation");
|
|
582
|
+
throw new import_common4.UnauthorizedException("Server configuration error");
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const decoded = jwt.verify(token, secret, {
|
|
586
|
+
algorithms: [
|
|
587
|
+
"HS256",
|
|
588
|
+
"HS512",
|
|
589
|
+
"RS256"
|
|
590
|
+
]
|
|
591
|
+
});
|
|
592
|
+
this.logger.debug(`Refresh token decoded for user: ${decoded.userId}`);
|
|
593
|
+
if (decoded.exp) {
|
|
594
|
+
const expiryTime = decoded.exp * 1e3;
|
|
595
|
+
const currentTime = Date.now();
|
|
596
|
+
if (currentTime > expiryTime) {
|
|
597
|
+
this.logger.warn("Refresh token has expired");
|
|
598
|
+
throw new import_common4.UnauthorizedException("Refresh token has expired. Please login again");
|
|
599
|
+
}
|
|
600
|
+
const timeRemaining = expiryTime - currentTime;
|
|
601
|
+
this.logger.debug(`Refresh token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
602
|
+
}
|
|
603
|
+
} catch (error) {
|
|
604
|
+
if (error instanceof import_common4.UnauthorizedException) {
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
const jwtError = error;
|
|
608
|
+
if (jwtError?.name === "TokenExpiredError") {
|
|
609
|
+
this.logger.warn(`Refresh token expired at: ${jwtError?.expiredAt}`);
|
|
610
|
+
throw new import_common4.UnauthorizedException("Refresh token has expired. Please login again");
|
|
611
|
+
}
|
|
612
|
+
if (jwtError?.name === "JsonWebTokenError") {
|
|
613
|
+
this.logger.warn(`Refresh token verification failed: ${jwtError?.message}`);
|
|
614
|
+
throw new import_common4.UnauthorizedException("Invalid refresh token");
|
|
615
|
+
}
|
|
616
|
+
if (jwtError?.name === "NotBeforeError") {
|
|
617
|
+
this.logger.warn("Refresh token used before valid (nbf claim)");
|
|
618
|
+
throw new import_common4.UnauthorizedException("Refresh token not yet valid");
|
|
619
|
+
}
|
|
620
|
+
this.logger.error("Unexpected error validating refresh token", error);
|
|
621
|
+
throw new import_common4.UnauthorizedException("Refresh token validation failed");
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
VrittiAuthGuard = _ts_decorate4([
|
|
626
|
+
(0, import_common4.Injectable)({
|
|
627
|
+
scope: import_common4.Scope.REQUEST
|
|
628
|
+
}),
|
|
629
|
+
_ts_metadata3("design:type", Function),
|
|
630
|
+
_ts_metadata3("design:paramtypes", [
|
|
631
|
+
typeof import_core2.Reflector === "undefined" ? Object : import_core2.Reflector,
|
|
632
|
+
typeof import_config.ConfigService === "undefined" ? Object : import_config.ConfigService,
|
|
633
|
+
typeof import_jwt.JwtService === "undefined" ? Object : import_jwt.JwtService,
|
|
634
|
+
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
635
|
+
typeof RequestService === "undefined" ? Object : RequestService
|
|
636
|
+
])
|
|
637
|
+
], VrittiAuthGuard);
|
|
638
|
+
|
|
639
|
+
// src/auth/auth-config.module.ts
|
|
640
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
641
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
642
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
643
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
644
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
645
|
+
}
|
|
646
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
647
|
+
var AuthConfigModule = class _AuthConfigModule {
|
|
648
|
+
static {
|
|
649
|
+
__name(this, "AuthConfigModule");
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Register the auth module with async configuration
|
|
653
|
+
*
|
|
654
|
+
* This method:
|
|
655
|
+
* 1. Configures JwtModule with JWT_SECRET from ConfigService
|
|
656
|
+
* 2. Provides VrittiAuthGuard globally (applies to all routes)
|
|
657
|
+
* 3. Exports JwtModule for use in other modules (e.g., for signing tokens)
|
|
658
|
+
*
|
|
659
|
+
* @returns Dynamic module configuration
|
|
660
|
+
*/
|
|
661
|
+
static forRootAsync() {
|
|
662
|
+
return {
|
|
663
|
+
module: _AuthConfigModule,
|
|
664
|
+
imports: [
|
|
665
|
+
import_config2.ConfigModule,
|
|
666
|
+
RequestModule,
|
|
667
|
+
import_jwt2.JwtModule.registerAsync({
|
|
668
|
+
imports: [
|
|
669
|
+
import_config2.ConfigModule
|
|
670
|
+
],
|
|
671
|
+
inject: [
|
|
672
|
+
import_config2.ConfigService
|
|
673
|
+
],
|
|
674
|
+
useFactory: /* @__PURE__ */ __name((config) => ({
|
|
675
|
+
secret: config.get("JWT_SECRET"),
|
|
676
|
+
signOptions: {
|
|
677
|
+
algorithm: "HS256"
|
|
678
|
+
}
|
|
679
|
+
}), "useFactory")
|
|
680
|
+
})
|
|
681
|
+
],
|
|
682
|
+
providers: [
|
|
683
|
+
{
|
|
684
|
+
provide: import_core3.APP_GUARD,
|
|
685
|
+
useClass: VrittiAuthGuard
|
|
686
|
+
}
|
|
687
|
+
],
|
|
688
|
+
exports: [
|
|
689
|
+
import_jwt2.JwtModule
|
|
690
|
+
]
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
AuthConfigModule = _ts_decorate5([
|
|
695
|
+
(0, import_common5.Global)(),
|
|
696
|
+
(0, import_common5.Module)({})
|
|
697
|
+
], AuthConfigModule);
|
|
698
|
+
|
|
699
|
+
// src/database/database.module.ts
|
|
700
|
+
var import_common10 = require("@nestjs/common");
|
|
701
|
+
var import_core4 = require("@nestjs/core");
|
|
702
|
+
|
|
703
|
+
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
704
|
+
var import_common7 = require("@nestjs/common");
|
|
705
|
+
var import_operators = require("rxjs/operators");
|
|
706
|
+
|
|
707
|
+
// src/database/services/tenant-context.service.ts
|
|
708
|
+
var import_common6 = require("@nestjs/common");
|
|
709
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
710
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
711
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
712
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
713
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
714
|
+
}
|
|
715
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
716
|
+
var TenantContextService = class {
|
|
717
|
+
static {
|
|
718
|
+
__name(this, "TenantContextService");
|
|
719
|
+
}
|
|
720
|
+
tenantInfo = null;
|
|
721
|
+
/**
|
|
722
|
+
* Set tenant information for this request/message
|
|
723
|
+
*
|
|
724
|
+
* This is typically called by:
|
|
725
|
+
* - TenantContextInterceptor (for HTTP requests in gateway)
|
|
726
|
+
* - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
|
|
727
|
+
* - Manual context setup in message handlers
|
|
728
|
+
*
|
|
729
|
+
* @param tenantInfo Complete tenant information
|
|
730
|
+
* @throws Error if tenant context is already set (prevents accidental overwrites)
|
|
731
|
+
*/
|
|
732
|
+
setTenant(tenantInfo) {
|
|
733
|
+
if (this.tenantInfo) {
|
|
734
|
+
throw new Error("Tenant context already set for this request");
|
|
735
|
+
}
|
|
736
|
+
this.tenantInfo = tenantInfo;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Get tenant information for this request/message
|
|
740
|
+
*
|
|
741
|
+
* @returns Tenant information
|
|
742
|
+
* @throws UnauthorizedException if tenant context hasn't been set
|
|
743
|
+
*/
|
|
744
|
+
getTenant() {
|
|
745
|
+
if (!this.tenantInfo) {
|
|
746
|
+
throw new import_common6.UnauthorizedException("Tenant context not set");
|
|
747
|
+
}
|
|
748
|
+
return this.tenantInfo;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Check if tenant context has been set
|
|
752
|
+
*
|
|
753
|
+
* @returns true if tenant context is available
|
|
754
|
+
*/
|
|
755
|
+
hasTenant() {
|
|
756
|
+
return this.tenantInfo !== null;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Clear tenant context
|
|
760
|
+
*
|
|
761
|
+
* This is useful for cleanup in RabbitMQ message handlers
|
|
762
|
+
* after the message has been processed.
|
|
763
|
+
*
|
|
764
|
+
* HTTP requests don't need manual cleanup as the service
|
|
765
|
+
* instance is destroyed when the request ends.
|
|
766
|
+
*/
|
|
767
|
+
clearTenant() {
|
|
768
|
+
this.tenantInfo = null;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Get tenant ID safely (returns null if not set)
|
|
772
|
+
*
|
|
773
|
+
* @returns Tenant ID or null
|
|
774
|
+
*/
|
|
775
|
+
getTenantIdSafe() {
|
|
776
|
+
return this.tenantInfo?.id ?? null;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Get tenant subdomain safely (returns null if not set)
|
|
780
|
+
*
|
|
781
|
+
* @returns Tenant subdomain or null
|
|
782
|
+
*/
|
|
783
|
+
getTenantSubdomainSafe() {
|
|
784
|
+
return this.tenantInfo?.subdomain ?? null;
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
TenantContextService = _ts_decorate6([
|
|
788
|
+
(0, import_common6.Injectable)({
|
|
789
|
+
scope: import_common6.Scope.REQUEST
|
|
790
|
+
})
|
|
791
|
+
], TenantContextService);
|
|
792
|
+
|
|
793
|
+
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
794
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
795
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
796
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
797
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
798
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
799
|
+
}
|
|
800
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
801
|
+
function _ts_metadata4(k, v) {
|
|
802
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
803
|
+
}
|
|
804
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
805
|
+
var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
|
|
806
|
+
static {
|
|
807
|
+
__name(this, "MessageTenantContextInterceptor");
|
|
808
|
+
}
|
|
809
|
+
tenantContext;
|
|
810
|
+
logger = new import_common7.Logger(_MessageTenantContextInterceptor.name);
|
|
811
|
+
constructor(tenantContext) {
|
|
812
|
+
this.tenantContext = tenantContext;
|
|
813
|
+
}
|
|
814
|
+
intercept(context, next) {
|
|
815
|
+
const contextType = context.getType();
|
|
816
|
+
if (contextType === "rpc") {
|
|
817
|
+
const rpcContext = context.switchToRpc();
|
|
818
|
+
const payload = rpcContext.getData();
|
|
819
|
+
if (payload && payload.tenant) {
|
|
820
|
+
const tenant = payload.tenant;
|
|
821
|
+
this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
|
|
822
|
+
try {
|
|
823
|
+
this.tenantContext.setTenant(tenant);
|
|
824
|
+
this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);
|
|
825
|
+
} catch (error) {
|
|
826
|
+
this.logger.error("Failed to set tenant context from message", error);
|
|
827
|
+
}
|
|
828
|
+
} else {
|
|
829
|
+
this.logger.warn("Message payload missing tenant information");
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return next.handle().pipe((0, import_operators.tap)({
|
|
833
|
+
next: /* @__PURE__ */ __name(() => {
|
|
834
|
+
this.cleanupContext();
|
|
835
|
+
}, "next"),
|
|
836
|
+
error: /* @__PURE__ */ __name(() => {
|
|
837
|
+
this.cleanupContext();
|
|
838
|
+
}, "error"),
|
|
839
|
+
complete: /* @__PURE__ */ __name(() => {
|
|
840
|
+
this.cleanupContext();
|
|
841
|
+
}, "complete")
|
|
842
|
+
}));
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Clean up tenant context after message is processed
|
|
846
|
+
*/
|
|
847
|
+
cleanupContext() {
|
|
848
|
+
if (this.tenantContext.hasTenant()) {
|
|
849
|
+
const tenant = this.tenantContext.getTenantIdSafe();
|
|
850
|
+
this.tenantContext.clearTenant();
|
|
851
|
+
this.logger.debug(`Cleaned up tenant context: ${tenant}`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
MessageTenantContextInterceptor = _ts_decorate7([
|
|
856
|
+
(0, import_common7.Injectable)({
|
|
857
|
+
scope: import_common7.Scope.REQUEST
|
|
858
|
+
}),
|
|
859
|
+
_ts_metadata4("design:type", Function),
|
|
860
|
+
_ts_metadata4("design:paramtypes", [
|
|
861
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService
|
|
862
|
+
])
|
|
863
|
+
], MessageTenantContextInterceptor);
|
|
864
|
+
|
|
865
|
+
// src/database/interceptors/tenant-context.interceptor.ts
|
|
866
|
+
var import_common8 = require("@nestjs/common");
|
|
867
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
868
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
869
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
870
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
871
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
872
|
+
}
|
|
873
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
874
|
+
function _ts_metadata5(k, v) {
|
|
875
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
876
|
+
}
|
|
877
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
878
|
+
var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
879
|
+
static {
|
|
880
|
+
__name(this, "TenantContextInterceptor");
|
|
881
|
+
}
|
|
882
|
+
tenantContext;
|
|
883
|
+
primaryDatabase;
|
|
884
|
+
requestService;
|
|
885
|
+
logger = new import_common8.Logger(_TenantContextInterceptor.name);
|
|
886
|
+
constructor(tenantContext, primaryDatabase, requestService) {
|
|
887
|
+
this.tenantContext = tenantContext;
|
|
888
|
+
this.primaryDatabase = primaryDatabase;
|
|
889
|
+
this.requestService = requestService;
|
|
890
|
+
}
|
|
891
|
+
async intercept(context, next) {
|
|
892
|
+
const request = context.switchToHttp().getRequest();
|
|
893
|
+
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
894
|
+
try {
|
|
895
|
+
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
896
|
+
if (!tenantIdentifier) {
|
|
897
|
+
throw new import_common8.UnauthorizedException("Tenant identifier not found in request");
|
|
898
|
+
}
|
|
899
|
+
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
900
|
+
if (tenantIdentifier === "cloud") {
|
|
901
|
+
this.logger.log("Cloud platform access detected, skipping tenant context setup");
|
|
902
|
+
return next.handle();
|
|
903
|
+
}
|
|
904
|
+
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
905
|
+
if (!tenantInfo) {
|
|
906
|
+
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
907
|
+
throw new import_common8.UnauthorizedException("Invalid tenant");
|
|
908
|
+
}
|
|
909
|
+
if (tenantInfo.status !== "ACTIVE") {
|
|
910
|
+
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
911
|
+
throw new import_common8.UnauthorizedException(`Tenant is ${tenantInfo.status}`);
|
|
912
|
+
}
|
|
913
|
+
this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
914
|
+
this.tenantContext.setTenant(tenantInfo);
|
|
915
|
+
request.tenant = tenantInfo;
|
|
916
|
+
this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);
|
|
917
|
+
} catch (error) {
|
|
918
|
+
this.logger.error("Failed to set tenant context", error);
|
|
919
|
+
throw error;
|
|
920
|
+
}
|
|
921
|
+
return next.handle();
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
TenantContextInterceptor = _ts_decorate8([
|
|
925
|
+
(0, import_common8.Injectable)({
|
|
926
|
+
scope: import_common8.Scope.REQUEST
|
|
927
|
+
}),
|
|
928
|
+
_ts_metadata5("design:type", Function),
|
|
929
|
+
_ts_metadata5("design:paramtypes", [
|
|
930
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
931
|
+
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
932
|
+
typeof RequestService === "undefined" ? Object : RequestService
|
|
933
|
+
])
|
|
934
|
+
], TenantContextInterceptor);
|
|
935
|
+
|
|
936
|
+
// src/database/services/tenant-database.service.ts
|
|
937
|
+
var import_common9 = require("@nestjs/common");
|
|
938
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
939
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
940
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
941
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
942
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
943
|
+
}
|
|
944
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
945
|
+
function _ts_metadata6(k, v) {
|
|
946
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
947
|
+
}
|
|
948
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
949
|
+
function _ts_param3(paramIndex, decorator) {
|
|
950
|
+
return function(target, key) {
|
|
951
|
+
decorator(target, key, paramIndex);
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
__name(_ts_param3, "_ts_param");
|
|
955
|
+
var TenantDatabaseService = class _TenantDatabaseService {
|
|
956
|
+
static {
|
|
957
|
+
__name(this, "TenantDatabaseService");
|
|
958
|
+
}
|
|
959
|
+
options;
|
|
960
|
+
tenantContext;
|
|
961
|
+
logger = new import_common9.Logger(_TenantDatabaseService.name);
|
|
962
|
+
/** Connection pool: Map<cacheKey, DbClient> */
|
|
963
|
+
clients = /* @__PURE__ */ new Map();
|
|
964
|
+
/** Track last usage time for idle connection cleanup */
|
|
965
|
+
clientLastUsed = /* @__PURE__ */ new Map();
|
|
966
|
+
/** Cleanup interval timer */
|
|
967
|
+
cleanupInterval;
|
|
968
|
+
constructor(options, tenantContext) {
|
|
969
|
+
this.options = options;
|
|
970
|
+
this.tenantContext = tenantContext;
|
|
971
|
+
this.startConnectionCleaner();
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Get tenant-scoped database client for the current request/message
|
|
975
|
+
*
|
|
976
|
+
* This method:
|
|
977
|
+
* 1. Gets tenant info from TenantContextService
|
|
978
|
+
* 2. Builds a connection URL based on tenant type
|
|
979
|
+
* 3. Returns cached client if exists, otherwise creates new one
|
|
980
|
+
*
|
|
981
|
+
* @returns Promise<Database client instance>
|
|
982
|
+
* @throws UnauthorizedException if tenant context not set
|
|
983
|
+
* @throws InternalServerErrorException if connection fails
|
|
984
|
+
*
|
|
985
|
+
* @example
|
|
986
|
+
* const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
|
|
987
|
+
* const users = await dbClient.user.findMany();
|
|
988
|
+
*/
|
|
989
|
+
async getDbClient() {
|
|
990
|
+
const tenant = this.tenantContext.getTenant();
|
|
991
|
+
const cacheKey = this.buildCacheKey(tenant);
|
|
992
|
+
if (this.clients.has(cacheKey)) {
|
|
993
|
+
this.clientLastUsed.set(cacheKey, Date.now());
|
|
994
|
+
this.logger.debug(`Reusing cached connection: ${cacheKey}`);
|
|
995
|
+
return this.clients.get(cacheKey);
|
|
996
|
+
}
|
|
997
|
+
this.logger.log(`Creating new database connection: ${cacheKey}`);
|
|
998
|
+
const client = await this.createDbClient(tenant);
|
|
999
|
+
this.clients.set(cacheKey, client);
|
|
1000
|
+
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1001
|
+
return client;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Create a new database client for the given tenant
|
|
1005
|
+
*/
|
|
1006
|
+
async createDbClient(tenant) {
|
|
1007
|
+
try {
|
|
1008
|
+
const databaseUrl = this.buildTenantDbUrl(tenant);
|
|
1009
|
+
const PrismaClient = await this.options.prismaClientConstructor;
|
|
1010
|
+
const client = new PrismaClient({
|
|
1011
|
+
datasources: {
|
|
1012
|
+
db: {
|
|
1013
|
+
url: databaseUrl
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
log: [
|
|
1017
|
+
"error",
|
|
1018
|
+
"warn"
|
|
1019
|
+
]
|
|
1020
|
+
});
|
|
1021
|
+
await client.$connect();
|
|
1022
|
+
this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
|
|
1023
|
+
return client;
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
|
|
1026
|
+
throw new import_common9.InternalServerErrorException("Failed to connect to tenant database");
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Build connection URL for enterprise tenant (dedicated database)
|
|
1031
|
+
*/
|
|
1032
|
+
buildTenantDbUrl(tenant) {
|
|
1033
|
+
const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
|
|
1034
|
+
if (!databaseHost || !databaseName || !databaseUsername) {
|
|
1035
|
+
throw new Error(`Enterprise tenant ${tenant.subdomain} missing database configuration`);
|
|
1036
|
+
}
|
|
1037
|
+
const port = databasePort || 5432;
|
|
1038
|
+
const sslMode = databaseSslMode || "require";
|
|
1039
|
+
const connectionUrl = `postgresql://${databaseUsername}:${databasePassword}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
|
|
1040
|
+
this.logger.debug(`Enterprise connection URL: ${this.maskPassword(connectionUrl)}`);
|
|
1041
|
+
return connectionUrl;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Build cache key for connection pooling
|
|
1045
|
+
*/
|
|
1046
|
+
buildCacheKey(tenant) {
|
|
1047
|
+
return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Start periodic cleanup of idle connections
|
|
1051
|
+
*/
|
|
1052
|
+
startConnectionCleaner() {
|
|
1053
|
+
const interval = this.options.connectionCacheTTL || 3e5;
|
|
1054
|
+
this.cleanupInterval = setInterval(() => {
|
|
1055
|
+
this.cleanupIdleConnections();
|
|
1056
|
+
}, interval);
|
|
1057
|
+
this.logger.log(`Connection cleanup scheduled every ${interval / 1e3} seconds`);
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Clean up idle connections that haven't been used recently
|
|
1061
|
+
*/
|
|
1062
|
+
cleanupIdleConnections() {
|
|
1063
|
+
const now = Date.now();
|
|
1064
|
+
const maxIdle = this.options.connectionCacheTTL || 3e5;
|
|
1065
|
+
let cleaned = 0;
|
|
1066
|
+
for (const [key, lastUsed] of this.clientLastUsed.entries()) {
|
|
1067
|
+
if (now - lastUsed > maxIdle) {
|
|
1068
|
+
const client = this.clients.get(key);
|
|
1069
|
+
if (client) {
|
|
1070
|
+
client.$disconnect().then(() => {
|
|
1071
|
+
this.logger.debug(`Cleaned up idle connection: ${key}`);
|
|
1072
|
+
}).catch((error) => {
|
|
1073
|
+
this.logger.error(`Error disconnecting idle client: ${key}`, error);
|
|
1074
|
+
});
|
|
1075
|
+
this.clients.delete(key);
|
|
1076
|
+
this.clientLastUsed.delete(key);
|
|
1077
|
+
cleaned++;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (cleaned > 0) {
|
|
1082
|
+
this.logger.log(`Cleaned up ${cleaned} idle connections`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Get current connection pool statistics
|
|
1087
|
+
*/
|
|
1088
|
+
getPoolStats() {
|
|
1089
|
+
return {
|
|
1090
|
+
activeConnections: this.clients.size,
|
|
1091
|
+
tenants: Array.from(this.clients.keys())
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Mask password in connection URL for logging
|
|
1096
|
+
*/
|
|
1097
|
+
maskPassword(url) {
|
|
1098
|
+
return url.replace(/:([^@]+)@/, ":****@");
|
|
1099
|
+
}
|
|
1100
|
+
async onModuleDestroy() {
|
|
1101
|
+
if (this.cleanupInterval) {
|
|
1102
|
+
clearInterval(this.cleanupInterval);
|
|
1103
|
+
}
|
|
1104
|
+
this.logger.log(`Disconnecting ${this.clients.size} database connections`);
|
|
1105
|
+
const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, client]) => {
|
|
1106
|
+
try {
|
|
1107
|
+
await client.$disconnect();
|
|
1108
|
+
this.logger.debug(`Disconnected: ${key}`);
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
this.logger.error(`Error disconnecting client: ${key}`, error);
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
await Promise.all(disconnectPromises);
|
|
1114
|
+
this.logger.log("All database connections closed");
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
TenantDatabaseService = _ts_decorate9([
|
|
1118
|
+
(0, import_common9.Injectable)(),
|
|
1119
|
+
_ts_param3(0, (0, import_common9.Inject)(DATABASE_MODULE_OPTIONS)),
|
|
1120
|
+
_ts_metadata6("design:type", Function),
|
|
1121
|
+
_ts_metadata6("design:paramtypes", [
|
|
1122
|
+
typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions,
|
|
1123
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService
|
|
1124
|
+
])
|
|
1125
|
+
], TenantDatabaseService);
|
|
1126
|
+
|
|
1127
|
+
// src/database/database.module.ts
|
|
1128
|
+
function _ts_decorate10(decorators, target, key, desc) {
|
|
1129
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1130
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1131
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1132
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1133
|
+
}
|
|
1134
|
+
__name(_ts_decorate10, "_ts_decorate");
|
|
1135
|
+
var DatabaseModule = class _DatabaseModule {
|
|
1136
|
+
static {
|
|
1137
|
+
__name(this, "DatabaseModule");
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
|
|
1141
|
+
*
|
|
1142
|
+
* This mode is for API Gateways that handle HTTP requests:
|
|
1143
|
+
* - Automatically registers TenantContextInterceptor
|
|
1144
|
+
* - Extracts tenant from subdomain or x-tenant-id header
|
|
1145
|
+
* - Queries primary database for tenant configuration
|
|
1146
|
+
* - Provides PrimaryDatabaseService for tenant lookup
|
|
1147
|
+
*
|
|
1148
|
+
* @param options Async configuration options
|
|
1149
|
+
* @returns Dynamic module configuration with HTTP interceptor
|
|
1150
|
+
*
|
|
1151
|
+
* @example
|
|
1152
|
+
* DatabaseModule.forServer({
|
|
1153
|
+
* inject: [ConfigService],
|
|
1154
|
+
* useFactory: (config: ConfigService) => ({
|
|
1155
|
+
* primaryDb: {
|
|
1156
|
+
* host: config.get('PRIMARY_DB_HOST'),
|
|
1157
|
+
* port: config.get('PRIMARY_DB_PORT'),
|
|
1158
|
+
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
1159
|
+
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
1160
|
+
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
1161
|
+
* },
|
|
1162
|
+
* prismaClientConstructor: PrismaClient,
|
|
1163
|
+
* }),
|
|
1164
|
+
* })
|
|
1165
|
+
*/
|
|
1166
|
+
static forServer(options) {
|
|
1167
|
+
return this.createDynamicModule(options, "gateway");
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
1171
|
+
*
|
|
1172
|
+
* This mode is for microservices that process messages from queues:
|
|
1173
|
+
* - Automatically registers MessageTenantContextInterceptor
|
|
1174
|
+
* - Extracts tenant from RabbitMQ message patterns
|
|
1175
|
+
* - No primary database needed (tenant comes from message context)
|
|
1176
|
+
*
|
|
1177
|
+
* @param options Async configuration options
|
|
1178
|
+
* @returns Dynamic module configuration with message interceptor
|
|
1179
|
+
*
|
|
1180
|
+
* @example
|
|
1181
|
+
* DatabaseModule.forMicroservice({
|
|
1182
|
+
* inject: [ConfigService],
|
|
1183
|
+
* useFactory: (config: ConfigService) => ({
|
|
1184
|
+
* prismaClientConstructor: PrismaClient,
|
|
1185
|
+
* }),
|
|
1186
|
+
* })
|
|
1187
|
+
*/
|
|
1188
|
+
static forMicroservice(options) {
|
|
1189
|
+
return this.createDynamicModule(options, "microservice");
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Internal helper to create dynamic module with conditional interceptor registration
|
|
1193
|
+
*
|
|
1194
|
+
* @param options Configuration options
|
|
1195
|
+
* @param mode Mode of operation (gateway or microservice)
|
|
1196
|
+
* @returns Dynamic module configuration
|
|
1197
|
+
*/
|
|
1198
|
+
static createDynamicModule(options, mode) {
|
|
1199
|
+
const asyncProvider = {
|
|
1200
|
+
provide: DATABASE_MODULE_OPTIONS,
|
|
1201
|
+
useFactory: options.useFactory,
|
|
1202
|
+
inject: options.inject || []
|
|
1203
|
+
};
|
|
1204
|
+
const providers = [
|
|
1205
|
+
asyncProvider,
|
|
1206
|
+
TenantContextService,
|
|
1207
|
+
PrimaryDatabaseService,
|
|
1208
|
+
TenantDatabaseService
|
|
1209
|
+
];
|
|
1210
|
+
if (mode === "gateway") {
|
|
1211
|
+
providers.push({
|
|
1212
|
+
provide: import_core4.APP_INTERCEPTOR,
|
|
1213
|
+
useClass: TenantContextInterceptor
|
|
1214
|
+
});
|
|
1215
|
+
} else {
|
|
1216
|
+
providers.push({
|
|
1217
|
+
provide: import_core4.APP_INTERCEPTOR,
|
|
1218
|
+
useClass: MessageTenantContextInterceptor
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
return {
|
|
1222
|
+
module: _DatabaseModule,
|
|
1223
|
+
imports: [
|
|
1224
|
+
RequestModule
|
|
1225
|
+
],
|
|
1226
|
+
providers,
|
|
1227
|
+
exports: [
|
|
1228
|
+
TenantDatabaseService,
|
|
1229
|
+
TenantContextService,
|
|
1230
|
+
PrimaryDatabaseService,
|
|
1231
|
+
asyncProvider
|
|
1232
|
+
]
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
DatabaseModule = _ts_decorate10([
|
|
1237
|
+
(0, import_common10.Global)(),
|
|
1238
|
+
(0, import_common10.Module)({})
|
|
1239
|
+
], DatabaseModule);
|
|
1240
|
+
|
|
1241
|
+
// src/database/decorators/tenant.decorator.ts
|
|
1242
|
+
var import_common11 = require("@nestjs/common");
|
|
1243
|
+
var Tenant = (0, import_common11.createParamDecorator)((data, ctx) => {
|
|
1244
|
+
const request = ctx.switchToHttp().getRequest();
|
|
1245
|
+
const tenantContext = request.app?.get?.(TenantContextService);
|
|
1246
|
+
if (!tenantContext) {
|
|
1247
|
+
throw new Error("TenantContextService not found.");
|
|
1248
|
+
}
|
|
1249
|
+
return tenantContext.getTenant();
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
// src/auth/decorators/onboarding.decorator.ts
|
|
1253
|
+
var import_common12 = require("@nestjs/common");
|
|
1254
|
+
var Onboarding = /* @__PURE__ */ __name(() => (0, import_common12.SetMetadata)("isOnboarding", true), "Onboarding");
|
|
1255
|
+
|
|
1256
|
+
// src/auth/decorators/public.decorator.ts
|
|
1257
|
+
var import_common13 = require("@nestjs/common");
|
|
1258
|
+
var Public = /* @__PURE__ */ __name(() => (0, import_common13.SetMetadata)("isPublic", true), "Public");
|
|
30
1259
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31
1260
|
0 && (module.exports = {
|
|
32
|
-
|
|
1261
|
+
AuthConfigModule,
|
|
1262
|
+
DatabaseModule,
|
|
1263
|
+
Onboarding,
|
|
1264
|
+
PrimaryDatabaseService,
|
|
1265
|
+
Public,
|
|
1266
|
+
Tenant,
|
|
1267
|
+
TenantContextService,
|
|
1268
|
+
TenantDatabaseService,
|
|
1269
|
+
VrittiAuthGuard
|
|
33
1270
|
});
|
|
34
1271
|
//# sourceMappingURL=index.cjs.map
|