Files
HOA_Financial_Platform/backend/src/app.module.ts
olsch01 19fb2c037c feat(security): address findings from v2 security assessment
- L2: Add server_tokens off to nginx configs to hide version
- M1: Add X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
  Permissions-Policy headers to all nginx routes
- L3: Add global NoCacheInterceptor (Cache-Control: no-store) on all
  API responses to prevent caching of sensitive financial data
- C1: Disable open registration by default (ALLOW_OPEN_REGISTRATION env)
- H3: Add logout endpoint with correct HTTP 200 status code
- M2: Implement full password reset flow (forgot-password, reset-password,
  change-password) with hashed tokens, 15-min expiry, single-use
- Reduce JWT access token expiry from 24h to 1h
- Add EmailService stub (logs to shared.email_log)
- Add DB migration 016 for password_reset_tokens table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 07:46:11 -04:00

110 lines
4.5 KiB
TypeScript

import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ThrottlerModule } from '@nestjs/throttler';
import { AppController } from './app.controller';
import { DatabaseModule } from './database/database.module';
import { TenantMiddleware } from './database/tenant.middleware';
import { WriteAccessGuard } from './common/guards/write-access.guard';
import { NoCacheInterceptor } from './common/interceptors/no-cache.interceptor';
import { AuthModule } from './modules/auth/auth.module';
import { OrganizationsModule } from './modules/organizations/organizations.module';
import { UsersModule } from './modules/users/users.module';
import { AccountsModule } from './modules/accounts/accounts.module';
import { FiscalPeriodsModule } from './modules/fiscal-periods/fiscal-periods.module';
import { JournalEntriesModule } from './modules/journal-entries/journal-entries.module';
import { BudgetsModule } from './modules/budgets/budgets.module';
import { UnitsModule } from './modules/units/units.module';
import { InvoicesModule } from './modules/invoices/invoices.module';
import { PaymentsModule } from './modules/payments/payments.module';
import { VendorsModule } from './modules/vendors/vendors.module';
import { ReserveComponentsModule } from './modules/reserve-components/reserve-components.module';
import { InvestmentsModule } from './modules/investments/investments.module';
import { CapitalProjectsModule } from './modules/capital-projects/capital-projects.module';
import { ReportsModule } from './modules/reports/reports.module';
import { AssessmentGroupsModule } from './modules/assessment-groups/assessment-groups.module';
import { ProjectsModule } from './modules/projects/projects.module';
import { MonthlyActualsModule } from './modules/monthly-actuals/monthly-actuals.module';
import { AttachmentsModule } from './modules/attachments/attachments.module';
import { InvestmentPlanningModule } from './modules/investment-planning/investment-planning.module';
import { HealthScoresModule } from './modules/health-scores/health-scores.module';
import { BoardPlanningModule } from './modules/board-planning/board-planning.module';
import { BillingModule } from './modules/billing/billing.module';
import { EmailModule } from './modules/email/email.module';
import { OnboardingModule } from './modules/onboarding/onboarding.module';
import { ScheduleModule } from '@nestjs/schedule';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
url: configService.get<string>('DATABASE_URL'),
autoLoadEntities: true,
synchronize: false,
logging: false,
// Connection pool — reuse connections instead of creating new ones per query
extra: {
max: 30, // max pool size (across all concurrent requests)
min: 5, // keep at least 5 idle connections warm
idleTimeoutMillis: 30000, // close idle connections after 30s
connectionTimeoutMillis: 5000, // fail fast if pool is exhausted
},
}),
}),
ThrottlerModule.forRoot([{
ttl: 60000, // 1-minute window
limit: 100, // 100 requests per minute (global default)
}]),
DatabaseModule,
AuthModule,
OrganizationsModule,
UsersModule,
AccountsModule,
FiscalPeriodsModule,
JournalEntriesModule,
BudgetsModule,
UnitsModule,
InvoicesModule,
PaymentsModule,
VendorsModule,
ReserveComponentsModule,
InvestmentsModule,
CapitalProjectsModule,
ReportsModule,
AssessmentGroupsModule,
ProjectsModule,
MonthlyActualsModule,
AttachmentsModule,
InvestmentPlanningModule,
HealthScoresModule,
BoardPlanningModule,
BillingModule,
EmailModule,
OnboardingModule,
ScheduleModule.forRoot(),
],
controllers: [AppController],
providers: [
{
provide: APP_GUARD,
useClass: WriteAccessGuard,
},
{
provide: APP_INTERCEPTOR,
useClass: NoCacheInterceptor,
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(TenantMiddleware).forRoutes('*');
}
}