@dismissible/nestjs-api 1.0.2 → 1.0.3-alpha.0b30e6d.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +507 -0
  2. package/bin/dismissible-storage-setup.js +22 -0
  3. package/config/.env.development.yaml +38 -0
  4. package/config/.env.yaml +18 -5
  5. package/package.json +34 -26
  6. package/src/app-test.factory.js +4 -3
  7. package/src/app-test.factory.js.map +1 -1
  8. package/src/app.module.js +9 -10
  9. package/src/app.module.js.map +1 -1
  10. package/src/app.setup.js +9 -40
  11. package/src/app.setup.js.map +1 -1
  12. package/src/config/app.config.d.ts +2 -2
  13. package/src/config/app.config.js +7 -4
  14. package/src/config/app.config.js.map +1 -1
  15. package/src/cors/cors.factory.d.ts +2 -0
  16. package/src/cors/cors.factory.js +28 -0
  17. package/src/cors/cors.factory.js.map +1 -0
  18. package/src/cors/index.d.ts +1 -0
  19. package/src/cors/index.js +1 -0
  20. package/src/cors/index.js.map +1 -1
  21. package/src/helmet/helmet.factory.d.ts +2 -0
  22. package/src/helmet/helmet.factory.js +29 -0
  23. package/src/helmet/helmet.factory.js.map +1 -0
  24. package/src/helmet/index.d.ts +1 -0
  25. package/src/helmet/index.js +1 -0
  26. package/src/helmet/index.js.map +1 -1
  27. package/src/storage/dynamic-storage.module.d.ts +8 -0
  28. package/src/storage/dynamic-storage.module.js +33 -0
  29. package/src/storage/dynamic-storage.module.js.map +1 -0
  30. package/src/storage/storage.config.d.ts +12 -0
  31. package/src/storage/storage.config.js +37 -0
  32. package/src/storage/storage.config.js.map +1 -0
  33. package/src/swagger/swagger.factory.d.ts +1 -2
  34. package/src/swagger/swagger.factory.js +10 -1
  35. package/src/swagger/swagger.factory.js.map +1 -1
  36. package/src/validation/index.d.ts +1 -0
  37. package/src/validation/index.js +1 -0
  38. package/src/validation/index.js.map +1 -1
  39. package/src/validation/validation.factory.d.ts +2 -0
  40. package/src/validation/validation.factory.js +19 -0
  41. package/src/validation/validation.factory.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,507 @@
