@digitaldefiance/express-suite-starter 2.3.5 → 2.3.7

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 (95) hide show
  1. package/package.json +3 -2
  2. package/scaffolding/api/.env.example.mustache +52 -0
  3. package/scaffolding/api/src/assets/.gitkeep +0 -0
  4. package/scaffolding/api/src/main.ts.mustache +19 -0
  5. package/scaffolding/api/src/views/index.ejs +34 -0
  6. package/scaffolding/api-lib/src/index.ts +9 -0
  7. package/scaffolding/api-lib/src/lib/application.ts +35 -0
  8. package/scaffolding/api-lib/src/lib/constants.ts.mustache +10 -0
  9. package/scaffolding/api-lib/src/lib/documents/index.ts +1 -0
  10. package/scaffolding/api-lib/src/lib/documents/user.ts +17 -0
  11. package/scaffolding/api-lib/src/lib/environment.ts +52 -0
  12. package/scaffolding/api-lib/src/lib/interfaces/constants.ts +9 -0
  13. package/scaffolding/api-lib/src/lib/interfaces/environment-aws.ts +7 -0
  14. package/scaffolding/api-lib/src/lib/interfaces/environment.ts +9 -0
  15. package/scaffolding/api-lib/src/lib/middlewares.ts.mustache +113 -0
  16. package/scaffolding/api-lib/src/lib/models/email-token.ts +12 -0
  17. package/scaffolding/api-lib/src/lib/models/mnemonic.ts +12 -0
  18. package/scaffolding/api-lib/src/lib/models/role.ts +12 -0
  19. package/scaffolding/api-lib/src/lib/models/used-direct-login-token.ts +12 -0
  20. package/scaffolding/api-lib/src/lib/models/user-role.ts +12 -0
  21. package/scaffolding/api-lib/src/lib/models/user.ts +14 -0
  22. package/scaffolding/api-lib/src/lib/routers/api.ts +23 -0
  23. package/scaffolding/api-lib/src/lib/routers/app.ts +31 -0
  24. package/scaffolding/api-lib/src/lib/routers/index.ts +2 -0
  25. package/scaffolding/api-lib/src/lib/schemas/index.ts +2 -0
  26. package/scaffolding/api-lib/src/lib/schemas/schema.ts +53 -0
  27. package/scaffolding/api-lib/src/lib/schemas/user.ts +13 -0
  28. package/scaffolding/api-lib/src/lib/services/email.ts +109 -0
  29. package/scaffolding/api-lib/src/lib/services/index.ts +1 -0
  30. package/scaffolding/api-lib/src/lib/shared-types.ts +172 -0
  31. package/scaffolding/devcontainer-mongodb/.devcontainer/.env.example +3 -0
  32. package/scaffolding/devcontainer-mongodb/.devcontainer/Dockerfile +6 -0
  33. package/scaffolding/devcontainer-mongodb/.devcontainer/devcontainer.json.mustache +31 -0
  34. package/scaffolding/devcontainer-mongodb/.devcontainer/docker-compose.yml +31 -0
  35. package/scaffolding/devcontainer-mongodb/.devcontainer/load-env.sh +20 -0
  36. package/scaffolding/devcontainer-mongodb/.devcontainer/post-create.sh.hbs +54 -0
  37. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/.env.example +14 -0
  38. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/Dockerfile +38 -0
  39. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/Mongo.Dockerfile +24 -0
  40. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/devcontainer.json +69 -0
  41. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/docker-compose.yml +66 -0
  42. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/entrypoint.sh +29 -0
  43. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/load-env.sh +20 -0
  44. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/mongodb_entrypoint.sh +124 -0
  45. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/mongodb_healthcheck.sh +36 -0
  46. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/post-create.sh.hbs +54 -0
  47. package/scaffolding/devcontainer-simple/.devcontainer/.env.example +4 -0
  48. package/scaffolding/devcontainer-simple/.devcontainer/devcontainer.json.mustache +60 -0
  49. package/scaffolding/devcontainer-simple/.devcontainer/post-create.sh.hbs +72 -0
  50. package/scaffolding/inituserdb/.env.example.mustache +9 -0
  51. package/scaffolding/inituserdb/src/main.ts.mustache +178 -0
  52. package/scaffolding/lib/src/index.ts.mustache +6 -0
  53. package/scaffolding/lib/src/lib/constants.ts.mustache +15 -0
  54. package/scaffolding/lib/src/lib/ecies-config.ts +10 -0
  55. package/scaffolding/lib/src/lib/enumerations/{{workspaceName}}-string-key.ts.mustache +5 -0
  56. package/scaffolding/lib/src/lib/i18n-setup.ts.mustache +99 -0
  57. package/scaffolding/lib/src/lib/interfaces/constants.ts +13 -0
  58. package/scaffolding/lib/src/lib/strings-collection.ts.mustache +45 -0
  59. package/scaffolding/react/src/app/app.tsx.mustache +170 -0
  60. package/scaffolding/react/src/app/menus/extraMenu.tsx +20 -0
  61. package/scaffolding/react/src/app/menus/index.ts +5 -0
  62. package/scaffolding/react/src/app/pages/SplashPage.tsx +60 -0
  63. package/scaffolding/react/src/app/theme.tsx.mustache +91 -0
  64. package/scaffolding/react/src/assets/albatross.ico +0 -0
  65. package/scaffolding/react/src/assets/albatross.png +0 -0
  66. package/scaffolding/react/src/assets/albatross.svg +108 -0
  67. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.otf +0 -0
  68. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.ttf +0 -0
  69. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.woff +0 -0
  70. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.woff2 +0 -0
  71. package/scaffolding/react/src/assets/gen-by-albatross.png +0 -0
  72. package/scaffolding/react/src/assets/gen-by-albatross.svg +124 -0
  73. package/scaffolding/react/src/config/ecies.ts +10 -0
  74. package/scaffolding/react/src/config/runtime-config.ts +25 -0
  75. package/scaffolding/react/src/environments/environment.prod.ts.mustache +16 -0
  76. package/scaffolding/react/src/environments/environment.ts +15 -0
  77. package/scaffolding/react/src/interfaces/environment.ts +5 -0
  78. package/scaffolding/react/src/main.tsx +22 -0
  79. package/scaffolding/react/src/styles.scss +103 -0
  80. package/scaffolding/react/src/test-setup.ts +1 -0
  81. package/scaffolding/react-lib/src/index.ts +6 -0
  82. package/scaffolding/react-lib/src/theme/theme.tsx +67 -0
  83. package/scaffolding/root/.github/dependabot.yml +11 -0
  84. package/scaffolding/root/.github/workflows/ci.yml +44 -0
  85. package/scaffolding/root/.vscode/extensions.json +9 -0
  86. package/scaffolding/root/DEPLOYMENT.md +104 -0
  87. package/scaffolding/root/do-yarn.sh +148 -0
  88. package/scaffolding/root/ensure-git-globals.sh +30 -0
  89. package/scaffolding/root/eslint.config.mjs +70 -0
  90. package/scaffolding/root/list-scripts.sh +12 -0
  91. package/scaffolding/root/npm-install-globals.sh +5 -0
  92. package/scaffolding/root/nuke-node-modules.sh +23 -0
  93. package/scaffolding/root/recover-yarn.sh +37 -0
  94. package/scaffolding/root/reset.sh.mustache +25 -0
  95. package/scaffolding/root/setup-nvm.sh +15 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitaldefiance/express-suite-starter",
