@unchainedshop/platform 1.1.3
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/README.md +3 -0
- package/lib/bulk-importer/createBulkImporter.d.ts +4 -0
- package/lib/bulk-importer/createBulkImporter.js +102 -0
- package/lib/bulk-importer/createBulkImporter.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/create.d.ts +11 -0
- package/lib/bulk-importer/handlers/assortment/create.js +59 -0
- package/lib/bulk-importer/handlers/assortment/create.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/index.d.ts +3 -0
- package/lib/bulk-importer/handlers/assortment/index.js +4 -0
- package/lib/bulk-importer/handlers/assortment/index.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/remove.d.ts +9 -0
- package/lib/bulk-importer/handlers/assortment/remove.js +13 -0
- package/lib/bulk-importer/handlers/assortment/remove.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/update.d.ts +10 -0
- package/lib/bulk-importer/handlers/assortment/update.js +61 -0
- package/lib/bulk-importer/handlers/assortment/update.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentChildren.d.ts +7 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentChildren.js +30 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentChildren.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentContent.d.ts +7 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentContent.js +9 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentContent.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentFilters.d.ts +7 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentFilters.js +29 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentFilters.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentProducts.d.ts +7 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentProducts.js +32 -0
- package/lib/bulk-importer/handlers/assortment/upsertAssortmentProducts.js.map +1 -0
- package/lib/bulk-importer/handlers/assortment/upsertMedia.d.ts +7 -0
- package/lib/bulk-importer/handlers/assortment/upsertMedia.js +67 -0
- package/lib/bulk-importer/handlers/assortment/upsertMedia.js.map +1 -0
- package/lib/bulk-importer/handlers/filter/create.d.ts +11 -0
- package/lib/bulk-importer/handlers/filter/create.js +42 -0
- package/lib/bulk-importer/handlers/filter/create.js.map +1 -0
- package/lib/bulk-importer/handlers/filter/index.d.ts +3 -0
- package/lib/bulk-importer/handlers/filter/index.js +4 -0
- package/lib/bulk-importer/handlers/filter/index.js.map +1 -0
- package/lib/bulk-importer/handlers/filter/remove.d.ts +9 -0
- package/lib/bulk-importer/handlers/filter/remove.js +14 -0
- package/lib/bulk-importer/handlers/filter/remove.js.map +1 -0
- package/lib/bulk-importer/handlers/filter/update.d.ts +10 -0
- package/lib/bulk-importer/handlers/filter/update.js +34 -0
- package/lib/bulk-importer/handlers/filter/update.js.map +1 -0
- package/lib/bulk-importer/handlers/filter/upsertFilterContent.d.ts +13 -0
- package/lib/bulk-importer/handlers/filter/upsertFilterContent.js +6 -0
- package/lib/bulk-importer/handlers/filter/upsertFilterContent.js.map +1 -0
- package/lib/bulk-importer/handlers/filter/upsertFilterOptionContent.d.ts +5 -0
- package/lib/bulk-importer/handlers/filter/upsertFilterOptionContent.js +8 -0
- package/lib/bulk-importer/handlers/filter/upsertFilterOptionContent.js.map +1 -0
- package/lib/bulk-importer/handlers/product/create.d.ts +11 -0
- package/lib/bulk-importer/handlers/product/create.js +51 -0
- package/lib/bulk-importer/handlers/product/create.js.map +1 -0
- package/lib/bulk-importer/handlers/product/index.d.ts +3 -0
- package/lib/bulk-importer/handlers/product/index.js +4 -0
- package/lib/bulk-importer/handlers/product/index.js.map +1 -0
- package/lib/bulk-importer/handlers/product/remove.d.ts +9 -0
- package/lib/bulk-importer/handlers/product/remove.js +14 -0
- package/lib/bulk-importer/handlers/product/remove.js.map +1 -0
- package/lib/bulk-importer/handlers/product/transformSpecificationToProductStructure.d.ts +2 -0
- package/lib/bulk-importer/handlers/product/transformSpecificationToProductStructure.js +13 -0
- package/lib/bulk-importer/handlers/product/transformSpecificationToProductStructure.js.map +1 -0
- package/lib/bulk-importer/handlers/product/update.d.ts +10 -0
- package/lib/bulk-importer/handlers/product/update.js +48 -0
- package/lib/bulk-importer/handlers/product/update.js.map +1 -0
- package/lib/bulk-importer/handlers/product/upsertMedia.d.ts +6 -0
- package/lib/bulk-importer/handlers/product/upsertMedia.js +69 -0
- package/lib/bulk-importer/handlers/product/upsertMedia.js.map +1 -0
- package/lib/bulk-importer/handlers/product/upsertProductContent.d.ts +6 -0
- package/lib/bulk-importer/handlers/product/upsertProductContent.js +9 -0
- package/lib/bulk-importer/handlers/product/upsertProductContent.js.map +1 -0
- package/lib/bulk-importer/handlers/product/upsertVariations.d.ts +6 -0
- package/lib/bulk-importer/handlers/product/upsertVariations.js +39 -0
- package/lib/bulk-importer/handlers/product/upsertVariations.js.map +1 -0
- package/lib/context/index.d.ts +2 -0
- package/lib/context/index.js +3 -0
- package/lib/context/index.js.map +1 -0
- package/lib/context/setAccessToken.d.ts +3 -0
- package/lib/context/setAccessToken.js +10 -0
- package/lib/context/setAccessToken.js.map +1 -0
- package/lib/context/withAccessToken.d.ts +2 -0
- package/lib/context/withAccessToken.js +33 -0
- package/lib/context/withAccessToken.js.map +1 -0
- package/lib/migrations/createMigrationRunner.d.ts +14 -0
- package/lib/migrations/createMigrationRunner.js +49 -0
- package/lib/migrations/createMigrationRunner.js.map +1 -0
- package/lib/migrations/migrationRepository.d.ts +3 -0
- package/lib/migrations/migrationRepository.js +16 -0
- package/lib/migrations/migrationRepository.js.map +1 -0
- package/lib/migrations/runMigrations.d.ts +7 -0
- package/lib/migrations/runMigrations.js +47 -0
- package/lib/migrations/runMigrations.js.map +1 -0
- package/lib/platform-index.d.ts +2 -0
- package/lib/platform-index.js +3 -0
- package/lib/platform-index.js.map +1 -0
- package/lib/setup/generateEventTypeDefs.d.ts +1 -0
- package/lib/setup/generateEventTypeDefs.js +9 -0
- package/lib/setup/generateEventTypeDefs.js.map +1 -0
- package/lib/setup/generateRoleActionTypeDefs.d.ts +1 -0
- package/lib/setup/generateRoleActionTypeDefs.js +9 -0
- package/lib/setup/generateRoleActionTypeDefs.js.map +1 -0
- package/lib/setup/generateWorkTypeDefs.d.ts +1 -0
- package/lib/setup/generateWorkTypeDefs.js +11 -0
- package/lib/setup/generateWorkTypeDefs.js.map +1 -0
- package/lib/setup/setupAccounts.d.ts +2 -0
- package/lib/setup/setupAccounts.js +91 -0
- package/lib/setup/setupAccounts.js.map +1 -0
- package/lib/setup/setupAutoScheduling.d.ts +1 -0
- package/lib/setup/setupAutoScheduling.js +5 -0
- package/lib/setup/setupAutoScheduling.js.map +1 -0
- package/lib/setup/setupCarts.d.ts +3 -0
- package/lib/setup/setupCarts.js +14 -0
- package/lib/setup/setupCarts.js.map +1 -0
- package/lib/setup/setupTemplates.d.ts +1 -0
- package/lib/setup/setupTemplates.js +17 -0
- package/lib/setup/setupTemplates.js.map +1 -0
- package/lib/setup/setupWorkqueue.d.ts +6 -0
- package/lib/setup/setupWorkqueue.js +11 -0
- package/lib/setup/setupWorkqueue.js.map +1 -0
- package/lib/startPlatform.d.ts +8 -0
- package/lib/startPlatform.js +105 -0
- package/lib/startPlatform.js.map +1 -0
- package/lib/templates/resolveAccountActionTemplate.d.ts +2 -0
- package/lib/templates/resolveAccountActionTemplate.js +131 -0
- package/lib/templates/resolveAccountActionTemplate.js.map +1 -0
- package/lib/templates/resolveEnrollmentStatusTemplate.d.ts +2 -0
- package/lib/templates/resolveEnrollmentStatusTemplate.js +34 -0
- package/lib/templates/resolveEnrollmentStatusTemplate.js.map +1 -0
- package/lib/templates/resolveForwardDeliveryTemplate.d.ts +2 -0
- package/lib/templates/resolveForwardDeliveryTemplate.js +114 -0
- package/lib/templates/resolveForwardDeliveryTemplate.js.map +1 -0
- package/lib/templates/resolveOrderConfirmationTemplate.d.ts +2 -0
- package/lib/templates/resolveOrderConfirmationTemplate.js +111 -0
- package/lib/templates/resolveOrderConfirmationTemplate.js.map +1 -0
- package/lib/templates/resolveOrderRejectionTemplate.d.ts +2 -0
- package/lib/templates/resolveOrderRejectionTemplate.js +77 -0
- package/lib/templates/resolveOrderRejectionTemplate.js.map +1 -0
- package/lib/templates/resolveQuotationStatusTemplate.d.ts +2 -0
- package/lib/templates/resolveQuotationStatusTemplate.js +40 -0
- package/lib/templates/resolveQuotationStatusTemplate.js.map +1 -0
- package/lib/templates/utils/getOrderAttachmentsData.d.ts +8 -0
- package/lib/templates/utils/getOrderAttachmentsData.js +28 -0
- package/lib/templates/utils/getOrderAttachmentsData.js.map +1 -0
- package/lib/templates/utils/getOrderPositionsData.d.ts +6 -0
- package/lib/templates/utils/getOrderPositionsData.js +32 -0
- package/lib/templates/utils/getOrderPositionsData.js.map +1 -0
- package/package.json +58 -0
- package/src/bulk-importer/createBulkImporter.ts +120 -0
- package/src/bulk-importer/handlers/assortment/create.ts +86 -0
- package/src/bulk-importer/handlers/assortment/index.js +3 -0
- package/src/bulk-importer/handlers/assortment/remove.ts +15 -0
- package/src/bulk-importer/handlers/assortment/update.ts +85 -0
- package/src/bulk-importer/handlers/assortment/upsertAssortmentChildren.ts +51 -0
- package/src/bulk-importer/handlers/assortment/upsertAssortmentContent.ts +20 -0
- package/src/bulk-importer/handlers/assortment/upsertAssortmentFilters.ts +39 -0
- package/src/bulk-importer/handlers/assortment/upsertAssortmentProducts.ts +55 -0
- package/src/bulk-importer/handlers/assortment/upsertMedia.ts +99 -0
- package/src/bulk-importer/handlers/filter/create.ts +62 -0
- package/src/bulk-importer/handlers/filter/index.js +3 -0
- package/src/bulk-importer/handlers/filter/remove.ts +16 -0
- package/src/bulk-importer/handlers/filter/update.ts +49 -0
- package/src/bulk-importer/handlers/filter/upsertFilterContent.ts +17 -0
- package/src/bulk-importer/handlers/filter/upsertFilterOptionContent.ts +21 -0
- package/src/bulk-importer/handlers/product/create.ts +73 -0
- package/src/bulk-importer/handlers/product/index.js +3 -0
- package/src/bulk-importer/handlers/product/remove.ts +17 -0
- package/src/bulk-importer/handlers/product/transformSpecificationToProductStructure.js +19 -0
- package/src/bulk-importer/handlers/product/update.ts +65 -0
- package/src/bulk-importer/handlers/product/upsertMedia.ts +97 -0
- package/src/bulk-importer/handlers/product/upsertProductContent.ts +23 -0
- package/src/bulk-importer/handlers/product/upsertVariations.ts +77 -0
- package/src/context/index.ts +2 -0
- package/src/context/setAccessToken.ts +15 -0
- package/src/context/withAccessToken.ts +34 -0
- package/src/migrations/createMigrationRunner.js +61 -0
- package/src/migrations/migrationRepository.ts +18 -0
- package/src/migrations/runMigrations.ts +64 -0
- package/src/platform-index.ts +2 -0
- package/src/setup/generateEventTypeDefs.js +9 -0
- package/src/setup/generateRoleActionTypeDefs.js +9 -0
- package/src/setup/generateWorkTypeDefs.js +11 -0
- package/src/setup/setupAccounts.ts +129 -0
- package/src/setup/setupAutoScheduling.ts +5 -0
- package/src/setup/setupCarts.ts +27 -0
- package/src/setup/setupTemplates.ts +17 -0
- package/src/setup/setupWorkqueue.ts +17 -0
- package/src/startPlatform.ts +145 -0
- package/src/templates/resolveAccountActionTemplate.ts +144 -0
- package/src/templates/resolveEnrollmentStatusTemplate.ts +43 -0
- package/src/templates/resolveForwardDeliveryTemplate.ts +128 -0
- package/src/templates/resolveOrderConfirmationTemplate.ts +133 -0
- package/src/templates/resolveOrderRejectionTemplate.ts +85 -0
- package/src/templates/resolveQuotationStatusTemplate.ts +54 -0
- package/src/templates/utils/getOrderAttachmentsData.ts +47 -0
- package/src/templates/utils/getOrderPositionsData.ts +49 -0
- package/tsconfig.build.json +26 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Migration } from '@unchainedshop/types/api';
|
|
2
|
+
import { MigrationRepository, Db } from '@unchainedshop/types/common';
|
|
3
|
+
|
|
4
|
+
export const createMigrationRepository = (db: Db): MigrationRepository<Migration> => {
|
|
5
|
+
const migrations = new Map();
|
|
6
|
+
return {
|
|
7
|
+
db,
|
|
8
|
+
migrations,
|
|
9
|
+
register: (migration: Migration) => {
|
|
10
|
+
migrations.set(migration.id, migration);
|
|
11
|
+
},
|
|
12
|
+
allMigrations: () => {
|
|
13
|
+
return [...migrations.values()].sort((left, right) => {
|
|
14
|
+
return left.id - right.id;
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { UnchainedAPI, Migration } from '@unchainedshop/types/api';
|
|
2
|
+
import { MigrationRepository } from '@unchainedshop/types/common';
|
|
3
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
4
|
+
import { generateDbFilterById } from '@unchainedshop/utils';
|
|
5
|
+
import { createMigrationRunner } from './createMigrationRunner';
|
|
6
|
+
|
|
7
|
+
export const runMigrations = async ({
|
|
8
|
+
migrationRepository,
|
|
9
|
+
logger = createLogger('unchained:migrations'),
|
|
10
|
+
unchainedAPI,
|
|
11
|
+
}: {
|
|
12
|
+
migrationRepository: MigrationRepository<Migration>;
|
|
13
|
+
logger?: any;
|
|
14
|
+
unchainedAPI: UnchainedAPI;
|
|
15
|
+
}) => {
|
|
16
|
+
const LastMigration = migrationRepository.db.collection('last-migration');
|
|
17
|
+
|
|
18
|
+
const findCurrentId = async () => {
|
|
19
|
+
const last = await LastMigration.findOne({ category: 'unchained' }, { sort: { _id: -1 } });
|
|
20
|
+
const id = last ? last._id : 0;
|
|
21
|
+
logger.verbose(`Most recent migration id: ${id}`);
|
|
22
|
+
return id;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const onMigrationComplete = async (id: string, action: string) => {
|
|
26
|
+
await LastMigration.updateOne(
|
|
27
|
+
generateDbFilterById(id, { category: 'unchained' }),
|
|
28
|
+
{
|
|
29
|
+
$set: {
|
|
30
|
+
_id: id,
|
|
31
|
+
action,
|
|
32
|
+
category: 'unchained',
|
|
33
|
+
timestamp: new Date(),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
upsert: true,
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
logger.verbose(`Migrated '${action}' to ${id}`);
|
|
41
|
+
return id;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const currentId = await findCurrentId();
|
|
45
|
+
const runner = createMigrationRunner({
|
|
46
|
+
currentId,
|
|
47
|
+
logger,
|
|
48
|
+
migrationRepository,
|
|
49
|
+
onMigrationComplete,
|
|
50
|
+
unchainedAPI,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const [lastMigrationId, operationCount] = await runner.run();
|
|
54
|
+
|
|
55
|
+
if (operationCount !== null) {
|
|
56
|
+
if (operationCount > 0) {
|
|
57
|
+
logger.info(`All ${operationCount} migrations completed with most recent id: ${lastMigrationId}`);
|
|
58
|
+
} else {
|
|
59
|
+
logger.info(`No migrations run, already at latest id: ${currentId}`);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
logger.info(`Some migrations failed, last successful id: ${lastMigrationId}`);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Context } from '@unchainedshop/types/api';
|
|
2
|
+
import { User } from '@unchainedshop/types/user';
|
|
3
|
+
import { randomValueHex } from '@unchainedshop/utils';
|
|
4
|
+
import { accountsSettings } from '@unchainedshop/core-accountsjs';
|
|
5
|
+
import moniker from 'moniker';
|
|
6
|
+
|
|
7
|
+
export const setupAccounts = (unchainedAPI: Context) => {
|
|
8
|
+
const accountsServer = unchainedAPI.modules.accounts.getAccountsServer();
|
|
9
|
+
|
|
10
|
+
accountsServer.users = unchainedAPI.modules.users;
|
|
11
|
+
|
|
12
|
+
accountsServer.options.prepareMail = (
|
|
13
|
+
to: string,
|
|
14
|
+
token: string,
|
|
15
|
+
user: User & { id: string },
|
|
16
|
+
pathFragment: string,
|
|
17
|
+
) => {
|
|
18
|
+
return {
|
|
19
|
+
template: 'ACCOUNT_ACTION',
|
|
20
|
+
recipientEmail: to,
|
|
21
|
+
action: pathFragment,
|
|
22
|
+
userId: user.id || user._id,
|
|
23
|
+
token,
|
|
24
|
+
skipMessaging: !!user.guest && pathFragment === 'verify-email',
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
accountsServer.options.sendMail = (input: any) => {
|
|
29
|
+
if (!input) return true;
|
|
30
|
+
if (input.skipMessaging) return true;
|
|
31
|
+
|
|
32
|
+
return unchainedAPI.modules.worker.addWork(
|
|
33
|
+
{
|
|
34
|
+
type: 'MESSAGE',
|
|
35
|
+
retries: 0,
|
|
36
|
+
input,
|
|
37
|
+
},
|
|
38
|
+
unchainedAPI.userId,
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
accountsServer.services.guest = {
|
|
43
|
+
async authenticate(params: { email?: string | null }) {
|
|
44
|
+
const guestname = `${moniker.choose()}-${randomValueHex(5)}`;
|
|
45
|
+
|
|
46
|
+
const guestUserId = await unchainedAPI.modules.accounts.createUser(
|
|
47
|
+
{
|
|
48
|
+
email: params.email || `${guestname}@unchained.local`,
|
|
49
|
+
guest: true,
|
|
50
|
+
profile: {},
|
|
51
|
+
password: null,
|
|
52
|
+
initialPassword: true,
|
|
53
|
+
lastBillingAddress: {},
|
|
54
|
+
},
|
|
55
|
+
{},
|
|
56
|
+
);
|
|
57
|
+
return unchainedAPI.modules.users.findUserById(guestUserId);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
accountsServer.on('LoginTokenCreated', async (props) => {
|
|
62
|
+
const { userId, connection = {} } = props;
|
|
63
|
+
const { userIdBeforeLogin, countryContext, remoteAddress, remotePort, userAgent, normalizedLocale } =
|
|
64
|
+
connection;
|
|
65
|
+
|
|
66
|
+
await unchainedAPI.modules.users.updateHeartbeat(userId, {
|
|
67
|
+
remoteAddress,
|
|
68
|
+
remotePort,
|
|
69
|
+
userAgent,
|
|
70
|
+
locale: normalizedLocale,
|
|
71
|
+
countryContext,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const user = await unchainedAPI.modules.users.findUserById(userId);
|
|
75
|
+
const context = {
|
|
76
|
+
...unchainedAPI,
|
|
77
|
+
countryContext,
|
|
78
|
+
userId,
|
|
79
|
+
user,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (userIdBeforeLogin) {
|
|
83
|
+
const userBeforeLogin = await unchainedAPI.modules.users.findUserById(userIdBeforeLogin);
|
|
84
|
+
|
|
85
|
+
await unchainedAPI.services.orders.migrateOrderCarts(
|
|
86
|
+
{
|
|
87
|
+
fromUser: userBeforeLogin,
|
|
88
|
+
toUser: user,
|
|
89
|
+
shouldMerge: accountsSettings.mergeUserCartsOnLogin,
|
|
90
|
+
},
|
|
91
|
+
context,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
await unchainedAPI.services.bookmarks.migrateBookmarks(
|
|
95
|
+
{
|
|
96
|
+
fromUser: userBeforeLogin,
|
|
97
|
+
toUser: user,
|
|
98
|
+
shouldMerge: accountsSettings.mergeUserCartsOnLogin,
|
|
99
|
+
},
|
|
100
|
+
context,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await unchainedAPI.modules.orders.ensureCartForUser(
|
|
105
|
+
{
|
|
106
|
+
user,
|
|
107
|
+
countryCode: countryContext,
|
|
108
|
+
},
|
|
109
|
+
context,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
accountsServer.on('ResetPasswordSuccess', async (user: User) => {
|
|
114
|
+
await unchainedAPI.modules.users.updateInitialPassword(user, false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
accountsServer.on('ChangePasswordSuccess', async (user: User) => {
|
|
118
|
+
await unchainedAPI.modules.users.updateInitialPassword(user, false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
accountsServer.on('ValidateLogin', async (params: { service: string; user: User }) => {
|
|
122
|
+
if (params.service !== 'guest' && params.user.guest) {
|
|
123
|
+
await unchainedAPI.modules.users.updateGuest(params.user, false);
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return accountsServer;
|
|
129
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Context } from '@unchainedshop/types/api';
|
|
2
|
+
import { SetupCartsOptions } from '@unchainedshop/types/platform';
|
|
3
|
+
|
|
4
|
+
const { UNCHAINED_DISABLE_PROVIDER_INVALIDATION, UNCHAINED_ASSIGN_CART_FOR_USERS } = process.env;
|
|
5
|
+
|
|
6
|
+
export const setupCarts = async (unchainedAPI: Context, options: SetupCartsOptions = {}) => {
|
|
7
|
+
if (options.invalidateProviders ?? !UNCHAINED_DISABLE_PROVIDER_INVALIDATION) {
|
|
8
|
+
await unchainedAPI.modules.orders.invalidateProviders(
|
|
9
|
+
unchainedAPI,
|
|
10
|
+
options.providerInvalidationMaxAgeDays,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (options.assignCartForUsers ?? Boolean(UNCHAINED_ASSIGN_CART_FOR_USERS)) {
|
|
15
|
+
const users = await unchainedAPI.modules.users.findUsers({});
|
|
16
|
+
|
|
17
|
+
await Promise.all(
|
|
18
|
+
users.map((user) => {
|
|
19
|
+
const locale = unchainedAPI.modules.users.userLocale(user);
|
|
20
|
+
return unchainedAPI.modules.orders.ensureCartForUser(
|
|
21
|
+
{ user, countryCode: locale.country },
|
|
22
|
+
unchainedAPI,
|
|
23
|
+
);
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MessageTypes } from '@unchainedshop/types/platform';
|
|
2
|
+
import { MessagingDirector } from '@unchainedshop/core-messaging';
|
|
3
|
+
import { resolveOrderRejectionTemplate } from '../templates/resolveOrderRejectionTemplate';
|
|
4
|
+
import { resolveAccountActionTemplate } from '../templates/resolveAccountActionTemplate';
|
|
5
|
+
import { resolveForwardDeliveryTemplate } from '../templates/resolveForwardDeliveryTemplate';
|
|
6
|
+
import { resolveOrderConfirmationTemplate } from '../templates/resolveOrderConfirmationTemplate';
|
|
7
|
+
import { resolveQuotationStatusTemplate } from '../templates/resolveQuotationStatusTemplate';
|
|
8
|
+
import { resolveEnrollmentStatusTemplate } from '../templates/resolveEnrollmentStatusTemplate';
|
|
9
|
+
|
|
10
|
+
export const setupTemplates = () => {
|
|
11
|
+
MessagingDirector.registerTemplate(MessageTypes.ACCOUNT_ACTION, resolveAccountActionTemplate);
|
|
12
|
+
MessagingDirector.registerTemplate(MessageTypes.DELIVERY, resolveForwardDeliveryTemplate);
|
|
13
|
+
MessagingDirector.registerTemplate(MessageTypes.ORDER_CONFIRMATION, resolveOrderConfirmationTemplate);
|
|
14
|
+
MessagingDirector.registerTemplate(MessageTypes.ORDER_REJECTION, resolveOrderRejectionTemplate);
|
|
15
|
+
MessagingDirector.registerTemplate(MessageTypes.QUOTATION_STATUS, resolveQuotationStatusTemplate);
|
|
16
|
+
MessagingDirector.registerTemplate(MessageTypes.ENROLLMENT_STATUS, resolveEnrollmentStatusTemplate);
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Context } from '@unchainedshop/types/api';
|
|
2
|
+
import { SetupWorkqueueOptions } from '@unchainedshop/types/platform';
|
|
3
|
+
import { EventListenerWorker, FailedRescheduler, IntervalWorker } from '@unchainedshop/core-worker';
|
|
4
|
+
|
|
5
|
+
export const setupWorkqueue = (
|
|
6
|
+
unchainedAPI: Context,
|
|
7
|
+
{ workerId, batchCount, schedule }: SetupWorkqueueOptions = {},
|
|
8
|
+
) => {
|
|
9
|
+
const handlers = [
|
|
10
|
+
FailedRescheduler.actions(unchainedAPI),
|
|
11
|
+
EventListenerWorker.actions({ workerId }, unchainedAPI),
|
|
12
|
+
IntervalWorker.actions({ workerId, batchCount, schedule }, unchainedAPI),
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
handlers.forEach((handler) => handler.start());
|
|
16
|
+
return handlers;
|
|
17
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { SetupWorkqueueOptions, PlatformOptions, MessageTypes } from '@unchainedshop/types/platform';
|
|
2
|
+
import { Meteor } from 'meteor/meteor';
|
|
3
|
+
import { startAPIServer, roles } from '@unchainedshop/api';
|
|
4
|
+
import { initCore } from '@unchainedshop/core';
|
|
5
|
+
import { initDb } from '@unchainedshop/mongodb';
|
|
6
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
7
|
+
import { createBulkImporterFactory } from './bulk-importer/createBulkImporter';
|
|
8
|
+
import { runMigrations } from './migrations/runMigrations';
|
|
9
|
+
import { generateEventTypeDefs } from './setup/generateEventTypeDefs';
|
|
10
|
+
import { generateWorkerTypeDefs } from './setup/generateWorkTypeDefs';
|
|
11
|
+
import { generateRoleActionTypeDefs } from './setup/generateRoleActionTypeDefs';
|
|
12
|
+
import { setupAccounts } from './setup/setupAccounts';
|
|
13
|
+
import { setupAutoScheduling } from './setup/setupAutoScheduling';
|
|
14
|
+
import { setupCarts } from './setup/setupCarts';
|
|
15
|
+
import { setupTemplates } from './setup/setupTemplates';
|
|
16
|
+
import { setupWorkqueue } from './setup/setupWorkqueue';
|
|
17
|
+
import { createMigrationRepository } from './migrations/migrationRepository';
|
|
18
|
+
|
|
19
|
+
// Workers
|
|
20
|
+
import '@unchainedshop/plugins/lib/worker/BulkImportWorker';
|
|
21
|
+
import '@unchainedshop/plugins/lib/worker/ZombieKillerWorker';
|
|
22
|
+
import '@unchainedshop/plugins/lib/worker/GenerateOrderWorker';
|
|
23
|
+
import '@unchainedshop/plugins/lib/worker/MessageWorker';
|
|
24
|
+
|
|
25
|
+
export { MessageTypes };
|
|
26
|
+
|
|
27
|
+
const logger = createLogger('unchained');
|
|
28
|
+
|
|
29
|
+
const REQUIRED_ENV_VARIABLES = ['EMAIL_WEBSITE_NAME', 'EMAIL_WEBSITE_URL', 'EMAIL_FROM'];
|
|
30
|
+
|
|
31
|
+
const { NODE_ENV, UNCHAINED_DISABLE_WORKER } = process.env;
|
|
32
|
+
|
|
33
|
+
const exitOnMissingEnvironmentVariables = () => {
|
|
34
|
+
const failedEnv = REQUIRED_ENV_VARIABLES.filter((key) => !process.env[key]);
|
|
35
|
+
if (failedEnv.length > 0) {
|
|
36
|
+
logger.error(`Missing required environment variables at boot time: ${failedEnv.join(', ')}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const checkWorkQueueEnabled = (options: SetupWorkqueueOptions) => {
|
|
42
|
+
if (options?.disableWorker) return false;
|
|
43
|
+
return !UNCHAINED_DISABLE_WORKER;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const queueWorkers = [];
|
|
47
|
+
|
|
48
|
+
export const startPlatform = async (
|
|
49
|
+
{
|
|
50
|
+
modules = {},
|
|
51
|
+
services = {},
|
|
52
|
+
typeDefs = [],
|
|
53
|
+
resolvers = [],
|
|
54
|
+
options = {},
|
|
55
|
+
rolesOptions = {},
|
|
56
|
+
bulkImporter: bulkImporterOptions,
|
|
57
|
+
schema,
|
|
58
|
+
plugins,
|
|
59
|
+
cache,
|
|
60
|
+
workQueueOptions,
|
|
61
|
+
context,
|
|
62
|
+
introspection,
|
|
63
|
+
playground,
|
|
64
|
+
tracing,
|
|
65
|
+
cacheControl,
|
|
66
|
+
corsOrigins,
|
|
67
|
+
}: PlatformOptions = {
|
|
68
|
+
modules: {},
|
|
69
|
+
services: {},
|
|
70
|
+
typeDefs: [],
|
|
71
|
+
resolvers: [],
|
|
72
|
+
options: {},
|
|
73
|
+
},
|
|
74
|
+
) => {
|
|
75
|
+
exitOnMissingEnvironmentVariables();
|
|
76
|
+
|
|
77
|
+
// Configure database
|
|
78
|
+
const db = await initDb();
|
|
79
|
+
|
|
80
|
+
// Prepare Migrations
|
|
81
|
+
const migrationRepository = createMigrationRepository(db);
|
|
82
|
+
const bulkImporter = createBulkImporterFactory(db, bulkImporterOptions?.handlers);
|
|
83
|
+
|
|
84
|
+
// Initialise core api using the database
|
|
85
|
+
const unchainedAPI = await initCore({
|
|
86
|
+
db,
|
|
87
|
+
migrationRepository,
|
|
88
|
+
bulkImporter,
|
|
89
|
+
modules,
|
|
90
|
+
services,
|
|
91
|
+
options,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const isWorkQueueEnabled = checkWorkQueueEnabled(workQueueOptions);
|
|
95
|
+
if (isWorkQueueEnabled) {
|
|
96
|
+
await runMigrations({ migrationRepository, unchainedAPI });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const configuredRoles = roles.configureRoles(rolesOptions);
|
|
100
|
+
|
|
101
|
+
// Setup accountsjs specific extensions and event handlers
|
|
102
|
+
setupAccounts(unchainedAPI);
|
|
103
|
+
|
|
104
|
+
// Setup email templates
|
|
105
|
+
setupTemplates();
|
|
106
|
+
|
|
107
|
+
const generatedTypeDefs = [
|
|
108
|
+
...generateEventTypeDefs(),
|
|
109
|
+
...generateWorkerTypeDefs(),
|
|
110
|
+
...generateRoleActionTypeDefs(),
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
// Start the graphQL server
|
|
114
|
+
startAPIServer({
|
|
115
|
+
unchainedAPI,
|
|
116
|
+
roles: configuredRoles,
|
|
117
|
+
typeDefs: [...generatedTypeDefs, ...typeDefs],
|
|
118
|
+
resolvers,
|
|
119
|
+
schema,
|
|
120
|
+
plugins,
|
|
121
|
+
cache,
|
|
122
|
+
context,
|
|
123
|
+
introspection,
|
|
124
|
+
playground,
|
|
125
|
+
tracing,
|
|
126
|
+
corsOrigins,
|
|
127
|
+
cacheControl,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Setup work queues for scheduled work
|
|
131
|
+
if (isWorkQueueEnabled) {
|
|
132
|
+
const handlers = setupWorkqueue(unchainedAPI, workQueueOptions);
|
|
133
|
+
handlers.forEach((handler) => queueWorkers.push(handler));
|
|
134
|
+
await setupCarts(unchainedAPI, workQueueOptions);
|
|
135
|
+
|
|
136
|
+
setupAutoScheduling();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Setup filter cache
|
|
140
|
+
if (!options.filters?.skipInvalidationOnStartup) {
|
|
141
|
+
Meteor.defer(() => unchainedAPI.modules.filters.invalidateCache({}, unchainedAPI));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return unchainedAPI;
|
|
145
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { TemplateResolver } from '@unchainedshop/types/messaging';
|
|
2
|
+
|
|
3
|
+
const { EMAIL_FROM, EMAIL_WEBSITE_URL, EMAIL_WEBSITE_NAME } = process.env;
|
|
4
|
+
|
|
5
|
+
const mjmlTemplate = `
|
|
6
|
+
<mjml>
|
|
7
|
+
<mj-body background-color="#FAFAFA">
|
|
8
|
+
<mj-section padding-bottom="32px" background-color="#fcfcfc">
|
|
9
|
+
<mj-column width="100%">
|
|
10
|
+
<mj-text align="center" font-size="20px" color="#232323" font-family="Helvetica Neue" font-weight="200">
|
|
11
|
+
<h2>{{subject}}</h2>
|
|
12
|
+
</mj-text>
|
|
13
|
+
<mj-text align="left" font-size="20px" color="#232323" font-family="Helvetica Neue" font-weight="200">
|
|
14
|
+
<span>{{message}}</span><br/>
|
|
15
|
+
</mj-text>
|
|
16
|
+
</mj-column>
|
|
17
|
+
</mj-section>
|
|
18
|
+
|
|
19
|
+
<mj-section padding-bottom="20px" background-color="#f3f3f3">
|
|
20
|
+
<mj-column>
|
|
21
|
+
<mj-button href="{{url}}" font-family="Helvetica" background-color="#31302E" color="white">
|
|
22
|
+
{{buttonText}}
|
|
23
|
+
</mj-button>
|
|
24
|
+
<mj-spacer/>
|
|
25
|
+
</mj-column>
|
|
26
|
+
</mj-section>
|
|
27
|
+
</mj-body>
|
|
28
|
+
</mjml>
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const verifyEmailEnglishConfig = {
|
|
32
|
+
buttonText: 'Verify email address',
|
|
33
|
+
message: 'We need to ensure that this email is yours as a commercial partner.',
|
|
34
|
+
subject: `${EMAIL_WEBSITE_NAME}: Verify your email address`,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const passwordChangedEmailEnglishConfig = {
|
|
38
|
+
message: `
|
|
39
|
+
Your account password has been successfully changed.\n
|
|
40
|
+
`,
|
|
41
|
+
subject: `${EMAIL_WEBSITE_NAME}: Your password has been changed`,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const textTemplate = `
|
|
45
|
+
{{message}}
|
|
46
|
+
{{#url}}
|
|
47
|
+
\n
|
|
48
|
+
-----------------\n
|
|
49
|
+
{{buttonText}}: {{url}}\n
|
|
50
|
+
-----------------\n
|
|
51
|
+
{{/url}}
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const emailConfig = {
|
|
55
|
+
'enroll-account': {
|
|
56
|
+
url: (token) => `${EMAIL_WEBSITE_URL}/enroll-account?token=${token}`,
|
|
57
|
+
en: {
|
|
58
|
+
buttonText: 'Set password',
|
|
59
|
+
message: `${EMAIL_WEBSITE_NAME} has set up an user account for you. Please click below to set up a password and to finish the registration.`,
|
|
60
|
+
subject: `${EMAIL_WEBSITE_NAME}: You got a new account`,
|
|
61
|
+
},
|
|
62
|
+
de: {
|
|
63
|
+
buttonText: 'Passwort setzen',
|
|
64
|
+
message: `${EMAIL_WEBSITE_NAME} hat für dich ein Benutzerkonto angelegt. Bitte klicke unten um ein Passwort zu setzen und damit die Registrierung abzuschliessen.`,
|
|
65
|
+
subject: `${EMAIL_WEBSITE_NAME}: Für dich wurde ein Konto eingerichtet`,
|
|
66
|
+
},
|
|
67
|
+
fr: {
|
|
68
|
+
buttonText: 'Set password',
|
|
69
|
+
message: `${EMAIL_WEBSITE_NAME} has set up an user account for you. Please click below to set up a password and to finish the registration.`,
|
|
70
|
+
subject: `${EMAIL_WEBSITE_NAME}: You got a new account`,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
'reset-password': {
|
|
74
|
+
url: (token) => `${EMAIL_WEBSITE_URL}/reset-password?token=${token}`,
|
|
75
|
+
en: {
|
|
76
|
+
buttonText: 'Set new password',
|
|
77
|
+
message: `
|
|
78
|
+
Click on „Set new password". If you don’t want to set a new password, just ignore this email.\n
|
|
79
|
+
`,
|
|
80
|
+
subject: `${EMAIL_WEBSITE_NAME}: Set password`,
|
|
81
|
+
},
|
|
82
|
+
de: {
|
|
83
|
+
buttonText: 'Neues Passwort setzen',
|
|
84
|
+
message: `
|
|
85
|
+
Klicke auf „Neues Passwort setzen", um ein neues Passwort anzulegen.\n
|
|
86
|
+
Falls du dein Passwort nicht zurücksetzen möchtest, ignoriere dieses E-Mail einfach.\n
|
|
87
|
+
`,
|
|
88
|
+
subject: `${EMAIL_WEBSITE_NAME}: Passwort zurücksetzen`,
|
|
89
|
+
},
|
|
90
|
+
fr: {
|
|
91
|
+
buttonText: 'Set new password',
|
|
92
|
+
message: `
|
|
93
|
+
Click on „Set new password". If you don’t want to set a new password, just ignore this email.\n
|
|
94
|
+
`,
|
|
95
|
+
subject: `${EMAIL_WEBSITE_NAME}: Set password`,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
'verify-email': {
|
|
99
|
+
url: (token) => `${EMAIL_WEBSITE_URL}/verify-email?token=${token}`,
|
|
100
|
+
en: verifyEmailEnglishConfig,
|
|
101
|
+
de: verifyEmailEnglishConfig,
|
|
102
|
+
fr: verifyEmailEnglishConfig,
|
|
103
|
+
},
|
|
104
|
+
'': {
|
|
105
|
+
// password changed!
|
|
106
|
+
url: () => null,
|
|
107
|
+
en: passwordChangedEmailEnglishConfig,
|
|
108
|
+
de: passwordChangedEmailEnglishConfig,
|
|
109
|
+
fr: passwordChangedEmailEnglishConfig,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const resolveAccountActionTemplate: TemplateResolver = async (
|
|
114
|
+
{ userId, action, recipientEmail, token },
|
|
115
|
+
{ modules },
|
|
116
|
+
) => {
|
|
117
|
+
if (!token || !action) return [];
|
|
118
|
+
|
|
119
|
+
const user = await modules.users.findUserById(userId);
|
|
120
|
+
const locale = modules.users.userLocale(user);
|
|
121
|
+
|
|
122
|
+
const { url } = emailConfig[action];
|
|
123
|
+
const { subject, message, buttonText } = emailConfig[action][locale.language];
|
|
124
|
+
|
|
125
|
+
const data = {
|
|
126
|
+
buttonText,
|
|
127
|
+
message,
|
|
128
|
+
subject,
|
|
129
|
+
url: url(token),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
type: 'EMAIL',
|
|
135
|
+
input: {
|
|
136
|
+
from: EMAIL_FROM || 'noreply@unchained.local',
|
|
137
|
+
to: recipientEmail || modules.users.primaryEmail(user)?.address,
|
|
138
|
+
subject,
|
|
139
|
+
text: modules.messaging.renderToText(textTemplate, data),
|
|
140
|
+
html: modules.messaging.renderMjmlToHtml(mjmlTemplate, data),
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { TemplateResolver } from '@unchainedshop/types/messaging';
|
|
2
|
+
|
|
3
|
+
const { EMAIL_FROM, EMAIL_WEBSITE_NAME, EMAIL_WEBSITE_URL } = process.env;
|
|
4
|
+
|
|
5
|
+
const textTemplate = `
|
|
6
|
+
{{subject}}\n
|
|
7
|
+
\n
|
|
8
|
+
Status: {{enrollment.status}}
|
|
9
|
+
\n
|
|
10
|
+
-----------------\n
|
|
11
|
+
Show: {{url}}\n
|
|
12
|
+
-----------------\n
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const resolveEnrollmentStatusTemplate: TemplateResolver = async (
|
|
16
|
+
{ enrollmentId, locale },
|
|
17
|
+
context,
|
|
18
|
+
) => {
|
|
19
|
+
const { modules } = context;
|
|
20
|
+
const enrollment = await modules.enrollments.findEnrollment({ enrollmentId });
|
|
21
|
+
const user = await modules.users.findUserById(enrollment.userId);
|
|
22
|
+
|
|
23
|
+
const subject = `${EMAIL_WEBSITE_NAME}: Updated Enrollment / ${enrollment.enrollmentNumber}`;
|
|
24
|
+
|
|
25
|
+
const data = {
|
|
26
|
+
enrollment,
|
|
27
|
+
locale,
|
|
28
|
+
subject,
|
|
29
|
+
url: `${EMAIL_WEBSITE_URL}/enrollment?_id=${enrollment._id}`,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
type: 'EMAIL',
|
|
35
|
+
input: {
|
|
36
|
+
from: EMAIL_FROM || 'noreply@unchained.local',
|
|
37
|
+
to: modules.users.primaryEmail(user)?.address,
|
|
38
|
+
subject,
|
|
39
|
+
text: modules.messaging.renderToText(textTemplate, data),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
};
|