@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,178 @@
1
+ import {
2
+ BaseApplication,
3
+ DatabaseInitializationService,
4
+ Environment,
5
+ IServerInitResult,
6
+ getSchemaMap,
7
+ } from '@digitaldefiance/node-express-suite';
8
+
9
+ // Simple debugLog implementation
10
+ function debugLog(debug: boolean, type: 'log' | 'warn' | 'error', ...args: any[]): void {
11
+ if (debug) {
12
+ console[type](...args);
13
+ }
14
+ }
15
+ import { Constants as AppConstants } from '{{namespace}}/api-lib';
16
+ import { GlobalActiveContext, IActiveContext } from '@digitaldefiance/i18n-lib';
17
+ import { IECIESConfig } from '@digitaldefiance/ecies-lib';
18
+ import { ECIESService } from '@digitaldefiance/node-ecies-lib';
19
+ import {
20
+ CoreLanguageCode,
21
+ } from '@digitaldefiance/i18n-lib';
22
+ import { getSuiteCoreI18nEngine, getSuiteCoreTranslation, IFailableResult, SuiteCoreStringKey } from '@digitaldefiance/suite-core-lib';
23
+ import { randomBytes } from 'crypto';
24
+ import { join } from 'path';
25
+
26
+ async function main() {
27
+ const context = GlobalActiveContext.getInstance<CoreLanguageCode, IActiveContext<CoreLanguageCode>>();
28
+ context.languageContextSpace = 'admin';
29
+
30
+ const config: IECIESConfig = {
31
+ curveName: AppConstants.ECIES.CURVE_NAME,
32
+ primaryKeyDerivationPath: AppConstants.ECIES.PRIMARY_KEY_DERIVATION_PATH,
33
+ mnemonicStrength: AppConstants.ECIES.MNEMONIC_STRENGTH,
34
+ symmetricAlgorithm: AppConstants.ECIES.SYMMETRIC_ALGORITHM_CONFIGURATION,
35
+ symmetricKeyBits: AppConstants.ECIES.SYMMETRIC.KEY_BITS,
36
+ symmetricKeyMode: AppConstants.ECIES.SYMMETRIC.MODE,
37
+ };
38
+ const eciesService = new ECIESService(config);
39
+ if (process.argv.includes('--gen-system-user-mnemonic')) {
40
+ const mnemonic = eciesService.generateNewMnemonic();
41
+ process.env.ADMIN_MNEMONIC = mnemonic.value;
42
+ console.log(
43
+ `ADMIN_MNEMONIC="${mnemonic.value}"\n`,
44
+ getSuiteCoreTranslation(
45
+ SuiteCoreStringKey.Admin_MakeSureToSetItInEnv,
46
+ ),
47
+ );
48
+ }
49
+ if (process.argv.includes('--gen-member-user-mnemonic')) {
50
+ const mnemonic = eciesService.generateNewMnemonic();
51
+ process.env.MEMBER_MNEMONIC = mnemonic.value;
52
+ console.log(
53
+ `MEMBER_MNEMONIC="${mnemonic.value}"\n`,
54
+ getSuiteCoreTranslation(
55
+ SuiteCoreStringKey.Admin_MakeSureToSetItInEnv,
56
+ ),
57
+ );
58
+ }
59
+ if (process.argv.includes('--gen-mnemonic-hmac-secret')) {
60
+ const mnemonicHmacSecret = randomBytes(32).toString('hex');
61
+ process.env.MNEMONIC_HMAC_SECRET = mnemonicHmacSecret;
62
+ console.log(
63
+ `MNEMONIC_HMAC_SECRET="${mnemonicHmacSecret}"\n`,
64
+ getSuiteCoreTranslation(
65
+ SuiteCoreStringKey.Admin_MakeSureToSetItInEnv,
66
+ ),
67
+ );
68
+ }
69
+ if (process.argv.includes('--gen-mnemonic-encryption-key')) {
70
+ const mnemonicEncryptionKey = randomBytes(32).toString('hex');
71
+ process.env.MNEMONIC_ENCRYPTION_KEY = mnemonicEncryptionKey;
72
+ console.log(
73
+ `MNEMONIC_ENCRYPTION_KEY="${mnemonicEncryptionKey}"\n`,
74
+ getSuiteCoreTranslation(
75
+ SuiteCoreStringKey.Admin_MakeSureToSetItInEnv,
76
+ ),
77
+ );
78
+ }
79
+
80
+ const envDir = join(
81
+ BaseApplication.distDir,
82
+ '{{prefix}}-inituserdb',
83
+ '.env',
84
+ );
85
+ const env: Environment = new Environment(envDir, true);
86
+ const app = new BaseApplication(env, getSchemaMap, DatabaseInitializationService.initUserDb, DatabaseInitializationService.serverInitResultHash, AppConstants);
87
+ context.languageContextSpace = 'admin';
88
+ const shouldDropDatabase = process.argv.includes('--drop');
89
+ debugLog(
90
+ app.environment.detailedDebug,
91
+ 'log',
92
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_TransactionsEnabledDisabledTemplate, {
93
+ STATE: getSuiteCoreTranslation(
94
+ app.environment.mongo.useTransactions
95
+ ? SuiteCoreStringKey.Common_Enabled
96
+ : SuiteCoreStringKey.Common_Disabled,
97
+ ),
98
+ }),
99
+ );
100
+ let exitCode = 1;
101
+ await app.start();
102
+ if (shouldDropDatabase) {
103
+ const result = await DatabaseInitializationService.dropDatabase(
104
+ app.db.connection,
105
+ );
106
+ if (result) {
107
+ debugLog(
108
+ app.environment.detailedDebug,
109
+ 'log',
110
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_DatabaseDropped),
111
+ );
112
+ await new Promise((resolve) => setTimeout(resolve, 1000));
113
+ debugLog(
114
+ app.environment.detailedDebug,
115
+ 'log',
116
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_ProceedingToDbInitialization),
117
+ );
118
+ } else {
119
+ debugLog(
120
+ app.environment.detailedDebug,
121
+ 'error',
122
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_Error_FailedToDropDatabase),
123
+ );
124
+ await app.stop();
125
+ process.exit(2);
126
+ }
127
+ }
128
+ debugLog(
129
+ app.environment.detailedDebug,
130
+ 'log',
131
+ getSuiteCoreTranslation(SuiteCoreStringKey.Admin_StartingDbInitialization),
132
+ );
133
+ const result: IFailableResult<IServerInitResult> =
134
+ await DatabaseInitializationService.initUserDb(app);
135
+ if (result.success && result.data) {
136
+ debugLog(
137
+ app.environment.debug,
138
+ 'log',
139
+ getSuiteCoreTranslation(
140
+ SuiteCoreStringKey.Admin_UserDatabaseInitialized,
141
+ ),
142
+ );
143
+ if (app.environment.detailedDebug) {
144
+ DatabaseInitializationService.printServerInitResults(result.data);
145
+ // DatabaseInitializationService.setEnvFromInitResults(result.data);
146
+ // app.reloadEnvironment(envDir, true);
147
+ const initHash = DatabaseInitializationService.serverInitResultHash(
148
+ result.data,
149
+ );
150
+ debugLog(
151
+ true,
152
+ 'log',
153
+ `Database initialized with options hash: ${initHash}`,
154
+ );
155
+ }
156
+ exitCode = 0;
157
+ } else {
158
+ const engine = getSuiteCoreI18nEngine();
159
+ console.error(
160
+ engine.t(
161
+ '{{SuiteCoreStringKey.Admin_Error_FailedToInitializeUserDatabase}}:',
162
+ ),
163
+ result.error,
164
+ );
165
+ exitCode++;
166
+ }
167
+ await app.stop();
168
+ process.exit(exitCode);
169
+ }
170
+
171
+ main().catch((err) => {
172
+ const engine = getSuiteCoreI18nEngine();
173
+ console.error(
174
+ engine.t('{{SuiteCoreStringKey.Admin_Error_UnhandledErrorInMain}}:'),
175
+ err,
176
+ );
177
+ process.exit(1);
178
+ });
@@ -0,0 +1,6 @@
1
+ export * from './lib/constants';
2
+ export * from './lib/i18n-setup';
3
+ export * from './lib/enumerations/{{workspaceName}}-string-key';
4
+ export * from './lib/interfaces/constants';
5
+ export * from './lib/strings-collection';
6
+ export * from './lib/ecies-config';
@@ -0,0 +1,15 @@
1
+ import { IConstants } from './interfaces/constants';
2
+ import { Constants } from '@digitaldefiance/suite-core-lib';
3
+
4
+ export const AppConstants: IConstants = {
5
+ ...Constants,
6
+ Site: '{{siteTitle}}' as const,
7
+ SiteTagline: '{{siteTagline}}' as const,
8
+ SiteDescription: '{{siteDescription}}' as const,
9
+ SiteHostname: '{{hostname}}' as const,
10
+ PasswordRegex: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?])[A-Za-z\d!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]{8,}$/,
11
+ UsernameMinLength: 3 as const,
12
+ UsernameMaxLength: 30 as const,
13
+ UsernameRegex: /^[A-Za-z0-9]{3,30}$/,
14
+ PasswordMinLength: 8 as const,
15
+ };
@@ -0,0 +1,10 @@
1
+ import { IECIESConfig, ECIES } from '@digitaldefiance/ecies-lib';
2
+
3
+ export const EciesConfig: IECIESConfig = {
4
+ curveName: ECIES.CURVE_NAME,
5
+ primaryKeyDerivationPath: ECIES.PRIMARY_KEY_DERIVATION_PATH,
6
+ mnemonicStrength: ECIES.MNEMONIC_STRENGTH,
7
+ symmetricAlgorithm: ECIES.SYMMETRIC_ALGORITHM_CONFIGURATION,
8
+ symmetricKeyBits: ECIES.SYMMETRIC.KEY_BITS,
9
+ symmetricKeyMode: ECIES.SYMMETRIC.MODE,
10
+ };
@@ -0,0 +1,5 @@
1
+ export enum {{WorkspaceName}}StringKey {
2
+ SiteTitle = 'siteTitle',
3
+ SiteDescription = 'siteDescription',
4
+ SiteTagline = 'siteTagline',
5
+ }
@@ -0,0 +1,99 @@
1
+ import {
2
+ CoreLanguageCode,
3
+ LanguageCodes,
4
+ GlobalActiveContext,
5
+ IActiveContext,
6
+ LanguageContextSpace,
7
+ getCoreLanguageDefinitions,
8
+ I18nBuilder,
9
+ I18nEngine,
10
+ } from '@digitaldefiance/i18n-lib';
11
+ import { AppConstants } from './constants';
12
+ import { {{WorkspaceName}}StringKey } from './enumerations/{{workspaceName}}-string-key';
13
+ import { Strings } from './strings-collection';
14
+ import { createSuiteCoreComponentConfig } from '@digitaldefiance/suite-core-lib';
15
+
16
+ export const ComponentId = '{{WorkspaceName}}';
17
+
18
+ const componentStrings = {
19
+ [LanguageCodes.EN_US]: Strings[LanguageCodes.EN_US],
20
+ [LanguageCodes.EN_GB]: Strings[LanguageCodes.EN_GB],
21
+ [LanguageCodes.FR]: Strings[LanguageCodes.FR],
22
+ [LanguageCodes.ES]: Strings[LanguageCodes.ES],
23
+ [LanguageCodes.DE]: Strings[LanguageCodes.DE],
24
+ [LanguageCodes.ZH_CN]: Strings[LanguageCodes.ZH_CN],
25
+ [LanguageCodes.JA]: Strings[LanguageCodes.JA],
26
+ [LanguageCodes.UK]: Strings[LanguageCodes.UK],
27
+ };
28
+
29
+ let i18nEngine: I18nEngine;
30
+ if (I18nEngine.hasInstance('default')) {
31
+ i18nEngine = I18nEngine.getInstance('default');
32
+ i18nEngine.mergeConstants(AppConstants);
33
+ } else {
34
+ i18nEngine = I18nBuilder.create()
35
+ .withLanguages(getCoreLanguageDefinitions())
36
+ .withDefaultLanguage(LanguageCodes.EN_US)
37
+ .withFallbackLanguage(LanguageCodes.EN_US)
38
+ .withValidation({
39
+ requireCompleteStrings: false,
40
+ allowPartialRegistration: true,
41
+ })
42
+ .withConstants(AppConstants)
43
+ .withInstanceKey('default')
44
+ .build();
45
+
46
+ i18nEngine.register(createSuiteCoreComponentConfig());
47
+ }
48
+
49
+ export { i18nEngine };
50
+
51
+ const registrationResult = i18nEngine.registerIfNotExists({
52
+ id: ComponentId,
53
+ strings: componentStrings,
54
+ aliases: ['{{WorkspaceName}}StringKey'],
55
+ });
56
+
57
+ if (!registrationResult.isValid) {
58
+ console.warn('Component has missing translations:', registrationResult.errors);
59
+ }
60
+
61
+ const globalContext = GlobalActiveContext.getInstance<CoreLanguageCode, IActiveContext<CoreLanguageCode>>();
62
+ globalContext.createContext(LanguageCodes.EN_US, LanguageCodes.EN_US, ComponentId);
63
+
64
+ export const i18nContext: IActiveContext<CoreLanguageCode> = {
65
+ get language() {
66
+ return globalContext.getContext(ComponentId).language;
67
+ },
68
+ get adminLanguage() {
69
+ return globalContext.getContext(ComponentId).adminLanguage;
70
+ },
71
+ get currentContext() {
72
+ return globalContext.getContext(ComponentId).currentContext;
73
+ },
74
+ get currencyCode() {
75
+ return globalContext.getContext(ComponentId).currencyCode;
76
+ },
77
+ get timezone() {
78
+ return globalContext.getContext(ComponentId).timezone;
79
+ },
80
+ get adminTimezone() {
81
+ return globalContext.getContext(ComponentId).adminTimezone;
82
+ },
83
+ };
84
+
85
+ export const translate = (
86
+ name: {{WorkspaceName}}StringKey,
87
+ variables?: Record<string, string | number>,
88
+ language?: CoreLanguageCode,
89
+ context?: LanguageContextSpace,
90
+ ): string => {
91
+ const activeContext = context ?? globalContext.getContext(ComponentId).currentContext;
92
+ const lang =
93
+ language ??
94
+ (activeContext === 'admin'
95
+ ? globalContext.getContext(ComponentId).adminLanguage
96
+ : globalContext.getContext(ComponentId).language);
97
+
98
+ return i18nEngine.translate(ComponentId, name, variables, lang);
99
+ };
@@ -0,0 +1,13 @@
1
+ import { IConstants as ISuiteCoreConstants } from '@digitaldefiance/suite-core-lib';
2
+
3
+ export interface IConstants extends ISuiteCoreConstants {
4
+ readonly Site: string;
5
+ readonly SiteTagline: string;
6
+ readonly SiteDescription: string;
7
+ readonly SiteHostname: string;
8
+ readonly PasswordRegex: RegExp;
9
+ readonly UsernameMinLength: number;
10
+ readonly UsernameMaxLength: number;
11
+ readonly UsernameRegex: RegExp;
12
+ readonly PasswordMinLength: number;
13
+ }
@@ -0,0 +1,45 @@
1
+ import { LanguageCodes } from '@digitaldefiance/i18n-lib';
2
+ import { {{WorkspaceName}}StringKey } from './enumerations/{{workspaceName}}-string-key';
3
+
4
+ export const Strings = {
5
+ [LanguageCodes.EN_US]: {
6
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isEnUs}}{{siteTitle}}{{/isEnUs}}{{^isEnUs}}Your Site Title{{/isEnUs}}',
7
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isEnUs}}{{siteDescription}}{{/isEnUs}}{{^isEnUs}}Your description here{{/isEnUs}}',
8
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isEnUs}}{{siteTagline}}{{/isEnUs}}{{^isEnUs}}Your tagline here{{/isEnUs}}',
9
+ },
10
+ [LanguageCodes.EN_GB]: {
11
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isEnGb}}{{siteTitle}}{{/isEnGb}}{{^isEnGb}}Your Site Title{{/isEnGb}}',
12
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isEnGb}}{{siteDescription}}{{/isEnGb}}{{^isEnGb}}Your description here{{/isEnGb}}',
13
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isEnGb}}{{siteTagline}}{{/isEnGb}}{{^isEnGb}}Your tagline here{{/isEnGb}}',
14
+ },
15
+ [LanguageCodes.FR]: {
16
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isFr}}{{siteTitle}}{{/isFr}}{{^isFr}}Titre de votre site{{/isFr}}',
17
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isFr}}{{siteDescription}}{{/isFr}}{{^isFr}}Votre description ici{{/isFr}}',
18
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isFr}}{{siteTagline}}{{/isFr}}{{^isFr}}Votre slogan ici{{/isFr}}',
19
+ },
20
+ [LanguageCodes.ES]: {
21
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isEs}}{{siteTitle}}{{/isEs}}{{^isEs}}Título de su sitio{{/isEs}}',
22
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isEs}}{{siteDescription}}{{/isEs}}{{^isEs}}Su descripción aquí{{/isEs}}',
23
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isEs}}{{siteTagline}}{{/isEs}}{{^isEs}}Su eslogan aquí{{/isEs}}',
24
+ },
25
+ [LanguageCodes.DE]: {
26
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isDe}}{{siteTitle}}{{/isDe}}{{^isDe}}Ihr Seitentitel{{/isDe}}',
27
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isDe}}{{siteDescription}}{{/isDe}}{{^isDe}}Ihre Beschreibung hier{{/isDe}}',
28
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isDe}}{{siteTagline}}{{/isDe}}{{^isDe}}Ihr Slogan hier{{/isDe}}',
29
+ },
30
+ [LanguageCodes.ZH_CN]: {
31
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isZhCn}}{{siteTitle}}{{/isZhCn}}{{^isZhCn}}您的网站标题{{/isZhCn}}',
32
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isZhCn}}{{siteDescription}}{{/isZhCn}}{{^isZhCn}}您的描述在这里{{/isZhCn}}',
33
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isZhCn}}{{siteTagline}}{{/isZhCn}}{{^isZhCn}}您的标语在这里{{/isZhCn}}',
34
+ },
35
+ [LanguageCodes.JA]: {
36
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isJa}}{{siteTitle}}{{/isJa}}{{^isJa}}サイトのタイトル{{/isJa}}',
37
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isJa}}{{siteDescription}}{{/isJa}}{{^isJa}}ここに説明を入力{{/isJa}}',
38
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isJa}}{{siteTagline}}{{/isJa}}{{^isJa}}ここにキャッチフレーズを入力{{/isJa}}',
39
+ },
40
+ [LanguageCodes.UK]: {
41
+ [{{WorkspaceName}}StringKey.SiteTitle]: '{{#isUk}}{{siteTitle}}{{/isUk}}{{^isUk}}Назва вашого сайту{{/isUk}}',
42
+ [{{WorkspaceName}}StringKey.SiteDescription]: '{{#isUk}}{{siteDescription}}{{/isUk}}{{^isUk}}Ваш опис тут{{/isUk}}',
43
+ [{{WorkspaceName}}StringKey.SiteTagline]: '{{#isUk}}{{siteTagline}}{{/isUk}}{{^isUk}}Ваш слоган тут{{/isUk}}',
44
+ },
45
+ };
@@ -0,0 +1,170 @@
1
+ import { Box, CssBaseline } from '@mui/material';
2
+ import { LocalizationProvider } from '@mui/x-date-pickers';
3
+ import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
4
+ import { FC, useCallback } from 'react';
5
+ import { Route, Routes, useNavigate } from 'react-router-dom';
6
+ import '../styles.scss';
7
+ import SplashPage from './pages/SplashPage';
8
+ import { createAppTheme } from './theme.tsx';
9
+ import { AppThemeProvider, DashboardPage, MenuProvider, I18nProvider as TranslationProvider, AuthProvider, SuiteConfigProvider, ApiAccess, ChangePasswordFormWrapper, VerifyEmailPageWrapper, RegisterFormWrapper, UserSettingsFormWrapper, LogoutPageWrapper, LoginFormWrapper, PrivateRoute, UnAuthRoute, TopMenu, TranslatedTitle, BackupCodeLoginWrapper, BackupCodesWrapper } from '@digitaldefiance/express-suite-react-components';
10
+ import { environment } from '../environments/environment';
11
+ import { {{WorkspaceName}}StringKey, ComponentId, i18nEngine } from '{{namespace}}/lib';
12
+ import AlbatrossLogo from '../assets/albatross.svg';
13
+ import { AppConstants, EciesConfig } from '{{namespace}}/lib';
14
+ import { appMenuConfigs } from './menus';
15
+ import { LanguageRegistry } from '@digitaldefiance/i18n-lib';
16
+ import { SuiteCoreComponentId, SuiteCoreStringKey } from '@digitaldefiance/suite-core-lib';
17
+
18
+ const getApiBaseUrl = () => {
19
+ if (
20
+ typeof window !== 'undefined' &&
21
+ (window as any).APP_CONFIG &&
22
+ (window as any).APP_CONFIG.apiUrl
23
+ ) {
24
+ return (window as any).APP_CONFIG.apiUrl;
25
+ }
26
+ return environment.apiUrl;
27
+ };
28
+
29
+ const API_BASE_URL = getApiBaseUrl();
30
+
31
+ const AuthProviderWithNavigation: FC<{ children: React.ReactNode }> = ({
32
+ children,
33
+ }) => {
34
+ const navigate = useNavigate();
35
+
36
+ const handleLogout = useCallback(() => {
37
+ navigate('/');
38
+ }, [navigate]);
39
+
40
+ return (
41
+ <SuiteConfigProvider
42
+ baseUrl={API_BASE_URL}
43
+ routes={ {
44
+ dashboard: '/dashboard',
45
+ login: '/login',
46
+ register: '/register',
47
+ verifyEmail: '/verify-email',
48
+ settings: '/user-settings',
49
+ } }
50
+ languages={LanguageRegistry.getCodeLabelMap()}
51
+ >
52
+ <AuthProvider
53
+ baseUrl={API_BASE_URL}
54
+ constants={AppConstants}
55
+ eciesConfig={EciesConfig}
56
+ onLogout={handleLogout}
57
+ >
58
+ {children}
59
+ </AuthProvider>
60
+ </SuiteConfigProvider>
61
+ );
62
+ };
63
+
64
+ const App: FC = () => {
65
+ return (
66
+ <LocalizationProvider dateAdapter={AdapterDateFns}>
67
+ <TranslationProvider i18nEngine={i18nEngine}>
68
+ <TranslatedTitle<SuiteCoreStringKey>
69
+ componentId={SuiteCoreComponentId}
70
+ stringKey={SuiteCoreStringKey.Common_SiteTemplate} />
71
+ <AppThemeProvider customTheme={createAppTheme}>
72
+ <CssBaseline />
73
+ <AuthProviderWithNavigation>
74
+ <InnerApp />
75
+ </AuthProviderWithNavigation>
76
+ </AppThemeProvider>
77
+ </TranslationProvider>
78
+ </LocalizationProvider>
79
+ );
80
+ };
81
+
82
+ const LogoComponent = () => <img src={AlbatrossLogo} alt="Logo" />;
83
+
84
+ const InnerApp: FC = () => {
85
+ return (
86
+ <MenuProvider menuConfigs={appMenuConfigs}>
87
+ <Box className="app-container" sx={ { paddingTop: '64px' } }>
88
+ <TopMenu Logo={<LogoComponent />} />
89
+ <Routes>
90
+ <Route path="/" element={<SplashPage />} />
91
+ <Route
92
+ path="/api-access"
93
+ element={
94
+ <PrivateRoute>
95
+ <ApiAccess />
96
+ </PrivateRoute>
97
+ }
98
+ />
99
+ <Route path="/backup-code" element={<BackupCodeLoginWrapper />} />
100
+ <Route
101
+ path="/backup-codes"
102
+ element={
103
+ <PrivateRoute>
104
+ <BackupCodesWrapper />
105
+ </PrivateRoute>
106
+ }
107
+ />
108
+ <Route
109
+ path="/login"
110
+ element={
111
+ <UnAuthRoute>
112
+ <LoginFormWrapper />
113
+ </UnAuthRoute>
114
+ }
115
+ />
116
+ <Route
117
+ path="/logout"
118
+ element={
119
+ <PrivateRoute>
120
+ <LogoutPageWrapper />
121
+ </PrivateRoute>
122
+ }
123
+ />
124
+ <Route
125
+ path="/register"
126
+ element={
127
+ <UnAuthRoute>
128
+ <RegisterFormWrapper />
129
+ </UnAuthRoute>
130
+ }
131
+ />
132
+ <Route
133
+ path="/dashboard"
134
+ element={
135
+ <PrivateRoute>
136
+ <DashboardPage />
137
+ </PrivateRoute>
138
+ }
139
+ />
140
+ <Route
141
+ path="/change-password"
142
+ element={
143
+ <PrivateRoute>
144
+ <ChangePasswordFormWrapper />
145
+ </PrivateRoute>
146
+ }
147
+ />
148
+ <Route
149
+ path="/verify-email"
150
+ element={
151
+ <UnAuthRoute>
152
+ <VerifyEmailPageWrapper />
153
+ </UnAuthRoute>
154
+ }
155
+ />
156
+ <Route
157
+ path="/user-settings"
158
+ element={
159
+ <PrivateRoute>
160
+ <UserSettingsFormWrapper />
161
+ </PrivateRoute>
162
+ }
163
+ />
164
+ </Routes>
165
+ </Box>
166
+ </MenuProvider>
167
+ );
168
+ };
169
+
170
+ export default App;
@@ -0,0 +1,20 @@
1
+ import { createMenuType, IMenuConfig } from '@digitaldefiance/express-suite-react-components';
2
+ import { Dashboard } from '@mui/icons-material';
3
+
4
+ const ExtraMenu = createMenuType('ExtraMenu');
5
+
6
+ export const extraMenuConfig: IMenuConfig = {
7
+ menuType: ExtraMenu,
8
+ menuIcon: <Dashboard />,
9
+ priority: 1,
10
+ options: [
11
+ {
12
+ id: 'extra-option-1',
13
+ label: 'Extra Option 1',
14
+ link: '/extra-1',
15
+ requiresAuth: true,
16
+ includeOnMenus: [ExtraMenu],
17
+ index: 0,
18
+ },
19
+ ],
20
+ };
@@ -0,0 +1,5 @@
1
+ import { extraMenuConfig } from './extraMenu';
2
+
3
+ export { extraMenuConfig } from './extraMenu';
4
+
5
+ export const appMenuConfigs = [extraMenuConfig];
@@ -0,0 +1,60 @@
1
+ import { Box, Button, Container, Typography } from '@mui/material';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import { useAuth } from '@digitaldefiance/express-suite-react-components';
4
+
5
+ const SplashPage = () => {
6
+ const navigate = useNavigate();
7
+ const { isAuthenticated } = useAuth();
8
+
9
+ return (
10
+ <Container maxWidth="md">
11
+ <Box
12
+ sx={{
13
+ display: 'flex',
14
+ flexDirection: 'column',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
17
+ minHeight: '80vh',
18
+ textAlign: 'center',
19
+ }}
20
+ >
21
+ <Typography variant="h2" component="h1" gutterBottom>
22
+ Welcome to @WORKSPACENAME@
23
+ </Typography>
24
+ <Typography variant="h5" color="text.secondary" paragraph>
25
+ A modern MERN stack application built with Express Suite
26
+ </Typography>
27
+ <Box sx={{ mt: 4, display: 'flex', gap: 2 }}>
28
+ {isAuthenticated ? (
29
+ <Button
30
+ variant="contained"
31
+ size="large"
32
+ onClick={() => navigate('/dashboard')}
33
+ >
34
+ Go to Dashboard
35
+ </Button>
36
+ ) : (
37
+ <>
38
+ <Button
39
+ variant="contained"
40
+ size="large"
41
+ onClick={() => navigate('/login')}
42
+ >
43
+ Login
44
+ </Button>
45
+ <Button
46
+ variant="outlined"
47
+ size="large"
48
+ onClick={() => navigate('/register')}
49
+ >
50
+ Register
51
+ </Button>
52
+ </>
53
+ )}
54
+ </Box>
55
+ </Box>
56
+ </Container>
57
+ );
58
+ };
59
+
60
+ export default SplashPage;