import Mongoose = require('mongoose'); import Express = require('express'); import * as Http from 'http'; import * as Https from 'https'; import { DyFM_Error } from '@futdevpro/fsm-dynamo'; import { DyNTS_RouteSecurity } from '../../_enums/route-security.enum'; import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model'; import { DyNTS_AppSystemControls } from '../../_models/control-models/app-system-controls.control-model'; import { DyNTS_Http_Settings } from '../../_models/control-models/http-settings.control-model'; import { DyNTS_Certification_Settings } from '../../_models/interfaces/certification-settings.interface'; import { DyNTS_GlobalService_Settings } from '../../_models/interfaces/global-service-settings.interface'; import { DyNTS_StaticClient_Settings } from '../../_models/interfaces/static-client-settings.interface'; import { DyNTS_Cors_Settings } from '../../_models/interfaces/cors-settings.interface'; import { DyNTS_SingletonService } from '../base/singleton.service'; import { DyNTS_RoutingModule } from '../route/routing-module.service'; /** * * function MyDecorator(config: any) { return function (target: Function) { // attach metadata or modify target target.prototype.myMeta = config; }; } @MyDecorator({ role: 'admin' }) class User { printRole() { console.log((this as any).myMeta.role); // → "admin" } } */ /** * This will be the MAIN service of our server project, * follow the types and type instructions while setting up your project * * In this service, there are abstract functions that you will need to implement, * where you need to set up the main params for your application. * * (after the example, you can find the list of services you can/should setup) * * @example * export class App extends DyNTS_AppExtended { * * ... * * // Setting up App params, and preparing project global settings * setupAppParams(): void { * this.params = new DyNTS_AppParams({ * name: 'Warbots Server', * title: warbotsTitleLog, * version: version, * dbName: 'warbots', * }); * * // dynamoNTS_GlobalSettings.logRequestsContent = false; * } * * ... * * // Setting up DBServices * setGlobalServiceCollection(): void { * DyNTS_GlobalService.setServices({ * authService: AuthService.getInstance(), * emailServiceCollection: EmailServiceCollectionService.getInstance(), * dbModels: [ * userModelParams, * userDataModelParams, * userOptionsModelParams, * userStatisticsModelParams, * userAchievementsModelParams, * userNotificationsModelParams, * * matchStatisticsModelParams, * matchDataModelParams, * DyFM_usageSession_dataParams, * DyFM_customData_dataParams, * ] * }); * } * * ... * * // Setting up Routes * setupRoutingModules(): void { * this.httpPort = env.port; * this.routingModules = [ * new DyNTS_RoutingModule({ * route: '/user', * controllers: [ * UserController.getInstance(), * UserDataController.getInstance(), * UserOptionsController.getInstance(), * UserStatisticsController.getInstance(), * UserAchievementsController.getInstance(), * UserNotificationsController.getInstance() * ] * }), * new DyNTS_RoutingModule({ * route: '/match', * controllers: [ * MatchController.getInstance(), * MatchDistributionController.getInstance(), * MatchStatisticsController.getInstance(), * ] * }), * new DyNTS_RoutingModule({ * route: '/server', * controllers: [ * ServerController.getInstance(), * ] * }), * getTestRoutingModule(), * getUsageRoutingModule() * ]; * } * } * * // * // The Services available * // * // Authentication Service * // A commonly used basic service, * // which is necessary fur certain functions (such as registering call issuers) * // * // This will handle Authentication Token checking/refreshing, * // checking issuer's identifier and routeParams, * // handling JWT Token, or maybe with OAuth2 or other commonly used security procedures * // * // You can create one with this Dynamo Object: * // * * @example * // follow the instructions on the abstract class (DyNTS_AuthService) * export class AuthService extends DyNTS_AuthService {} * * * * // */ /** * This will be the MAIN service of our server project, * follow the types and type instructions while setting up your project * * In this service, there are abstract functions that you will need to implement, * where you need to set up the main params for your application. * * (after the example, you can find the list of services you can/should setup) * * You need to setup the following functions: * ```ts * // this is where you set up the main params for your application * getAppParams(): DyNTS_AppParams * * // this is where you connect your main services * getGlobalServiceSettings(): DyNTS_GlobalService_Settings * * // this is where you set up your ports * getPorts(): DyNTS_PortSettings * * // this is where you set up your routes * getRoutingModules(): DyNTS_RoutingModule[] * * * * ``` * optionally you can setup the following functions: * ```ts * // this is where you set up your certifications * getCertificationSettings(): DyNTS_CertificationSettings * * // this is where you set up additional root services * getRootServices(): DyNTS_SingletonService[] * * // this is where you set up your initial db entries * createEntries(): void * * // this is where you can define post setup processes * postProcess(): void * * * * ``` * */ /** FR-258 / SR-2 — one entry from `collection.indexes()` (only the fields the TTL-installer reads). */ export interface DyNTS_TtlIndexInfo { name?: string; key?: Record; expireAfterSeconds?: number; } /** * FR-258 / SR-2 — the minimal native-driver collection surface the TTL-installer needs. Declared * structurally (not importing mongodb types) so it stays dependency-light and is trivially mockable * in unit tests. */ export interface DyNTS_TtlIndexCollection { indexes?: () => Promise; createIndex: (keys: Record, options: { expireAfterSeconds: number; }) => Promise; dropIndex?: (name: string) => Promise; } /** * FR-258 / SR-2 — outcome of {@link DyNTS_App.ensureRetentionTtlIndex}. * - `created`: no `__created` TTL index existed → one was created. * - `noop`: an index with the SAME TTL already existed → nothing done. * - `differs`: an index exists with a DIFFERENT TTL (or a non-TTL `__created` index) → left UNTOUCHED, * only a warning is emitted. We deliberately DO NOT drop+recreate at boot: rebuilding a TTL index on a * multi-GB production collection is a heavy server-side op that can starve concurrent boot queries (e.g. * the health-probe) and trip a fail-safe deploy revert. A deliberate retention CHANGE must be applied via * maintenance, not silently at every startup. */ export type DyNTS_TtlIndexAction = 'created' | 'differs' | 'noop'; export declare abstract class DyNTS_App extends DyNTS_SingletonService { protected systemControls: DyNTS_AppSystemControls; get started(): boolean; protected get superStarted(): boolean; protected constructErrors: (Error | DyFM_Error)[]; get serverName(): string; private _params; protected get params(): DyNTS_App_Params; protected mongoose: typeof Mongoose; private _security; protected get security(): DyNTS_RouteSecurity; protected _portSettings: DyNTS_Http_Settings; protected get portSettings(): DyNTS_Http_Settings; private _cert?; protected get cert(): DyNTS_Certification_Settings; protected openExpress: Express.Application; protected secureExpress: Express.Application; protected httpsServer: Https.Server; protected httpServer: Http.Server; private globalService; private _rootServices; private _routingModules; protected readonly defaultReadyTimeout: number; readonly defaultErrorUserMsg: string; get logSetup(): boolean; get deepLog(): boolean; get fnLogs(): boolean; debugLog: boolean; constructor(); protected asyncConstruct(extended?: boolean): Promise; ready(timeout?: number): Promise; protected getSimplifiedConstructErrors(): string[]; stop(dontLog?: boolean): Promise; /** * */ private startDB; /** * FR-258 / SR-2 — install MongoDB TTL indexes for every data-model that declared `retention`. * * Runs ONCE right after the DB connection opens. For each registered DB service (including the * auto-created `_archived` siblings, which inherit the parent model's `retention`), it ensures a * TTL index on the `__created` Date field with the resolved `expireAfterSeconds` — so MongoDB * NATIVELY auto-deletes documents older than the configured age. This is the fleet-wide defense * against the unbounded-collection-growth → in-memory-load → GC-thrash/OOM class. * * Guarantees: * - **Non-fatal** — any error is logged but NEVER blocks startup (called fire-and-forget). * - **Idempotent** — an existing index with the same TTL is a no-op; a CHANGED retention (different * `expireAfterSeconds`, or an existing non-TTL `__created` index) is reconciled by drop+recreate * (the field index is restored immediately, so query coverage is preserved). */ private installRetentionTtlIndexes; /** * FR-258 / SR-2 — ensure a single `{ __created: 1 }` TTL index with the given `expireAfterSeconds`. * Extracted as a pure-ish static so it is unit-testable with a mock collection. Idempotent + BOOT-SAFE: * - no existing `__created` index → create it → `'created'` * - existing with the SAME TTL → no-op → `'noop'` * - existing with a DIFFERENT TTL / non-TTL → leave it as-is → `'differs'` (warn only — NO drop+recreate) * * The `'differs'` case intentionally does NOT mutate the live index. Dropping + recreating a TTL index on a * large (multi-GB) production collection is a heavy server-side operation; doing it automatically inside the * startup path can starve the concurrent boot queries (including the deploy health-probe) and trigger a * fail-safe revert. Create-if-missing is the essential growth-prevention behaviour; a deliberate retention * CHANGE on an already-indexed collection is a maintenance action, not a silent every-boot rebuild. */ static ensureRetentionTtlIndex(collection: DyNTS_TtlIndexCollection, ttlSeconds: number): Promise; /** * */ private initExpresses; /** * */ protected initOpenExpress(): Promise; /** * */ protected initSecureExpress(): Promise; /** * */ private startExpresses; /** * */ private expressErrorHandling; /** * */ private mountSecureRoutes; /** * */ private mountOpenRoutes; /** * FR-041 — CORS middleware. Echoes a matching `Access-Control-Allow-Origin` * for any request whose Origin header is in `getCorsSettings().allowedOrigins`, * adds the canonical `Access-Control-*` companion headers, and short-circuits * the OPTIONS preflight with a 204. * * No-op when the subclass does NOT override `getCorsSettings()` — preserves * back-compat for apps that have always been same-origin and never needed * CORS (the typical pre-FR-041 case). * * Why hand-rolled instead of the `cors` npm package: * - ~30 lines, zero new dependency. * - Full control over Vary/credentials/preflight semantics. * - Aligns with FDP "no unnecessary deps" philosophy. */ /** * BFR-011 / SEC-R2-8 — security response-headerek mountolása (a `mountCors` tükörképe). * A `DyNTS_global_settings.securityHeaders` alapján (default-ON) minden válaszra ráteszi a * biztonsági headereket + eltávolítja az `x-powered-by`-t. A CORS-mount ELŐTT hívandó, hogy az * OPTIONS preflight-válaszok (amiket a mountCors 204-gyel rövidre zár) is megkapják. */ protected mountSecurityHeaders(express: Express.Application): void; protected mountCors(express: Express.Application): void; /** * Generikus, auth-agnosztikus extension-point orchestrator. A `registerCustomMiddleware` * hookot hívja (ha a subclass override-olja) minden AKTÍV Express instance-ra * (open + secure), az API route-ok UTÁN, de a SPA static catch-all (`app.get('*')`) * ELŐTT — ez az EGYETLEN korrekt pont egy sub-path middleware-hez (pl. reverse-proxy), * mert a catch-all minden utána mountolt route-ot elnyel. No-op ha nincs override (back-compat). */ private mountCustomMiddleware; /** * Ha getStaticClientSettings() visszaad beállításokat, a client static fájlok a '/' alatt * lesznek kiszolgálva (API route-ok után). SPA fallback opcionális (fallbackPath). * Asset cache (assetCacheMaxAge, assetCacheImmutable) és fallback cache (fallbackCacheMaxAge) * opcionálisak; ha nincs megadva fallbackCacheMaxAge, DyNTS_defaultFallbackCacheMaxAge (0) marad. */ private mountStaticClient; /** * */ private setSecurity; private _getDefaultErrorSettings; /** * MISSING Description (TODO) */ abstract getAppParams(): DyNTS_App_Params; /** * MISSING Description (TODO) */ abstract getGlobalServiceCollection(): DyNTS_GlobalService_Settings; /** * MISSING Description (TODO) */ abstract getPortSettings(): DyNTS_Http_Settings; /** * MISSING Description (TODO) */ overrideDynamoNTSGlobalSettings?(): void; /** * Ha megadva, a visszaadott értéket használjuk a route prefix-ként * (DyNTS_global_settings.baseUrl felülírása erre az app-ra). */ getApiBasePath?(): string; /** * MISSING Description (TODO) */ getRoutingModules?(): DyNTS_RoutingModule[]; /** * MISSING Description (TODO) */ getRootServices?(): Promise; /** * MISSING Description (TODO) */ getCertificationSettings?(): DyNTS_Certification_Settings; /** * Ha megadva, a client static fájlok a '/' alatt lesznek kiszolgálva (API route-ok után). * Opcionális fallbackPath pl. SPA index.html-hez. */ getStaticClientSettings?(): DyNTS_StaticClient_Settings | undefined; /** * FR-041 — Ha megadva, CORS middleware mount-olódik mindkét Express instance-ra * (open + secure). Az `allowedOrigins` listán szereplő Origin-eket echo-zza vissza, * `Access-Control-Allow-Credentials: true`-val (default) együtt a Bearer JWT * cross-domain auth flow miatt. OPTIONS preflight 204-gyel rövidre záródik. * Ha undefined → no CORS headers, no preflight handling — back-compat. */ getCorsSettings?(): DyNTS_Cors_Settings | undefined; /** * Opcionális generikus hook: custom Express middleware regisztrálása az API route-ok * UTÁN és a SPA static catch-all ELŐTT. Auth-AGNOSZTIKUS framework extension-point — * a subclass (pl. FDP base App) ezen keresztül mount-olhat reverse-proxyt egy sub-path-re * (pl. `/auth-api`), ami a `app.get('*')` catch-all elé kell kerüljön. A hívási időzítést * lásd: `mountCustomMiddleware`. Undefined → no-op (back-compat). */ registerCustomMiddleware?(express: Express.Application, security: DyNTS_RouteSecurity): void | Promise; /** * MISSING Description (TODO) */ createEntries?(): Promise; /** * MISSING Description (TODO) */ postProcess?(): Promise; } //# sourceMappingURL=app.server.d.ts.map