3
- "version": "2.3.5",
3
+ "version": "2.3.7",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "create-express-suite": "./dist/src/cli.js"
@@ -8,6 +8,7 @@
8
8
  "files": [
9
9
  "dist",
10
10
  "templates",
11
+ "scaffolding",
11
12
  "config",
12
13
  "README.md"
13
14
  ],
@@ -29,6 +30,7 @@
29
30
  "@digitaldefiance/i18n-lib": "3.8.5",
30
31
  "@inquirer/prompts": "^7.5.0",
31
32
  "chalk": "^4.1.2",
33
+ "handlebars": "^4.7.8",
32
34
  "mustache": "^4.2.0",
33
35
  "validator": "^13.15.0"
34
36
  },
@@ -40,7 +42,6 @@
40
42
  "@types/mustache": "^4.2.5",
41
43
  "@types/node": "^20.0.0",
42
44
  "@types/validator": "^13.15.0",
43
- "handlebars": "^4.7.8",
44
45
  "jest": "^29.7.0",
45
46
  "nx": "22.0.2",
46
47
  "plop": "^4.0.1",
@@ -0,0 +1,52 @@
1
+ # set to test db name to use an in-memory database for development, supercedes MONGO_URI if set
2
+ # leave empty to use MONGO_URI instead
3
+ DEV_DATABASE={{devDatabase}}
4
+ # mongo-uri to connect to (.devcontainer mongo, or production mongo uri)
5
+ MONGO_URI=mongodb://admin:{{password}}@db:27017/{{prefix}}?authSource=admin&directConnection=true
6
+ # requires a running mongo replica set for transactions to work
7
+ MONGO_USE_TRANSACTIONS=false
8
+ # SES credentials for email sending
9
+ AWS_ACCESS_KEY_ID=
10
+ AWS_SECRET_ACCESS_KEY=
11
+ AWS_REGION=us-west-2
12
+ # 64 character hex string
13
+ JWT_SECRET={{jwtSecret}}
14
+ # /workspaces presumes .devcontainer setup, change as needed
15
+ API_DIST_DIR=/workspaces/{{workspaceName}}/dist/{{prefix}}-api
16
+ REACT_DIST_DIR=/workspaces/{{workspaceName}}/dist/{{prefix}}-react
17
+ DEBUG=true
18
+ DETAILED_DEBUG=true
19
+ # Admin and test credentials
20
+ ADMIN_ID=""
21
+ ADMIN_MNEMONIC=""
22
+ ADMIN_ROLE_ID=""
23
+ ADMIN_USER_ROLE_ID=""
24
+ ADMIN_PASSWORD=""
25
+ MEMBER_ID=""
26
+ MEMBER_MNEMONIC=""
27
+ MEMBER_ROLE_ID=""
28
+ MEMBER_PASSWORD=""
29
+ MEMBER_USER_ROLE_ID=""
30
+ # System credentials used to sign system messages
31
+ SYSTEM_ID=""
32
+ SYSTEM_MNEMONIC=""
33
+ SYSTEM_PUBLIC_KEY=""
34
+ SYSTEM_ROLE_ID=""
35
+ SYSTEM_PASSWORD=""
36
+ SYSTEM_USER_ROLE_ID=""
37
+ # 64 character hex string
38
+ MNEMONIC_ENCRYPTION_KEY={{mnemonicEncryptionKey}}
39
+ # 64 character hex string
40
+ MNEMONIC_HMAC_SECRET={{mnemonicHmacSecret}}
41
+ HTTPS_DEV_CERT_DIR=/workspaces/{{workspaceName}}
42
+ HTTPS_DEV_PORT=3443
43
+ MONGO_TRANSACTION_RETRY_BASE_DELAY=75
44
+ MONGO_TRANSACTION_TIMEOUT=60000
45
+ MONGO_TRANSACTION_LOCK_REQUEST_TIMEOUT=30000
46
+ MONGO_MAX_POOL_SIZE=10
47
+ MONGO_MIN_POOL_SIZE=2
48
+ MONGO_MAX_IDLE_TIME_MS=30000
49
+ MONGO_SERVER_SELECTION_TIMEOUT_MS=5000
50
+ MONGO_SOCKET_TIMEOUT_MS=45000
51
+ MONGO_RETRY_WRITES=true
52
+ MONGO_RETRY_READS=true
File without changes
@@ -0,0 +1,19 @@
1
+ import { App, Environment, Constants } from '{{namespace}}/api-lib';
2
+ import { DatabaseInitializationService } from '@digitaldefiance/node-express-suite';
3
+ import { GlobalActiveContext, CoreLanguageCode, IActiveContext } from '@digitaldefiance/i18n-lib';
4
+ import { join } from 'path';
5
+
6
+ const env: Environment = new Environment(
7
+ join(App.distDir, '{{prefix}}-api', '.env'),
8
+ );
9
+ const app: App = new App(
10
+ env,
11
+ DatabaseInitializationService.initUserDb.bind(DatabaseInitializationService),
12
+ DatabaseInitializationService.serverInitResultHash.bind(DatabaseInitializationService),
13
+ Constants,
14
+ );
15
+ const context = GlobalActiveContext.getInstance<CoreLanguageCode, IActiveContext<CoreLanguageCode>>();
16
+ context.languageContextSpace = 'admin';
17
+ (async () => {
18
+ await app.start();
19
+ })();
@@ -0,0 +1,34 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title><%= title %></title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <link rel="icon" type="image/x-icon" href="/assets/favicon.ico" />
8
+ <script nonce="<%= cspNonce %>"></script>
9
+ <base href="<%= baseHref %>" />
10
+
11
+ <script nonce="<%= cspNonce %>">
12
+ window.APP_CONFIG = {
13
+ hostname: '<%= hostname %>',
14
+ siteTitle: '<%= title %>',
15
+ server: '<%= server %>',
16
+ };
17
+ </script>
18
+ <% if (cssFile) { %>
19
+ <link rel="stylesheet" href="<%= cssFile %>" />
20
+ <% } %>
21
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
22
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
23
+ <link
24
+ href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
25
+ rel="stylesheet"
26
+ />
27
+ </head>
28
+ <body>
29
+ <div id="root"></div>
30
+ <% if (jsFile) { %>
31
+ <script nonce="<%= cspNonce %>" type="module" src="<%= jsFile %>"></script>
32
+ <% } %>
33
+ </body>
34
+ </html>
@@ -0,0 +1,9 @@
1
+ export * from './lib/application';
2
+ export * from './lib/constants';
3
+ export * from './lib/environment';
4
+ export * from './lib/interfaces/constants';
5
+ export * from './lib/interfaces/environment';
6
+ export * from './lib/documents';
7
+ export * from './lib/routers';
8
+ export * from './lib/schemas';
9
+ export * from './lib/services';
@@ -0,0 +1,35 @@
1
+ import { Application, BaseApplication, IFailableResult, IServerInitResult, DummyEmailService, emailServiceRegistry, BaseRouter, IApplication } from '@digitaldefiance/node-express-suite';
2
+ import { Environment } from './environment';
3
+ import { IConstants } from './interfaces/constants';
4
+ import { Constants } from './constants';
5
+ import { ApiRouter } from './routers/api';
6
+ import { AppRouter } from './routers/app';
7
+ import { getSchemaMap } from './schemas/schema';
8
+ import { initMiddleware } from './middlewares';
9
+ import { EmailService } from './services/email';
10
+
11
+ export class App<TInitResults extends IServerInitResult = IServerInitResult, TConstants extends IConstants = IConstants> extends Application<TInitResults, any, Environment, TConstants, any> {
12
+ constructor(
13
+ environment: Environment,
14
+ databaseInitFunction: (application: BaseApplication<any, TInitResults>) => Promise<IFailableResult<TInitResults>>,
15
+ initResultHashFunction: (initResults: TInitResults) => string,
16
+ constants: TConstants = Constants as TConstants,
17
+ ) {
18
+ super(
19
+ environment,
20
+ (app: IApplication): BaseRouter<IApplication> => new ApiRouter(app),
21
+ getSchemaMap,
22
+ databaseInitFunction,
23
+ initResultHashFunction,
24
+ undefined, // Default CSP config
25
+ constants,
26
+ (apiRouter: BaseRouter<IApplication>): AppRouter => new AppRouter(apiRouter),
27
+ initMiddleware,
28
+ );
29
+
30
+ // Register the DummyEmailService - users should replace this with their own email service
31
+ const emailService = new DummyEmailService(this);
32
+ //const emailService = new EmailService(this);
33
+ emailServiceRegistry.setService(emailService);
34
+ }
35
+ }
@@ -0,0 +1,10 @@
1
+ import { IConstants } from './interfaces/constants';
2
+ import { createExpressConstants } from '@digitaldefiance/node-express-suite';
3
+
4
+ export const Constants: IConstants = {
5
+ ...createExpressConstants('{{hostname}}', '{{hostname}}'),
6
+ Site: '{{workspaceName}}' as const,
7
+ SiteTagline: 'Your tagline here' as const,
8
+ SiteDescription: 'Your description here' as const,
9
+ PasswordRegex: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?])[A-Za-z\d!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]{8,}$/,
10
+ };
@@ -0,0 +1 @@
1
+ export type { IUserDocument } from './user';
@@ -0,0 +1,17 @@
1
+ import { Types } from '@digitaldefiance/mongoose-types';
2
+ import { AccountStatus, IUserBase } from '@digitaldefiance/suite-core-lib';
3
+ import { IBaseDocument } from '@digitaldefiance/node-express-suite';
4
+ import { CoreLanguageCode } from '@digitaldefiance/i18n-lib';
5
+
6
+ /**
7
+ * Composite interface for user collection documents
8
+ */
9
+ export interface IUserDocument<I = Types.ObjectId, D extends Date = Date, S extends string = CoreLanguageCode, A extends AccountStatus = AccountStatus> extends IBaseDocument<
10
+ IUserBase<
11
+ I,
12
+ D,
13
+ S,
14
+ A
15
+ >
16
+ > {
17
+ }
@@ -0,0 +1,52 @@
1
+ import { SecureString } from '@digitaldefiance/ecies-lib';
2
+ import { Environment as BaseEnvironment, emailServiceRegistry } from '@digitaldefiance/node-express-suite';
3
+ import { IEnvironment } from './interfaces/environment';
4
+ import { IEnvironmentAws } from './interfaces/environment-aws';
5
+ import { Constants } from './constants';
6
+ import { getSuiteCoreTranslation, SuiteCoreStringKey } from '@digitaldefiance/suite-core-lib';
7
+ import { EmailService } from './services/email';
8
+
9
+ export class Environment extends BaseEnvironment implements IEnvironment {
10
+ private _aws: IEnvironmentAws;
11
+
12
+ constructor(path?: string, initialization = false, override = true) {
13
+ super(path, initialization, override, Constants);
14
+ const envObj = this.getObject();
15
+
16
+ this._aws = {
17
+ accessKeyId: new SecureString((envObj as any)['AWS_ACCESS_KEY_ID'] ?? ''),
18
+ secretAccessKey: new SecureString((envObj as any)['AWS_SECRET_ACCESS_KEY'] ?? ''),
19
+ region: (envObj as any)['AWS_REGION'] ?? 'us-west-2',
20
+ };
21
+
22
+ if (process.env['NODE_ENV'] !== 'test' &&
23
+ process.env['NODE_ENV'] !== 'development' &&
24
+ emailServiceRegistry.isServiceType(EmailService) && // only enforce if real EmailService is used
25
+ this._aws.accessKeyId.length === 0) {
26
+ throw new Error(
27
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_EnvNotSetTemplate, {
28
+ variable: 'AWS_ACCESS_KEY_ID',
29
+ }),
30
+ );
31
+ }
32
+ if (process.env['NODE_ENV'] !== 'test' &&
33
+ process.env['NODE_ENV'] !== 'development' &&
34
+ emailServiceRegistry.isServiceType(EmailService) && // only enforce if real EmailService is used
35
+ this._aws.secretAccessKey.length === 0) {
36
+ throw new Error(
37
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_EnvNotSetTemplate, {
38
+ variable: 'AWS_SECRET_ACCESS_KEY',
39
+ }),
40
+ );
41
+ }
42
+ if (!this._aws.region) {
43
+ throw new Error(
44
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_EnvNotSetTemplate, { variable: 'AWS_REGION' }),
45
+ );
46
+ }
47
+ }
48
+
49
+ public get aws(): IEnvironmentAws {
50
+ return this._aws;
51
+ }
52
+ }
@@ -0,0 +1,9 @@
1
+ import { IConstants as IBaseConstants } from '@digitaldefiance/node-express-suite';
2
+
3
+ export interface IConstants extends IBaseConstants {
4
+ readonly Site: string;
5
+ readonly SiteTagline: string;
6
+ readonly SiteDescription: string;
7
+ readonly SiteHostname: string;
8
+ readonly PasswordRegex: RegExp;
9
+ }
@@ -0,0 +1,7 @@
1
+ import { SecureString } from '@digitaldefiance/ecies-lib';
2
+
3
+ export interface IEnvironmentAws {
4
+ accessKeyId: SecureString;
5
+ secretAccessKey: SecureString;
6
+ region: string;
7
+ }
@@ -0,0 +1,9 @@
1
+ import { IEnvironment as IBaseEnvironment } from '@digitaldefiance/node-express-suite';
2
+ import { IEnvironmentAws } from './environment-aws';
3
+
4
+ export interface IEnvironment extends IBaseEnvironment {
5
+ /**
6
+ * AWS configuration
7
+ */
8
+ aws: IEnvironmentAws;
9
+ }
@@ -0,0 +1,113 @@
1
+ import { AppConstants } from '{{namespace}}/lib';
2
+ import cors from 'cors';
3
+ import { randomBytes } from 'crypto';
4
+ import {
5
+ Application,
6
+ json,
7
+ NextFunction,
8
+ Request,
9
+ Response,
10
+ urlencoded,
11
+ } from 'express';
12
+ import helmet from 'helmet';
13
+ import { IncomingMessage, ServerResponse } from 'http';
14
+
15
+ /**
16
+ * CORS whitelist
17
+ */
18
+ const corsWhitelist = [
19
+ /^https?:\/\/localhost:(3000|3443|4200)$/,
20
+ new RegExp(
21
+ `^https?:\\/\\/${AppConstants.SiteHostname.replace(
22
+ /[.*+?^${}()|[\]\\]/g,
23
+ '\\$&'
24
+ )}$`
25
+ ),
26
+ ];
27
+
28
+ /**
29
+ * CORS options delegate
30
+ * @param req - CORS request
31
+ * @param callback - Callback function
32
+ */
33
+ const corsOptionsDelegate = (
34
+ req: cors.CorsRequest,
35
+ callback: (
36
+ error: Error | null,
37
+ options: cors.CorsOptions | undefined,
38
+ ) => void,
39
+ ) => {
40
+ let corsOptions: cors.CorsOptions;
41
+ const origin = req.headers.origin;
42
+ if (
43
+ origin &&
44
+ corsWhitelist.find((w) => {
45
+ if (w instanceof RegExp) {
46
+ return w.test(origin);
47
+ } else {
48
+ return w === origin;
49
+ }
50
+ })
51
+ ) {
52
+ corsOptions = { origin: true };
53
+ } else {
54
+ corsOptions = { origin: false };
55
+ }
56
+ callback(null, corsOptions);
57
+ };
58
+
59
+ /**
60
+ * Initialize the middleware
61
+ * @param app - Express application
62
+ */
63
+ export const initMiddleware = (app: Application): void => {
64
+ // Helmet helps you secure your Express apps by setting various HTTP headers
65
+ // CSP nonce
66
+ app.use((req: Request, res: Response, next: NextFunction) => {
67
+ res.locals['cspNonce'] = randomBytes(32).toString('hex');
68
+ next();
69
+ });
70
+ app.use(
71
+ helmet({
72
+ contentSecurityPolicy: {
73
+ directives: {
74
+ defaultSrc: ["'self'"],
75
+ imgSrc: ["'self'", 'https://flagcdn.com', 'data:', 'blob:'],
76
+ connectSrc: [
77
+ "'self'",
78
+ 'http://localhost:3000',
79
+ 'https://localhost:3000',
80
+ 'http://localhost:4200',
81
+ 'https://localhost:4200',
82
+ 'https://localhost:3443',
83
+ `http://${AppConstants.SiteHostname}`,
84
+ `https://${AppConstants.SiteHostname}`,
85
+ ],
86
+ scriptSrc: [
87
+ "'self'",
88
+ //"'unsafe-inline'",
89
+ "'strict-dynamic'",
90
+ (req: IncomingMessage, res: ServerResponse) =>
91
+ `'nonce-${(res as Response).locals['cspNonce']}'`,
92
+ ],
93
+ styleSrc: [
94
+ "'self'",
95
+ "'unsafe-inline'",
96
+ 'https://fonts.googleapis.com',
97
+ ],
98
+ fontSrc: [
99
+ "'self'",
100
+ 'https://fonts.gstatic.com',
101
+ ],
102
+ frameSrc: ["'self'"],
103
+ },
104
+ },
105
+ }),
106
+ );
107
+ // Enable CORS
108
+ app.use(cors(corsOptionsDelegate));
109
+ // Parse incoming requests with JSON payloads
110
+ app.use(json());
111
+ // Parse incoming requests with urlencoded payloads
112
+ app.use(urlencoded({ extended: true }));
113
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseModelName, SchemaCollection, IEmailTokenDocument, EmailTokenSchema } from '@digitaldefiance/node-express-suite';
2
+ import { Connection } from '@digitaldefiance/mongoose-types';
3
+
4
+ export function EmailTokenModel(connection: Connection) {
5
+ return connection.model<IEmailTokenDocument>(
6
+ BaseModelName.EmailToken,
7
+ EmailTokenSchema,
8
+ SchemaCollection.EmailToken,
9
+ );
10
+ }
11
+
12
+ export default EmailTokenModel;
@@ -0,0 +1,12 @@
1
+ import { BaseModelName, SchemaCollection, IMnemonicDocument, MnemonicSchema } from '@digitaldefiance/node-express-suite';
2
+ import { Connection } from '@digitaldefiance/mongoose-types';
3
+
4
+ export function MnemonicModel(connection: Connection) {
5
+ return connection.model<IMnemonicDocument>(
6
+ BaseModelName.Mnemonic,
7
+ MnemonicSchema,
8
+ SchemaCollection.Mnemonic,
9
+ );
10
+ }
11
+
12
+ export default MnemonicModel;
@@ -0,0 +1,12 @@
1
+ import { BaseModelName, SchemaCollection, IRoleDocument, RoleSchema } from '@digitaldefiance/node-express-suite';
2
+ import { Connection } from '@digitaldefiance/mongoose-types';
3
+
4
+ export function RoleModel(connection: Connection) {
5
+ return connection.model<IRoleDocument>(
6
+ BaseModelName.Role,
7
+ RoleSchema,
8
+ SchemaCollection.Role,
9
+ );
10
+ }
11
+
12
+ export default RoleModel;
@@ -0,0 +1,12 @@
1
+ import { BaseModelName, IUsedDirectLoginTokenDocument, SchemaCollection,UsedDirectLoginTokenSchema } from '@digitaldefiance/node-express-suite';
2
+ import { Connection, Model } from '@digitaldefiance/mongoose-types';
3
+
4
+ export default function UsedDirectLoginTokenModel(
5
+ connection: Connection,
6
+ ): Model<IUsedDirectLoginTokenDocument> {
7
+ return connection.model<IUsedDirectLoginTokenDocument>(
8
+ BaseModelName.UsedDirectLoginToken,
9
+ UsedDirectLoginTokenSchema,
10
+ SchemaCollection.UsedDirectLoginToken,
11
+ );
12
+ }
@@ -0,0 +1,12 @@
1
+ import { SchemaCollection, IUserRoleDocument, UserRoleSchema, BaseModelName } from '@digitaldefiance/node-express-suite';
2
+ import { Connection, Model } from '@digitaldefiance/mongoose-types';
3
+
4
+ export default function UserRoleModel(
5
+ connection: Connection,
6
+ ): Model<IUserRoleDocument> {
7
+ return connection.model<IUserRoleDocument>(
8
+ BaseModelName.UserRole,
9
+ UserRoleSchema,
10
+ SchemaCollection.UserRole,
11
+ );
12
+ }
@@ -0,0 +1,14 @@
1
+ import { Connection } from '@digitaldefiance/mongoose-types';
2
+ import { IUserDocument } from '../documents/user';
3
+ import { UserSchema } from '../schemas/user';
4
+ import { BaseModelName, SchemaCollection } from '@digitaldefiance/node-express-suite';
5
+
6
+ export function UserModel(connection: Connection) {
7
+ return connection.model<IUserDocument>(
8
+ BaseModelName.User,
9
+ UserSchema,
10
+ SchemaCollection.User,
11
+ );
12
+ }
13
+
14
+ export default UserModel;
@@ -0,0 +1,23 @@
1
+ import { ApiRouter as BaseApiRouter, IApplication, IBaseDocument } from '@digitaldefiance/node-express-suite';
2
+ import { ITokenRole, ITokenUser, IUserBase } from '@digitaldefiance/suite-core-lib';
3
+ import { Types } from '@digitaldefiance/mongoose-types';
4
+ import { Environment } from '../environment';
5
+ import { IConstants } from '../interfaces/constants';
6
+
7
+ export class ApiRouter<
8
+ I extends Types.ObjectId | string = Types.ObjectId,
9
+ D extends Date = Date,
10
+ S extends string = string,
11
+ A extends string = string,
12
+ TUser extends IUserBase<I, D, S, A> = IUserBase<I, D, S, A>,
13
+ TTokenRole extends ITokenRole<I, D> = ITokenRole<I, D>,
14
+ TTokenUser extends ITokenUser = ITokenUser,
15
+ TEnvironment extends Environment = Environment,
16
+ TConstants extends IConstants = IConstants,
17
+ TBaseDocument extends IBaseDocument<any, Types.ObjectId> = IBaseDocument<any, Types.ObjectId>,
18
+ TApplication extends IApplication = IApplication,
19
+ > extends BaseApiRouter<I,D,S,A, TUser,TTokenRole,TBaseDocument, TTokenUser,TConstants, TEnvironment, TApplication> {
20
+ constructor(app: TApplication) {
21
+ super(app);
22
+ }
23
+ }
@@ -0,0 +1,31 @@
1
+ import { AppRouter as BaseAppRouter, BaseRouter, IApplication } from '@digitaldefiance/node-express-suite';
2
+ import { Request, Response, NextFunction } from 'express';
3
+ import { Environment } from '../environment';
4
+ import { IConstants } from '../interfaces/constants';
5
+ import { Constants } from '../constants';
6
+
7
+ export class AppRouter extends BaseAppRouter<IApplication> {
8
+ constructor(apiRouter: BaseRouter<IApplication>) {
9
+ super(apiRouter);
10
+ }
11
+
12
+ public override renderIndex(req: Request, res: Response, next: NextFunction): void {
13
+ if (req.url.endsWith('.js')) {
14
+ res.type('application/javascript');
15
+ }
16
+
17
+ const jsFile = this.getAssetFilename(this.assetsDir, /^index-.*\.js$/);
18
+ const cssFile = this.getAssetFilename(this.assetsDir, /^index-.*\.css$/);
19
+ const constants = this.application.constants as IConstants;
20
+ const environment = this.application.environment as Environment;
21
+ const SiteName = constants.Site;
22
+ const locals = {
23
+ ...this.getBaseViewLocals(req, res),
24
+ title: SiteName,
25
+ jsFile: jsFile ? `assets/${jsFile}` : undefined,
26
+ cssFile: cssFile ? `assets/${cssFile}` : undefined,
27
+ };
28
+
29
+ this.renderTemplate(req, res, next, 'index', locals);
30
+ }
31
+ }
@@ -0,0 +1,2 @@
1
+ export * from './api';
2
+ export * from './app';
@@ -0,0 +1,2 @@
1
+ export * from './schema';
2
+ export * from './user';
@@ -0,0 +1,53 @@
1
+ import { Connection } from '@digitaldefiance/mongoose-types';
2
+ import { BaseModelName, createEmailTokenSchema, createMnemonicSchema, createRoleSchema, createUsedDirectLoginTokenSchema, createUserRoleSchema, MnemonicSchema, RoleSchema, SchemaCollection, SchemaMap, UsedDirectLoginTokenSchema, UserRoleSchema } from '@digitaldefiance/node-express-suite';
3
+ import EmailTokenModel from '../models/email-token';
4
+ import MnemonicModel from '../models/mnemonic';
5
+ import RoleModel from '../models/role';
6
+ import UsedDirectLoginTokenModel from '../models/used-direct-login-token';
7
+ import UserModel from '../models/user';
8
+ import UserRoleModel from '../models/user-role';
9
+ import { UserSchema } from './user';
10
+ import { ModelDocMap } from '../shared-types';
11
+ import { IConstants } from '../interfaces/constants';
12
+ import { Constants } from '../constants';
13
+
14
+ export function getSchemaMap(connection: Connection, constants: IConstants = Constants): SchemaMap<ModelDocMap> {
15
+ return {
16
+ EmailToken: {
17
+ collection: SchemaCollection.EmailToken,
18
+ model: EmailTokenModel(connection),
19
+ modelName: BaseModelName.EmailToken,
20
+ schema: createEmailTokenSchema(undefined, constants),
21
+ },
22
+ Mnemonic: {
23
+ collection: SchemaCollection.Mnemonic,
24
+ model: MnemonicModel(connection),
25
+ modelName: BaseModelName.Mnemonic,
26
+ schema: createMnemonicSchema(undefined, constants),
27
+ },
28
+ Role: {
29
+ collection: SchemaCollection.Role,
30
+ model: RoleModel(connection),
31
+ modelName: BaseModelName.Role,
32
+ schema: createRoleSchema(undefined, constants),
33
+ },
34
+ UsedDirectLoginToken: {
35
+ collection: SchemaCollection.UsedDirectLoginToken,
36
+ model: UsedDirectLoginTokenModel(connection),
37
+ modelName: BaseModelName.UsedDirectLoginToken,
38
+ schema: createUsedDirectLoginTokenSchema(undefined, constants),
39
+ },
40
+ User: {
41
+ collection: SchemaCollection.User,
42
+ model: UserModel(connection),
43
+ modelName: BaseModelName.User,
44
+ schema: UserSchema,
45
+ },
46
+ UserRole: {
47
+ collection: SchemaCollection.UserRole,
48
+ model: UserRoleModel(connection),
49
+ modelName: BaseModelName.UserRole,
50
+ schema: createUserRoleSchema(undefined, constants),
51
+ },
52
+ };
53
+ }
@@ -0,0 +1,13 @@
1
+ import { createUserSchema, IConstants } from '@digitaldefiance/node-express-suite';
2
+ import { Constants } from '../constants';
3
+ import { Schema } from '@digitaldefiance/mongoose-types';
4
+ import { IUserDocument } from '../documents';
5
+
6
+ // Clone base schema and extend
7
+ const BaseUserSchema = createUserSchema<IConstants>(undefined, undefined, undefined, undefined, undefined, Constants);
8
+ export const UserSchema: Schema<IUserDocument> = BaseUserSchema.clone();
9
+
10
+ // // Add site-specific fields
11
+ // UserSchema.add(
12
+ // {
13
+ // });