1
+ <p align="center">
2
+ <a href="https://dismissible.io" target="_blank"><img src="../docs/images/dismissible_logo.png" width="120" alt="Dismissible" /></a>
3
+ </p>
4
+
5
+ <p align="center">Never Show The Same Thing Twice!</p>
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/@dismissible/nestjs-api" target="_blank"><img src="https://img.shields.io/npm/v/@dismissible/nestjs-api.svg" alt="NPM Version" /></a>
8
+ <a href="https://github.com/dismissibleio/dismissible-api/blob/main/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@dismissible/nestjs-api.svg" alt="Package License" /></a>
9
+ <a href="https://www.npmjs.com/package/@dismissible/nestjs-api" target="_blank"><img src="https://img.shields.io/npm/dm/@dismissible/nestjs-api.svg" alt="NPM Downloads" /></a>
10
+ <a href="https://github.com/dismissibleio/dismissible-api" target="_blank"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/dismissibleio/dismissible-api/release.yml"></a>
11
+ <a href="https://paypal.me/joshstuartx" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
12
+ </p>
13
+
14
+ Dismissible manages the state of your UI elements across sessions, so your users see what matters, once! No more onboarding messages reappearing on every tab, no more notifications haunting users across devices. Dismissible syncs dismissal state everywhere, so every message is intentional, never repetitive.
15
+
16
+ **Visit [dismissible.io](https://dismissible.io)** for documentation, help, and support.
17
+
18
+ ## Overview
19
+
20
+ The NestJS Dismissible API module:
21
+
22
+ - Maintains all the dismissal state for dismissible items
23
+ - Provides a REST API for managing dismissible items (get, dismiss, restore)
24
+ - Supports JWT authentication via OIDC-compliant identity providers
25
+ - Supports multiple storage backends (PostgreSQL, DynamoDB, memory)
26
+ - Includes comprehensive security features (Helmet, CORS, validation)
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @dismissible/nestjs-api
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Standalone Application
37
+
38
+ This contains all the bells and whistles out of the box. You create an entry point that uses the `DismissibleNestFactory` to create a new NestJS application:
39
+
40
+ ```typescript
41
+ // src/main.ts
42
+ import { DismissibleNestFactory } from '@dismissible/nestjs-api';
43
+ import { CustomModule } from './custom.module';
44
+
45
+ async function bootstrap() {
46
+ const app = await DismissibleNestFactory.create({
47
+ imports: [CustomModule],
48
+ });
49
+ // The app returned is an extended version of the INestApplication app.
50
+ // So you can use it in all the ways NestJS provides.
51
+ await app.start();
52
+ }
53
+
54
+ bootstrap();
55
+ ```
56
+
57
+ This is an extension of `NestFactory.create()`, but also wires up all our config, swagger, storage, security and much more.
58
+
59
+ #### DismissibleNestFactory Config
60
+
61
+ ```typescript
62
+ interface IDismissibleNestFactoryOptions {
63
+ configPath?: string; // Path to config files
64
+ schema?: new () => DefaultAppConfig; // Config validation schema
65
+ logger?: Type<IDismissibleLogger>; // Custom logger implementation
66
+ imports?: DynamicModule[]; // Additional modules to import
67
+ }
68
+ ```
69
+
70
+ ### NestJS Module
71
+
72
+ Import the DismissibleModule into your existing NestJS application:
73
+
74
+ ```typescript
75
+ import { Module } from '@nestjs/common';
76
+ import { DismissibleModule } from '@dismissible/nestjs-api';
77
+ import { PostgresStorageModule } from '@dismissible/nestjs-postgres-storage';
78
+ import { DynamoDbStorageModule } from '@dismissible/nestjs-dynamodb-storage';
79
+ import { MemoryStorageModule } from '@dismissible/nestjs-storage';
80
+
81
+ @Module({
82
+ imports: [
83
+ // PostgreSQL Storage
84
+ DismissibleModule.forRoot({
85
+ storage: PostgresStorageModule.forRoot({
86
+ connectionString: process.env.DATABASE_URL,
87
+ }),
88
+ }),
89
+
90
+ // Or DynamoDB Storage
91
+ DismissibleModule.forRoot({
92
+ storage: DynamoDbStorageModule.forRoot({
93
+ tableName: process.env.DYNAMODB_TABLE,
94
+ region: process.env.AWS_REGION,
95
+ accessKeyId:
96
+ }),
97
+ }),
98
+
99
+ // Or In-Memory Storage
100
+ DismissibleModule.forRoot({
101
+ storage: MemoryStorageModule.forRoot(),
102
+ }),
103
+ ],
104
+ })
105
+ export class AppModule {}
106
+ ```
107
+
108
+ ## API Endpoints
109
+
110
+ The API provides the following REST endpoints:
111
+
112
+ | Endpoint | Method | Description |
113
+ | ----------------------------------- | ------ | ----------------------------------- |
114
+ | `/health` | GET | Health check endpoint |
115
+ | `/v1/users/{userId}/items/{itemId}` | GET | Get or create a dismissible item |
116
+ | `/v1/users/{userId}/items/{itemId}` | DELETE | Dismiss an item |
117
+ | `/v1/users/{userId}/items/{itemId}` | POST | Restore a previously dismissed item |
118
+
119
+ Enable Swagger documentation by setting `DISMISSIBLE_SWAGGER_ENABLED=true` and visit `/docs` for interactive API docs.
120
+
121
+ ## Configuration
122
+
123
+ ### Core Settings
124
+
125
+ | Variable | Description | Default |
126
+ | ------------------------------- | ------------------------------------------------------------------------- | ---------- |
127
+ | `DISMISSIBLE_PORT` | Port the API listens on | `3001` |
128
+ | `DISMISSIBLE_STORAGE_TYPE` | Storage backend type eg. `postgres`, `dynamodb`, `memory` | `postgres` |
129
+ | `DISMISSIBLE_RUN_STORAGE_SETUP` | Run database migrations on startup. Each storage type will have their own | `false` |
130
+
131
+ ### Storage Settings
132
+
133
+ #### PostgreSQL
134
+
135
+ PostgreSQL is a fantastic open-source relational database. It's highly performant and the majority of cloud providers and hosting companies provide a managed services. To enable PostgreSQL, set `DISMISSIBLE_STORAGE_TYPE=postgres` and provide a connection string.
136
+
137
+ | Variable | Description | Default |
138
+ | ------------------------------------------------ | ---------------------------- | ---------- |
139
+ | `DISMISSIBLE_STORAGE_POSTGRES_CONNECTION_STRING` | PostgreSQL connection string | _required_ |
140
+
141
+ > [!TIP]
142
+ > An example connection string is: `postgresql://user:password@domain.com:port/database-name` eg. `postgresql://postgres:postgres@localhost:5432/dismissible`
143
+
144
+ #### DynamoDB
145
+
146
+ DynamoDB is an infintely scalable document store as a service provided by AWS. It's perfect if you do not want to manage infrastructure or need to run at scale. To enable DynamoDB, set `DISMISSIBLE_STORAGE_TYPE=dynamodb` and pass in the following:
147
+
148
+ | Variable | Description | Default |
149
+ | ---------------------------------------------------- | -------------------------------------- | ------------------- |
150
+ | `DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME` | DynamoDB table name | `dismissible-items` |
151
+ | `DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION` | AWS region | `us-east-1` |
152
+ | `DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID` | AWS access key ID | - |
153
+ | `DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY` | AWS secret access key | - |
154
+ | `DISMISSIBLE_STORAGE_DYNAMODB_AWS_SESSION_TOKEN` | AWS session token | - |
155
+ | `DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT` | LocalStack/DynamoDB Local endpoint URL | - |
156
+
157
+ Depending how your IAMs is configured will depend what config you need to pass. Review the official [AWS documentation](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-your-credentials.html) to determine the config you need to pass.
158
+
159
+ #### In-Memory
160
+
161
+ No configuration required. This storage backend is intended for testing and development purposes only.
162
+
163
+ ### Swagger
164
+
165
+ When enabled, Swagger documentation is published to the path specified by `DISMISSIBLE_SWAGGER_PATH` (defaults to `/docs`). The OpenAPI schema is available at `${PATH}-json` (JSON format) and `${PATH}-yaml` (YAML format). For example:
166
+
167
+ - `DISMISSIBLE_SWAGGER_PATH` default is `docs`: `/docs`, `/docs-json`, `/docs-yaml`
168
+ - `swagger`: `/swagger`, `/swagger-json`, `/swagger-yaml`
169
+ - `swagger/docs`: `/swagger/docs`, `/swagger/docs-json`, `/swagger/docs-yaml`
170
+
171
+ | Variable | Description | Default |
172
+ | ----------------------------- | ------------------------------------------------------------------------- | -------- |
173
+ | `DISMISSIBLE_SWAGGER_ENABLED` | Enable Swagger API docs at the path defined in `DISMISSIBLE_SWAGGER_PATH` | `false` |
174
+ | `DISMISSIBLE_SWAGGER_PATH` | Path for Swagger docs eg. `/docs` | `"docs"` |
175
+
176
+ ### JWT Authentication
177
+
178
+ You can secure your API with any OIDC-compliant provider (Auth0, Okta, Keycloak, etc.). This ensures that no one can fill your dismissible service with junk.
179
+
180
+ | Variable | Description | Default |
181
+ | ------------------------------------------ | ------------------------------------------- | --------------------- |
182
+ | `DISMISSIBLE_JWT_AUTH_ENABLED` | Enable JWT authentication | `false` |
183
+ | `DISMISSIBLE_JWT_AUTH_WELL_KNOWN_URL` | OIDC discovery URL | _required_ if enabled |
184
+ | `DISMISSIBLE_JWT_AUTH_ISSUER` | Expected JWT issuer | _-_ |
185
+ | `DISMISSIBLE_JWT_AUTH_AUDIENCE` | Expected JWT audience | _-_ |
186
+ | `DISMISSIBLE_JWT_AUTH_ALGORITHMS` | Allowed algorithms (comma-separated) | `RS256` |
187
+ | `DISMISSIBLE_JWT_AUTH_JWKS_CACHE_DURATION` | JWKS cache duration (ms) | `600000` |
188
+ | `DISMISSIBLE_JWT_AUTH_REQUEST_TIMEOUT` | Request timeout (ms) | `30000` |
189
+ | `DISMISSIBLE_JWT_AUTH_PRIORITY` | Hook priority (lower runs first) | `-100` |
190
+ | `DISMISSIBLE_JWT_AUTH_MATCH_USER_ID` | Enable user ID matching | `true` |
191
+ | `DISMISSIBLE_JWT_AUTH_USER_ID_CLAIM` | JWT claim key for user ID matching | `sub` |
192
+ | `DISMISSIBLE_JWT_AUTH_USER_ID_MATCH_TYPE` | Match method: `exact`, `substring`, `regex` | `exact` |
193
+ | `DISMISSIBLE_JWT_AUTH_USER_ID_MATCH_REGEX` | Regex pattern (required if type=regex) | _-_ |
194
+
195
+ ### CORS Settings
196
+
197
+ Cross-Origin Resource Sharing (CORS) controls which domains can access your API resources from browser-based clients. By default, only requests from `localhost:3000` are allowed, which is only helpful for local testing.
198
+
199
+ > [!TIP]
200
+ > Configure `DISMISSIBLE_CORS_ORIGINS` with the domains your frontend applications run on. Be careful with wildcard origins (`*`) as they may expose your API to unauthorized cross-origin requests, especially when credentials are enabled.
201
+
202
+ | Variable | Description | Default |
203
+ | ---------------------------------- | ---------------------------------- | ----------------------------------------- |
204
+ | `DISMISSIBLE_CORS_ENABLED` | Enable CORS | `true` |
205
+ | `DISMISSIBLE_CORS_ORIGINS` | Allowed origins (comma-separated) | `http://localhost:3000` |
206
+ | `DISMISSIBLE_CORS_METHODS` | Allowed HTTP methods | `GET,POST,DELETE,OPTIONS` |
207
+ | `DISMISSIBLE_CORS_ALLOWED_HEADERS` | Allowed headers | `Content-Type,Authorization,x-request-id` |
208
+ | `DISMISSIBLE_CORS_CREDENTIALS` | Allow credentials | `true` |
209
+ | `DISMISSIBLE_CORS_MAX_AGE` | Preflight cache duration (seconds) | `86400` |
210
+
211
+ ### Security Headers (Helmet)
212
+
213
+ Helmet is a collection of middleware functions that set security-related HTTP headers. These headers help protect your API against common vulnerabilities like cross-site scripting (XSS), clickjacking, and other attacks by instructing browsers to enforce security policies. For most production deployments, keeping Helmet enabled is recommended. You may disable it if you have specific requirements for custom headers or are behind a separate security layer (e.g., a CDN or WAF that handles these protections).
214
+
215
+ | Variable | Description | Default |
216
+ | -------------------------------------------- | ----------------------------------- | ---------- |
217
+ | `DISMISSIBLE_HELMET_ENABLED` | Enable Helmet security headers | `true` |
218
+ | `DISMISSIBLE_HELMET_CSP` | Enable Content Security Policy | `true` |
219
+ | `DISMISSIBLE_HELMET_COEP` | Enable Cross-Origin Embedder Policy | `true` |
220
+ | `DISMISSIBLE_HELMET_HSTS_MAX_AGE` | HSTS max age (seconds) | `31536000` |
221
+ | `DISMISSIBLE_HELMET_HSTS_INCLUDE_SUBDOMAINS` | Include subdomains in HSTS | `true` |
222
+ | `DISMISSIBLE_HELMET_HSTS_PRELOAD` | Enable HSTS preload | `false` |
223
+
224
+ ### Validation Settings
225
+
226
+ This config prevents internal error messages from leaking out. In production environements, these should all be set to `true`, but in lower environements they can be set to `false` to help with debugging.
227
+
228
+ | Variable | Description | Default |
229
+ | ----------------------------------------------- | ------------------------------- | ------- |
230
+ | `DISMISSIBLE_VALIDATION_DISABLE_ERROR_MESSAGES` | Hide detailed validation errors | `true` |
231
+ | `DISMISSIBLE_VALIDATION_WHITELIST` | Strip unknown properties | `true` |
232
+ | `DISMISSIBLE_VALIDATION_FORBID_NON_WHITELISTED` | Reject unknown properties | `true` |
233
+ | `DISMISSIBLE_VALIDATION_TRANSFORM` | Auto-transform payloads to DTOs | `true` |
234
+
235
+ ## NestJS API Module Configuration
236
+
237
+ The [Dismissible NestJS Module API library](https://www.npmjs.com/package/@dismissible/nestjs-api) has an alternative way to set configuration by using a `.env.yaml` file eg. [`api/config/.env.yaml`](api/config/.env.yaml)
238
+
239
+ ### Example YAML file
240
+
241
+ Below is the default config file which contains both default values and a way to interpolate the [environment variables](#all-configuration-options) defined above.
242
+
243
+ ```yaml
244
+ server:
245
+ port: ${DISMISSIBLE_PORT:-3001}
246
+
247
+ swagger:
248
+ enabled: ${DISMISSIBLE_SWAGGER_ENABLED:-true}
249
+ path: ${DISMISSIBLE_SWAGGER_PATH:-docs}
250
+
251
+ helmet:
252
+ enabled: ${DISMISSIBLE_HELMET_ENABLED:-true}
253
+ contentSecurityPolicy: ${DISMISSIBLE_HELMET_CSP:-false}
254
+ crossOriginEmbedderPolicy: ${DISMISSIBLE_HELMET_COEP:-false}
255
+ hstsMaxAge: ${DISMISSIBLE_HELMET_HSTS_MAX_AGE:-31536000}
256
+ hstsIncludeSubDomains: ${DISMISSIBLE_HELMET_HSTS_INCLUDE_SUBDOMAINS:-true}
257
+ hstsPreload: ${DISMISSIBLE_HELMET_HSTS_PRELOAD:-false}
258
+
259
+ cors:
260
+ enabled: ${DISMISSIBLE_CORS_ENABLED:-true}
261
+ origins: ${DISMISSIBLE_CORS_ORIGINS:-http://localhost:3001,http://localhost:5173}
262
+ methods: ${DISMISSIBLE_CORS_METHODS:-GET,POST,DELETE,OPTIONS}
263
+ allowedHeaders: ${DISMISSIBLE_CORS_ALLOWED_HEADERS:-Content-Type,Authorization,x-request-id}
264
+ credentials: ${DISMISSIBLE_CORS_CREDENTIALS:-true}
265
+ maxAge: ${DISMISSIBLE_CORS_MAX_AGE:-86400}
266
+
267
+ storage:
268
+ # postgres | dynamodb | memory
269
+ type: ${DISMISSIBLE_STORAGE_TYPE:-postgres}
270
+ postgres:
271
+ connectionString: ${DISMISSIBLE_STORAGE_POSTGRES_CONNECTION_STRING:-postgresql://postgres:postgres@localhost:5432/dismissible}
272
+ dynamodb:
273
+ tableName: ${DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME:-dismissible-items}
274
+ region: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION:-us-east-1}
275
+ endpoint: ${DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT:-}
276
+ accessKeyId: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID:-}
277
+ secretAccessKey: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY:-}
278
+ sessionToken: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_SESSION_TOKEN:-}
279
+
280
+ jwtAuth:
281
+ enabled: ${DISMISSIBLE_JWT_AUTH_ENABLED:-false}
282
+ wellKnownUrl: ${DISMISSIBLE_JWT_AUTH_WELL_KNOWN_URL:-}
283
+ issuer: ${DISMISSIBLE_JWT_AUTH_ISSUER:-}
284
+ audience: ${DISMISSIBLE_JWT_AUTH_AUDIENCE:-}
285
+ algorithms:
286
+ - ${DISMISSIBLE_JWT_AUTH_ALGORITHMS:-RS256}
287
+ jwksCacheDuration: ${DISMISSIBLE_JWT_AUTH_JWKS_CACHE_DURATION:-600000}
288
+ requestTimeout: ${DISMISSIBLE_JWT_AUTH_REQUEST_TIMEOUT:-30000}
289
+ priority: ${DISMISSIBLE_JWT_AUTH_PRIORITY:--100}
290
+ matchUserId: ${DISMISSIBLE_JWT_AUTH_MATCH_USER_ID:-true}
291
+ userIdClaim: ${DISMISSIBLE_JWT_AUTH_USER_ID_CLAIM:-sub}
292
+ userIdMatchType: ${DISMISSIBLE_JWT_AUTH_USER_ID_MATCH_TYPE:-exact}
293
+ userIdMatchRegex: ${DISMISSIBLE_JWT_AUTH_USER_ID_MATCH_REGEX:-}
294
+
295
+ validation:
296
+ disableErrorMessages: ${DISMISSIBLE_VALIDATION_DISABLE_ERROR_MESSAGES:-true}
297
+ whitelist: ${DISMISSIBLE_VALIDATION_WHITELIST:-true}
298
+ forbidNonWhitelisted: ${DISMISSIBLE_VALIDATION_FORBID_NON_WHITELISTED:-true}
299
+ transform: ${DISMISSIBLE_VALIDATION_TRANSFORM:-true}
300
+ ```
301
+
302
+ ### Usage
303
+
304
+ When creating your application, you can specify the directory that contains your YAML config files eg.
305
+
306
+ If we have a structure like:
307
+
308
+ ```
309
+ config/
310
+ └── .env.yaml
311
+ src/
312
+ ├── main.ts
313
+ └── custom.module.ts
314
+ ```
315
+
316
+ And our `main.ts` file is:
317
+
318
+ ```typescript
319
+ import { DismissibleNestFactory } from '@dismissible/nestjs-api';
320
+ import { CustomModule } from './custom.module';
321
+
322
+ const configPath = join(__dirname, '../config');
323
+
324
+ async function bootstrap() {
325
+ const app = await DismissibleNestFactory.create({
326
+ imports: [CustomModule],
327
+ configPath,
328
+ });
329
+ await app.start();
330
+ }
331
+
332
+ bootstrap();
333
+ ```
334
+
335
+ ### Environment Aware
336
+
337
+ You can maintain multiple config files which are controlled by `NODE_ENV`. The format needs to follow:
338
+
339
+ `.env.{NODE_ENV}.yaml`
340
+
341
+ eg. If we have the following files:
342
+
343
+ ```
344
+ config/
345
+ └── .env.yaml
346
+ └── .env.dev.yaml
347
+ └── .env.staging.yaml
348
+ └── .env.production.yaml
349
+ ```
350
+
351
+ Then if we pass `NODE_ENV=production`, the `.env.production.yaml` file will be loaded automaticall. This is super handy to maintain separate config files per environment.
352
+
353
+ ### Environment Variable Interpolation
354
+
355
+ You can specify environment variables within the YAML file, including a default, and they will be interpolated at run time.
356
+
357
+ The fomat needs to follow: `${ENV_VAR:-default-value}`.
358
+
359
+ > [!WARNING]
360
+ > If you don't specify a `default-value`, and only `${ENV_VAR}`, then if the config is required but not present, and error will be thrown.
361
+
362
+ > [!TIP]
363
+ > Use interpolation of environment variables for sensitive config like secrets and connection strings.
364
+
365
+ ### Extending the Configuration
366
+
367
+ You can extend the default `AppConfig` with additional configuration options by creating your own config schema and passing it via the `schema` option. This is useful when you need custom settings beyond what's provided by default.
368
+
369
+ ```typescript
370
+ // custom.config.ts
371
+ import { AppConfig } from '@dismissible/nestjs-api';
372
+ import { IsString, IsOptional } from 'class-validator';
373
+
374
+ export class CustomConfig extends AppConfig {
375
+ @IsString()
376
+ @IsOptional()
377
+ myCustomSetting?: string;
378
+ }
379
+ ```
380
+
381
+ ```typescript
382
+ // src/main.ts
383
+ import { DismissibleNestFactory } from '@dismissible/nestjs-api';
384
+ import { CustomConfig } from './custom.config';
385
+
386
+ async function bootstrap() {
387
+ const app = await DismissibleNestFactory.create({
388
+ configPath: './config',
389
+ schema: CustomConfig, // Pass your extended config schema
390
+ });
391
+ await app.start();
392
+ }
393
+
394
+ bootstrap();
395
+ ```
396
+
397
+ Then add your custom settings to the YAML file:
398
+
399
+ ```yaml
400
+ # @api/config/.env.yaml
401
+ server:
402
+ port: 3001
403
+ myCustomSetting: 'custom-value'
404
+ # ... rest of default config
405
+ ```
406
+
407
+ The nest-typed-config library will automatically merge your custom config with the defaults, and your custom config class will be available for injection throughout your NestJS application.
408
+
409
+ ## Running the Application
410
+
411
+ ### Development
412
+
413
+ ```bash
414
+ # Start the API
415
+ npm run start
416
+
417
+ # Start with hot reload
418
+ npm run start:dev
419
+ ```
420
+
421
+ ### Docker
422
+
423
+ The fastest way to get started:
424
+
425
+ ```bash
426
+ # PostgreSQL (default)
427
+ docker run -p 3001:3001 \
428
+ -e DISMISSIBLE_STORAGE_TYPE=postgres \
429
+ -e DISMISSIBLE_RUN_STORAGE_SETUP=true \
430
+ -e DISMISSIBLE_STORAGE_POSTGRES_CONNECTION_STRING="postgresql://user:password@host:5432/dismissible" \
431
+ dismissibleio/dismissible-api:latest
432
+
433
+ # DynamoDB
434
+ docker run -p 3001:3001 \
435
+ -e DISMISSIBLE_STORAGE_TYPE=dynamodb \
436
+ -e DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME="items" \
437
+ -e DISMISSIBLE_STORAGE_DYNAMODB_REGION="us-east-1" \
438
+ dismissibleio/dismissible-api:latest
439
+
440
+ # In-Memory (development/testing)
441
+ docker run -p 3001:3001 \
442
+ -e DISMISSIBLE_STORAGE_TYPE=memory \
443
+ dismissibleio/dismissible-api:latest
444
+ ```
445
+
446
+ See [docs/DOCKER.md](../docs/DOCKER.md) for complete deployment instructions.
447
+
448
+ ## Storage Setup
449
+
450
+ **CRITICAL**: The storage schema must be initialized before starting the API. The setup process depends on your chosen storage backend.
451
+
452
+ ### PostgreSQL Setup
453
+
454
+ ```bash
455
+ # Generate Prisma client
456
+ npm run storage:init
457
+
458
+ # Run migrations (development)
459
+ npm run prisma:migrate:dev
460
+
461
+ # Deploy migrations (production)
462
+ npm run prisma:migrate:deploy
463
+
464
+ # Push schema (development only)
465
+ npm run prisma:db:push
466
+ ```
467
+
468
+ ### DynamoDB Setup
469
+
470
+ ```bash
471
+ # Create the DynamoDB table
472
+ npm run storage:init
473
+ npm run dynamodb:setup
474
+ ```
475
+
476
+ The DynamoDB table will be created with the following schema:
477
+
478
+ - Partition key: `userId` (String)
479
+ - Sort key: `itemId` (String)
480
+
481
+ ### In-Memory Setup
482
+
483
+ No setup required. The storage is ready to use immediately.
484
+
485
+ ## Features
486
+
487
+ - **REST API**: Clean, simple endpoints for managing dismissible items
488
+ - **JWT Authentication**: Secure your API with OIDC-compliant identity providers (Auth0, Okta, Keycloak)
489
+ - **Multiple Storage Backends**: PostgreSQL, DynamoDB, or memory storage
490
+ - **Swagger Documentation**: Interactive API docs when enabled
491
+ - **Security**: Helmet for security headers, CORS configuration, request validation
492
+ - **TypeScript**: Full TypeScript support with type safety
493
+ - **Docker Ready**: Multi-stage Dockerfile for production deployments
494
+
495
+ ## Related Packages
496
+
497
+ - `@dismissible/nestjs-core` - Main dismissible code service and logic
498
+ - `@dismissible/nestjs-postgres-storage` - PostgreSQL storage adapter
499
+ - `@dismissible/nestjs-dynamodb-storage` - DynamoDB storage adapter
500
+ - `@dismissible/nestjs-storage` - Storage interface definition
501
+ - `@dismissible/nestjs-logger` - Logging abstraction
502
+ - `@dismissible/nestjs-jwt-auth-hook` - JWT authentication hook
503
+ - `@dismissible/react-client` - React client for the Dismissible system
504
+
505
+ ## License
506
+
507
+ MIT
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ const { execSync } = require('child_process');
3
+
4
+ const storageType = process.env.DISMISSIBLE_STORAGE_TYPE || '';
5
+
6
+ switch (storageType) {
7
+ case 'postgres':
8
+ console.log('Running Postgres storage setup...');
9
+ execSync('npm run storage:setup:postgres', { stdio: 'inherit' });
10
+ break;
11
+ case 'dynamodb':
12
+ console.log('Running DynamoDB storage setup...');
13
+ execSync('npm run storage:setup:dynamodb', { stdio: 'inherit' });
14
+ break;
15
+ default:
16
+ if (storageType) {
17
+ console.log(`Warning: Unknown storage type '${storageType}'. Skipping storage setup.`);
18
+ } else {
19
+ console.log('DISMISSIBLE_STORAGE_TYPE not set. Skipping storage setup.');
20
+ }
21
+ process.exit(0);
22
+ }
@@ -0,0 +1,38 @@
1
+ server:
2
+ port: 3001
3
+
4
+ swagger:
5
+ enabled: true
6
+ path: docs
7
+
8
+ helmet:
9
+ enabled: false
10
+
11
+ cors:
12
+ enabled: true
13
+ origins: http://localhost:5173
14
+ methods: GET,POST,DELETE,OPTIONS
15
+ allowedHeaders: Content-Type,Authorization,x-request-id
16
+ credentials: true
17
+ maxAge: 86400
18
+
19
+ storage:
20
+ # postgres | dynamodb | memory
21
+ type: ${DISMISSIBLE_STORAGE_TYPE:-memory}
22
+ postgres:
23
+ connectionString: postgresql://postgres:postgres@localhost:5432/dismissible
24
+ dynamodb:
25
+ tableName: dismissible-items
26
+ region: us-east-1
27
+ endpoint: http://localhost:4566
28
+ accessKeyId: test
29
+ secretAccessKey: test
30
+
31
+ jwtAuth:
32
+ enabled: false
33
+
34
+ validation:
35
+ disableErrorMessages: false
36
+ whitelist: false
37
+ forbidNonWhitelisted: false
38
+ transform: false
package/config/.env.yaml CHANGED
@@ -15,25 +15,38 @@ helmet:
15
15
 
16
16
  cors:
17
17
  enabled: ${DISMISSIBLE_CORS_ENABLED:-true}
18
- origins: ${DISMISSIBLE_CORS_ORIGINS:-http://localhost:3001,http://localhost:5173}
18
+ origins: ${DISMISSIBLE_CORS_ORIGINS:-}
19
19
  methods: ${DISMISSIBLE_CORS_METHODS:-GET,POST,DELETE,OPTIONS}
20
20
  allowedHeaders: ${DISMISSIBLE_CORS_ALLOWED_HEADERS:-Content-Type,Authorization,x-request-id}
21
21
  credentials: ${DISMISSIBLE_CORS_CREDENTIALS:-true}
22
22
  maxAge: ${DISMISSIBLE_CORS_MAX_AGE:-86400}
23
23
 
24
- db:
25
- connectionString: ${DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING:-postgresql://postgres:postgres@localhost:5432/dismissible}
24
+ storage:
25
+ # postgres | dynamodb | memory
26
+ type: ${DISMISSIBLE_STORAGE_TYPE:-memory}
27
+ postgres:
28
+ connectionString: ${DISMISSIBLE_STORAGE_POSTGRES_CONNECTION_STRING:-}
29
+ dynamodb:
30
+ tableName: ${DISMISSIBLE_STORAGE_DYNAMODB_TABLE_NAME:-dismissible-items}
31
+ region: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_REGION:-us-east-1}
32
+ endpoint: ${DISMISSIBLE_STORAGE_DYNAMODB_ENDPOINT:-}
33
+ accessKeyId: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_ACCESS_KEY_ID:-}
34
+ secretAccessKey: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_SECRET_ACCESS_KEY:-}
35
+ sessionToken: ${DISMISSIBLE_STORAGE_DYNAMODB_AWS_SESSION_TOKEN:-}
26
36
 
27
37
  jwtAuth:
28
38
  enabled: ${DISMISSIBLE_JWT_AUTH_ENABLED:-false}
29
39
  wellKnownUrl: ${DISMISSIBLE_JWT_AUTH_WELL_KNOWN_URL:-}
30
40
  issuer: ${DISMISSIBLE_JWT_AUTH_ISSUER:-}
31
41
  audience: ${DISMISSIBLE_JWT_AUTH_AUDIENCE:-}
32
- algorithms:
33
- - ${DISMISSIBLE_JWT_AUTH_ALGORITHMS:-RS256}
42
+ algorithms: ${DISMISSIBLE_JWT_AUTH_ALGORITHMS:-RS256}
34
43
  jwksCacheDuration: ${DISMISSIBLE_JWT_AUTH_JWKS_CACHE_DURATION:-600000}
35
44
  requestTimeout: ${DISMISSIBLE_JWT_AUTH_REQUEST_TIMEOUT:-30000}
36
45
  priority: ${DISMISSIBLE_JWT_AUTH_PRIORITY:--100}
46
+ matchUserId: ${DISMISSIBLE_JWT_AUTH_MATCH_USER_ID:-true}
47
+ userIdMatchType: ${DISMISSIBLE_JWT_AUTH_USER_ID_MATCH_TYPE:-exact}
48
+ userIdMatchRegex: ${DISMISSIBLE_JWT_AUTH_USER_ID_MATCH_REGEX:-}
49
+ userIdClaim: ${DISMISSIBLE_JWT_AUTH_USER_ID_CLAIM:-sub}
37
50
 
38
51
  validation:
39
52
  disableErrorMessages: ${DISMISSIBLE_VALIDATION_DISABLE_ERROR_MESSAGES:-true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dismissible/nestjs-api",
3
- "version": "1.0.2",
3
+ "version": "1.0.3-alpha.0b30e6d.0",
4
4
  "description": "Dismissible API application",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -11,36 +11,44 @@
11
11
  "types": "./src/index.d.ts"
12
12
  }
13
13
  },
14
+ "files": [
15
+ "src",
16
+ "bin",
17
+ "config",
18
+ "README.md"
19
+ ],
14
20
  "bin": {
15
- "dismissible-api": "./src/main.js"
21
+ "dismissible-api": "./src/main.js",
22
+ "dismissible-storage-setup": "./bin/dismissible-storage-setup.js"
16
23
  },
17
24
  "scripts": {
18
25
  "start": "node src/main.js",
19
- "prisma:generate": "npx dismissible-prisma generate",
20
- "prisma:migrate:deploy": "npx dismissible-prisma migrate deploy",
21
- "prisma:migrate:dev": "npx dismissible-prisma migrate dev",
22
- "prisma:db:push": "npx dismissible-prisma db push",
23
- "prisma:studio": "npx dismissible-prisma studio"
26
+ "storage:init": "npm run storage:postgres:init",
27
+ "storage:postgres:init": "npx dismissible-prisma generate",
28
+ "storage:setup": "npm run storage:setup:postgres && npm run storage:setup:dynamodb",
29
+ "storage:setup:postgres": "npx dismissible-prisma migrate deploy",
30
+ "storage:setup:dynamodb": "npx dismissible-dynamodb-setup"
24
31
  },
25
32
  "dependencies": {
26
- "@dismissible/nestjs-dismissible": "^1.0.2",
27
- "@dismissible/nestjs-dismissible-item": "^1.0.2",
28
- "@dismissible/nestjs-jwt-auth-hook": "^1.0.2",
29
- "@dismissible/nestjs-storage": "^1.0.2",
30
- "@dismissible/nestjs-postgres-storage": "^1.0.2",
31
- "@dismissible/nestjs-logger": "^1.0.2",
32
- "@nestjs/common": "^11.1.10",
33
- "@nestjs/core": "^11.1.10",
34
- "@nestjs/platform-fastify": "^11.1.10",
35
- "fastify": "^5.6.2",
36
- "@fastify/helmet": "^13.0.2",
37
- "@fastify/static": "^8.3.0",
38
- "@nestjs/swagger": "^11.2.3",
39
- "class-transformer": "^0.5.1",
40
- "class-validator": "^0.14.3",
41
- "nest-typed-config": "^2.10.1",
42
- "reflect-metadata": "^0.2.2",
43
- "rxjs": "^7.8.2"
33
+ "@dismissible/nestjs-core": "1.0.3-alpha.0b30e6d.0",
34
+ "@dismissible/nestjs-item": "1.0.3-alpha.0b30e6d.0",
35
+ "@dismissible/nestjs-jwt-auth-hook": "1.0.3-alpha.0b30e6d.0",
36
+ "@dismissible/nestjs-storage": "1.0.3-alpha.0b30e6d.0",
37
+ "@dismissible/nestjs-postgres-storage": "1.0.3-alpha.0b30e6d.0",
38
+ "@dismissible/nestjs-dynamodb-storage": "1.0.3-alpha.0b30e6d.0",
39
+ "@dismissible/nestjs-logger": "1.0.3-alpha.0b30e6d.0",
40
+ "@nestjs/common": "11.1.11",
41
+ "@nestjs/core": "11.1.11",
42
+ "@nestjs/platform-fastify": "11.1.11",
43
+ "fastify": "5.6.2",
44
+ "@fastify/helmet": "13.0.2",
45
+ "@fastify/static": "8.3.0",
46
+ "@nestjs/swagger": "11.2.3",
47
+ "class-transformer": "0.5.1",
48
+ "class-validator": "0.14.3",
49
+ "nest-typed-config": "2.10.1",
50
+ "reflect-metadata": "0.2.2",
51
+ "rxjs": "7.8.2"
44
52
  },
45
53
  "keywords": [
46
54
  "nestjs",
@@ -56,4 +64,4 @@
56
64
  "access": "public"
57
65
  },
58
66
  "type": "commonjs"
59
- }
67
+ }
@@ -6,7 +6,7 @@ const testing_1 = require("@nestjs/testing");
6
6
  const platform_fastify_1 = require("@nestjs/platform-fastify");
