import * as dotenv from "dotenv"; dotenv.config(); require("newrelic"); const now = require("performance-now"); import { Reflector } from "@nestjs/core"; import * as _ from "lodash"; import * as moment from "moment"; import { v4 as uuid } from "uuid"; import * as rTracer from "cls-rtracer"; import { NestFactory } from "@nestjs/core"; import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify"; import { AppModule } from "./app.module"; import * as path from "path"; import { AppClusterService } from "./app_cluster.service"; import { CUSTOM_MODULES as LIB_CUSTOM_MODULES } from "@shopstack/cc-core-lib/module"; import { Logger, RequestMethod } from "@nestjs/common"; import { IGNORE_ENDPOINT_PATHS, CONST, EntitiesService, TemplateService, SettingService, TranslateService, AllExceptionsFilter, AuthService, UserRolePermissonService, UserRoleService, UserService, ActivityLogService, DeployLogService, ImportExportLogService, LanguageService, ImportExportService, AttachmentService, CommentDataService, AddressService, CurrencyService, CountryService, DistrictService, ProvinceService, SubdistrictService, TimeZoneService, CompanyService, DeployDestinationService, DeployPackageService, CustomFunctionService, SmsService, ReasonDataService, ReasonMasterService, ReasonOptionService, WidgetService, VersionDataService, ScheduleActionService, ScheduleLogService, MapLocationService, MailSettingService, MailLogService, request, SmsLogService, ExecutionLogService, initialCustomModules, initialCustomProviders, initialSettingLayouts, NotificationSubscriptionService, NotificationMessageService, NotificationMessageQueueService, NotificationLogService, ShortlyUrlService, LayoutShareWidgetService, ApiKeyService, JWTAuthGuard, CsrfGuard, CacheInterceptor, FilterInterceptor, AuditLogService, PipelineService, PipelineJobService, PipelineLogService, PrintLogService, redisCache, RewriteUrlService, SearchTextService, ReindexSearchTextService, sanitizeDeep } from "@shopstack/cc-core-lib/core"; import { CUSTOM_MODULES } from "src/modules/modules"; import * as packageData from "../package.json"; async function initialService(app: NestFastifyApplication, serviceClass: any) { let start = now(); const service = await app.resolve(serviceClass); await service.init(); let end = now(); Logger.log(`${service.constructor.name} initial done (${(end - start).toFixed(3)})`, "MAIN"); } async function refreshService(app: NestFastifyApplication, serviceClass: any) { let start = now(); const service = await app.resolve(serviceClass); await service.initRefresh(); let end = now(); Logger.log(`${service.constructor.name} initial refresh done (${(end - start).toFixed(3)})`, "MAIN"); } async function initialCustomService(app: NestFastifyApplication) { const SPECIFY_CUSTOM_MODULES = process.env.CUSTOM_MODULES === "" || process.env.CUSTOM_MODULES === undefined ? [] : process.env.CUSTOM_MODULES.split(","); const RUN_CUSTOM_SERVICES = []; SPECIFY_CUSTOM_MODULES.length === 0 ? CUSTOM_MODULES.forEach((c) => { RUN_CUSTOM_SERVICES.push(...(c.services || [])); }) : CUSTOM_MODULES.filter((c) => SPECIFY_CUSTOM_MODULES.includes(c.module.name)).forEach((c) => { RUN_CUSTOM_SERVICES.push(...(c.services || [])); }); for (const customService of RUN_CUSTOM_SERVICES) { await initialService(app, customService); } } async function initial(app: NestFastifyApplication) { LIB_CUSTOM_MODULES.push(...CUSTOM_MODULES); await initialService(app, SettingService); await initialService(app, EntitiesService); await initialService(app, UserRolePermissonService); await initialService(app, UserRoleService); await initialService(app, UserService); await initialService(app, ApiKeyService); await initialService(app, LanguageService); await initialService(app, TranslateService); await initialService(app, ActivityLogService); await initialService(app, AttachmentService); await initialService(app, AuditLogService); await initialService(app, TimeZoneService); await initialService(app, CountryService); await initialService(app, ProvinceService); await initialService(app, DistrictService); await initialService(app, SubdistrictService); await initialService(app, AddressService); await initialService(app, CurrencyService); await initialService(app, CompanyService); await initialService(app, MailSettingService); await initialService(app, MailLogService); await initialService(app, SmsLogService); await initialService(app, SmsService); await initialService(app, ReasonMasterService); await initialService(app, ReasonOptionService); await initialService(app, ReasonDataService); await initialService(app, WidgetService); await initialService(app, MapLocationService); await initialService(app, VersionDataService); await initialService(app, CommentDataService); await initialService(app, ScheduleLogService); await initialService(app, ScheduleActionService); await initialService(app, NotificationSubscriptionService); await initialService(app, NotificationMessageService); await initialService(app, NotificationMessageQueueService); await initialService(app, NotificationLogService); await initialService(app, ShortlyUrlService); await initialService(app, DeployDestinationService); await initialService(app, DeployPackageService); await initialService(app, DeployLogService); await initialService(app, CustomFunctionService); await initialService(app, ExecutionLogService); await initialService(app, LayoutShareWidgetService); await initialService(app, SearchTextService); await initialService(app, ReindexSearchTextService); await initialCustomModules(); await initialCustomProviders(); await initialSettingLayouts(); await initialCustomService(app); await initialService(app, AuthService); await initialService(app, TemplateService); await initialService(app, ImportExportService); await initialService(app, ImportExportLogService); await initialService(app, PrintLogService); await initialService(app, PipelineService); await initialService(app, PipelineJobService); await initialService(app, PipelineLogService); await initialService(app, RewriteUrlService); await initialService(app, EntitiesService); await refreshService(app, EntitiesService); await refreshService(app, ScheduleActionService); await refreshService(app, SettingService); await refreshService(app, AttachmentService); redisCache.set("START_TIME", moment().format("DD MMMM YYYY HH:mm")); } async function bootstrap() { dotenv.config(); const fastify = new FastifyAdapter({ logger: true, bodyLimit: 104857600 }); fastify.register(require("@fastify/cors"), { // put your options here }); fastify.register(rTracer.fastifyPlugin, { requestIdFactory: (req) => ({ request_id: uuid(), user_ip: req.headers["client-ip"], user_agent: req.headers["user-agent"], }), }); // Register the multipart plugin fastify.register(require("@fastify/multipart"), { attachFieldsToBody: true }); // Register the fastify-xml-body-parser plugin fastify.register(require("fastify-xml-body-parser")); const app = await NestFactory.create(AppModule, fastify); app.useStaticAssets({ root: path.resolve(process.cwd(), "storage/public"), prefix: `/public`, }); // Serve specific file for "/" fastify.get("/", (request, reply) => { reply.type("text/html").sendFile("index.html", path.join(process.cwd(), "storage/documents")); }); _.set(CONST, "app", app); await initial(app); const reflector = app.get(Reflector); const auth = app.get(AuthService); app.useGlobalGuards(new JWTAuthGuard(reflector, auth)); app.useGlobalGuards(new CsrfGuard(reflector)); app.setGlobalPrefix("api/v1", { exclude: [{ path: "private/*", method: RequestMethod.GET }], }); app.use(request); app.useGlobalInterceptors(new FilterInterceptor(reflector)); app.useGlobalInterceptors(new CacheInterceptor(reflector)); // app.enableCors(); app.useGlobalFilters(new AllExceptionsFilter()); const routes = []; const ignoreRouteRegex = new RegExp(IGNORE_ENDPOINT_PATHS.join("|")); app .getHttpAdapter() .getInstance() .addHook("onRoute", (route) => { if (!ignoreRouteRegex.test(route.path)) { let method = route.method; if (_.isArray(method)) { method = method[0] || "GET"; } else if (_.isObject(method)) { method = Object.keys(method)[0] || "GET"; } method = String(method).toUpperCase(); routes.push({ path: route.path.replace("/api/v1/", ""), method: method }); } }); app .getHttpAdapter() .getInstance() .addHook("onReady", (done) => { CONST.ENDPOINT_PATHS = routes; done(); }); if (process.env.ENABLE_SANITIZE == 'true') { app .getHttpAdapter() .getInstance() .addHook('preHandler', async (request, reply) => { if (request.body) { request.body = sanitizeDeep(request.body); } }); } await app.listen(parseInt(process.env.PORT, 10), "0.0.0.0"); CONST.HEALTH_CHECK = true; CONST.SYSTEM_NAME = process.env.SYSTEM_NAME || _.get(packageData, "name"); } AppClusterService.clusterize(bootstrap); // +process.env.QUEUE_CONCURRENCY > 0 || process.env.ENABLE_SCHEDULE_ACTION === "true" ? bootstrap() : AppClusterService.clusterize(bootstrap); // bootstrap();