@vritti/api-sdk 0.0.2 → 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/dist/index.d.ts CHANGED
@@ -1,5 +1,83 @@
1
- import { DynamicModule, OnModuleInit, OnModuleDestroy, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
- import { Observable } from 'rxjs';
1
+ import * as _nestjs_common from '@nestjs/common';
2
+ import { DynamicModule, OnModuleDestroy, OnModuleInit, CanActivate, ExecutionContext } from '@nestjs/common';
3
+ import { ConfigService } from '@nestjs/config';
4
+ import { Reflector } from '@nestjs/core';
5
+ import { JwtService } from '@nestjs/jwt';
6
+ import { FastifyRequest } from 'fastify';
7
+
8
+ /**
9
+ * Global authentication configuration module
10
+ *
11
+ * This module provides:
12
+ * - JWT token verification (JwtModule)
13
+ * - Global authentication guard (VrittiAuthGuard)
14
+ * - Support for @Public and @Onboarding decorators
15
+ *
16
+ * ## Features:
17
+ * - Automatically applies VrittiAuthGuard to all routes
18
+ * - Configures JwtModule with JWT_SECRET from environment
19
+ * - Exports JwtModule for token generation in services
20
+ *
21
+ * ## Usage in Application:
22
+ *
23
+ * @example
24
+ * // In app.module.ts
25
+ * @Module({
26
+ * imports: [
27
+ * ConfigModule.forRoot({ isGlobal: true }),
28
+ *
29
+ * // Auth configuration (global guard + JWT)
30
+ * AuthConfigModule.forRootAsync(),
31
+ *
32
+ * // Database configuration (Gateway mode)
33
+ * DatabaseModule.forServer({
34
+ * useFactory: (config: ConfigService) => ({
35
+ * primaryDb: {
36
+ * host: config.get('PRIMARY_DB_HOST'),
37
+ * // ... other config
38
+ * },
39
+ * prismaClientConstructor: PrismaClient,
40
+ * }),
41
+ * inject: [ConfigService],
42
+ * }),
43
+ * ],
44
+ * })
45
+ * export class AppModule {}
46
+ *
47
+ * ## Environment Variables Required:
48
+ * - JWT_SECRET: Secret key to verify access tokens (required)
49
+ * - JWT_REFRESH_SECRET: Secret key for refresh tokens (optional, falls back to JWT_SECRET)
50
+ *
51
+ * ## Bypass Authentication:
52
+ *
53
+ * @example
54
+ * // Skip authentication on specific endpoints
55
+ * @Public()
56
+ * @Post('auth/login')
57
+ * async login() { ... }
58
+ *
59
+ * @example
60
+ * // Onboarding endpoints (only accept onboarding tokens)
61
+ * @Onboarding()
62
+ * @Post('onboarding/verify-email')
63
+ * async verifyEmail(@Request() req) {
64
+ * const userId = req.user.id; // Available from guard
65
+ * ...
66
+ * }
67
+ */
68
+ declare class AuthConfigModule {
69
+ /**
70
+ * Register the auth module with async configuration
71
+ *
72
+ * This method:
73
+ * 1. Configures JwtModule with JWT_SECRET from ConfigService
74
+ * 2. Provides VrittiAuthGuard globally (applies to all routes)
75
+ * 3. Exports JwtModule for use in other modules (e.g., for signing tokens)
76
+ *
77
+ * @returns Dynamic module configuration
78
+ */
79
+ static forRootAsync(): DynamicModule;
80
+ }
3
81
 
4
82
  /**
5
83
  * Primary database connection configuration
@@ -66,19 +144,22 @@ interface DatabaseModuleOptions {
66
144
  /**
67
145
  * Tenant configuration stored in cloud database
68
146
  * This is the shape of data returned from the tenant registry
147
+ *
148
+ * Note: Database configuration is now stored in a separate TenantDatabaseConfig table
149
+ * but is flattened into this interface for convenience.
69
150
  */
70
151
  interface TenantInfo {
71
152
  /** Unique tenant identifier */
72
153
  id: string;
73
154
  /** Human-readable tenant slug */
74
155
  subdomain: string;
75
- /** Tenant type */
76
- type: 'SHARED' | 'DEDIACTED';
156
+ /** Tenant type - SHARED or DEDICATED */
157
+ type: 'SHARED' | 'DEDICATED';
77
158
  /** Tenant status */
78
159
  status: string;
79
- /** For CLOUD tenants: schema name */
160
+ /** For SHARED tenants: schema name within the shared database */
80
161
  schemaName?: string;
81
- /** For ENTERPRISE tenants: database configuration */
162
+ /** For DEDICATED tenants: database configuration (from TenantDatabaseConfig table) */
82
163
  databaseName?: string;
83
164
  databaseHost?: string;
84
165
  databasePort?: number;
@@ -98,20 +179,24 @@ interface TenantInfo {
98
179
  * - Support for both gateway and microservice modes
99
180
  *
100
181
  * ## Gateway Mode (API Gateway)
182
+ * - Use DatabaseModule.forServer() method
101
183
  * - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
102
184
  * - Provide primaryDb configuration and prismaClientConstructor
103
185
  * - Automatically queries primary DB for tenant config
104
- * - Attaches TenantContextInterceptor globally
186
+ * - Automatically registers TenantContextInterceptor globally
187
+ * - No manual interceptor registration needed
105
188
  *
106
- * ## Microservice Mode
189
+ * ## Microservice Mode (RabbitMQ Workers)
190
+ * - Use DatabaseModule.forMicroservice() method
107
191
  * - Only provide prismaClientConstructor
108
192
  * - Tenant context comes from RabbitMQ messages
109
- * - Use MessageTenantContextInterceptor manually
193
+ * - Automatically registers MessageTenantContextInterceptor globally
194
+ * - No manual interceptor registration needed
110
195
  *
111
196
  * @example
112
197
  * // Gateway configuration
113
- * DatabaseModule.forRootAsync({
114
- * imports: [ConfigModule],
198
+ * DatabaseModule.forServer({
199
+ * inject: [ConfigService],
115
200
  * useFactory: (config: ConfigService) => ({
116
201
  * primaryDb: {
117
202
  * host: config.get('PRIMARY_DB_HOST'),
@@ -122,127 +207,80 @@ interface TenantInfo {
122
207
  * },
123
208
  * prismaClientConstructor: PrismaClient,
124
209
  * }),
125
- * inject: [ConfigService],
126
210
  * })
127
211
  *
128
212
  * @example
129
213
  * // Microservice configuration
130
- * DatabaseModule.forRoot({
131
- * prismaClientConstructor: PrismaClient,
214
+ * DatabaseModule.forMicroservice({
215
+ * inject: [ConfigService],
216
+ * useFactory: (config: ConfigService) => ({
217
+ * prismaClientConstructor: PrismaClient,
218
+ * }),
132
219
  * })
133
220
  */
134
221
  declare class DatabaseModule {
135
222
  /**
136
- * Synchronous configuration
137
- *
138
- * @param options Module configuration options
139
- * @returns Dynamic module configuration
140
- */
141
- static forRoot(options: DatabaseModuleOptions): DynamicModule;
142
- /**
143
- * Asynchronous configuration (recommended)
223
+ * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
144
224
  *
145
- * Allows injecting ConfigService or other dependencies
225
+ * This mode is for API Gateways that handle HTTP requests:
226
+ * - Automatically registers TenantContextInterceptor
227
+ * - Extracts tenant from subdomain or x-tenant-id header
228
+ * - Queries primary database for tenant configuration
229
+ * - Provides PrimaryDatabaseService for tenant lookup
146
230
  *
147
231
  * @param options Async configuration options
148
- * @returns Dynamic module configuration
232
+ * @returns Dynamic module configuration with HTTP interceptor
149
233
  *
150
234
  * @example
151
- * DatabaseModule.forRootAsync({
152
- * imports: [ConfigModule],
153
- * useFactory: async (config: ConfigService) => ({
154
- * cloudDatabaseUrl: config.get('CLOUD_DATABASE_URL'),
235
+ * DatabaseModule.forServer({
236
+ * inject: [ConfigService],
237
+ * useFactory: (config: ConfigService) => ({
238
+ * primaryDb: {
239
+ * host: config.get('PRIMARY_DB_HOST'),
240
+ * port: config.get('PRIMARY_DB_PORT'),
241
+ * username: config.get('PRIMARY_DB_USERNAME'),
242
+ * password: config.get('PRIMARY_DB_PASSWORD'),
243
+ * database: config.get('PRIMARY_DB_DATABASE'),
244
+ * },
155
245
  * prismaClientConstructor: PrismaClient,
156
- * tenantResolver: 'subdomain',
157
246
  * }),
158
- * inject: [ConfigService],
159
247
  * })
160
248
  */
161
- static forRootAsync(options: {
249
+ static forServer(options: {
162
250
  useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
163
251
  inject?: any[];
164
252
  }): DynamicModule;
165
- }
166
-
167
- /**
168
- * Service responsible for querying the primary database to resolve tenant configurations
169
- *
170
- * This service:
171
- * - Connects to the primary database (tenant registry)
172
- * - Queries tenant metadata (database location, credentials, etc.)
173
- * - Caches tenant configs in memory to reduce database load
174
- * - Only used in GATEWAY MODE (microservices receive tenant config from messages)
175
- *
176
- * @example
177
- * // In API Gateway
178
- * const config = await primaryDatabase.getTenantConfig('acme');
179
- * // Returns: { id, slug, type, databaseHost, databaseName, ... }
180
- */
181
- declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
182
- private readonly options;
183
- private readonly logger;
184
- /** Primary database client for querying tenant registry */
185
- private primaryDbClient;
186
- /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
187
- private readonly tenantConfigCache;
188
- /** Cache TTL in milliseconds */
189
- private readonly cacheTTL;
190
- constructor(options: DatabaseModuleOptions);
191
- onModuleInit(): Promise<void>;
192
- /**
193
- * Initialize connection to primary database
194
- */
195
- private initializePrimaryDbClient;
196
- /**
197
- * Build connection URL from primary database properties
198
- */
199
- private buildPrimaryDbUrl;
200
- /**
201
- * Mask password in connection URL for logging
202
- */
203
- private maskPassword;
204
- /**
205
- * Get tenant configuration by identifier (ID or slug)
206
- *
207
- * @param tenantIdentifier Tenant ID or slug
208
- * @returns Tenant configuration or null if not found
209
- */
210
- getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
211
- /**
212
- * Cache tenant information with TTL
213
- */
214
- private cacheInfo;
215
253
  /**
216
- * Clear cached tenant information
254
+ * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
217
255
  *
218
- * Useful when tenant settings are updated and cache needs to be invalidated
256
+ * This mode is for microservices that process messages from queues:
257
+ * - Automatically registers MessageTenantContextInterceptor
258
+ * - Extracts tenant from RabbitMQ message patterns
259
+ * - No primary database needed (tenant comes from message context)
219
260
  *
220
- * @param tenantIdentifier Tenant ID or slug
221
- */
222
- clearTenantCache(tenantIdentifier: string): void;
223
- /**
224
- * Clear all cached tenant configurations
225
- */
226
- clearAllCaches(): void;
227
- /**
228
- * Get primary database client for direct database access
229
- *
230
- * This is useful for platform admin operations (creating tenants, billing, etc.)
261
+ * @param options Async configuration options
262
+ * @returns Dynamic module configuration with message interceptor
231
263
  *
232
- * @returns Primary database client instance
233
- * @throws Error if primary database client is not initialized
264
+ * @example
265
+ * DatabaseModule.forMicroservice({
266
+ * inject: [ConfigService],
267
+ * useFactory: (config: ConfigService) => ({
268
+ * prismaClientConstructor: PrismaClient,
269
+ * }),
270
+ * })
234
271
  */
235
- getPrimaryDbClient<T = any>(): T;
272
+ static forMicroservice(options: {
273
+ useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
274
+ inject?: any[];
275
+ }): DynamicModule;
236
276
  /**
237
- * Decrypt database credentials
277
+ * Internal helper to create dynamic module with conditional interceptor registration
238
278
  *
239
- * Override this method to implement your encryption strategy
240
- *
241
- * @param encrypted Encrypted value
242
- * @returns Decrypted value
279
+ * @param options Configuration options
280
+ * @param mode Mode of operation (gateway or microservice)
281
+ * @returns Dynamic module configuration
243
282
  */
244
- private decrypt;
245
- onModuleDestroy(): Promise<void>;
283
+ private static createDynamicModule;
246
284
  }
247
285
 
248
286
  /**
@@ -390,83 +428,209 @@ declare class TenantDatabaseService implements OnModuleDestroy {
390
428
  }
391
429
 
392
430
  /**
393
- * Interceptor that extracts tenant context from RabbitMQ messages (Microservice Mode)
394
- *
395
- * This interceptor:
396
- * 1. Extracts tenant info from RabbitMQ message payload
397
- * 2. Sets it in REQUEST-SCOPED TenantContextService
398
- * 3. Cleans up after message is processed
399
- *
400
- * Expected message format:
401
- * {
402
- * dto: { ... },
403
- * tenant: {
404
- * tenantId: 'abc-123',
405
- * tenantSlug: 'acme',
406
- * tenantType: 'ENTERPRISE',
407
- * databaseHost: 'enterprise-1.aws.com',
408
- * databaseName: 'acme_db',
409
- * ...
410
- * }
411
- * }
431
+ * Service responsible for querying the primary database to resolve tenant configurations
432
+ *
433
+ * This service:
434
+ * - Connects to the primary database (tenant registry)
435
+ * - Queries tenant metadata (database location, credentials, etc.)
436
+ * - Caches tenant configs in memory to reduce database load
437
+ * - Only used in GATEWAY MODE (microservices receive tenant config from messages)
412
438
  *
413
439
  * @example
414
- * // In microservice module
415
- * {
416
- * provide: APP_INTERCEPTOR,
417
- * useClass: MessageTenantContextInterceptor,
418
- * }
440
+ * // In API Gateway
441
+ * const config = await primaryDatabase.getTenantConfig('acme');
442
+ * // Returns: { id, slug, type, databaseHost, databaseName, ... }
419
443
  */
420
- declare class MessageTenantContextInterceptor implements NestInterceptor {
421
- private readonly tenantContext;
444
+ declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
445
+ private readonly options;
422
446
  private readonly logger;
423
- constructor(tenantContext: TenantContextService);
424
- intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
447
+ /** Primary database client for querying tenant registry */
448
+ private primaryDbClient;
449
+ /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
450
+ private readonly tenantConfigCache;
451
+ /** Cache TTL in milliseconds */
452
+ private readonly cacheTTL;
453
+ constructor(options: DatabaseModuleOptions);
454
+ onModuleInit(): Promise<void>;
455
+ /**
456
+ * Initialize connection to primary database
457
+ */
458
+ private initializePrimaryDbClient;
459
+ /**
460
+ * Build connection URL from primary database properties
461
+ */
462
+ private buildPrimaryDbUrl;
463
+ /**
464
+ * Mask password in connection URL for logging
465
+ */
466
+ private maskPassword;
467
+ /**
468
+ * Get tenant configuration by identifier (ID or slug)
469
+ *
470
+ * @param tenantIdentifier Tenant ID or slug
471
+ * @returns Tenant configuration or null if not found
472
+ */
473
+ getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
474
+ /**
475
+ * Cache tenant information with TTL
476
+ */
477
+ private cacheInfo;
478
+ /**
479
+ * Clear cached tenant information
480
+ *
481
+ * Useful when tenant settings are updated and cache needs to be invalidated
482
+ *
483
+ * @param tenantIdentifier Tenant ID or slug
484
+ */
485
+ clearTenantCache(tenantIdentifier: string): void;
486
+ /**
487
+ * Clear all cached tenant configurations
488
+ */
489
+ clearAllCaches(): void;
490
+ /**
491
+ * Get primary database client for direct database access
492
+ *
493
+ * This is useful for platform admin operations (creating tenants, billing, etc.)
494
+ *
495
+ * @returns Primary database client instance
496
+ * @throws Error if primary database client is not initialized
497
+ */
498
+ getPrimaryDbClient<T = any>(): T;
499
+ /**
500
+ * Decrypt database credentials
501
+ *
502
+ * Override this method to implement your encryption strategy
503
+ *
504
+ * @param encrypted Encrypted value
505
+ * @returns Decrypted value
506
+ */
507
+ private decrypt;
508
+ onModuleDestroy(): Promise<void>;
509
+ }
510
+
511
+ declare class RequestService {
512
+ private readonly request;
513
+ constructor(request: FastifyRequest);
514
+ /**
515
+ * Extract tenant identifier from request headers
516
+ * Priority: x-tenant-id > x-subdomain
517
+ * @returns Tenant identifier or null if not found
518
+ */
519
+ getTenantIdentifier(): string | null;
520
+ /**
521
+ * Extract access token from Authorization header
522
+ * Format: "Bearer <token>"
523
+ * @returns Access token or null if not found
524
+ */
525
+ getAccessToken(): string | null;
526
+ /**
527
+ * Extract refresh token from session-id cookie
528
+ * Cookie name: session-id
529
+ * @returns Refresh token or null if not found
530
+ */
531
+ getRefreshToken(): string | null;
532
+ /**
533
+ * Get a specific header value
534
+ * @param key Header key
535
+ * @returns Header value (string, array, or undefined)
536
+ */
537
+ getHeader(key: string): string | string[] | undefined;
425
538
  /**
426
- * Clean up tenant context after message is processed
539
+ * Get all headers
540
+ * @returns Record of all headers
427
541
  */
428
- private cleanupContext;
542
+ getAllHeaders(): FastifyRequest['headers'];
429
543
  }
430
544
 
431
545
  /**
432
- * Interceptor that extracts tenant context from HTTP requests (Gateway Mode)
546
+ * Vritti Authentication Guard - Validates JWT tokens and tenant context
547
+ *
548
+ * This guard performs comprehensive validation and attaches user data to request.
549
+ *
550
+ * Validation Flow:
551
+ * 1. Checks if endpoint is marked with @Public() decorator → skip all validation
552
+ * 2. Checks if endpoint is marked with @Onboarding() decorator:
553
+ * - Requires token type='onboarding'
554
+ * - Validates JWT signature and expiry only
555
+ * - Skips tenant and refresh token validation
556
+ * - Attaches user data to request.user
557
+ * 3. For regular endpoints (no decorator):
558
+ * - Rejects tokens with type='onboarding'
559
+ * - Validates access token (JWT signature, expiry, nbf)
560
+ * - Validates refresh token from session-id cookie
561
+ * - Validates tenant exists and is ACTIVE
562
+ * - Attaches user data to request.user
563
+ *
564
+ * Token Format:
565
+ * - Access Token: "Authorization: Bearer <jwt_token>"
566
+ * - Refresh Token: "session-id" cookie
433
567
  *
434
- * This interceptor runs BEFORE the controller and:
435
- * 1. Extracts tenant identifier from request (tries subdomain first, then falls back to header)
436
- * 2. Queries primary database for tenant configuration
437
- * 3. Stores tenant info in REQUEST-SCOPED TenantContextService
568
+ * Token Types:
569
+ * - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
570
+ * - type='access': Full access to authenticated endpoints
438
571
  *
439
- * Tenant resolution order:
440
- * - First: Subdomain (e.g., acme.vritti.com 'acme')
441
- * - Fallback: x-tenant-id or x-tenant-slug header
572
+ * Environment Variables Required:
573
+ * - JWT_SECRET: Secret key to verify access tokens (required)
574
+ * - JWT_REFRESH_SECRET: Secret key for refresh tokens (optional, falls back to JWT_SECRET)
442
575
  *
443
- * Only used in API Gateway. Microservices use MessageTenantContextInterceptor instead.
576
+ * Error Responses:
577
+ * - 401: Invalid/expired access token
578
+ * - 401: Invalid/expired refresh token
579
+ * - 401: Tenant not found or inactive
580
+ * - 401: Tenant identifier not found
581
+ * - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
582
+ *
583
+ * @example
584
+ * // Automatically registered by AuthConfigModule.forRootAsync()
585
+ * // No manual registration needed
586
+ * //
587
+ * // Internal registration uses useExisting pattern:
588
+ * // providers: [
589
+ * // VrittiAuthGuard,
590
+ * // {
591
+ * // provide: APP_GUARD,
592
+ * // useExisting: VrittiAuthGuard,
593
+ * // },
594
+ * // ]
595
+ *
596
+ * @example
597
+ * // Bypass guard with @Public() decorator
598
+ * @Public()
599
+ * @Post('auth/login')
600
+ * async login(@Body() dto: LoginDto) { ... }
444
601
  *
445
602
  * @example
446
- * // Request: https://acme.vritti.com/api/users
447
- * // Interceptor extracts "acme" from subdomain, queries primary DB, sets context
603
+ * // Restrict to onboarding tokens with @Onboarding() decorator
604
+ * @Onboarding()
605
+ * @Post('onboarding/verify-email')
606
+ * async verifyEmail(@Request() req) {
607
+ * const userId = req.user.id; // Available from guard
608
+ * ...
609
+ * }
448
610
  */
449
- declare class TenantContextInterceptor implements NestInterceptor {
450
- private readonly tenantContext;
611
+ declare class VrittiAuthGuard implements CanActivate {
612
+ private readonly reflector;
613
+ private readonly configService;
614
+ private readonly jwtService;
451
615
  private readonly primaryDatabase;
452
- private readonly options;
616
+ private readonly requestService;
453
617
  private readonly logger;
454
- constructor(tenantContext: TenantContextService, primaryDatabase: PrimaryDatabaseService, options: DatabaseModuleOptions);
455
- intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>>;
618
+ constructor(reflector: Reflector, configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
619
+ canActivate(context: ExecutionContext): Promise<boolean>;
456
620
  /**
457
- * Extract tenant identifier: tries subdomain first, then falls back to header
621
+ * Validate access token with proper expiry checks
622
+ * Throws UnauthorizedException if token is invalid or expired
458
623
  */
459
- private extractTenantIdentifier;
624
+ private validateAccessToken;
460
625
  /**
461
- * Extract tenant from subdomain
462
- * @example acme.vritti.com 'acme'
626
+ * Validate refresh token with proper expiry checks
627
+ * Throws UnauthorizedException if token is invalid or expired
463
628
  */
464
- private extractFromSubdomain;
629
+ private validateRefreshToken;
465
630
  /**
466
- * Extract tenant from HTTP headers
467
- * Checks x-tenant-id and x-subdomain headers
631
+ * Helper to validate refresh token with specific secret
468
632
  */
469
- private extractFromHeader;
633
+ private validateRefreshTokenWithSecret;
470
634
  }
471
635
 
472
636
  /**
@@ -488,9 +652,9 @@ declare class TenantContextInterceptor implements NestInterceptor {
488
652
  * @Get('info')
489
653
  * async getTenantInfo(@Tenant() tenant: TenantInfo) {
490
654
  * return {
491
- * tenantId: tenant.tenantId,
492
- * tenantSlug: tenant.tenantSlug,
493
- * tenantType: tenant.tenantType,
655
+ * id: tenant.id,
656
+ * subdomain: tenant.subdomain,
657
+ * type: tenant.type,
494
658
  * };
495
659
  * }
496
660
  *
@@ -501,7 +665,7 @@ declare class TenantContextInterceptor implements NestInterceptor {
501
665
  * @Body() dto: CreateUserDto,
502
666
  * @Tenant() tenant: TenantInfo,
503
667
  * ) {
504
- * this.logger.log(`Creating user for tenant: ${tenant.tenantSlug}`);
668
+ * this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
505
669
  * // ...
506
670
  * }
507
671
  *
@@ -509,7 +673,7 @@ declare class TenantContextInterceptor implements NestInterceptor {
509
673
  * // Conditional business logic
510
674
  * @Get('features')
511
675
  * async getFeatures(@Tenant() tenant: TenantInfo) {
512
- * if (tenant.tenantType === 'ENTERPRISE') {
676
+ * if (tenant.type === 'ENTERPRISE') {
513
677
  * return ['feature-a', 'feature-b', 'feature-c'];
514
678
  * }
515
679
  * return ['feature-a'];
@@ -518,17 +682,75 @@ declare class TenantContextInterceptor implements NestInterceptor {
518
682
  declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
519
683
 
520
684
  /**
521
- * Extract subdomain from hostname
685
+ * Onboarding Decorator - Marks endpoints that require onboarding token
686
+ *
687
+ * Use this decorator on controllers or route handlers that should only be
688
+ * accessible during the onboarding flow with JWT tokens containing type='onboarding'.
689
+ *
690
+ * These endpoints:
691
+ * - Accept ONLY tokens with type='onboarding'
692
+ * - Reject regular access tokens (type='access')
693
+ * - Skip tenant validation and refresh token checks
694
+ * - Only validate JWT signature and expiry
695
+ *
696
+ * Useful for:
697
+ * - Email/phone verification during onboarding
698
+ * - Onboarding status checks
699
+ * - Resending OTPs during registration
700
+ *
701
+ * @example
702
+ * // On a controller method
703
+ * @Post('verify-email')
704
+ * @Onboarding()
705
+ * async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
706
+ * const userId = req.user.id; // Available from VrittiAuthGuard
707
+ * return this.service.verifyEmail(userId, dto.otp);
708
+ * }
709
+ *
710
+ * @example
711
+ * // Multiple onboarding endpoints
712
+ * @Controller('onboarding')
713
+ * export class OnboardingController {
714
+ * @Post('verify-email')
715
+ * @Onboarding()
716
+ * async verifyEmail() { ... }
717
+ *
718
+ * @Post('resend-otp')
719
+ * @Onboarding()
720
+ * async resendOtp() { ... }
721
+ * }
722
+ */
723
+ declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
724
+
725
+ /**
726
+ * Public Decorator - Marks endpoints that don't require authentication
522
727
  *
523
- * @param host Full hostname (e.g., 'acme.vritti.com:3000' or 'acme.vritti.com')
524
- * @returns Subdomain or null if not found
728
+ * Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
729
+ * tenant validation. Useful for:
730
+ * - Login/signup endpoints
731
+ * - Health checks
732
+ * - Public documentation endpoints
733
+ * - Webhook endpoints that don't require tenant context
525
734
  *
526
735
  * @example
527
- * extractSubdomain('acme.vritti.com') // 'acme'
528
- * extractSubdomain('staging-acme.vritti.com') // 'staging-acme'
529
- * extractSubdomain('localhost') // null
530
- * extractSubdomain('vritti.com') // null
736
+ * // On a controller method
737
+ * @Public()
738
+ * @Post('auth/login')
739
+ * async login(@Body() dto: LoginDto) {
740
+ * return this.authService.login(dto);
741
+ * }
742
+ *
743
+ * @example
744
+ * // On an entire controller
745
+ * @Public()
746
+ * @Controller('health')
747
+ * export class HealthController {
748
+ * @Get()
749
+ * check() {
750
+ * return { status: 'ok' };
751
+ * }
752
+ * }
531
753
  */
532
- declare function extractSubdomain(host: string): string | null;
754
+ declare const Public: () => _nestjs_common.CustomDecorator<string>;
533
755
 
534
- export { DatabaseModule, type DatabaseModuleOptions, MessageTenantContextInterceptor, PrimaryDatabaseService, type PrimaryDbConfig, Tenant, TenantContextInterceptor, TenantContextService, TenantDatabaseService, type TenantInfo, extractSubdomain };
756
+ export { AuthConfigModule, DatabaseModule, type DatabaseModuleOptions, Onboarding, PrimaryDatabaseService, type PrimaryDbConfig, Public, Tenant, TenantContextService, TenantDatabaseService, type TenantInfo, VrittiAuthGuard };