7
7
  const app_module_1 = require("./app.module");
8
8
  const app_setup_1 = require("./app.setup");
9
- const nestjs_postgres_storage_1 = require("@dismissible/nestjs-postgres-storage");
9
+ const nestjs_storage_1 = require("@dismissible/nestjs-storage");
10
10
  async function createTestApp(options) {
11
11
  let builder = testing_1.Test.createTestingModule({
12
12
  imports: [app_module_1.AppModule.forRoot(options?.moduleOptions)],
@@ -18,13 +18,14 @@ async function createTestApp(options) {
18
18
  const app = moduleFixture.createNestApplication(new platform_fastify_1.FastifyAdapter({
19
19
  bodyLimit: 10 * 1024, // 10kb
20
20
  }));
21
+ app.useLogger(false);
21
22
  await (0, app_setup_1.configureApp)(app);
22
23
  await app.init();
23
24
  await app.getHttpAdapter().getInstance().ready();
24
25
  return app;
25
26
  }
26
27
  async function cleanupTestData(app) {
27
- const prisma = app.get(nestjs_postgres_storage_1.PrismaService);
28
- await prisma.dismissibleItem.deleteMany({});
28
+ const storage = app.get(nestjs_storage_1.DISMISSIBLE_STORAGE_ADAPTER);
29
+ await storage.deleteAll();
29
30
  }
30
31
  //# sourceMappingURL=app-test.factory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-test.factory.js","sourceRoot":"","sources":["../../../api/src/app-test.factory.ts"],"names":[],"mappings":";;AAYA,sCAqBC;AAED,0CAGC;AAtCD,6CAA6D;AAE7D,+DAAkF;AAClF,6CAA2D;AAC3D,2CAA2C;AAC3C,kFAAqE;AAO9D,KAAK,UAAU,aAAa,CAAC,OAAwB;IAC1D,IAAI,OAAO,GAAG,cAAI,CAAC,mBAAmB,CAAC;QACrC,OAAO,EAAE,CAAC,sBAAS,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;KACrD,CAAC,CAAC;IAEH,IAAI,OAAO,EAAE,SAAS,EAAE,CAAC;QACvB,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,aAAa,CAAC,qBAAqB,CAC7C,IAAI,iCAAc,CAAC;QACjB,SAAS,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO;KAC9B,CAAC,CACH,CAAC;IACF,MAAM,IAAA,wBAAY,EAAC,GAAG,CAAC,CAAC;IACxB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAEjB,MAAM,GAAG,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC;IAEjD,OAAO,GAAG,CAAC;AACb,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,GAAqB;IACzD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,uCAAa,CAAC,CAAC;IACtC,MAAM,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"app-test.factory.js","sourceRoot":"","sources":["../../../api/src/app-test.factory.ts"],"names":[],"mappings":";;AAYA,sCAwBC;AAED,0CAGC;AAzCD,6CAA6D;AAE7D,+DAAkF;AAClF,6CAA2D;AAC3D,2CAA2C;AAC3C,gEAA+F;AAOxF,KAAK,UAAU,aAAa,CAAC,OAAwB;IAC1D,IAAI,OAAO,GAAG,cAAI,CAAC,mBAAmB,CAAC;QACrC,OAAO,EAAE,CAAC,sBAAS,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;KACrD,CAAC,CAAC;IAEH,IAAI,OAAO,EAAE,SAAS,EAAE,CAAC;QACvB,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,aAAa,CAAC,qBAAqB,CAC7C,IAAI,iCAAc,CAAC;QACjB,SAAS,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO;KAC9B,CAAC,CACH,CAAC;IAEF,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAErB,MAAM,IAAA,wBAAY,EAAC,GAAG,CAAC,CAAC;IACxB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAEjB,MAAM,GAAG,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC;IAEjD,OAAO,GAAG,CAAC;AACb,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,GAAqB;IACzD,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAsB,4CAA2B,CAAC,CAAC;IAC1E,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;AAC5B,CAAC"}
package/src/app.module.js CHANGED
@@ -8,8 +8,8 @@ const health_1 = require("./health");
8
8
  const config_1 = require("./config");
9
9
  const app_config_1 = require("./config/app.config");
10
10
  const path_1 = require("path");
11
- const nestjs_dismissible_1 = require("@dismissible/nestjs-dismissible");
12
- const nestjs_postgres_storage_1 = require("@dismissible/nestjs-postgres-storage");
11
+ const nestjs_core_1 = require("@dismissible/nestjs-core");
12
+ const dynamic_storage_module_1 = require("./storage/dynamic-storage.module");
13
13
  const nestjs_jwt_auth_hook_1 = require("@dismissible/nestjs-jwt-auth-hook");
14
14
  let AppModule = AppModule_1 = class AppModule {
15
15
  static forRoot(options) {
@@ -31,16 +31,15 @@ let AppModule = AppModule_1 = class AppModule {
31
31
  useFactory: (config) => config,
32
32
  inject: [nestjs_jwt_auth_hook_1.JwtAuthHookConfig],
33
33
  }),
34
- nestjs_dismissible_1.DismissibleModule.forRoot({
34
+ nestjs_core_1.DismissibleModule.forRoot({
35
35
  logger: options?.logger,
36
36
  hooks: [nestjs_jwt_auth_hook_1.JwtAuthHook],
37
- storage: nestjs_postgres_storage_1.PostgresStorageModule.forRootAsync({
38
- useFactory(config) {
39
- return {
40
- connectionString: config.connectionString,
41
- };
42
- },
43
- inject: [nestjs_postgres_storage_1.PostgresStorageConfig],
37
+ storage: dynamic_storage_module_1.DynamicStorageModule.forRootAsync({
38
+ // TODO: nestjs doesn't support optional dynamic modules.
39
+ // So instead, we are just using the env vars to switch between modules.
40
+ // This isn't ideal, but there's not a great option. I will look to see
41
+ // if we can raise an issue similar to this: https://github.com/nestjs/nest/issues/9868
42
+ storage: process.env.DISMISSIBLE_STORAGE_TYPE,
44
43
  }),
45
44
  }),
46
45
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"app.module.js","sourceRoot":"","sources":["../../../api/src/app.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAA6E;AAC7E,qCAAwC;AACxC,qCAAwC;AACxC,oDAAgD;AAChD,+BAA4B;AAC5B,wEAAoE;AAGpE,kFAAoG;AACpG,4EAI2C;AAUpC,IAAM,SAAS,iBAAf,MAAM,SAAS;IACpB,MAAM,CAAC,OAAO,CAAC,OAA0B;QACvC,OAAO;YACL,MAAM,EAAE,WAAS;YACjB,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;SACnC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,iBAAiB,CAAC,OAA0B;QACjD,OAAO;YACL,OAAO,EAAE;gBACP,qBAAY,CAAC,OAAO,CAAC;oBACnB,IAAI,EAAE,OAAO,EAAE,UAAU,IAAI,IAAA,WAAI,EAAC,SAAS,EAAE,WAAW,CAAC;oBACzD,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,sBAAS;iBACrC,CAAC;gBACF,qBAAY;gBACZ,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;gBAC3B,wCAAiB,CAAC,YAAY,CAAC;oBAC7B,UAAU,EAAE,CAAC,MAAyB,EAAE,EAAE,CAAC,MAAM;oBACjD,MAAM,EAAE,CAAC,wCAAiB,CAAC;iBAC5B,CAAC;gBACF,sCAAiB,CAAC,OAAO,CAAC;oBACxB,MAAM,EAAE,OAAO,EAAE,MAAM;oBACvB,KAAK,EAAE,CAAC,kCAAW,CAAC;oBACpB,OAAO,EAAE,+CAAqB,CAAC,YAAY,CAAC;wBAC1C,UAAU,CAAC,MAA6B;4BACtC,OAAO;gCACL,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;6BAC1C,CAAC;wBACJ,CAAC;wBACD,MAAM,EAAE,CAAC,+CAAqB,CAAC;qBAChC,CAAC;iBACH,CAAC;aACH;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AApCY,8BAAS;oBAAT,SAAS;IADrB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,SAAS,CAoCrB"}
1
+ {"version":3,"file":"app.module.js","sourceRoot":"","sources":["../../../api/src/app.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAA6E;AAC7E,qCAAwC;AACxC,qCAAwC;AACxC,oDAAgD;AAChD,+BAA4B;AAC5B,0DAA6D;AAG7D,6EAAwE;AACxE,4EAI2C;AAWpC,IAAM,SAAS,iBAAf,MAAM,SAAS;IACpB,MAAM,CAAC,OAAO,CAAC,OAA0B;QACvC,OAAO;YACL,MAAM,EAAE,WAAS;YACjB,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;SACnC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,iBAAiB,CAAC,OAA0B;QACjD,OAAO;YACL,OAAO,EAAE;gBACP,qBAAY,CAAC,OAAO,CAAC;oBACnB,IAAI,EAAE,OAAO,EAAE,UAAU,IAAI,IAAA,WAAI,EAAC,SAAS,EAAE,WAAW,CAAC;oBACzD,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,sBAAS;iBACrC,CAAC;gBACF,qBAAY;gBACZ,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;gBAC3B,wCAAiB,CAAC,YAAY,CAAC;oBAC7B,UAAU,EAAE,CAAC,MAAyB,EAAE,EAAE,CAAC,MAAM;oBACjD,MAAM,EAAE,CAAC,wCAAiB,CAAC;iBAC5B,CAAC;gBACF,+BAAiB,CAAC,OAAO,CAAC;oBACxB,MAAM,EAAE,OAAO,EAAE,MAAM;oBACvB,KAAK,EAAE,CAAC,kCAAW,CAAC;oBACpB,OAAO,EAAE,6CAAoB,CAAC,YAAY,CAAC;wBACzC,yDAAyD;wBACzD,0EAA0E;wBAC1E,yEAAyE;wBACzE,yFAAyF;wBACzF,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAuC;qBAC7D,CAAC;iBACH,CAAC;aACH;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AAnCY,8BAAS;oBAAT,SAAS;IADrB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,SAAS,CAmCrB"}
package/src/app.setup.js CHANGED
@@ -1,49 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.configureApp = configureApp;
4
- const tslib_1 = require("tslib");
5
- const common_1 = require("@nestjs/common");
6
- const helmet_1 = tslib_1.__importDefault(require("@fastify/helmet"));
7
4
  const swagger_1 = require("./swagger");
8
5
  const cors_1 = require("./cors");
9
- const helmet_2 = require("./helmet");
6
+ const helmet_1 = require("./helmet");
10
7
  const validation_1 = require("./validation");
8
+ const nestjs_logger_1 = require("@dismissible/nestjs-logger");
11
9
  async function configureApp(app) {
12
- const fastifyApp = app;
13
- const helmetConfig = app.get(helmet_2.HelmetConfig);
14
- if (helmetConfig.enabled) {
15
- await fastifyApp.register(helmet_1.default, {
16
- contentSecurityPolicy: helmetConfig.contentSecurityPolicy ?? true,
17
- crossOriginEmbedderPolicy: helmetConfig.crossOriginEmbedderPolicy ?? true,
18
- hsts: {
19
- maxAge: helmetConfig.hstsMaxAge ?? 31536000,
20
- includeSubDomains: helmetConfig.hstsIncludeSubDomains ?? true,
21
- preload: helmetConfig.hstsPreload ?? false,
22
- },
23
- });
24
- }
25
- const corsConfig = app.get(cors_1.CorsConfig);
26
- if (corsConfig.enabled) {
27
- app.enableCors({
28
- origin: corsConfig.origins ?? ['http://localhost:3001', 'http://localhost:5173'],
29
- methods: corsConfig.methods ?? ['GET', 'POST', 'DELETE', 'OPTIONS'],
30
- allowedHeaders: corsConfig.allowedHeaders ?? [
31
- 'Content-Type',
32
- 'Authorization',
33
- 'x-request-id',
34
- ],
35
- credentials: corsConfig.credentials ?? true,
36
- maxAge: corsConfig.maxAge ?? 86400,
37
- });
38
- }
39
- const validationConfig = app.get(validation_1.ValidationConfig);
40
- app.useGlobalPipes(new common_1.ValidationPipe({
41
- whitelist: validationConfig.whitelist ?? true,
42
- forbidNonWhitelisted: validationConfig.forbidNonWhitelisted ?? true,
43
- transform: validationConfig.transform ?? true,
44
- disableErrorMessages: validationConfig.disableErrorMessages ?? true,
45
- }));
46
- const swaggerConfig = app.get(swagger_1.SwaggerConfig);
47
- (0, swagger_1.configureAppWithSwagger)(app, swaggerConfig);
10
+ const logger = app.get(nestjs_logger_1.DISMISSIBLE_LOGGER);
11
+ logger.setContext('AppSetup');
12
+ logger.info('Configuring application');
13
+ await (0, helmet_1.configureAppWithHelmet)(app);
14
+ (0, cors_1.configureAppWithCors)(app);
15
+ (0, validation_1.configureAppWithValidation)(app);
16
+ (0, swagger_1.configureAppWithSwagger)(app);
48
17
  }
49
18
  //# sourceMappingURL=app.setup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app.setup.js","sourceRoot":"","sources":["../../../api/src/app.setup.ts"],"names":[],"mappings":";;AAQA,oCA2CC;;AAnDD,2CAAkE;AAElE,qEAA4C;AAC5C,uCAAmE;AACnE,iCAAoC;AACpC,qCAAwC;AACxC,6CAAgD;AAEzC,KAAK,UAAU,YAAY,CAAC,GAAqB;IACtD,MAAM,UAAU,GAAG,GAA6B,CAAC;IAEjD,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,qBAAY,CAAC,CAAC;IAC3C,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,UAAU,CAAC,QAAQ,CAAC,gBAAa,EAAE;YACvC,qBAAqB,EAAE,YAAY,CAAC,qBAAqB,IAAI,IAAI;YACjE,yBAAyB,EAAE,YAAY,CAAC,yBAAyB,IAAI,IAAI;YACzE,IAAI,EAAE;gBACJ,MAAM,EAAE,YAAY,CAAC,UAAU,IAAI,QAAQ;gBAC3C,iBAAiB,EAAE,YAAY,CAAC,qBAAqB,IAAI,IAAI;gBAC7D,OAAO,EAAE,YAAY,CAAC,WAAW,IAAI,KAAK;aAC3C;SACF,CAAC,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,iBAAU,CAAC,CAAC;IACvC,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,GAAG,CAAC,UAAU,CAAC;YACb,MAAM,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,uBAAuB,EAAE,uBAAuB,CAAC;YAChF,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC;YACnE,cAAc,EAAE,UAAU,CAAC,cAAc,IAAI;gBAC3C,cAAc;gBACd,eAAe;gBACf,cAAc;aACf;YACD,WAAW,EAAE,UAAU,CAAC,WAAW,IAAI,IAAI;YAC3C,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,KAAK;SACnC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,gBAAgB,GAAG,GAAG,CAAC,GAAG,CAAC,6BAAgB,CAAC,CAAC;IACnD,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,gBAAgB,CAAC,SAAS,IAAI,IAAI;QAC7C,oBAAoB,EAAE,gBAAgB,CAAC,oBAAoB,IAAI,IAAI;QACnE,SAAS,EAAE,gBAAgB,CAAC,SAAS,IAAI,IAAI;QAC7C,oBAAoB,EAAE,gBAAgB,CAAC,oBAAoB,IAAI,IAAI;KACpE,CAAC,CACH,CAAC;IAEF,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,uBAAa,CAAC,CAAC;IAC7C,IAAA,iCAAuB,EAAC,GAAG,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"app.setup.js","sourceRoot":"","sources":["../../../api/src/app.setup.ts"],"names":[],"mappings":";;AAOA,oCASC;AAfD,uCAAoD;AACpD,iCAA8C;AAC9C,qCAAkD;AAClD,6CAA0D;AAC1D,8DAAoF;AAE7E,KAAK,UAAU,YAAY,CAAC,GAAqB;IACtD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAqB,kCAAkB,CAAC,CAAC;IAC/D,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAC9B,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAEvC,MAAM,IAAA,+BAAsB,EAAC,GAAG,CAAC,CAAC;IAClC,IAAA,2BAAoB,EAAC,GAAG,CAAC,CAAC;IAC1B,IAAA,uCAA0B,EAAC,GAAG,CAAC,CAAC;IAChC,IAAA,iCAAuB,EAAC,GAAG,CAAC,CAAC;AAC/B,CAAC"}
@@ -1,9 +1,9 @@
1
1
  import { SwaggerConfig } from '../swagger';
2
2
  import { DefaultAppConfig } from './default-app.config';
3
- import { PostgresStorageConfig } from '@dismissible/nestjs-postgres-storage';
4
3
  import { JwtAuthHookConfig } from '@dismissible/nestjs-jwt-auth-hook';
4
+ import { StorageConfig } from '../storage/storage.config';
5
5
  export declare class AppConfig extends DefaultAppConfig {
6
6
  readonly swagger: SwaggerConfig;
7
- readonly db: PostgresStorageConfig;
7
+ readonly storage: StorageConfig;
8
8
  readonly jwtAuth: JwtAuthHookConfig;
9
9
  }
@@ -6,23 +6,26 @@ const class_validator_1 = require("class-validator");
6
6
  const class_transformer_1 = require("class-transformer");
7
7
  const swagger_1 = require("../swagger");
8
8
  const default_app_config_1 = require("./default-app.config");
9
- const nestjs_postgres_storage_1 = require("@dismissible/nestjs-postgres-storage");
10
9
  const nestjs_jwt_auth_hook_1 = require("@dismissible/nestjs-jwt-auth-hook");
10
+ const storage_config_1 = require("../storage/storage.config");
11
11
  class AppConfig extends default_app_config_1.DefaultAppConfig {
12
12
  }
13
13
  exports.AppConfig = AppConfig;
14
14
  tslib_1.__decorate([
15
15
  (0, class_validator_1.ValidateNested)(),
16
+ (0, class_validator_1.IsDefined)(),
16
17
  (0, class_transformer_1.Type)(() => swagger_1.SwaggerConfig),
17
18
  tslib_1.__metadata("design:type", swagger_1.SwaggerConfig)
18
19
  ], AppConfig.prototype, "swagger", void 0);
19
20
  tslib_1.__decorate([
20
21
  (0, class_validator_1.ValidateNested)(),
21
- (0, class_transformer_1.Type)(() => nestjs_postgres_storage_1.PostgresStorageConfig),
22
- tslib_1.__metadata("design:type", nestjs_postgres_storage_1.PostgresStorageConfig)
23
- ], AppConfig.prototype, "db", void 0);
22
+ (0, class_validator_1.IsDefined)(),
23
+ (0, class_transformer_1.Type)(() => storage_config_1.StorageConfig),
24
+ tslib_1.__metadata("design:type", storage_config_1.StorageConfig)
25
+ ], AppConfig.prototype, "storage", void 0);
24
26
  tslib_1.__decorate([
25
27
  (0, class_validator_1.ValidateNested)(),
28
+ (0, class_validator_1.IsDefined)(),
26
29
  (0, class_transformer_1.Type)(() => nestjs_jwt_auth_hook_1.JwtAuthHookConfig),
27
30
  tslib_1.__metadata("design:type", nestjs_jwt_auth_hook_1.JwtAuthHookConfig)
28
31
  ], AppConfig.prototype, "jwtAuth", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"app.config.js","sourceRoot":"","sources":["../../../../api/src/config/app.config.ts"],"names":[],"mappings":";;;;AAAA,qDAAiD;AACjD,yDAAyC;AACzC,wCAA2C;AAC3C,6DAAwD;AACxD,kFAA6E;AAC7E,4EAAsE;AAEtE,MAAa,SAAU,SAAQ,qCAAgB;CAY9C;AAZD,8BAYC;AATiB;IAFf,IAAA,gCAAc,GAAE;IAChB,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,uBAAa,CAAC;sCACA,uBAAa;0CAAC;AAIxB;IAFf,IAAA,gCAAc,GAAE;IAChB,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,+CAAqB,CAAC;sCACb,+CAAqB;qCAAC;AAI3B;IAFf,IAAA,gCAAc,GAAE;IAChB,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,wCAAiB,CAAC;sCACJ,wCAAiB;0CAAC"}
1
+ {"version":3,"file":"app.config.js","sourceRoot":"","sources":["../../../../api/src/config/app.config.ts"],"names":[],"mappings":";;;;AAAA,qDAA4D;AAC5D,yDAAyC;AACzC,wCAA2C;AAC3C,6DAAwD;AACxD,4EAAsE;AACtE,8DAA0D;AAE1D,MAAa,SAAU,SAAQ,qCAAgB;CAe9C;AAfD,8BAeC;AAXiB;IAHf,IAAA,gCAAc,GAAE;IAChB,IAAA,2BAAS,GAAE;IACX,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,uBAAa,CAAC;sCACA,uBAAa;0CAAC;AAKxB;IAHf,IAAA,gCAAc,GAAE;IAChB,IAAA,2BAAS,GAAE;IACX,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,8BAAa,CAAC;sCACA,8BAAa;0CAAC;AAKxB;IAHf,IAAA,gCAAc,GAAE;IAChB,IAAA,2BAAS,GAAE;IACX,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,wCAAiB,CAAC;sCACJ,wCAAiB;0CAAC"}
@@ -0,0 +1,2 @@
1
+ import { INestApplication } from '@nestjs/common';
2
+ export declare function configureAppWithCors(app: INestApplication): void;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configureAppWithCors = configureAppWithCors;
4
+ const cors_config_1 = require("./cors.config");
5
+ const nestjs_logger_1 = require("@dismissible/nestjs-logger");
6
+ function configureAppWithCors(app) {
7
+ const logger = app.get(nestjs_logger_1.DISMISSIBLE_LOGGER);
8
+ logger.setContext('CORS');
9
+ const corsConfig = app.get(cors_config_1.CorsConfig);
10
+ if (corsConfig.enabled) {
11
+ app.enableCors({
12
+ origin: corsConfig.origins ?? ['http://localhost:3001', 'http://localhost:5173'],
13
+ methods: corsConfig.methods ?? ['GET', 'POST', 'DELETE', 'OPTIONS'],
14
+ allowedHeaders: corsConfig.allowedHeaders ?? [
15
+ 'Content-Type',
16
+ 'Authorization',
17
+ 'x-request-id',
18
+ ],
19
+ credentials: corsConfig.credentials ?? true,
20
+ maxAge: corsConfig.maxAge ?? 86400,
21
+ });
22
+ logger.info('CORS is enabled', { corsConfig });
23
+ }
24
+ else {
25
+ logger.info('CORS is disabled');
26
+ }
27
+ }
28
+ //# sourceMappingURL=cors.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.factory.js","sourceRoot":"","sources":["../../../../api/src/cors/cors.factory.ts"],"names":[],"mappings":";;AAIA,oDAqBC;AAxBD,+CAA2C;AAC3C,8DAAoF;AAEpF,SAAgB,oBAAoB,CAAC,GAAqB;IACxD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAqB,kCAAkB,CAAC,CAAC;IAC/D,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1B,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAU,CAAC,CAAC;IAEvC,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,GAAG,CAAC,UAAU,CAAC;YACb,MAAM,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,uBAAuB,EAAE,uBAAuB,CAAC;YAChF,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC;YACnE,cAAc,EAAE,UAAU,CAAC,cAAc,IAAI;gBAC3C,cAAc;gBACd,eAAe;gBACf,cAAc;aACf;YACD,WAAW,EAAE,UAAU,CAAC,WAAW,IAAI,IAAI;YAC3C,MAAM,EAAE,UAAU,CAAC,MAAM,IAAI,KAAK;SACnC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACjD,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
@@ -1 +1,2 @@
1
1
  export * from './cors.config';
2
+ export * from './cors.factory';
package/src/cors/index.js CHANGED
@@ -2,4 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./cors.config"), exports);
5
+ tslib_1.__exportStar(require("./cors.factory"), exports);
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/src/cors/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/src/cors/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B;AAC9B,yDAA+B"}
@@ -0,0 +1,2 @@
1
+ import { INestApplication } from '@nestjs/common';
2
+ export declare function configureAppWithHelmet(app: INestApplication): Promise<void>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configureAppWithHelmet = configureAppWithHelmet;
4
+ const tslib_1 = require("tslib");
5
+ const helmet_1 = tslib_1.__importDefault(require("@fastify/helmet"));
6
+ const helmet_config_1 = require("./helmet.config");
7
+ const nestjs_logger_1 = require("@dismissible/nestjs-logger");
8
+ async function configureAppWithHelmet(app) {
9
+ const logger = app.get(nestjs_logger_1.DISMISSIBLE_LOGGER);
10
+ logger.setContext('Helmet');
11
+ const helmetConfig = app.get(helmet_config_1.HelmetConfig);
12
+ if (helmetConfig.enabled) {
13
+ const fastifyApp = app;
14
+ await fastifyApp.register(helmet_1.default, {
15
+ contentSecurityPolicy: helmetConfig.contentSecurityPolicy ?? true,
16
+ crossOriginEmbedderPolicy: helmetConfig.crossOriginEmbedderPolicy ?? true,
17
+ hsts: {
18
+ maxAge: helmetConfig.hstsMaxAge ?? 31536000,
19
+ includeSubDomains: helmetConfig.hstsIncludeSubDomains ?? true,
20
+ preload: helmetConfig.hstsPreload ?? false,
21
+ },
22
+ });
23
+ logger.info('Helmet is enabled', { helmetConfig });
24
+ }
25
+ else {
26
+ logger.info('Helmet is disabled');
27
+ }
28
+ }
29
+ //# sourceMappingURL=helmet.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helmet.factory.js","sourceRoot":"","sources":["../../../../api/src/helmet/helmet.factory.ts"],"names":[],"mappings":";;AAMA,wDAoBC;;AAxBD,qEAA4C;AAC5C,mDAA+C;AAC/C,8DAAoF;AAE7E,KAAK,UAAU,sBAAsB,CAAC,GAAqB;IAChE,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAqB,kCAAkB,CAAC,CAAC;IAC/D,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,4BAAY,CAAC,CAAC;IAE3C,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,GAA6B,CAAC;QACjD,MAAM,UAAU,CAAC,QAAQ,CAAC,gBAAa,EAAE;YACvC,qBAAqB,EAAE,YAAY,CAAC,qBAAqB,IAAI,IAAI;YACjE,yBAAyB,EAAE,YAAY,CAAC,yBAAyB,IAAI,IAAI;YACzE,IAAI,EAAE;gBACJ,MAAM,EAAE,YAAY,CAAC,UAAU,IAAI,QAAQ;gBAC3C,iBAAiB,EAAE,YAAY,CAAC,qBAAqB,IAAI,IAAI;gBAC7D,OAAO,EAAE,YAAY,CAAC,WAAW,IAAI,KAAK;aAC3C;SACF,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
@@ -1 +1,2 @@
1
1
  export * from './helmet.config';
2
+ export * from './helmet.factory';
@@ -2,4 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./helmet.config"), exports);
5
+ tslib_1.__exportStar(require("./helmet.factory"), exports);
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/src/helmet/index.ts"],"names":[],"mappings":";;;AAAA,0DAAgC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/src/helmet/index.ts"],"names":[],"mappings":";;;AAAA,0DAAgC;AAChC,2DAAiC"}
@@ -0,0 +1,8 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import { StorageType } from './storage.config';
3
+ export type DynamicStorageModuleOptions = {
4
+ storage?: StorageType;
5
+ };
6
+ export declare class DynamicStorageModule {
7
+ static forRootAsync({ storage }: DynamicStorageModuleOptions): DynamicModule;
8
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DynamicStorageModule = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const nestjs_postgres_storage_1 = require("@dismissible/nestjs-postgres-storage");
7
+ const storage_config_1 = require("./storage.config");
8
+ const nestjs_dynamodb_storage_1 = require("@dismissible/nestjs-dynamodb-storage");
9
+ const nestjs_storage_1 = require("@dismissible/nestjs-storage");
10
+ let DynamicStorageModule = class DynamicStorageModule {
11
+ static forRootAsync({ storage }) {
12
+ switch (storage) {
13
+ case storage_config_1.StorageType.DYNAMODB:
14
+ return nestjs_dynamodb_storage_1.DynamoDBStorageModule.forRootAsync({
15
+ useFactory: (config) => config.dynamodb,
16
+ inject: [storage_config_1.StorageConfig],
17
+ });
18
+ case storage_config_1.StorageType.POSTGRES:
19
+ return nestjs_postgres_storage_1.PostgresStorageModule.forRootAsync({
20
+ useFactory: (config) => config.postgres,
21
+ inject: [storage_config_1.StorageConfig],
22
+ });
23
+ case storage_config_1.StorageType.MEMORY:
24
+ default:
25
+ return nestjs_storage_1.MemoryStorageModule.forRoot();
26
+ }
27
+ }
28
+ };
29
+ exports.DynamicStorageModule = DynamicStorageModule;
30
+ exports.DynamicStorageModule = DynamicStorageModule = tslib_1.__decorate([
31
+ (0, common_1.Module)({})
32
+ ], DynamicStorageModule);
33
+ //# sourceMappingURL=dynamic-storage.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-storage.module.js","sourceRoot":"","sources":["../../../../api/src/storage/dynamic-storage.module.ts"],"names":[],"mappings":";;;;AAAA,2CAAuD;AACvD,kFAA6E;AAC7E,qDAA8D;AAC9D,kFAA6E;AAC7E,gEAAkE;AAO3D,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAC/B,MAAM,CAAC,YAAY,CAAC,EAAE,OAAO,EAA+B;QAC1D,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,4BAAW,CAAC,QAAQ;gBACvB,OAAO,+CAAqB,CAAC,YAAY,CAAC;oBACxC,UAAU,EAAE,CAAC,MAAqB,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ;oBACtD,MAAM,EAAE,CAAC,8BAAa,CAAC;iBACxB,CAAC,CAAC;YACL,KAAK,4BAAW,CAAC,QAAQ;gBACvB,OAAO,+CAAqB,CAAC,YAAY,CAAC;oBACxC,UAAU,EAAE,CAAC,MAAqB,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ;oBACtD,MAAM,EAAE,CAAC,8BAAa,CAAC;iBACxB,CAAC,CAAC;YACL,KAAK,4BAAW,CAAC,MAAM,CAAC;YACxB;gBACE,OAAO,oCAAmB,CAAC,OAAO,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;CACF,CAAA;AAlBY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,oBAAoB,CAkBhC"}
@@ -0,0 +1,12 @@
1
+ import { PostgresStorageConfig } from '@dismissible/nestjs-postgres-storage';
2
+ import { DynamoDBStorageConfig } from '@dismissible/nestjs-dynamodb-storage';
3
+ export declare enum StorageType {
4
+ MEMORY = "memory",
5
+ POSTGRES = "postgres",
6
+ DYNAMODB = "dynamodb"
7
+ }
8
+ export declare class StorageConfig {
9
+ readonly type: StorageType;
10
+ readonly postgres: PostgresStorageConfig;
11
+ readonly dynamodb: DynamoDBStorageConfig;
12
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StorageConfig = exports.StorageType = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const class_validator_1 = require("class-validator");
6
+ const class_transformer_1 = require("class-transformer");
7
+ const nestjs_postgres_storage_1 = require("@dismissible/nestjs-postgres-storage");
8
+ const nestjs_dynamodb_storage_1 = require("@dismissible/nestjs-dynamodb-storage");
9
+ var StorageType;
10
+ (function (StorageType) {
11
+ StorageType["MEMORY"] = "memory";
12
+ StorageType["POSTGRES"] = "postgres";
13
+ StorageType["DYNAMODB"] = "dynamodb";
14
+ })(StorageType || (exports.StorageType = StorageType = {}));
15
+ class StorageConfig {
16
+ }
17
+ exports.StorageConfig = StorageConfig;
18
+ tslib_1.__decorate([
19
+ (0, class_validator_1.IsDefined)(),
20
+ (0, class_validator_1.IsEnum)(StorageType),
21
+ tslib_1.__metadata("design:type", String)
22
+ ], StorageConfig.prototype, "type", void 0);
23
+ tslib_1.__decorate([
24
+ (0, class_validator_1.ValidateIf)((o) => o.type === StorageType.POSTGRES),
25
+ (0, class_validator_1.IsDefined)(),
26
+ (0, class_validator_1.ValidateNested)(),
27
+ (0, class_transformer_1.Type)(() => nestjs_postgres_storage_1.PostgresStorageConfig),
28
+ tslib_1.__metadata("design:type", nestjs_postgres_storage_1.PostgresStorageConfig)
29
+ ], StorageConfig.prototype, "postgres", void 0);
30
+ tslib_1.__decorate([
31
+ (0, class_validator_1.ValidateIf)((o) => o.type === StorageType.DYNAMODB),
32
+ (0, class_validator_1.IsDefined)(),
33
+ (0, class_validator_1.ValidateNested)(),
34
+ (0, class_transformer_1.Type)(() => nestjs_dynamodb_storage_1.DynamoDBStorageConfig),
35
+ tslib_1.__metadata("design:type", nestjs_dynamodb_storage_1.DynamoDBStorageConfig)
36
+ ], StorageConfig.prototype, "dynamodb", void 0);
37
+ //# sourceMappingURL=storage.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.config.js","sourceRoot":"","sources":["../../../../api/src/storage/storage.config.ts"],"names":[],"mappings":";;;;AAAA,qDAAgF;AAChF,yDAAyC;AACzC,kFAA6E;AAC7E,kFAA6E;AAE7E,IAAY,WAIX;AAJD,WAAY,WAAW;IACrB,gCAAiB,CAAA;IACjB,oCAAqB,CAAA;IACrB,oCAAqB,CAAA;AACvB,CAAC,EAJW,WAAW,2BAAX,WAAW,QAItB;AAED,MAAa,aAAa;CAgBzB;AAhBD,sCAgBC;AAbiB;IAFf,IAAA,2BAAS,GAAE;IACX,IAAA,wBAAM,EAAC,WAAW,CAAC;;2CACe;AAMnB;IAJf,IAAA,4BAAU,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,QAAQ,CAAC;IAClD,IAAA,2BAAS,GAAE;IACX,IAAA,gCAAc,GAAE;IAChB,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,+CAAqB,CAAC;sCACP,+CAAqB;+CAAC;AAMjC;IAJf,IAAA,4BAAU,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,QAAQ,CAAC;IAClD,IAAA,2BAAS,GAAE;IACX,IAAA,gCAAc,GAAE;IAChB,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,+CAAqB,CAAC;sCACP,+CAAqB;+CAAC"}
@@ -1,3 +1,2 @@
1
1
  import { INestApplication } from '@nestjs/common';
2
- import { SwaggerConfig } from './swagger.config';
3
- export declare function configureAppWithSwagger(app: INestApplication, swaggerConfig: SwaggerConfig): void;
2
+ export declare function configureAppWithSwagger(app: INestApplication): void;
@@ -2,11 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.configureAppWithSwagger = configureAppWithSwagger;
4
4
  const swagger_1 = require("@nestjs/swagger");
5
+ const swagger_config_1 = require("./swagger.config");
6
+ const nestjs_logger_1 = require("@dismissible/nestjs-logger");
5
7
  const swaggerDocumentOptions = {
6
8
  operationIdFactory: (_controllerKey, methodKey) => methodKey,
7
9
  };
8
- function configureAppWithSwagger(app, swaggerConfig) {
10
+ function configureAppWithSwagger(app) {
11
+ const logger = app.get(nestjs_logger_1.DISMISSIBLE_LOGGER);
12
+ logger.setContext('Swagger');
13
+ const swaggerConfig = app.get(swagger_config_1.SwaggerConfig);
9
14
  if (swaggerConfig.enabled) {
15
+ logger.info('Swagger is enabled', { swaggerConfig });
10
16
  const { path = 'docs' } = swaggerConfig;
11
17
  const config = new swagger_1.DocumentBuilder()
12
18
  .setTitle('Dismissible')
@@ -18,5 +24,8 @@ function configureAppWithSwagger(app, swaggerConfig) {
18
24
  useGlobalPrefix: true,
19
25
  });
20
26
  }
27
+ else {
28
+ logger.info('Swagger is disabled');
29
+ }
21
30
  }
22
31
  //# sourceMappingURL=swagger.factory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"swagger.factory.js","sourceRoot":"","sources":["../../../../api/src/swagger/swagger.factory.ts"],"names":[],"mappings":";;AAQA,0DAeC;AAtBD,6CAAyF;AAGzF,MAAM,sBAAsB,GAA2B;IACrD,kBAAkB,EAAE,CAAC,cAAsB,EAAE,SAAiB,EAAE,EAAE,CAAC,SAAS;CAC7E,CAAC;AAEF,SAAgB,uBAAuB,CAAC,GAAqB,EAAE,aAA4B;IACzF,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QAC1B,MAAM,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,aAAa,CAAC;QAExC,MAAM,MAAM,GAAG,IAAI,yBAAe,EAAE;aACjC,QAAQ,CAAC,aAAa,CAAC;aACvB,cAAc,CAAC,8CAA8C,CAAC;aAC9D,UAAU,CAAC,KAAK,CAAC;aACjB,KAAK,EAAE,CAAC;QAEX,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,sBAAsB,CAAC,CAAC;QAChG,uBAAa,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE;YAC9C,eAAe,EAAE,IAAI;SACtB,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"swagger.factory.js","sourceRoot":"","sources":["../../../../api/src/swagger/swagger.factory.ts"],"names":[],"mappings":";;AASA,0DAsBC;AA9BD,6CAAyF;AACzF,qDAAiD;AACjD,8DAAoF;AAEpF,MAAM,sBAAsB,GAA2B;IACrD,kBAAkB,EAAE,CAAC,cAAsB,EAAE,SAAiB,EAAE,EAAE,CAAC,SAAS;CAC7E,CAAC;AAEF,SAAgB,uBAAuB,CAAC,GAAqB;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAqB,kCAAkB,CAAC,CAAC;IAC/D,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,8BAAa,CAAC,CAAC;IAE7C,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;QACrD,MAAM,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,aAAa,CAAC;QAExC,MAAM,MAAM,GAAG,IAAI,yBAAe,EAAE;aACjC,QAAQ,CAAC,aAAa,CAAC;aACvB,cAAc,CAAC,8CAA8C,CAAC;aAC9D,UAAU,CAAC,KAAK,CAAC;aACjB,KAAK,EAAE,CAAC;QAEX,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,sBAAsB,CAAC,CAAC;QAChG,uBAAa,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE;YAC9C,eAAe,EAAE,IAAI;SACtB,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
@@ -1 +1,2 @@
1
1
  export * from './validation.config';
2
+ export * from './validation.factory';
@@ -2,4 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./validation.config"), exports);
5
+ tslib_1.__exportStar(require("./validation.factory"), exports);
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/src/validation/index.ts"],"names":[],"mappings":";;;AAAA,8DAAoC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../api/src/validation/index.ts"],"names":[],"mappings":";;;AAAA,8DAAoC;AACpC,+DAAqC"}
@@ -0,0 +1,2 @@
1
+ import { INestApplication } from '@nestjs/common';
2
+ export declare function configureAppWithValidation(app: INestApplication): void;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configureAppWithValidation = configureAppWithValidation;
4
+ const common_1 = require("@nestjs/common");
5
+ const validation_config_1 = require("./validation.config");
6
+ const nestjs_logger_1 = require("@dismissible/nestjs-logger");
7
+ function configureAppWithValidation(app) {
8
+ const logger = app.get(nestjs_logger_1.DISMISSIBLE_LOGGER);
9
+ logger.setContext('Validation');
10
+ const validationConfig = app.get(validation_config_1.ValidationConfig);
11
+ logger.info('Registering ValidationPipe', { validationConfig });
12
+ app.useGlobalPipes(new common_1.ValidationPipe({
13
+ whitelist: validationConfig.whitelist ?? true,
14
+ forbidNonWhitelisted: validationConfig.forbidNonWhitelisted ?? true,
15
+ transform: validationConfig.transform ?? true,
16
+ disableErrorMessages: validationConfig.disableErrorMessages ?? true,
17
+ }));
18
+ }
19
+ //# sourceMappingURL=validation.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.factory.js","sourceRoot":"","sources":["../../../../api/src/validation/validation.factory.ts"],"names":[],"mappings":";;AAIA,gEAcC;AAlBD,2CAAkE;AAClE,2DAAuD;AACvD,8DAAoF;AAEpF,SAAgB,0BAA0B,CAAC,GAAqB;IAC9D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAqB,kCAAkB,CAAC,CAAC;IAC/D,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAChC,MAAM,gBAAgB,GAAG,GAAG,CAAC,GAAG,CAAC,oCAAgB,CAAC,CAAC;IAEnD,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAChE,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,gBAAgB,CAAC,SAAS,IAAI,IAAI;QAC7C,oBAAoB,EAAE,gBAAgB,CAAC,oBAAoB,IAAI,IAAI;QACnE,SAAS,EAAE,gBAAgB,CAAC,SAAS,IAAI,IAAI;QAC7C,oBAAoB,EAAE,gBAAgB,CAAC,oBAAoB,IAAI,IAAI;KACpE,CAAC,CACH,CAAC;AACJ,CAAC"}