@digitaldefiance/express-suite-starter 2.3.5 → 2.3.6
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.
- package/package.json +2 -1
- package/scaffolding/api/.env.example.mustache +52 -0
- package/scaffolding/api/src/assets/.gitkeep +0 -0
- package/scaffolding/api/src/main.ts.mustache +19 -0
- package/scaffolding/api/src/views/index.ejs +34 -0
- package/scaffolding/api-lib/src/index.ts +9 -0
- package/scaffolding/api-lib/src/lib/application.ts +35 -0
- package/scaffolding/api-lib/src/lib/constants.ts.mustache +10 -0
- package/scaffolding/api-lib/src/lib/documents/index.ts +1 -0
- package/scaffolding/api-lib/src/lib/documents/user.ts +17 -0
- package/scaffolding/api-lib/src/lib/environment.ts +52 -0
- package/scaffolding/api-lib/src/lib/interfaces/constants.ts +9 -0
- package/scaffolding/api-lib/src/lib/interfaces/environment-aws.ts +7 -0
- package/scaffolding/api-lib/src/lib/interfaces/environment.ts +9 -0
- package/scaffolding/api-lib/src/lib/middlewares.ts.mustache +113 -0
- package/scaffolding/api-lib/src/lib/models/email-token.ts +12 -0
- package/scaffolding/api-lib/src/lib/models/mnemonic.ts +12 -0
- package/scaffolding/api-lib/src/lib/models/role.ts +12 -0
- package/scaffolding/api-lib/src/lib/models/used-direct-login-token.ts +12 -0
- package/scaffolding/api-lib/src/lib/models/user-role.ts +12 -0
- package/scaffolding/api-lib/src/lib/models/user.ts +14 -0
- package/scaffolding/api-lib/src/lib/routers/api.ts +23 -0
- package/scaffolding/api-lib/src/lib/routers/app.ts +31 -0
- package/scaffolding/api-lib/src/lib/routers/index.ts +2 -0
- package/scaffolding/api-lib/src/lib/schemas/index.ts +2 -0
- package/scaffolding/api-lib/src/lib/schemas/schema.ts +53 -0
- package/scaffolding/api-lib/src/lib/schemas/user.ts +13 -0
- package/scaffolding/api-lib/src/lib/services/email.ts +109 -0
- package/scaffolding/api-lib/src/lib/services/index.ts +1 -0
- package/scaffolding/api-lib/src/lib/shared-types.ts +172 -0
- package/scaffolding/devcontainer-mongodb/.devcontainer/.env.example +3 -0
- package/scaffolding/devcontainer-mongodb/.devcontainer/Dockerfile +6 -0
- package/scaffolding/devcontainer-mongodb/.devcontainer/devcontainer.json.mustache +31 -0
- package/scaffolding/devcontainer-mongodb/.devcontainer/docker-compose.yml +31 -0
- package/scaffolding/devcontainer-mongodb/.devcontainer/load-env.sh +20 -0
- package/scaffolding/devcontainer-mongodb/.devcontainer/post-create.sh.hbs +54 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/.env.example +14 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/Dockerfile +38 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/Mongo.Dockerfile +24 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/devcontainer.json +69 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/docker-compose.yml +66 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/entrypoint.sh +29 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/load-env.sh +20 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/mongodb_entrypoint.sh +124 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/mongodb_healthcheck.sh +36 -0
- package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/post-create.sh.hbs +54 -0
- package/scaffolding/devcontainer-simple/.devcontainer/.env.example +4 -0
- package/scaffolding/devcontainer-simple/.devcontainer/devcontainer.json.mustache +60 -0
- package/scaffolding/devcontainer-simple/.devcontainer/post-create.sh.hbs +72 -0
- package/scaffolding/inituserdb/.env.example.mustache +9 -0
- package/scaffolding/inituserdb/src/main.ts.mustache +178 -0
- package/scaffolding/lib/src/index.ts.mustache +6 -0
- package/scaffolding/lib/src/lib/constants.ts.mustache +15 -0
- package/scaffolding/lib/src/lib/ecies-config.ts +10 -0
- package/scaffolding/lib/src/lib/enumerations/{{workspaceName}}-string-key.ts.mustache +5 -0
- package/scaffolding/lib/src/lib/i18n-setup.ts.mustache +99 -0
- package/scaffolding/lib/src/lib/interfaces/constants.ts +13 -0
- package/scaffolding/lib/src/lib/strings-collection.ts.mustache +45 -0
- package/scaffolding/react/src/app/app.tsx.mustache +170 -0
- package/scaffolding/react/src/app/menus/extraMenu.tsx +20 -0
- package/scaffolding/react/src/app/menus/index.ts +5 -0
- package/scaffolding/react/src/app/pages/SplashPage.tsx +60 -0
- package/scaffolding/react/src/app/theme.tsx.mustache +91 -0
- package/scaffolding/react/src/assets/albatross.ico +0 -0
- package/scaffolding/react/src/assets/albatross.png +0 -0
- package/scaffolding/react/src/assets/albatross.svg +108 -0
- package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.otf +0 -0
- package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.ttf +0 -0
- package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.woff +0 -0
- package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.woff2 +0 -0
- package/scaffolding/react/src/assets/gen-by-albatross.png +0 -0
- package/scaffolding/react/src/assets/gen-by-albatross.svg +124 -0
- package/scaffolding/react/src/config/ecies.ts +10 -0
- package/scaffolding/react/src/config/runtime-config.ts +25 -0
- package/scaffolding/react/src/environments/environment.prod.ts.mustache +16 -0
- package/scaffolding/react/src/environments/environment.ts +15 -0
- package/scaffolding/react/src/interfaces/environment.ts +5 -0
- package/scaffolding/react/src/main.tsx +22 -0
- package/scaffolding/react/src/styles.scss +103 -0
- package/scaffolding/react/src/test-setup.ts +1 -0
- package/scaffolding/react-lib/src/index.ts +6 -0
- package/scaffolding/react-lib/src/theme/theme.tsx +67 -0
- package/scaffolding/root/.github/dependabot.yml +11 -0
- package/scaffolding/root/.github/workflows/ci.yml +44 -0
- package/scaffolding/root/.vscode/extensions.json +9 -0
- package/scaffolding/root/DEPLOYMENT.md +104 -0
- package/scaffolding/root/do-yarn.sh +148 -0
- package/scaffolding/root/ensure-git-globals.sh +30 -0
- package/scaffolding/root/eslint.config.mjs +70 -0
- package/scaffolding/root/list-scripts.sh +12 -0
- package/scaffolding/root/npm-install-globals.sh +5 -0
- package/scaffolding/root/nuke-node-modules.sh +23 -0
- package/scaffolding/root/recover-yarn.sh +37 -0
- package/scaffolding/root/reset.sh.mustache +25 -0
- 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.
|
|
3
|
+
"version": "2.3.6",
|
|
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
|
],
|
|
@@ -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,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,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
|
+
// });
|