@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,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,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,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,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;
|