1- import { Module } from "@nestjs/common"
2- import { AppController } from "./app.controller"
3- import { AppService } from "./app.service"
4- import { TypeOrmModule } from "@nestjs/typeorm"
5- import { ConfigModule , ConfigService } from "@nestjs/config"
6- import { AuthModule } from "./auth/auth.module"
7- import { UserInventoryModule } from "./user-inventory/user-inventory.module"
8- import { CacheModule } from "./cache/cache.module"
9- import appConfig from "config/app.config"
10- import databaseConfig from "config/database.config"
11- import { PuzzleCategoryModule } from "./puzzle-category/puzzle-category.module"
12- import { RewardsModule } from "./rewards/rewards.module"
13- import { PuzzleModule } from "./puzzle/puzzle.module"
14- import { PuzzleSubmissionModule } from "./puzzle-submission/puzzle-submission.module"
15- import { ContentModule } from "./content/content.module"
16- import { UserReportCardModule } from "./user-report-card/user-report-card.module"
17- import { PuzzleDependencyModule } from "./puzzle-dependency/puzzle-dependency.module"
18- import { TimeTrialModule } from "./time-trial/time-trial.module"
19- import { InAppNotificationsModule } from "./in-app-notifications/in-app-notifications.module"
20- import { User } from "./auth/entities/user.entity"
21- import { TimeTrial } from "./time-trial/time-trial.entity"
22- import { Puzzle } from "./puzzle/puzzle.entity"
23- import { Category } from "./puzzle-category/entities/category.entity"
24- import { AnalyticsModule } from './analytics/analytics.module' ;
25- import { RewardShopModule } from './reward-shop/reward-shop.module' ;
26- import { ApiKeyModule } from './api-key/api-key.module' ;
271import { NestFactory } from '@nestjs/core' ;
282import { Logger , ValidationPipe } from '@nestjs/common' ;
293import { ConfigService } from '@nestjs/config' ;
@@ -37,19 +11,6 @@ async function bootstrap(): Promise<void> {
3711 const app = await NestFactory . create ( AppModule ) ;
3812 const configService = app . get < ConfigService > ( ConfigService ) ;
3913
40- // The frontend calls endpoints under the `/api` prefix (see
41- // frontend/store calls to /api/login, /api/register, etc.), so the global
42- // prefix is set on the whole Nest app. This also resolves issue #105 which
43- // expects the /api/users/:userId/history URL shape.
44- //
45- // Swagger UI is excluded so /docs, its JSON sibling /docs-json, and its
46- // nested asset routes (e.g. /docs/swagger-ui-init.js) stay at canonical
47- // paths instead of being double-prefixed to /api. Nest treats string
48- // entries as exact paths, so we also pass a RegExp to cover /docs/....
49- // A single anchored regex covers `docs`, `docs-json`, and any nested
50- // /docs/<asset> route (e.g. /docs/swagger-ui-init.js). Nest evaluates the
51- // exclude list against the registered handler path before the global
52- // prefix is applied.
5314 app . setGlobalPrefix ( 'api' , { exclude : [ / ^ d o c s / ] } ) ;
5415
5516 app . enableCors ( {
@@ -71,55 +32,37 @@ async function bootstrap(): Promise<void> {
7132 configService . get < boolean > ( 'appConfig.cors.credentials' ) ?? true ,
7233 } ) ;
7334
74- app . use ( helmet ( {
75- contentSecurityPolicy : {
76- directives : {
77- defaultSrc : [ "'self'" ] ,
78- scriptSrc : [ "'self'" , "'unsafe-eval'" , "'unsafe-inline'" ] ,
79- styleSrc : [ "'self'" , "'unsafe-inline'" ] ,
80- imgSrc : [ "'self'" , "data:" , "blob:" , "https:" ] ,
81- fontSrc : [ "'self'" ] ,
82- connectSrc : [ "'self'" , "https://soroban-testnet.stellar.org" ] ,
83- frameAncestors : [ "'none'" ] ,
84- baseUri : [ "'self'" ] ,
85- formAction : [ "'self'" ] ,
35+ // Security Headers via Helmet middleware
36+ app . use (
37+ helmet ( {
38+ contentSecurityPolicy : {
39+ directives : {
40+ defaultSrc : [ "'self'" ] ,
41+ scriptSrc : [ "'self'" , "'unsafe-eval'" , "'unsafe-inline'" ] ,
42+ styleSrc : [ "'self'" , "'unsafe-inline'" ] ,
43+ imgSrc : [ "'self'" , "data:" , "blob:" , "https:" ] ,
44+ fontSrc : [ "'self'" ] ,
45+ connectSrc : [ "'self'" , "https://soroban-testnet.stellar.org" ] ,
46+ frameAncestors : [ "'none'" ] ,
47+ baseUri : [ "'self'" ] ,
48+ formAction : [ "'self'" ] ,
49+ } ,
8650 } ,
87- } ,
88- hsts : { maxAge : 63072000 , includeSubDomains : true , preload : true } ,
89- referrerPolicy : { policy : 'strict-origin-when-cross-origin' } ,
90- } ) ) ;
51+ hsts : { maxAge : 63072000 , includeSubDomains : true , preload : true } ,
52+ referrerPolicy : { policy : 'strict-origin-when-cross-origin' } ,
53+ } ) ,
54+ ) ;
9155
9256 app . useGlobalPipes (
9357 new ValidationPipe ( {
9458 whitelist : true ,
9559 transform : true ,
9660 forbidNonWhitelisted : false ,
9761 } ) ,
98- PuzzleModule ,
99- PuzzleSubmissionModule ,
100- ContentModule ,
101- UserReportCardModule ,
102- PuzzleDependencyModule ,
103- TimeTrialModule ,
104- InAppNotificationsModule ,
105- PuzzleTranslationModule ,
106- NFTClaimModule ,
107- AnalyticsModule ,
108- RewardShopModule ,
109- ApiKeyModule ,
110- UserReactionModule ,
111- MultiplayerQueueModule ,
112- // Redis-backed caching + single-flight for the read-heavy endpoints
113- // (`/streaks/leaderboard`, `/analytics/puzzles/most-solved`) (#107).
114- CacheModule ,
115- ] ,
116- controllers : [ AppController ] ,
117- providers : [ AppService ] ,
118- } )
119- export class AppModule { }
12062 ) ;
12163
122- const apiVersion = configService . get < string > ( 'appConfig.apiVersion' ) ?? '1.0' ;
64+ const apiVersion =
65+ configService . get < string > ( 'appConfig.apiVersion' ) ?? '1.0' ;
12366 const swaggerConfig = new DocumentBuilder ( )
12467 . setTitle ( 'StellarHunts API' )
12568 . setDescription ( 'StellarHunts backend REST API documentation.' )
@@ -130,10 +73,9 @@ export class AppModule {}
13073 )
13174 . build ( ) ;
13275 const document = SwaggerModule . createDocument ( app , swaggerConfig ) ;
133- // Excluded from the global prefix above, so this resolves to /docs.
13476 SwaggerModule . setup ( 'docs' , app , document ) ;
13577
136- const port = parseInt ( process . env . PORT , 10 ) || 3001 ;
78+ const port = parseInt ( process . env . PORT || ' 3001' , 10 ) ;
13779 await app . listen ( port ) ;
13880 logger . log ( `StellarHunts API listening on http://localhost:${ port } ` ) ;
13981 logger . log ( `Swagger UI available at http://localhost:${ port } /docs` ) ;
0 commit comments