@digitaldefiance/express-suite-starter 2.3.4 → 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/dist/src/generate-monorepo.d.ts.map +1 -1
- package/dist/src/generate-monorepo.js +7 -2
- package/dist/src/generate-monorepo.js.map +1 -1
- 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
|
@@ -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
|
+
// });
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
|
|
2
|
+
import { IApplication, IBaseDocument } from '@digitaldefiance/node-express-suite';
|
|
3
|
+
|
|
4
|
+
// Simple debugLog implementation
|
|
5
|
+
function debugLog(debug: boolean, type: 'log' | 'warn' | 'error', ...args: any[]): void {
|
|
6
|
+
if (debug) {
|
|
7
|
+
console[type](...args);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
import { Types } from '@digitaldefiance/mongoose-types';
|
|
11
|
+
import { Environment } from '../environment';
|
|
12
|
+
import { IConstants } from '../interfaces/constants';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A generic service for sending emails using Amazon SES.
|
|
16
|
+
*/
|
|
17
|
+
export class EmailService {
|
|
18
|
+
private readonly sesClient: SESClient;
|
|
19
|
+
private readonly emailSender: string;
|
|
20
|
+
private readonly disableEmailSend: boolean;
|
|
21
|
+
private readonly debug: boolean;
|
|
22
|
+
private readonly application: IApplication;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Constructs an instance of EmailService.
|
|
26
|
+
* @param config Configuration object containing AWS credentials, region, sender email, and debug/disable flags.
|
|
27
|
+
*/
|
|
28
|
+
constructor(application: IApplication) {
|
|
29
|
+
this.application = application;
|
|
30
|
+
const environment = application.environment as Environment;
|
|
31
|
+
this.emailSender = environment.emailSender;
|
|
32
|
+
this.disableEmailSend = environment.disableEmailSend;
|
|
33
|
+
this.debug = environment.debug;
|
|
34
|
+
|
|
35
|
+
// Initialize the SES client with provided AWS credentials and region.
|
|
36
|
+
this.sesClient = new SESClient({
|
|
37
|
+
region: environment.aws.region,
|
|
38
|
+
credentials: {
|
|
39
|
+
accessKeyId: environment.aws.accessKeyId.value ?? '',
|
|
40
|
+
secretAccessKey:
|
|
41
|
+
environment.aws.secretAccessKey.value ?? '',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sends an email using Amazon SES.
|
|
48
|
+
* @param to The recipient's email address.
|
|
49
|
+
* @param subject The subject line of the email.
|
|
50
|
+
* @param text The plain text body of the email.
|
|
51
|
+
* @param html The HTML body of the email.
|
|
52
|
+
* @returns A Promise that resolves when the email is sent successfully, or rejects on failure.
|
|
53
|
+
* @throws Error if email sending is disabled or if SES encounters an error.
|
|
54
|
+
*/
|
|
55
|
+
public async sendEmail(
|
|
56
|
+
to: string,
|
|
57
|
+
subject: string,
|
|
58
|
+
text: string,
|
|
59
|
+
html: string,
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
if (this.disableEmailSend) {
|
|
62
|
+
debugLog(
|
|
63
|
+
this.debug,
|
|
64
|
+
'log',
|
|
65
|
+
`Email sending disabled for: ${to} - Subject: ${subject}`,
|
|
66
|
+
);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sendCommand = new SendEmailCommand({
|
|
71
|
+
Destination: {
|
|
72
|
+
ToAddresses: [to], // Recipient email address
|
|
73
|
+
},
|
|
74
|
+
Message: {
|
|
75
|
+
Body: {
|
|
76
|
+
Html: {
|
|
77
|
+
Charset: 'UTF-8',
|
|
78
|
+
Data: html, // HTML content of the email
|
|
79
|
+
},
|
|
80
|
+
Text: {
|
|
81
|
+
Charset: 'UTF-8',
|
|
82
|
+
Data: text, // Plain text content of the email
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
Subject: {
|
|
86
|
+
Charset: 'UTF-8',
|
|
87
|
+
Data: subject, // Subject of the email
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
Source: this.emailSender, // Sender email address (must be verified in SES)
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await this.sesClient.send(sendCommand);
|
|
95
|
+
debugLog(
|
|
96
|
+
this.debug,
|
|
97
|
+
'log',
|
|
98
|
+
`Email sent successfully to ${to} with subject: ${subject}`,
|
|
99
|
+
);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`Failed to send email to ${to}:`, error);
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Failed to send email: ${
|
|
104
|
+
error instanceof Error ? error.message : String(error)
|
|
105
|
+
}`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './email';
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
2
|
+
import { ValidationChain } from 'express-validator';
|
|
3
|
+
import { ClientSession, Document, Model, Types } from '@digitaldefiance/mongoose-types';
|
|
4
|
+
import { Brand } from 'ts-brand';
|
|
5
|
+
import type { IUserDocument } from './documents/user';
|
|
6
|
+
import { CoreLanguageCode } from '@digitaldefiance/i18n-lib';
|
|
7
|
+
import { IKeyPairBufferWithUnEncryptedPrivateKey, ISigningKeyPrivateKeyInfo, ISimplePublicKeyOnly, ISimpleKeyPairBuffer, ISimplePublicKeyOnlyBuffer } from '@digitaldefiance/node-ecies-lib'
|
|
8
|
+
import {IApiErrorResponse, IApiMessageResponse, ISchema, IStatusCodeResponse, IApiExpressValidationErrorResponse, IApiMongoValidationErrorResponse, IUserRoleDocument, IUsedDirectLoginTokenDocument, IRoleDocument, IMnemonicDocument, IEmailTokenDocument } from '@digitaldefiance/node-express-suite';
|
|
9
|
+
|
|
10
|
+
export type DefaultBackendIdType = Types.ObjectId;
|
|
11
|
+
|
|
12
|
+
export type MongooseDocument = Document;
|
|
13
|
+
export type MongooseModel<
|
|
14
|
+
TRawDocType,
|
|
15
|
+
TQueryHelpers = {},
|
|
16
|
+
TInstanceMethods = {},
|
|
17
|
+
TVirtuals = {},
|
|
18
|
+
> = Model<TRawDocType, TQueryHelpers, TInstanceMethods, TVirtuals>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Transaction callback type for withTransaction
|
|
22
|
+
*/
|
|
23
|
+
export type TransactionCallback<T> = (
|
|
24
|
+
session: ClientSession | undefined,
|
|
25
|
+
...args: Array<unknown>
|
|
26
|
+
) => Promise<T>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validated body for express-validator
|
|
30
|
+
*/
|
|
31
|
+
export type ValidatedBody<T extends string> = {
|
|
32
|
+
[K in T]: unknown;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Schema map interface
|
|
37
|
+
*/
|
|
38
|
+
export type ModelDocMap = {
|
|
39
|
+
EmailToken: IEmailTokenDocument;
|
|
40
|
+
Mnemonic: IMnemonicDocument;
|
|
41
|
+
Role: IRoleDocument;
|
|
42
|
+
UsedDirectLoginToken: IUsedDirectLoginTokenDocument;
|
|
43
|
+
User: IUserDocument;
|
|
44
|
+
UserRole: IUserRoleDocument;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type SchemaMap = {
|
|
48
|
+
/**
|
|
49
|
+
* For each model name, contains the corresponding schema and model
|
|
50
|
+
*/
|
|
51
|
+
[K in keyof ModelDocMap]: ISchema<ModelDocMap[K]>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type ApiRequestHandler<T extends ApiResponse> = (
|
|
55
|
+
req: Request,
|
|
56
|
+
res: Response,
|
|
57
|
+
next: NextFunction,
|
|
58
|
+
) => Promise<IStatusCodeResponse<T>>;
|
|
59
|
+
|
|
60
|
+
export type TypedHandlers = {
|
|
61
|
+
[key: string]: ApiRequestHandler<ApiResponse>;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export interface IRouteDefinition<T extends TypedHandlers> {
|
|
65
|
+
method: 'get' | 'post' | 'put' | 'delete' | 'patch';
|
|
66
|
+
path: string;
|
|
67
|
+
options: {
|
|
68
|
+
handlerKey: keyof T;
|
|
69
|
+
validation?: (validationLanguage: CoreLanguageCode) => ValidationChain[];
|
|
70
|
+
useAuthentication: boolean;
|
|
71
|
+
useCryptoAuthentication: boolean;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type RouteHandlers = Record<string, ApiRequestHandler<ApiResponse>>;
|
|
76
|
+
|
|
77
|
+
export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
|
|
78
|
+
|
|
79
|
+
export interface RouteConfig<H extends object> {
|
|
80
|
+
method: HttpMethod;
|
|
81
|
+
path: string;
|
|
82
|
+
handlerKey: keyof H;
|
|
83
|
+
handlerArgs?: Array<unknown>;
|
|
84
|
+
useAuthentication: boolean;
|
|
85
|
+
useCryptoAuthentication: boolean;
|
|
86
|
+
middleware?: RequestHandler[];
|
|
87
|
+
validation?: FlexibleValidationChain;
|
|
88
|
+
rawJsonHandler?: boolean;
|
|
89
|
+
authFailureStatusCode?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function routeConfig<T extends object>(
|
|
93
|
+
method: 'get' | 'post' | 'put' | 'delete' | 'patch',
|
|
94
|
+
path: string,
|
|
95
|
+
options: {
|
|
96
|
+
handlerKey: keyof T;
|
|
97
|
+
validation?: (validationLanguage: CoreLanguageCode) => ValidationChain[];
|
|
98
|
+
useAuthentication: boolean;
|
|
99
|
+
useCryptoAuthentication: boolean;
|
|
100
|
+
},
|
|
101
|
+
): RouteConfig<T> {
|
|
102
|
+
return {
|
|
103
|
+
method,
|
|
104
|
+
path,
|
|
105
|
+
handlerKey: options.handlerKey,
|
|
106
|
+
validation: options.validation,
|
|
107
|
+
useAuthentication: options.useAuthentication,
|
|
108
|
+
useCryptoAuthentication: options.useCryptoAuthentication,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type THandlerArgs<T extends Array<unknown>> = T;
|
|
113
|
+
|
|
114
|
+
export type FlexibleValidationChain =
|
|
115
|
+
| ValidationChain[]
|
|
116
|
+
| ((lang: CoreLanguageCode) => ValidationChain[]);
|
|
117
|
+
|
|
118
|
+
export type JsonPrimitive = string | number | boolean | null | undefined;
|
|
119
|
+
|
|
120
|
+
export type JsonResponse =
|
|
121
|
+
| JsonPrimitive
|
|
122
|
+
| { [key: string]: JsonResponse }
|
|
123
|
+
| JsonResponse[];
|
|
124
|
+
|
|
125
|
+
export type ApiErrorResponse =
|
|
126
|
+
| IApiErrorResponse
|
|
127
|
+
| IApiExpressValidationErrorResponse
|
|
128
|
+
| IApiMongoValidationErrorResponse;
|
|
129
|
+
|
|
130
|
+
export type ApiResponse = IApiMessageResponse | ApiErrorResponse | JsonResponse;
|
|
131
|
+
|
|
132
|
+
export type SendFunction<T extends ApiResponse> = (
|
|
133
|
+
statusCode: number,
|
|
134
|
+
data: T,
|
|
135
|
+
res: Response<T>,
|
|
136
|
+
) => void;
|
|
137
|
+
|
|
138
|
+
export type KeyPairBufferWithUnEncryptedPrivateKey = Brand<
|
|
139
|
+
IKeyPairBufferWithUnEncryptedPrivateKey,
|
|
140
|
+
'KeyPairBufferWithUnEncryptedPrivateKey'
|
|
141
|
+
>;
|
|
142
|
+
export type SigningKeyPrivateKeyInfo = Brand<
|
|
143
|
+
ISigningKeyPrivateKeyInfo,
|
|
144
|
+
'SigningKeyPrivateKeyInfo'
|
|
145
|
+
>;
|
|
146
|
+
export type SimpleKeyPair = Brand<SimplePublicKeyOnly, 'SimpleKeyPair'>;
|
|
147
|
+
export type SimplePublicKeyOnly = Brand<
|
|
148
|
+
ISimplePublicKeyOnly,
|
|
149
|
+
'SimplePublicKeyOnly'
|
|
150
|
+
>;
|
|
151
|
+
export type SimpleKeyPairBuffer = Brand<
|
|
152
|
+
ISimpleKeyPairBuffer,
|
|
153
|
+
'SimpleKeyPairBuffer'
|
|
154
|
+
>;
|
|
155
|
+
export type SimplePublicKeyOnlyBuffer = Brand<
|
|
156
|
+
ISimplePublicKeyOnlyBuffer,
|
|
157
|
+
'SimplePublicKeyOnlyBuffer'
|
|
158
|
+
>;
|
|
159
|
+
export type HexString = Brand<string, 'HexString'>;
|
|
160
|
+
export type SignatureString = Brand<HexString, 'SignatureString'>;
|
|
161
|
+
export type SignatureBuffer = Buffer & Brand<Buffer, 'SignatureBuffer'>;
|
|
162
|
+
export type ChecksumBuffer = Buffer &
|
|
163
|
+
Brand<Buffer, 'Sha3Checksum', 'ChecksumBuffer'>;
|
|
164
|
+
export type ChecksumString = Brand<HexString, 'Sha3Checksum', 'ChecksumString'>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Extended Buffer type for data
|
|
168
|
+
*/
|
|
169
|
+
export type DataBuffer = Buffer & {
|
|
170
|
+
toBuffer(): Buffer;
|
|
171
|
+
toHex(): string;
|
|
172
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{workspaceName}} - Node.js & MongoDB",
|
|
3
|
+
"dockerComposeFile": "docker-compose.yml",
|
|
4
|
+
"service": "app",
|
|
5
|
+
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
|
6
|
+
"customizations": {
|
|
7
|
+
"vscode": {
|
|
8
|
+
"extensions": [
|
|
9
|
+
"mongodb.mongodb-vscode",
|
|
10
|
+
"dbaeumer.vscode-eslint",
|
|
11
|
+
"esbenp.prettier-vscode",
|
|
12
|
+
"firsttris.vscode-jest-runner",
|
|
13
|
+
"nrwl.angular-console"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"hostRequirements": {
|
|
18
|
+
"memory": "16gb",
|
|
19
|
+
"cpus": 4
|
|
20
|
+
},
|
|
21
|
+
"runArgs": [
|
|
22
|
+
"--memory=12g",
|
|
23
|
+
"--memory-swap=16g",
|
|
24
|
+
"--cpus=4",
|
|
25
|
+
"--dns=1.1.1.1",
|
|
26
|
+
"--dns=8.8.8.8",
|
|
27
|
+
"--add-host=host.docker.internal:host-gateway"
|
|
28
|
+
],
|
|
29
|
+
"forwardPorts": [3000, 3443, 4200, 27017],
|
|
30
|
+
"postCreateCommand": "yarn install"
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
services:
|
|
2
|
+
app:
|
|
3
|
+
build:
|
|
4
|
+
context: .
|
|
5
|
+
dockerfile: Dockerfile
|
|
6
|
+
volumes:
|
|
7
|
+
- ../..:/workspaces:cached
|
|
8
|
+
command: sleep infinity
|
|
9
|
+
network_mode: service:db
|
|
10
|
+
dns:
|
|
11
|
+
- 1.1.1.1
|
|
12
|
+
- 8.8.8.8
|
|
13
|
+
extra_hosts:
|
|
14
|
+
- "host.docker.internal:host-gateway"
|
|
15
|
+
# Add memory limits to prevent OOM kills
|
|
16
|
+
mem_limit: 16g
|
|
17
|
+
memswap_limit: 16g
|
|
18
|
+
cpus: 4
|
|
19
|
+
|
|
20
|
+
db:
|
|
21
|
+
image: mongo:8
|
|
22
|
+
restart: unless-stopped
|
|
23
|
+
volumes:
|
|
24
|
+
- mongodb-data:/data/db
|
|
25
|
+
environment:
|
|
26
|
+
MONGO_INITDB_ROOT_USERNAME: admin
|
|
27
|
+
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
|
|
28
|
+
MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE}
|
|
29
|
+
|
|
30
|
+
volumes:
|
|
31
|
+
mongodb-data:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
|
3
|
+
|
|
4
|
+
# Check if we're in a /workspaces or /workspace path
|
|
5
|
+
if [[ "${PWD}" =~ ^/workspaces?/ ]]; then
|
|
6
|
+
WORKSPACE=$(echo "${PWD}" | grep -oP '^/workspaces?/[^/]+')
|
|
7
|
+
elif [ "$(basename "${PWD}")" = ".devcontainer" ]; then
|
|
8
|
+
WORKSPACE=$(realpath "${PWD}/..")
|
|
9
|
+
else
|
|
10
|
+
WORKSPACE=$(realpath "${SCRIPT_DIR}/..")
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
DEVCONTAINER_DIR="${WORKSPACE}/.devcontainer"
|
|
14
|
+
|
|
15
|
+
if [ -f "${DEVCONTAINER_DIR}/.env" ]; then
|
|
16
|
+
echo "Loading environment variables from ${DEVCONTAINER_DIR}/.env"
|
|
17
|
+
set -a
|
|
18
|
+
source "${DEVCONTAINER_DIR}/.env"
|
|
19
|
+
set +a
|
|
20
|
+
fi
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
|
4
|
+
WORKSPACE=$(realpath "${SCRIPT_DIR}/..")
|
|
5
|
+
|
|
6
|
+
echo "Post-create setup starting..."
|
|
7
|
+
|
|
8
|
+
cd ${WORKSPACE} && source .devcontainer/load-env.sh
|
|
9
|
+
|
|
10
|
+
# Basic git setup
|
|
11
|
+
git config --global --add safe.directory ${WORKSPACE} 2>/dev/null || true
|
|
12
|
+
|
|
13
|
+
cd ${WORKSPACE} && ./setup-nvm.sh
|
|
14
|
+
cd ${WORKSPACE} && ./ensure-git-globals.sh
|
|
15
|
+
cd ${WORKSPACE} && ./recover-yarn.sh
|
|
16
|
+
|
|
17
|
+
cd ${WORKSPACE} && export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 && yes | corepack enable
|
|
18
|
+
|
|
19
|
+
# Setup Yarn Berry
|
|
20
|
+
echo "Setting up Yarn Berry..."
|
|
21
|
+
cd ${WORKSPACE} && corepack prepare yarn@${DEFAULT_YARN_VERSION} --activate && corepack yarn set version ${DEFAULT_YARN_VERSION} || echo "Yarn Berry setup failed"
|
|
22
|
+
|
|
23
|
+
# Install dependencies
|
|
24
|
+
echo "Installing dependencies..."
|
|
25
|
+
cd ${WORKSPACE} && corepack yarn config set nodeLinker node-modules
|
|
26
|
+
|
|
27
|
+
MAX_ATTEMPTS=5;
|
|
28
|
+
WAIT_TIME=2;
|
|
29
|
+
REGISTRY_URL=http://host.docker.internal:4873/;
|
|
30
|
+
ATTEMPTS=0;
|
|
31
|
+
echo 'Waiting for Verdaccio registry at $REGISTRY_URL (Max 10s total, 4s timeout per attempt)...';
|
|
32
|
+
while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do
|
|
33
|
+
# Increased timeouts for cross-host networking stability: 2s connect, 4s total time.
|
|
34
|
+
if curl -sS -o /dev/null --fail --connect-timeout 2 --max-time 4 $REGISTRY_URL; then
|
|
35
|
+
echo 'Verdaccio detected. Configuring NPM and YARN.';
|
|
36
|
+
npm config set registry $REGISTRY_URL --location=project;
|
|
37
|
+
yarn config set npmRegistryServer $REGISTRY_URL;
|
|
38
|
+
VERDACCIO=1;
|
|
39
|
+
break;
|
|
40
|
+
fi;
|
|
41
|
+
sleep $WAIT_TIME;
|
|
42
|
+
ATTEMPTS=$((ATTEMPTS + 1));
|
|
43
|
+
done;
|
|
44
|
+
[ -z "$VERDACCIO" ] && echo 'Verdaccio not detected after 5 attempts. Using default public registry.';
|
|
45
|
+
|
|
46
|
+
cd ${WORKSPACE} && ./npm-install-globals.sh
|
|
47
|
+
cd ${WORKSPACE} && ./do-yarn.sh || echo "Dependency installation failed"
|
|
48
|
+
|
|
49
|
+
cd ${WORKSPACE} && /usr/local/bin/mkcert localhost 127.0.0.1 ::1 {{hostname}}
|
|
50
|
+
|
|
51
|
+
echo ""
|
|
52
|
+
cd ${WORKSPACE} && yarn playwright install --with-deps || echo "Playwright installation failed"
|
|
53
|
+
|
|
54
|
+
echo "Post-create setup complete"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
MONGO_INITDB_ROOT_USERNAME=admin
|
|
2
|
+
MONGO_INITDB_ROOT_PASSWORD={{password}}
|
|
3
|
+
MONGO_INITDB_DATABASE={{workspaceName}}
|
|
4
|
+
MONGO_REPLICA_SET_NAME=rs0
|
|
5
|
+
MONGO_PORT=27017
|
|
6
|
+
MONGO_KEYFILE=/tmp/replica.key
|
|
7
|
+
MONGO_DB_PATH=/data/db
|
|
8
|
+
DEFAULT_YARN_VERSION=4.11.0
|
|
9
|
+
DEFAULT_NVM_VERSION=22
|
|
10
|
+
GIT_EMAIL=your@email.com
|
|
11
|
+
GIT_NAME="Your Name"
|
|
12
|
+
DEBUG="false"
|
|
13
|
+
|
|
14
|
+
COMPOSE_PROJECT_NAME={{prefix}}_devcontainer
|