All files / apps/api/src main.ts

0% Statements 0/127
0% Branches 0/1
0% Functions 0/1
0% Lines 0/127

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128                                                                                                                                                                                                                                                               
import '@amalia/kernel/config/server';

import { Logger, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { type NestExpressApplication } from '@nestjs/platform-express';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { useContainer as classValidatorUseContainer } from 'class-validator';
import compression from 'compression';
import helmet from 'helmet';
import { parse } from 'qs';
import { initializeTransactionalContext, StorageDriver } from 'typeorm-transactional';

import { executeRefreshmentsTasks } from '@amalia/data-capture/imports/core';
import { executeNightlySchedulerTasks } from '@amalia/data-capture/nightly-scheduler/core';
import { executeCustomObjectsTasks } from '@amalia/data-capture/records/core';
import { TimeoutInterceptor } from '@amalia/kernel/api';
import { executeFirstTaskHandler, registerLifecycleRoutes } from '@amalia/kernel/bootstrap/server';
import { Logger as JSONLogger } from '@amalia/kernel/logger/server';
import { QueueModule, QueueService } from '@amalia/kernel/queue/core';
import { executeNotificationsTasks } from '@amalia/kernel/user-notifications/in-app/core';
import { executeLtiTask } from '@amalia/lti/core';
import { executeCalculationsTasks } from '@amalia/payout-calculation/compute-engine/core-engine';
import { executePaymentsTasks } from '@amalia/payout-calculation/payments/core';
import { executeStatementsTask } from '@amalia/payout-calculation/statements/core';
import { executeChallengesTask } from '@amalia/payout-definition/challenges/core';
import { executePlanAgreementsTask } from '@amalia/plan-agreements/core';
import { executeCurrenciesTasks } from '@amalia/tenants/companies/currency-rates/core';

import { ApiModule } from './api.module';
import { configuration } from './configuration';

async function bootstrap() {
  initializeTransactionalContext({ storageDriver: StorageDriver.AUTO });

  const app = await NestFactory.create<NestExpressApplication>(ApiModule, {
    // We need to activate this option for Slack incoming message validation, because we need
    // to check Slack message signature using its raw body.
    rawBody: true,
    bufferLogs: true,
  });
  app.use(
    compression({
      level: 6,
      threshold: 0,
    }),
  );

  app.useLogger(app.get(JSONLogger));

  app.setGlobalPrefix('api');

  // Override parser options to allow parsing larger arrays (default value is 20).
  app.set('query parser', (str: string) => parse(str, { arrayLimit: 1000 }));

  // this one was tough => https://github.com/nestjs/nest/issues/528
  // used with validator in calculations
  classValidatorUseContainer(app.select(ApiModule), { fallbackOnErrors: true });

  app.use(
    helmet({
      frameguard: false,
    }),
  );

  app.enableCors({
    origin: [
      configuration.domain,
      'http://localhost:3000',
      'http://localhost:3001',
      'https://app.ci.amalia.io',
      'https://app.staging.amalia.io',
      'https://app.amalia.io',
      'https://m-app.ci.amalia.io',
      'https://m-app.staging.amalia.io',
      'https://m-app.amalia.io',
    ].filter(Boolean),
    methods: 'GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS',
    preflightContinue: false,
    optionsSuccessStatus: 204,
    credentials: true,
    allowedHeaders: ['Accept', 'Content-Type', 'Authorization'],
  });

  app.useGlobalPipes(new ValidationPipe({ transform: true }));
  app.useGlobalInterceptors(new TimeoutInterceptor());

  app.useBodyParser('json', { limit: '10mb' });

  if (configuration.development.swagger) {
    const swaggerOptions = new DocumentBuilder().setTitle('Amalia API').setVersion('1.0').addBearerAuth().build();

    const document = SwaggerModule.createDocument(app, swaggerOptions);
    SwaggerModule.setup('/api', app, document);
  }

  if (configuration.queue.runInApi) {
    const queueService = app.select(QueueModule).get(QueueService, { strict: true });

    await queueService.startSubscriber(configuration.apps.api.subscriptionName, async (attributes, messageContent) => {
      await executeFirstTaskHandler(app, attributes, messageContent, [
        executeChallengesTask,
        executePlanAgreementsTask,
        executeStatementsTask,
        executeCalculationsTasks,
        executePaymentsTasks,
        executeNotificationsTasks,
        executeCurrenciesTasks,
        executeRefreshmentsTasks,
        executeCustomObjectsTasks,
        executeNightlySchedulerTasks,
        executeLtiTask,
      ]);
    });
  }

  registerLifecycleRoutes(app, app.getHttpAdapter(), '/api/probe');

  await app.listen(configuration.apps.api.port);
}

bootstrap().catch((error) => {
  // Loggers may not be ready yet.
  // eslint-disable-next-line no-console
  console.error('Error at application startup', error);
  Logger.flush();
  process.exit(1);
});