@unchainedshop/api 2.0.0-beta7 → 2.0.0-beta8
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/lib/api-index.d.ts +3 -6
- package/lib/api-index.js +4 -12
- package/lib/api-index.js.map +1 -1
- package/lib/context.d.ts +0 -1
- package/lib/context.js +0 -12
- package/lib/context.js.map +1 -1
- package/lib/createGraphQLServer.d.ts +1 -1
- package/lib/createGraphQLServer.js +2 -33
- package/lib/createGraphQLServer.js.map +1 -1
- package/lib/express/createApolloMiddleware.d.ts +4 -0
- package/lib/express/createApolloMiddleware.js +34 -0
- package/lib/express/createApolloMiddleware.js.map +1 -0
- package/lib/express/createBulkImportMiddleware.d.ts +2 -0
- package/lib/express/createBulkImportMiddleware.js +79 -0
- package/lib/express/createBulkImportMiddleware.js.map +1 -0
- package/lib/express/createERCMetadataMiddleware.d.ts +3 -0
- package/lib/express/createERCMetadataMiddleware.js +56 -0
- package/lib/express/createERCMetadataMiddleware.js.map +1 -0
- package/lib/express/createSingleSignOnMiddleware.d.ts +6 -0
- package/lib/express/createSingleSignOnMiddleware.js +71 -0
- package/lib/express/createSingleSignOnMiddleware.js.map +1 -0
- package/lib/express/index.d.ts +6 -0
- package/lib/express/index.js +27 -0
- package/lib/express/index.js.map +1 -0
- package/lib/resolvers/mutations/accounts/changePassword.d.ts +0 -2
- package/lib/resolvers/mutations/accounts/changePassword.js +2 -2
- package/lib/resolvers/mutations/accounts/changePassword.js.map +1 -1
- package/lib/resolvers/mutations/accounts/createUser.js +2 -2
- package/lib/resolvers/mutations/accounts/createUser.js.map +1 -1
- package/lib/resolvers/mutations/accounts/enrollUser.js +1 -1
- package/lib/resolvers/mutations/accounts/enrollUser.js.map +1 -1
- package/lib/resolvers/mutations/accounts/loginWithPassword.d.ts +0 -1
- package/lib/resolvers/mutations/accounts/loginWithPassword.js +3 -3
- package/lib/resolvers/mutations/accounts/loginWithPassword.js.map +1 -1
- package/lib/resolvers/mutations/accounts/resetPassword.d.ts +0 -1
- package/lib/resolvers/mutations/accounts/resetPassword.js +1 -1
- package/lib/resolvers/mutations/accounts/resetPassword.js.map +1 -1
- package/lib/resolvers/mutations/accounts/setPassword.d.ts +0 -1
- package/lib/resolvers/mutations/accounts/setPassword.js +1 -1
- package/lib/resolvers/mutations/accounts/setPassword.js.map +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortment.js +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortment.js.map +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortmentFilter.js +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortmentFilter.js.map +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortmentLink.js +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortmentLink.js.map +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortmentProduct.js +1 -1
- package/lib/resolvers/mutations/assortments/removeAssortmentProduct.js.map +1 -1
- package/lib/resolvers/mutations/enrollments/updateEnrollment.d.ts +1 -1
- package/lib/resolvers/mutations/filters/removeFilter.js +1 -1
- package/lib/resolvers/mutations/filters/removeFilter.js.map +1 -1
- package/lib/resolvers/mutations/index.d.ts +1 -1
- package/lib/resolvers/mutations/pageView.d.ts +1 -1
- package/lib/resolvers/mutations/pageView.js +2 -2
- package/lib/resolvers/mutations/pageView.js.map +1 -1
- package/lib/resolvers/mutations/products/removeProduct.d.ts +1 -1
- package/lib/resolvers/mutations/products/removeProduct.js +5 -8
- package/lib/resolvers/mutations/products/removeProduct.js.map +1 -1
- package/lib/resolvers/type/assortment/assortment-filter-types.d.ts +2 -2
- package/lib/resolvers/type/assortment/assortment-link-types.d.ts +2 -2
- package/lib/resolvers/type/assortment/assortment-media-types.d.ts +1 -1
- package/lib/resolvers/type/assortment/assortment-path-link-types.d.ts +1 -4
- package/lib/resolvers/type/assortment/assortment-path-link-types.js +0 -7
- package/lib/resolvers/type/assortment/assortment-path-link-types.js.map +1 -1
- package/lib/resolvers/type/assortment/assortment-product-types.d.ts +2 -2
- package/lib/resolvers/type/assortment/assortment-types.d.ts +1 -1
- package/lib/resolvers/type/bookmark-types.d.ts +1 -1
- package/lib/resolvers/type/country-types.d.ts +1 -1
- package/lib/resolvers/type/delivery-provider-types.d.ts +1 -1
- package/lib/resolvers/type/enrollment/enrollment-delivery-types.d.ts +2 -2
- package/lib/resolvers/type/enrollment/enrollment-payment-types.d.ts +2 -2
- package/lib/resolvers/type/enrollment/enrollment-period-types.d.ts +2 -2
- package/lib/resolvers/type/enrollment/enrollment-plan-tyes.d.ts +2 -2
- package/lib/resolvers/type/enrollment/enrollment-types.d.ts +2 -2
- package/lib/resolvers/type/event-types.d.ts +1 -1
- package/lib/resolvers/type/filter/filter-option-types.d.ts +2 -2
- package/lib/resolvers/type/filter/filter-types.d.ts +2 -2
- package/lib/resolvers/type/order/order-delivery-discount-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-delivery-pickup-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-delivery-shipping-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-discount-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-global-discount-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-item-discount-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-item-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-payment-card-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-payment-discount-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-payment-generic-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-payment-invoice-types.d.ts +1 -1
- package/lib/resolvers/type/order/order-types.d.ts +1 -1
- package/lib/resolvers/type/price-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-discount.d.ts +1 -1
- package/lib/resolvers/type/product/product-review-types.d.ts +1 -1
- package/lib/resolvers/type/quotation-types.d.ts +2 -2
- package/lib/resolvers/type/shop-types.d.ts +1 -1
- package/lib/resolvers/type/token-types.d.ts +1 -1
- package/lib/resolvers/type/user-types.d.ts +1 -3
- package/lib/resolvers/type/user-types.js +0 -12
- package/lib/resolvers/type/user-types.js.map +1 -1
- package/lib/resolvers/type/webauthn-credentials-types.d.ts +1 -1
- package/lib/resolvers/type/work-types.d.ts +1 -1
- package/lib/schema/{types/log.d.ts → directives.d.ts} +0 -0
- package/lib/schema/directives.js +15 -0
- package/lib/schema/directives.js.map +1 -0
- package/lib/schema/index.js +2 -1
- package/lib/schema/index.js.map +1 -1
- package/lib/schema/inputTypes.js +0 -15
- package/lib/schema/inputTypes.js.map +1 -1
- package/lib/schema/mutation.js +4 -23
- package/lib/schema/mutation.js.map +1 -1
- package/lib/schema/query.js +28 -10
- package/lib/schema/query.js.map +1 -1
- package/lib/schema/types/assortment.js +9 -10
- package/lib/schema/types/assortment.js.map +1 -1
- package/lib/schema/types/color.js +1 -1
- package/lib/schema/types/country.js +1 -1
- package/lib/schema/types/currency.js +1 -1
- package/lib/schema/types/delivery.js +3 -2
- package/lib/schema/types/delivery.js.map +1 -1
- package/lib/schema/types/dimensions.js +1 -1
- package/lib/schema/types/discount.js +1 -1
- package/lib/schema/types/dispatch.js +1 -0
- package/lib/schema/types/dispatch.js.map +1 -1
- package/lib/schema/types/filter.js +5 -8
- package/lib/schema/types/filter.js.map +1 -1
- package/lib/schema/types/geo-position.js +1 -1
- package/lib/schema/types/language.js +1 -1
- package/lib/schema/types/media.js +1 -1
- package/lib/schema/types/payment.js +3 -3
- package/lib/schema/types/price.js +2 -2
- package/lib/schema/types/product/bundle-product.js +2 -3
- package/lib/schema/types/product/bundle-product.js.map +1 -1
- package/lib/schema/types/product/configurable-product.js +7 -7
- package/lib/schema/types/product/plan-product.js +4 -3
- package/lib/schema/types/product/plan-product.js.map +1 -1
- package/lib/schema/types/product/product.js +11 -11
- package/lib/schema/types/product/simple-product.js +5 -4
- package/lib/schema/types/product/simple-product.js.map +1 -1
- package/lib/schema/types/product/tokenized-product.js +4 -3
- package/lib/schema/types/product/tokenized-product.js.map +1 -1
- package/lib/schema/types/search.js +3 -8
- package/lib/schema/types/search.js.map +1 -1
- package/lib/schema/types/shop.js +2 -2
- package/lib/schema/types/stock.js +1 -0
- package/lib/schema/types/stock.js.map +1 -1
- package/lib/schema/types/types.js +1 -1
- package/lib/schema/types/user.js +1 -3
- package/lib/schema/types/user.js.map +1 -1
- package/lib/schema/types/warehousing.js +3 -3
- package/package.json +23 -10
- package/src/api-index.ts +5 -26
- package/src/context.ts +0 -18
- package/src/createGraphQLServer.ts +1 -44
- package/src/express/createApolloMiddleware.ts +40 -0
- package/src/express/createBulkImportMiddleware.ts +90 -0
- package/src/express/createERCMetadataMiddleware.ts +72 -0
- package/src/express/createSingleSignOnMiddleware.ts +83 -0
- package/src/express/index.ts +48 -0
- package/src/resolvers/mutations/accounts/changePassword.ts +2 -4
- package/src/resolvers/mutations/accounts/createUser.ts +2 -2
- package/src/resolvers/mutations/accounts/enrollUser.ts +1 -1
- package/src/resolvers/mutations/accounts/loginWithPassword.ts +3 -4
- package/src/resolvers/mutations/accounts/resetPassword.ts +2 -2
- package/src/resolvers/mutations/accounts/setPassword.ts +2 -2
- package/src/resolvers/mutations/assortments/removeAssortment.ts +1 -1
- package/src/resolvers/mutations/assortments/removeAssortmentFilter.ts +1 -1
- package/src/resolvers/mutations/assortments/removeAssortmentLink.ts +1 -1
- package/src/resolvers/mutations/assortments/removeAssortmentProduct.ts +1 -1
- package/src/resolvers/mutations/filters/removeFilter.ts +1 -1
- package/src/resolvers/mutations/pageView.ts +2 -2
- package/src/resolvers/mutations/products/removeProduct.ts +6 -8
- package/src/resolvers/type/assortment/assortment-path-link-types.ts +0 -11
- package/src/resolvers/type/user-types.ts +0 -15
- package/src/schema/directives.js +14 -0
- package/src/schema/index.js +2 -1
- package/src/schema/inputTypes.js +0 -15
- package/src/schema/mutation.js +4 -23
- package/src/schema/query.js +28 -10
- package/src/schema/types/assortment.js +9 -10
- package/src/schema/types/color.js +1 -1
- package/src/schema/types/country.js +1 -1
- package/src/schema/types/currency.js +1 -1
- package/src/schema/types/delivery.js +3 -2
- package/src/schema/types/dimensions.js +1 -1
- package/src/schema/types/discount.js +1 -1
- package/src/schema/types/dispatch.js +1 -0
- package/src/schema/types/filter.js +5 -8
- package/src/schema/types/geo-position.js +1 -1
- package/src/schema/types/language.js +1 -1
- package/src/schema/types/media.js +1 -1
- package/src/schema/types/payment.js +3 -3
- package/src/schema/types/price.js +2 -2
- package/src/schema/types/product/bundle-product.js +2 -3
- package/src/schema/types/product/configurable-product.js +7 -7
- package/src/schema/types/product/plan-product.js +4 -3
- package/src/schema/types/product/product.js +11 -11
- package/src/schema/types/product/simple-product.js +5 -4
- package/src/schema/types/product/tokenized-product.js +4 -3
- package/src/schema/types/search.js +3 -8
- package/src/schema/types/shop.js +2 -2
- package/src/schema/types/stock.js +1 -0
- package/src/schema/types/types.js +1 -1
- package/src/schema/types/user.js +1 -3
- package/src/schema/types/warehousing.js +3 -3
- package/lib/createBulkImportServer.d.ts +0 -2
- package/lib/createBulkImportServer.js +0 -81
- package/lib/createBulkImportServer.js.map +0 -1
- package/lib/createERCMetadataServer.d.ts +0 -2
- package/lib/createERCMetadataServer.js +0 -59
- package/lib/createERCMetadataServer.js.map +0 -1
- package/lib/schema/types/log.js +0 -16
- package/lib/schema/types/log.js.map +0 -1
- package/src/createBulkImportServer.ts +0 -95
- package/src/createERCMetadataServer.ts +0 -78
- package/src/schema/types/log.js +0 -15
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { ApolloServer } from '@apollo/server';
|
|
2
|
-
import { expressMiddleware } from '@apollo/server/express4';
|
|
3
2
|
import { GraphQLError } from 'graphql';
|
|
4
|
-
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
|
5
3
|
import { log, LogLevel } from '@unchainedshop/logger';
|
|
6
|
-
import cors from 'cors';
|
|
7
|
-
import bodyParser from 'body-parser';
|
|
8
4
|
import typeDefs from './schema';
|
|
9
5
|
import resolvers from './resolvers';
|
|
10
|
-
import { getCurrentContextResolver } from './context';
|
|
11
6
|
|
|
12
7
|
const { APOLLO_ENGINE_KEY } = process.env;
|
|
13
8
|
|
|
@@ -30,17 +25,14 @@ const logGraphQLServerError = (error) => {
|
|
|
30
25
|
} catch (e) {} // eslint-disable-line
|
|
31
26
|
};
|
|
32
27
|
|
|
33
|
-
export default async (
|
|
28
|
+
export default async (options) => {
|
|
34
29
|
const {
|
|
35
|
-
corsOrigins = null, // no cookie handling
|
|
36
30
|
typeDefs: additionalTypeDefs = [],
|
|
37
31
|
resolvers: additionalResolvers = [],
|
|
38
32
|
engine = {},
|
|
39
33
|
...apolloServerOptions
|
|
40
34
|
} = options || {};
|
|
41
35
|
|
|
42
|
-
const context = getCurrentContextResolver();
|
|
43
|
-
|
|
44
36
|
const server = new ApolloServer({
|
|
45
37
|
typeDefs: [...typeDefs, ...additionalTypeDefs],
|
|
46
38
|
resolvers: [resolvers, ...additionalResolvers],
|
|
@@ -74,40 +66,5 @@ export default async (expressApp, options) => {
|
|
|
74
66
|
|
|
75
67
|
await server.start();
|
|
76
68
|
|
|
77
|
-
const originFn =
|
|
78
|
-
corsOrigins && Array.isArray(corsOrigins)
|
|
79
|
-
? (origin, callback) => {
|
|
80
|
-
if (corsOrigins.length === 0 || !origin) {
|
|
81
|
-
callback(null, true);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (corsOrigins.indexOf(origin) !== -1) {
|
|
85
|
-
callback(null, true);
|
|
86
|
-
} else {
|
|
87
|
-
callback(new Error('Not allowed by CORS'));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
: corsOrigins;
|
|
91
|
-
|
|
92
|
-
expressApp.use(
|
|
93
|
-
'/graphql',
|
|
94
|
-
cors(
|
|
95
|
-
!originFn
|
|
96
|
-
? undefined
|
|
97
|
-
: {
|
|
98
|
-
origin: originFn,
|
|
99
|
-
credentials: true,
|
|
100
|
-
},
|
|
101
|
-
),
|
|
102
|
-
bodyParser.json(),
|
|
103
|
-
graphqlUploadExpress(),
|
|
104
|
-
expressMiddleware(server, {
|
|
105
|
-
context,
|
|
106
|
-
}),
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
// expressApp.use(handleUploads({ maxFileSize: 10000000, maxFiles: 10 }));
|
|
110
|
-
// expressApp.use(middleware);
|
|
111
|
-
|
|
112
69
|
return server;
|
|
113
70
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import cors from 'cors';
|
|
2
|
+
import { expressMiddleware } from '@apollo/server/express4';
|
|
3
|
+
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
|
4
|
+
import e from 'express';
|
|
5
|
+
|
|
6
|
+
export default function createApolloMiddleware(
|
|
7
|
+
contextResolver,
|
|
8
|
+
{ apolloGraphQLServer, corsOrigins = undefined },
|
|
9
|
+
) {
|
|
10
|
+
const originFn =
|
|
11
|
+
corsOrigins && Array.isArray(corsOrigins)
|
|
12
|
+
? (origin, callback) => {
|
|
13
|
+
if (corsOrigins.length === 0 || !origin) {
|
|
14
|
+
callback(null, true);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (corsOrigins.indexOf(origin) !== -1) {
|
|
18
|
+
callback(null, true);
|
|
19
|
+
} else {
|
|
20
|
+
callback(new Error('Not allowed by CORS'));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
: corsOrigins;
|
|
24
|
+
|
|
25
|
+
return [
|
|
26
|
+
cors(
|
|
27
|
+
!originFn
|
|
28
|
+
? undefined
|
|
29
|
+
: {
|
|
30
|
+
origin: originFn,
|
|
31
|
+
credentials: true,
|
|
32
|
+
},
|
|
33
|
+
),
|
|
34
|
+
e.json(),
|
|
35
|
+
graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 }),
|
|
36
|
+
expressMiddleware(apolloGraphQLServer, {
|
|
37
|
+
context: contextResolver,
|
|
38
|
+
}),
|
|
39
|
+
];
|
|
40
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import { UnchainedContextResolver } from '@unchainedshop/types/api';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { checkAction } from '../acl';
|
|
5
|
+
import { actions } from '../roles';
|
|
6
|
+
|
|
7
|
+
const logger = createLogger('unchained:bulk-import');
|
|
8
|
+
|
|
9
|
+
const { BULK_IMPORT_PAYLOAD_CACHE_DIRECTORY } = process.env;
|
|
10
|
+
|
|
11
|
+
const errorHandler = (res) => (e) => {
|
|
12
|
+
logger.error(e.message);
|
|
13
|
+
res.writeHead(503);
|
|
14
|
+
res.end(JSON.stringify({ name: e.name, code: e.code, message: e.message }));
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const methodWrongHandler = (res) => () => {
|
|
18
|
+
logger.error('Method not supported, return 404');
|
|
19
|
+
res.writeHead(404);
|
|
20
|
+
res.end();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const createWriteStreamToFilesystem = (cacheDirectory) => {
|
|
24
|
+
const date = new Date().toISOString();
|
|
25
|
+
const payloadFilePath = `${cacheDirectory}/${date}.json`;
|
|
26
|
+
const stream = fs.createWriteStream(payloadFilePath, { flags: 'a' });
|
|
27
|
+
return stream;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const createWriteStreamToMongoDB = (BulkImportPayloads) => {
|
|
31
|
+
const date = new Date().toISOString();
|
|
32
|
+
const stream = BulkImportPayloads.openUploadStreamWithId(date, `${date}.json`, {
|
|
33
|
+
contentType: 'application/json',
|
|
34
|
+
});
|
|
35
|
+
return stream;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default function bulkImportMiddleware(contextResolver: UnchainedContextResolver) {
|
|
39
|
+
return async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
if (req.method !== 'POST') {
|
|
42
|
+
methodWrongHandler(res)();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const context = await contextResolver({ req, res });
|
|
47
|
+
await checkAction(context, (actions as any).bulkImport);
|
|
48
|
+
|
|
49
|
+
const input: any = {
|
|
50
|
+
createShouldUpsertIfIDExists: !!req.query?.createShouldUpsertIfIDExists,
|
|
51
|
+
skipCacheInvalidation: !!req.query?.skipCacheInvalidation,
|
|
52
|
+
fsPayloadCacheDirectory: BULK_IMPORT_PAYLOAD_CACHE_DIRECTORY,
|
|
53
|
+
remoteAddress: context.remoteAddress,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const stream = input.fsPayloadCacheDirectory
|
|
57
|
+
? createWriteStreamToFilesystem(input.fsPayloadCacheDirectory)
|
|
58
|
+
: createWriteStreamToMongoDB(context.bulkImporter.BulkImportPayloads);
|
|
59
|
+
|
|
60
|
+
req
|
|
61
|
+
.pipe(stream)
|
|
62
|
+
.on('error', errorHandler(res))
|
|
63
|
+
.on('finish', async (file) => {
|
|
64
|
+
try {
|
|
65
|
+
if (input.fsPayloadCacheDirectory) {
|
|
66
|
+
input.payloadFilePath = stream.path;
|
|
67
|
+
} else {
|
|
68
|
+
input.payloadId = file._id;
|
|
69
|
+
input.payloadSize = file.length;
|
|
70
|
+
}
|
|
71
|
+
const work = await context.modules.worker.addWork(
|
|
72
|
+
{
|
|
73
|
+
type: 'BULK_IMPORT',
|
|
74
|
+
input: Object.fromEntries(Object.entries(input).filter(([, value]) => Boolean(value))),
|
|
75
|
+
retries: 0,
|
|
76
|
+
priority: 10,
|
|
77
|
+
},
|
|
78
|
+
context.userId,
|
|
79
|
+
);
|
|
80
|
+
res.writeHead(200);
|
|
81
|
+
res.end(JSON.stringify(work));
|
|
82
|
+
} catch (e) {
|
|
83
|
+
errorHandler(res)(e);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
} catch (e) {
|
|
87
|
+
errorHandler(res)(e);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import { UnchainedContextResolver } from '@unchainedshop/types/api';
|
|
3
|
+
import { systemLocale } from '@unchainedshop/utils';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
5
|
+
import localePkg from 'locale';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
const { Locale } = localePkg;
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('unchained:erc-metadata');
|
|
11
|
+
|
|
12
|
+
const errorHandler = (res) => (e) => {
|
|
13
|
+
logger.error(e.message);
|
|
14
|
+
res.writeHead(503);
|
|
15
|
+
res.end(JSON.stringify({ name: e.name, code: e.code, message: e.message }));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const methodWrongHandler = (res) => () => {
|
|
19
|
+
logger.error('Method not supported, return 404');
|
|
20
|
+
res.writeHead(404);
|
|
21
|
+
res.end();
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default function ercMetadataMiddleware(contextResolver: UnchainedContextResolver) {
|
|
25
|
+
return async (req: IncomingMessage, res) => {
|
|
26
|
+
try {
|
|
27
|
+
if (req.method !== 'GET') {
|
|
28
|
+
methodWrongHandler(res)();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const context = await contextResolver({ req, res });
|
|
33
|
+
const url = new URL(req.url, process.env.ROOT_URL);
|
|
34
|
+
const parsedPath = path.parse(url.pathname);
|
|
35
|
+
|
|
36
|
+
if (parsedPath.ext !== '.json') throw new Error('Invalid ERC Metadata URI');
|
|
37
|
+
|
|
38
|
+
const [, productId, localeOrTokenFilename, tokenFileName] = url.pathname.split('/');
|
|
39
|
+
|
|
40
|
+
const locale = tokenFileName ? localeOrTokenFilename : systemLocale.language;
|
|
41
|
+
const chainTokenId = parsedPath.name;
|
|
42
|
+
|
|
43
|
+
const product = await context.modules.products.findProduct({
|
|
44
|
+
productId,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const [token] = await context.modules.warehousing.findTokens({
|
|
48
|
+
chainTokenId,
|
|
49
|
+
contractAddress: product?.tokenization?.contractAddress,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const ercMetadata = await context.modules.warehousing.tokenMetadata(
|
|
53
|
+
chainTokenId,
|
|
54
|
+
{
|
|
55
|
+
token,
|
|
56
|
+
product,
|
|
57
|
+
referenceDate: new Date(),
|
|
58
|
+
},
|
|
59
|
+
{ ...context, localeContext: new Locale(locale) },
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const body = JSON.stringify(ercMetadata);
|
|
63
|
+
res.writeHead(200, {
|
|
64
|
+
'Content-Length': Buffer.byteLength(body),
|
|
65
|
+
'Content-Type': 'text/plain',
|
|
66
|
+
});
|
|
67
|
+
res.end(body);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
errorHandler(res)(e);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
3
|
+
import { Context, UnchainedContextResolver } from '@unchainedshop/types/api';
|
|
4
|
+
import { IncomingMessage } from 'http';
|
|
5
|
+
import cookie from 'cookie';
|
|
6
|
+
|
|
7
|
+
const { ROOT_URL, NODE_ENV, UNCHAINED_CLOUD_ENDPOINT } = process.env;
|
|
8
|
+
|
|
9
|
+
const logger = createLogger('unchained:unchained-cloud-sso');
|
|
10
|
+
|
|
11
|
+
const loginWithSingleSignOn = async (remoteToken, context: Context) => {
|
|
12
|
+
const domain = process.env.UNCHAINED_COOKIE_DOMAIN || new URL(ROOT_URL).hostname || 'localhost';
|
|
13
|
+
const result = await fetch(UNCHAINED_CLOUD_ENDPOINT, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: {
|
|
16
|
+
'content-type': 'application/json',
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
operationName: 'consume',
|
|
20
|
+
variables: {
|
|
21
|
+
token: remoteToken,
|
|
22
|
+
},
|
|
23
|
+
query:
|
|
24
|
+
'mutation consume($token: ID!) { controlConsumeSingleSignOnToken(token: $token) { domain } }',
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
const json: any = await result.json();
|
|
28
|
+
if (json?.errors?.length > 0) {
|
|
29
|
+
throw new Error(json.errors[0].message);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (domain === 'localhost' || domain === json?.data?.controlConsumeSingleSignOnToken?.domain) {
|
|
33
|
+
// create sso user if not exist and login
|
|
34
|
+
const ssoUserId =
|
|
35
|
+
(await context.modules.users.findUser({ username: 'sso' }))?._id ||
|
|
36
|
+
(await context.modules.accounts.createUser(
|
|
37
|
+
{
|
|
38
|
+
username: 'sso',
|
|
39
|
+
roles: ['admin'],
|
|
40
|
+
email: 'sso@unchained.local',
|
|
41
|
+
password: null,
|
|
42
|
+
guest: false,
|
|
43
|
+
},
|
|
44
|
+
{ skipMessaging: true },
|
|
45
|
+
));
|
|
46
|
+
const { tokenExpires, token } = await context.modules.accounts.createLoginToken(ssoUserId, context);
|
|
47
|
+
const expires = new Date(tokenExpires || new Date().getTime() + 100000);
|
|
48
|
+
const authCookie = cookie.serialize(process.env.UNCHAINED_COOKIE_NAME, token, {
|
|
49
|
+
domain,
|
|
50
|
+
httpOnly: true,
|
|
51
|
+
expires,
|
|
52
|
+
path: '/',
|
|
53
|
+
sameSite: 'strict',
|
|
54
|
+
secure: NODE_ENV === 'production',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return authCookie;
|
|
58
|
+
}
|
|
59
|
+
throw new Error('Invalid token/domain pair');
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default function singleSignOnMiddleware(contextResolver: UnchainedContextResolver) {
|
|
63
|
+
return async (req: IncomingMessage & { query?: any }, res, next) => {
|
|
64
|
+
try {
|
|
65
|
+
if (req.query?.token) {
|
|
66
|
+
const context = await contextResolver({ req, res });
|
|
67
|
+
const authCookie = await loginWithSingleSignOn(req.query.token, context);
|
|
68
|
+
|
|
69
|
+
if (authCookie) {
|
|
70
|
+
res.writeHead(303, {
|
|
71
|
+
'Set-Cookie': authCookie,
|
|
72
|
+
Location: '/',
|
|
73
|
+
});
|
|
74
|
+
res.end();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
logger.error(e.message);
|
|
80
|
+
}
|
|
81
|
+
next();
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { UnchainedCore } from '@unchainedshop/types/core';
|
|
2
|
+
import { IncomingMessage, OutgoingMessage } from 'http';
|
|
3
|
+
import { ApolloServer } from '@apollo/server';
|
|
4
|
+
import type e from 'express';
|
|
5
|
+
import { getCurrentContextResolver } from '../context';
|
|
6
|
+
import createBulkImportMiddleware from './createBulkImportMiddleware';
|
|
7
|
+
import createERCMetadataMiddleware from './createERCMetadataMiddleware';
|
|
8
|
+
import createApolloMiddleware from './createApolloMiddleware';
|
|
9
|
+
import createSingleSignOnMiddleware from './createSingleSignOnMiddleware';
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
BULK_IMPORT_API_PATH = '/bulk-import',
|
|
13
|
+
ERC_METADATA_API_PATH = '/erc-metadata',
|
|
14
|
+
GRAPHQL_API_PATH = '/graphql',
|
|
15
|
+
} = process.env;
|
|
16
|
+
|
|
17
|
+
export const useMiddlewareWithCurrentContext = (expressApp, path, ...middleware) => {
|
|
18
|
+
const context = getCurrentContextResolver();
|
|
19
|
+
const addContext = async function middlewareWithContext(
|
|
20
|
+
req: IncomingMessage & { unchainedContext: UnchainedCore },
|
|
21
|
+
res: OutgoingMessage,
|
|
22
|
+
next,
|
|
23
|
+
) {
|
|
24
|
+
try {
|
|
25
|
+
req.unchainedContext = await context({ req, res });
|
|
26
|
+
next();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
next(error);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
expressApp.use(path, addContext, ...middleware);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const connect = (
|
|
36
|
+
expressApp: e.Express,
|
|
37
|
+
{ apolloGraphQLServer }: { apolloGraphQLServer: ApolloServer },
|
|
38
|
+
) => {
|
|
39
|
+
const contextResolver = getCurrentContextResolver();
|
|
40
|
+
|
|
41
|
+
expressApp.use(GRAPHQL_API_PATH, createApolloMiddleware(contextResolver, { apolloGraphQLServer }));
|
|
42
|
+
expressApp.use(ERC_METADATA_API_PATH, createERCMetadataMiddleware(contextResolver));
|
|
43
|
+
expressApp.use(BULK_IMPORT_API_PATH, createBulkImportMiddleware(contextResolver));
|
|
44
|
+
expressApp.use(
|
|
45
|
+
['/', '/.well-known/unchained/cloud-sso'],
|
|
46
|
+
createSingleSignOnMiddleware(contextResolver),
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -4,19 +4,17 @@ import { Context, Root } from '@unchainedshop/types/api';
|
|
|
4
4
|
export default async function changePassword(
|
|
5
5
|
root: Root,
|
|
6
6
|
params: {
|
|
7
|
-
oldPassword?: string;
|
|
8
7
|
oldPlainPassword?: string;
|
|
9
|
-
newPassword?: string;
|
|
10
8
|
newPlainPassword?: string;
|
|
11
9
|
},
|
|
12
10
|
{ modules, userId }: Context,
|
|
13
11
|
) {
|
|
14
12
|
log('mutation changePassword', { userId });
|
|
15
13
|
|
|
16
|
-
if (!params.
|
|
14
|
+
if (!params.newPlainPassword) {
|
|
17
15
|
throw new Error('New password is required');
|
|
18
16
|
}
|
|
19
|
-
if (!params.
|
|
17
|
+
if (!params.oldPlainPassword) {
|
|
20
18
|
throw new Error('Old password is required');
|
|
21
19
|
}
|
|
22
20
|
|
|
@@ -8,12 +8,12 @@ export default async function createUser(root: Root, params: UserData, context:
|
|
|
8
8
|
|
|
9
9
|
log('mutation createUser', { email: params.email, username: params.username, userId });
|
|
10
10
|
|
|
11
|
-
if (!params.
|
|
11
|
+
if (!params.plainPassword && !params.webAuthnPublicKeyCredentials) {
|
|
12
12
|
throw new Error('Password or Public Key is required');
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const mappedUser = { ...params };
|
|
16
|
-
if (
|
|
16
|
+
if (mappedUser.plainPassword) {
|
|
17
17
|
mappedUser.password = hashPassword(mappedUser.plainPassword);
|
|
18
18
|
}
|
|
19
19
|
delete mappedUser.plainPassword;
|
|
@@ -10,7 +10,7 @@ export default async function enrollUser(root: Root, params: UserData, context:
|
|
|
10
10
|
|
|
11
11
|
const mappedUserData = params;
|
|
12
12
|
mappedUserData.initialPassword = true;
|
|
13
|
-
if (
|
|
13
|
+
if (mappedUserData.plainPassword) {
|
|
14
14
|
mappedUserData.password = hashPassword(mappedUserData.plainPassword);
|
|
15
15
|
}
|
|
16
16
|
delete mappedUserData.plainPassword;
|
|
@@ -7,24 +7,23 @@ export default async function loginWithPassword(
|
|
|
7
7
|
params: {
|
|
8
8
|
username?: string;
|
|
9
9
|
email?: string;
|
|
10
|
-
password?: string;
|
|
11
10
|
plainPassword?: string;
|
|
12
11
|
totpCode?: string;
|
|
13
12
|
},
|
|
14
13
|
context: Context,
|
|
15
14
|
) {
|
|
16
15
|
const { modules } = context;
|
|
17
|
-
const { username, email,
|
|
16
|
+
const { username, email, plainPassword, totpCode } = params;
|
|
18
17
|
|
|
19
18
|
log('mutation loginWithPassword', { username, email });
|
|
20
19
|
|
|
21
|
-
if (!
|
|
20
|
+
if (!plainPassword) {
|
|
22
21
|
throw new Error('Password is required');
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
const mappedUserLoginParams = {
|
|
26
25
|
user: email ? { email } : { username },
|
|
27
|
-
password:
|
|
26
|
+
password: hashPassword(plainPassword),
|
|
28
27
|
code: totpCode,
|
|
29
28
|
};
|
|
30
29
|
|
|
@@ -3,14 +3,14 @@ import { Context, Root } from '@unchainedshop/types/api';
|
|
|
3
3
|
|
|
4
4
|
export default async function resetPassword(
|
|
5
5
|
root: Root,
|
|
6
|
-
params: {
|
|
6
|
+
params: { newPlainPassword?: string; token: string },
|
|
7
7
|
context: Context,
|
|
8
8
|
) {
|
|
9
9
|
const { modules, userId } = context;
|
|
10
10
|
|
|
11
11
|
log('mutation resetPassword', { userId });
|
|
12
12
|
|
|
13
|
-
if (!params.
|
|
13
|
+
if (!params.newPlainPassword) {
|
|
14
14
|
throw new Error('Password is required');
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -4,7 +4,7 @@ import { UserNotFoundError, InvalidIdError } from '../../../errors';
|
|
|
4
4
|
|
|
5
5
|
export default async function setPassword(
|
|
6
6
|
root: Root,
|
|
7
|
-
params: {
|
|
7
|
+
params: { newPlainPassword: string; userId: string },
|
|
8
8
|
{ modules, userId }: Context,
|
|
9
9
|
) {
|
|
10
10
|
const normalizedUserId = params.userId || userId;
|
|
@@ -12,7 +12,7 @@ export default async function setPassword(
|
|
|
12
12
|
log(`mutation setPassword ${normalizedUserId}`, { userId });
|
|
13
13
|
|
|
14
14
|
if (!normalizedUserId) throw new InvalidIdError({ userId: normalizedUserId });
|
|
15
|
-
if (!params.
|
|
15
|
+
if (!params.newPlainPassword) {
|
|
16
16
|
throw new Error('Password is required');
|
|
17
17
|
}
|
|
18
18
|
if (!(await modules.users.userExists({ userId: normalizedUserId })))
|
|
@@ -14,7 +14,7 @@ export default async function removeAssortment(
|
|
|
14
14
|
const assortment = await modules.assortments.findAssortment({ assortmentId });
|
|
15
15
|
if (!assortment) throw new AssortmentNotFoundError({ assortmentId });
|
|
16
16
|
|
|
17
|
-
await modules.assortments.delete(assortmentId, { skipInvalidation: false }
|
|
17
|
+
await modules.assortments.delete(assortmentId, { skipInvalidation: false });
|
|
18
18
|
|
|
19
19
|
return assortment;
|
|
20
20
|
}
|
|
@@ -18,7 +18,7 @@ export default async function removeAssortmentFilter(
|
|
|
18
18
|
|
|
19
19
|
if (!assortmentFilter) throw new AssortmentFilterNotFoundError({ assortmentFilterId });
|
|
20
20
|
|
|
21
|
-
await modules.assortments.filters.delete(assortmentFilterId
|
|
21
|
+
await modules.assortments.filters.delete(assortmentFilterId);
|
|
22
22
|
|
|
23
23
|
return assortmentFilter;
|
|
24
24
|
}
|
|
@@ -16,7 +16,7 @@ export default async function removeAssortmentLink(
|
|
|
16
16
|
|
|
17
17
|
if (!assortmentLink) throw new AssortmentLinkNotFoundError({ assortmentLinkId });
|
|
18
18
|
|
|
19
|
-
await modules.assortments.links.delete(assortmentLinkId, { skipInvalidation: false }
|
|
19
|
+
await modules.assortments.links.delete(assortmentLinkId, { skipInvalidation: false });
|
|
20
20
|
|
|
21
21
|
return assortmentLink;
|
|
22
22
|
}
|
|
@@ -18,7 +18,7 @@ export default async function removeAssortmentProduct(
|
|
|
18
18
|
});
|
|
19
19
|
if (!assortmentProduct) throw new AssortmentProductNotFoundError({ assortmentProductId });
|
|
20
20
|
|
|
21
|
-
await modules.assortments.products.delete(assortmentProductId, { skipInvalidation: false }
|
|
21
|
+
await modules.assortments.products.delete(assortmentProductId, { skipInvalidation: false });
|
|
22
22
|
|
|
23
23
|
return assortmentProduct;
|
|
24
24
|
}
|
|
@@ -15,7 +15,7 @@ export default async function removeFilter(
|
|
|
15
15
|
const filter = await modules.filters.findFilter({ filterId });
|
|
16
16
|
if (!filter) throw new FilterNotFoundError({ filterId });
|
|
17
17
|
|
|
18
|
-
await modules.assortments.filters.deleteMany({ filterId }
|
|
18
|
+
await modules.assortments.filters.deleteMany({ filterId });
|
|
19
19
|
await modules.filters.delete(filterId, userId);
|
|
20
20
|
|
|
21
21
|
return filter;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { emit } from '@unchainedshop/events';
|
|
3
3
|
|
|
4
|
-
export default (root, { path, referrer }, { userId }) => {
|
|
4
|
+
export default async (root, { path, referrer }, { userId }) => {
|
|
5
5
|
log(`mutation pageView ${path} ${referrer}`, { userId });
|
|
6
6
|
|
|
7
|
-
emit('PAGE_VIEW', {
|
|
7
|
+
await emit('PAGE_VIEW', {
|
|
8
8
|
path,
|
|
9
9
|
referrer,
|
|
10
10
|
});
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { Context, Root } from '@unchainedshop/types/api';
|
|
3
|
-
import { ProductNotFoundError,
|
|
3
|
+
import { ProductNotFoundError, InvalidIdError } from '../../../errors';
|
|
4
4
|
|
|
5
5
|
export default async function removeProduct(
|
|
6
6
|
root: Root,
|
|
7
7
|
{ productId }: { productId: string },
|
|
8
|
-
|
|
8
|
+
context: Context,
|
|
9
9
|
) {
|
|
10
|
+
const { modules, services, userId } = context;
|
|
10
11
|
log(`mutation removeProduct ${productId}`, { userId });
|
|
11
12
|
|
|
12
13
|
if (!productId) throw new InvalidIdError({ productId });
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (!(await modules.products.productExists({ productId })))
|
|
16
|
+
throw new ProductNotFoundError({ productId });
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
await modules.assortments.products.delete(productId as string, {}, userId);
|
|
20
|
-
await modules.products.delete(productId, userId);
|
|
18
|
+
await services.products.removeProduct({ productId, userId: context.userId }, context);
|
|
21
19
|
|
|
22
20
|
return modules.products.findProduct({ productId });
|
|
23
21
|
}
|
|
@@ -9,9 +9,6 @@ type HelperType<P, T> = (assortmentPathLink: AssortmentPathLinkType, params: P,
|
|
|
9
9
|
|
|
10
10
|
export interface AssortmentPathLinkHelperTypes {
|
|
11
11
|
link: HelperType<never, Promise<AssortmentLinkType>>;
|
|
12
|
-
|
|
13
|
-
assortmentSlug: HelperType<{ forceLocale?: string }, Promise<string>>;
|
|
14
|
-
|
|
15
12
|
assortmentTexts: HelperType<{ forceLocale?: string }, Promise<AssortmentText>>;
|
|
16
13
|
}
|
|
17
14
|
|
|
@@ -23,14 +20,6 @@ export const AssortmentPathLink: AssortmentPathLinkHelperTypes = {
|
|
|
23
20
|
});
|
|
24
21
|
},
|
|
25
22
|
|
|
26
|
-
assortmentSlug: async ({ assortmentId }, params, { loaders, localeContext }) => {
|
|
27
|
-
const text = await loaders.assortmentTextLoader.load({
|
|
28
|
-
assortmentId,
|
|
29
|
-
locale: params.forceLocale || localeContext.normalized,
|
|
30
|
-
});
|
|
31
|
-
return text.slug;
|
|
32
|
-
},
|
|
33
|
-
|
|
34
23
|
assortmentTexts: async ({ assortmentId }, params, { loaders, localeContext }) => {
|
|
35
24
|
const text = await loaders.assortmentTextLoader.load({
|
|
36
25
|
assortmentId,
|