* chore: deploy api v2 on vercel * fix: replace console.log with logger.log in Vercel handler Address Cubic AI review feedback to use the logging framework consistently instead of console.log in the serverless handler. Co-Authored-By: unknown <> * chore: enable esModuleInterop * chore: deploy api v2 on vercel * chore: deploy api v2 on vercel * Update apps/api/v2/src/bootstrap.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * fixup! Merge branch 'main' into deploy-api-v2-vercel * Revert "chore: deploy api v2 on vercel" This reverts commit 45c704a48e8396c46118069e1a25d8d7a5ee84be. * chore: deploy api v2 on vercel * fix: address Cubic AI review feedback in main.ts - Replace console.log with logger.log for consistent logging - Replace console.error with logger.error for consistent error logging - Restore comma: true option in qs.parse to support comma-separated arrays Co-Authored-By: unknown <> * fix: remove comma: true from qs.parse to maintain backward compatibility The main branch does not have comma: true in the query parser, so adding it would be a breaking change for existing API consumers. Removing it to maintain consistency with the current production behavior. Co-Authored-By: unknown <> * chore: deploy api v2 on vercel * small fixes * chore: add try catch around bootstrap.ts * fix: use NestJS Logger and throw error instead of process.exit in bootstrap - Replace console.error with logger.error for consistent logging - Replace process.exit(1) with throw error to avoid breaking Vercel serverless instance reuse Addresses Cubic AI review feedback (confidence 10/10 for both issues) Co-Authored-By: unknown <> * chore: try log redis url * fix: sanitize REDIS_URL logging to avoid exposing credentials Replace full REDIS_URL logging with a boolean check that only indicates whether Redis is configured, without exposing the connection string. Addresses Cubic AI review feedback (confidence 9/10) Co-Authored-By: unknown <> * chore: remove unnecessary logs * fix: prisma adapter * chore: handle USE_POOL platform libraries * fix: use JSON.stringify for Vite define value Wrap usePool with JSON.stringify() to properly serialize the string value. Without this, Vite injects the raw value as an identifier instead of a string literal, breaking runtime behavior. Addresses Cubic AI review feedback (confidence 9/10) Co-Authored-By: unknown <> * fix: docker file builds * fix: correct Dockerfile build order for platform packages Reorder builds to match the dependency graph from dev:build script: constants → enums → utils → types → libraries → trpc → api-v2 platform-libraries depends on the other platform packages, so they must be built first. Addresses Cubic AI review feedback (confidence 9/10) Co-Authored-By: unknown <> * fix: docker file builds * chore: add docker build * chore: upgrade nest/bull --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
121 lines
4.0 KiB
TypeScript
121 lines
4.0 KiB
TypeScript
import "dotenv/config";
|
|
|
|
import { IncomingMessage, Server, ServerResponse } from "node:http";
|
|
|
|
import { Logger } from "@nestjs/common";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { NestFactory } from "@nestjs/core";
|
|
import type { NestExpressApplication } from "@nestjs/platform-express";
|
|
import type { Express, Request, Response } from "express";
|
|
import { WinstonModule } from "nest-winston";
|
|
import qs from "qs";
|
|
import type { AppConfig } from "@/config/type";
|
|
|
|
import { AppModule } from "./app.module";
|
|
import { bootstrap } from "./bootstrap";
|
|
import { loggerConfig } from "./lib/logger";
|
|
|
|
const logger: Logger = new Logger("App");
|
|
|
|
/**
|
|
* Singleton Class to manage the NestJS App instance.
|
|
* Ensures we only initialize the app once per container lifecycle.
|
|
*/
|
|
class NestServer {
|
|
private static server: Express; // The underlying Express instance
|
|
|
|
private constructor() {}
|
|
|
|
/**
|
|
* Returns the cached server instance.
|
|
* If it doesn't exist, it creates, bootstraps, and initializes it.
|
|
*/
|
|
public static async getInstance(): Promise<Express> {
|
|
if (!NestServer.server) {
|
|
const app = await createNestApp();
|
|
|
|
// Execute bootstrap (Pipes, Interceptors, CORS, etc.)
|
|
bootstrap(app);
|
|
|
|
// Initialize the app (connects to DB, resolves modules)
|
|
await app.init();
|
|
|
|
// extract the Express instance to pass to Vercel
|
|
NestServer.server = app.getHttpAdapter().getInstance();
|
|
}
|
|
return NestServer.server;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// LOCAL DEVELOPMENT STARTUP
|
|
// -----------------------------------------------------------------------------
|
|
if (!process.env.VERCEL) {
|
|
run().catch((error: Error) => {
|
|
logger.error("Failed to start Cal Platform API", { error: error.stack });
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
async function run(): Promise<void> {
|
|
const app = await createNestApp();
|
|
try {
|
|
bootstrap(app);
|
|
const config = app.get(ConfigService<AppConfig, true>);
|
|
const port = config.get("api.port", { infer: true });
|
|
|
|
if (config.get("env.type", { infer: true }) === "development") {
|
|
const { generateSwaggerForApp } = await import("./swagger/generate-swagger");
|
|
generateSwaggerForApp(app);
|
|
}
|
|
|
|
await app.listen(port);
|
|
logger.log(`Application started locally on port: ${port}`);
|
|
} catch (error) {
|
|
logger.error("Application crashed during local startup", { error });
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// VERCEL SERVERLESS HANDLER
|
|
// -----------------------------------------------------------------------------
|
|
export default async (req: Request, res: Response): Promise<void> => {
|
|
try {
|
|
const server = await NestServer.getInstance();
|
|
|
|
// Vercel/AWS specific: Re-parse query strings to support array formats
|
|
// (e.g., ?ids[]=1&ids[]=2) which Vercel's native parser might simplify.
|
|
if (req.url) {
|
|
const [_path, queryString] = req.url.split("?");
|
|
if (queryString) {
|
|
req.query = qs.parse(queryString, { arrayLimit: 1000 });
|
|
}
|
|
}
|
|
|
|
// Delegate request to the cached Express instance
|
|
return server(req, res);
|
|
} catch (error) {
|
|
logger.error("Critical: Failed to initialize NestJS Serverless instance", error);
|
|
res.statusCode = 500;
|
|
res.end("Internal Server Error: Initialization Failed");
|
|
}
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// APP FACTORY
|
|
// -----------------------------------------------------------------------------
|
|
export async function createNestApp(): Promise<
|
|
NestExpressApplication<Server<typeof IncomingMessage, typeof ServerResponse>>
|
|
> {
|
|
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
|
logger: WinstonModule.createLogger(loggerConfig()),
|
|
// Preserved as requested:
|
|
bodyParser: false,
|
|
});
|
|
|
|
// Custom query parser configuration for the underlying Express app
|
|
app.set("query parser", (str: string) => qs.parse(str, { arrayLimit: 1000 }));
|
|
|
|
return app;
|
|
}
|