Added auth functionality, initial work with views and field types
This commit is contained in:
@@ -1,16 +1,88 @@
|
||||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
|
||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { TenantDatabaseService } from './tenant-database.service';
|
||||
|
||||
@Injectable()
|
||||
export class TenantMiddleware implements NestMiddleware {
|
||||
use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) {
|
||||
const tenantId = req.headers['x-tenant-id'] as string;
|
||||
|
||||
if (tenantId) {
|
||||
// Attach tenantId to request object
|
||||
(req as any).tenantId = tenantId;
|
||||
private readonly logger = new Logger(TenantMiddleware.name);
|
||||
|
||||
constructor(private readonly tenantDbService: TenantDatabaseService) {}
|
||||
|
||||
async use(
|
||||
req: FastifyRequest['raw'],
|
||||
res: FastifyReply['raw'],
|
||||
next: () => void,
|
||||
) {
|
||||
try {
|
||||
// Extract subdomain from hostname
|
||||
const host = req.headers.host || '';
|
||||
const hostname = host.split(':')[0]; // Remove port if present
|
||||
const parts = hostname.split('.');
|
||||
|
||||
this.logger.log(`Host header: ${host}, hostname: ${hostname}, parts: ${JSON.stringify(parts)}`);
|
||||
|
||||
// For local development, accept x-tenant-id header
|
||||
let tenantId = req.headers['x-tenant-id'] as string;
|
||||
let subdomain: string | null = null;
|
||||
|
||||
this.logger.log(`Host header: ${host}, hostname: ${hostname}, parts: ${JSON.stringify(parts)}, x-tenant-id: ${tenantId}`);
|
||||
|
||||
// If x-tenant-id is explicitly provided, use it directly
|
||||
if (tenantId) {
|
||||
this.logger.log(`Using explicit x-tenant-id: ${tenantId}`);
|
||||
(req as any).tenantId = tenantId;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract subdomain (e.g., "tenant1" from "tenant1.routebox.co")
|
||||
// For production domains with 3+ parts, extract first part as subdomain
|
||||
if (parts.length >= 3) {
|
||||
subdomain = parts[0];
|
||||
// Ignore www subdomain
|
||||
if (subdomain === 'www') {
|
||||
subdomain = null;
|
||||
}
|
||||
}
|
||||
// For development (e.g., tenant1.localhost), also check 2 parts
|
||||
else if (parts.length === 2 && parts[1] === 'localhost') {
|
||||
subdomain = parts[0];
|
||||
}
|
||||
|
||||
this.logger.log(`Extracted subdomain: ${subdomain}`);
|
||||
|
||||
// Get tenant by subdomain if available
|
||||
if (subdomain) {
|
||||
try {
|
||||
const tenant = await this.tenantDbService.getTenantByDomain(subdomain);
|
||||
if (tenant) {
|
||||
tenantId = tenant.id;
|
||||
this.logger.log(
|
||||
`Tenant identified: ${tenant.name} (${tenant.id}) from subdomain: ${subdomain}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn(`No tenant found for subdomain: ${subdomain}`, error.message);
|
||||
// Fall back to using subdomain as tenantId directly if domain lookup fails
|
||||
tenantId = subdomain;
|
||||
this.logger.log(`Using subdomain as tenantId fallback: ${tenantId}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (tenantId) {
|
||||
// Attach tenant info to request object
|
||||
(req as any).tenantId = tenantId;
|
||||
if (subdomain) {
|
||||
(req as any).subdomain = subdomain;
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(`No tenant identified from host: ${hostname}`);
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
this.logger.error('Error in tenant middleware', error);
|
||||
next();
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user