All files / src main.ts

0% Statements 0/59
0% Branches 0/4
0% Functions 0/7
0% Lines 0/59

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                                                                                                                                                                                                                                                             
#!/usr/bin/env node
import * as dotenv from 'dotenv';
dotenv.config();
 
// IMPORTANT: Initialize tracing BEFORE any other imports, especially NestFactory.
import { initTracing } from './tracing';
 
import { Agent, setGlobalDispatcher } from 'undici';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './http-exception.filter';
import { ValidationPipe } from '@nestjs/common';
import { SeedingService } from './seeding/seeding.service';
import { TimeoutInterceptor } from './timeout.interceptor';
import { json, urlencoded } from 'express';
import { AppService } from './app.service';
import * as net from 'net';
import { WsAdapter } from '@nestjs/platform-ws';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
 
function isPortInUse(port: number): Promise<boolean> {
  return new Promise((resolve) => {
    const server = net.createServer();
    server.once('error', (err: any) => {
      Iif (err.code === 'EADDRINUSE') {
        resolve(true);
      }
    });
    server.once('listening', () => {
      server.close(() => {
        resolve(false);
      });
    });
    server.listen(port);
  });
}
 
async function findAvailablePort(startPort: number): Promise<number> {
  let port = startPort;
  while (await isPortInUse(port)) {
    port++;
  }
  return port;
}
 
async function bootstrap() {
  // Start tracing before anything else
  await initTracing();
 
  // Override global undici dispatcher to increase fetch timeouts for LLM API calls
  setGlobalDispatcher(
    new Agent({
      headersTimeout: 1_200_000, // 20 min — time to receive response headers
      bodyTimeout: 1_200_000, // 20 min — time to receive full response body
      connect: { timeout: 60_000 }, // 60s — TCP connect timeout
    }),
  );
 
  Iif (process.env.REPOBURG_PROJECT_PATH) {
    try {
      process.chdir(process.env.REPOBURG_PROJECT_PATH);
    } catch (err) {
      console.error(
        `[FATAL] Could not change directory to specified path: ${process.env.REPOBURG_PROJECT_PATH}`,
      );
      console.error(err);
      process.exit(1);
    }
  }
 
  const app = await NestFactory.create(AppModule);
 
  app.useWebSocketAdapter(new WsAdapter(app));
 
  app.use(json({ limit: '50mb' }));
  app.use(urlencoded({ extended: true, limit: '50mb' }));
 
  const seeder = app.get(SeedingService);
  await seeder.seed();
 
  // Enable CORS
  app.enableCors({
    origin: '*', // Allow all
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    credentials: true,
  });
 
  // Apply global interceptor for request timeouts
  app.useGlobalInterceptors(new TimeoutInterceptor());
 
  // Apply global filter for consistent error responses
  app.useGlobalFilters(new AllExceptionsFilter());
 
  // Apply global validation pipe to ensure all endpoints are protected
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
    }),
  );
 
  const preferredPort = process.env.PORT
    ? parseInt(process.env.PORT, 10)
    : 3000;
  const port = await findAvailablePort(preferredPort);
 
  const appService = app.get(AppService);
  appService.setPort(port);
 
  // Setup Swagger/OpenAPI
  const config = new DocumentBuilder()
    .setTitle('Repoburg API')
    .setDescription('API documentation for Repoburg backend')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('docs/openapi', app, document);
 
  await app.listen(port);
  console.log(`Application is running on: ${await app.getUrl()}`);
  console.log(`Repoburg backend serving project from: ${process.cwd()}`);
  console.log(`Open in browser: http://localhost:3001?bport=${port}`);
  console.log(`API Docs: http://localhost:${port}/docs/openapi`);
}
bootstrap();