@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.
Files changed (98) hide show
  1. package/dist/src/generate-monorepo.d.ts.map +1 -1
  2. package/dist/src/generate-monorepo.js +7 -2
  3. package/dist/src/generate-monorepo.js.map +1 -1
  4. package/package.json +2 -1
  5. package/scaffolding/api/.env.example.mustache +52 -0
  6. package/scaffolding/api/src/assets/.gitkeep +0 -0
  7. package/scaffolding/api/src/main.ts.mustache +19 -0
  8. package/scaffolding/api/src/views/index.ejs +34 -0
  9. package/scaffolding/api-lib/src/index.ts +9 -0
  10. package/scaffolding/api-lib/src/lib/application.ts +35 -0
  11. package/scaffolding/api-lib/src/lib/constants.ts.mustache +10 -0
  12. package/scaffolding/api-lib/src/lib/documents/index.ts +1 -0
  13. package/scaffolding/api-lib/src/lib/documents/user.ts +17 -0
  14. package/scaffolding/api-lib/src/lib/environment.ts +52 -0
  15. package/scaffolding/api-lib/src/lib/interfaces/constants.ts +9 -0
  16. package/scaffolding/api-lib/src/lib/interfaces/environment-aws.ts +7 -0
  17. package/scaffolding/api-lib/src/lib/interfaces/environment.ts +9 -0
  18. package/scaffolding/api-lib/src/lib/middlewares.ts.mustache +113 -0
  19. package/scaffolding/api-lib/src/lib/models/email-token.ts +12 -0
  20. package/scaffolding/api-lib/src/lib/models/mnemonic.ts +12 -0
  21. package/scaffolding/api-lib/src/lib/models/role.ts +12 -0
  22. package/scaffolding/api-lib/src/lib/models/used-direct-login-token.ts +12 -0
  23. package/scaffolding/api-lib/src/lib/models/user-role.ts +12 -0
  24. package/scaffolding/api-lib/src/lib/models/user.ts +14 -0
  25. package/scaffolding/api-lib/src/lib/routers/api.ts +23 -0
  26. package/scaffolding/api-lib/src/lib/routers/app.ts +31 -0
  27. package/scaffolding/api-lib/src/lib/routers/index.ts +2 -0
  28. package/scaffolding/api-lib/src/lib/schemas/index.ts +2 -0
  29. package/scaffolding/api-lib/src/lib/schemas/schema.ts +53 -0
  30. package/scaffolding/api-lib/src/lib/schemas/user.ts +13 -0
  31. package/scaffolding/api-lib/src/lib/services/email.ts +109 -0
  32. package/scaffolding/api-lib/src/lib/services/index.ts +1 -0
  33. package/scaffolding/api-lib/src/lib/shared-types.ts +172 -0
  34. package/scaffolding/devcontainer-mongodb/.devcontainer/.env.example +3 -0
  35. package/scaffolding/devcontainer-mongodb/.devcontainer/Dockerfile +6 -0
  36. package/scaffolding/devcontainer-mongodb/.devcontainer/devcontainer.json.mustache +31 -0
  37. package/scaffolding/devcontainer-mongodb/.devcontainer/docker-compose.yml +31 -0
  38. package/scaffolding/devcontainer-mongodb/.devcontainer/load-env.sh +20 -0
  39. package/scaffolding/devcontainer-mongodb/.devcontainer/post-create.sh.hbs +54 -0
  40. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/.env.example +14 -0
  41. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/Dockerfile +38 -0
  42. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/Mongo.Dockerfile +24 -0
  43. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/devcontainer.json +69 -0
  44. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/docker-compose.yml +66 -0
  45. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/entrypoint.sh +29 -0
  46. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/load-env.sh +20 -0
  47. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/mongodb_entrypoint.sh +124 -0
  48. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/mongodb_healthcheck.sh +36 -0
  49. package/scaffolding/devcontainer-mongodb-replicaset/.devcontainer/post-create.sh.hbs +54 -0
  50. package/scaffolding/devcontainer-simple/.devcontainer/.env.example +4 -0
  51. package/scaffolding/devcontainer-simple/.devcontainer/devcontainer.json.mustache +60 -0
  52. package/scaffolding/devcontainer-simple/.devcontainer/post-create.sh.hbs +72 -0
  53. package/scaffolding/inituserdb/.env.example.mustache +9 -0
  54. package/scaffolding/inituserdb/src/main.ts.mustache +178 -0
  55. package/scaffolding/lib/src/index.ts.mustache +6 -0
  56. package/scaffolding/lib/src/lib/constants.ts.mustache +15 -0
  57. package/scaffolding/lib/src/lib/ecies-config.ts +10 -0
  58. package/scaffolding/lib/src/lib/enumerations/{{workspaceName}}-string-key.ts.mustache +5 -0
  59. package/scaffolding/lib/src/lib/i18n-setup.ts.mustache +99 -0
  60. package/scaffolding/lib/src/lib/interfaces/constants.ts +13 -0
  61. package/scaffolding/lib/src/lib/strings-collection.ts.mustache +45 -0
  62. package/scaffolding/react/src/app/app.tsx.mustache +170 -0
  63. package/scaffolding/react/src/app/menus/extraMenu.tsx +20 -0
  64. package/scaffolding/react/src/app/menus/index.ts +5 -0
  65. package/scaffolding/react/src/app/pages/SplashPage.tsx +60 -0
  66. package/scaffolding/react/src/app/theme.tsx.mustache +91 -0
  67. package/scaffolding/react/src/assets/albatross.ico +0 -0
  68. package/scaffolding/react/src/assets/albatross.png +0 -0
  69. package/scaffolding/react/src/assets/albatross.svg +108 -0
  70. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.otf +0 -0
  71. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.ttf +0 -0
  72. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.woff +0 -0
  73. package/scaffolding/react/src/assets/fonts/allroundgothic-xlig.woff2 +0 -0
  74. package/scaffolding/react/src/assets/gen-by-albatross.png +0 -0
  75. package/scaffolding/react/src/assets/gen-by-albatross.svg +124 -0
  76. package/scaffolding/react/src/config/ecies.ts +10 -0
  77. package/scaffolding/react/src/config/runtime-config.ts +25 -0
  78. package/scaffolding/react/src/environments/environment.prod.ts.mustache +16 -0
  79. package/scaffolding/react/src/environments/environment.ts +15 -0
  80. package/scaffolding/react/src/interfaces/environment.ts +5 -0
  81. package/scaffolding/react/src/main.tsx +22 -0
  82. package/scaffolding/react/src/styles.scss +103 -0
  83. package/scaffolding/react/src/test-setup.ts +1 -0
  84. package/scaffolding/react-lib/src/index.ts +6 -0
  85. package/scaffolding/react-lib/src/theme/theme.tsx +67 -0
  86. package/scaffolding/root/.github/dependabot.yml +11 -0
  87. package/scaffolding/root/.github/workflows/ci.yml +44 -0
  88. package/scaffolding/root/.vscode/extensions.json +9 -0
  89. package/scaffolding/root/DEPLOYMENT.md +104 -0
  90. package/scaffolding/root/do-yarn.sh +148 -0
  91. package/scaffolding/root/ensure-git-globals.sh +30 -0
  92. package/scaffolding/root/eslint.config.mjs +70 -0
  93. package/scaffolding/root/list-scripts.sh +12 -0
  94. package/scaffolding/root/npm-install-globals.sh +5 -0
  95. package/scaffolding/root/nuke-node-modules.sh +23 -0
  96. package/scaffolding/root/recover-yarn.sh +37 -0
  97. package/scaffolding/root/reset.sh.mustache +25 -0
  98. 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,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
+ // });
@@ -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,3 @@
1
+ MONGO_PASSWORD={{password}}
2
+ MONGO_INITDB_DATABASE={{workspaceName}}
3
+ COMPOSE_PROJECT_NAME={{prefix}}_devcontainer
@@ -0,0 +1,6 @@
1
+ FROM mcr.microsoft.com/devcontainers/javascript-node:20
2
+
3
+ # Install MongoDB tools
4
+ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
5
+ && apt-get install -y mongodb-clients \
6
+ && apt-get clean -y && rm -rf /var/lib/apt/lists/*
@@ -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