@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.
Files changed (214) hide show
  1. package/lib/api-index.d.ts +3 -6
  2. package/lib/api-index.js +4 -12
  3. package/lib/api-index.js.map +1 -1
  4. package/lib/context.d.ts +0 -1
  5. package/lib/context.js +0 -12
  6. package/lib/context.js.map +1 -1
  7. package/lib/createGraphQLServer.d.ts +1 -1
  8. package/lib/createGraphQLServer.js +2 -33
  9. package/lib/createGraphQLServer.js.map +1 -1
  10. package/lib/express/createApolloMiddleware.d.ts +4 -0
  11. package/lib/express/createApolloMiddleware.js +34 -0
  12. package/lib/express/createApolloMiddleware.js.map +1 -0
  13. package/lib/express/createBulkImportMiddleware.d.ts +2 -0
  14. package/lib/express/createBulkImportMiddleware.js +79 -0
  15. package/lib/express/createBulkImportMiddleware.js.map +1 -0
  16. package/lib/express/createERCMetadataMiddleware.d.ts +3 -0
  17. package/lib/express/createERCMetadataMiddleware.js +56 -0
  18. package/lib/express/createERCMetadataMiddleware.js.map +1 -0
  19. package/lib/express/createSingleSignOnMiddleware.d.ts +6 -0
  20. package/lib/express/createSingleSignOnMiddleware.js +71 -0
  21. package/lib/express/createSingleSignOnMiddleware.js.map +1 -0
  22. package/lib/express/index.d.ts +6 -0
  23. package/lib/express/index.js +27 -0
  24. package/lib/express/index.js.map +1 -0
  25. package/lib/resolvers/mutations/accounts/changePassword.d.ts +0 -2
  26. package/lib/resolvers/mutations/accounts/changePassword.js +2 -2
  27. package/lib/resolvers/mutations/accounts/changePassword.js.map +1 -1
  28. package/lib/resolvers/mutations/accounts/createUser.js +2 -2
  29. package/lib/resolvers/mutations/accounts/createUser.js.map +1 -1
  30. package/lib/resolvers/mutations/accounts/enrollUser.js +1 -1
  31. package/lib/resolvers/mutations/accounts/enrollUser.js.map +1 -1
  32. package/lib/resolvers/mutations/accounts/loginWithPassword.d.ts +0 -1
  33. package/lib/resolvers/mutations/accounts/loginWithPassword.js +3 -3
  34. package/lib/resolvers/mutations/accounts/loginWithPassword.js.map +1 -1
  35. package/lib/resolvers/mutations/accounts/resetPassword.d.ts +0 -1
  36. package/lib/resolvers/mutations/accounts/resetPassword.js +1 -1
  37. package/lib/resolvers/mutations/accounts/resetPassword.js.map +1 -1
  38. package/lib/resolvers/mutations/accounts/setPassword.d.ts +0 -1
  39. package/lib/resolvers/mutations/accounts/setPassword.js +1 -1
  40. package/lib/resolvers/mutations/accounts/setPassword.js.map +1 -1
  41. package/lib/resolvers/mutations/assortments/removeAssortment.js +1 -1
  42. package/lib/resolvers/mutations/assortments/removeAssortment.js.map +1 -1
  43. package/lib/resolvers/mutations/assortments/removeAssortmentFilter.js +1 -1
  44. package/lib/resolvers/mutations/assortments/removeAssortmentFilter.js.map +1 -1
  45. package/lib/resolvers/mutations/assortments/removeAssortmentLink.js +1 -1
  46. package/lib/resolvers/mutations/assortments/removeAssortmentLink.js.map +1 -1
  47. package/lib/resolvers/mutations/assortments/removeAssortmentProduct.js +1 -1
  48. package/lib/resolvers/mutations/assortments/removeAssortmentProduct.js.map +1 -1
  49. package/lib/resolvers/mutations/enrollments/updateEnrollment.d.ts +1 -1
  50. package/lib/resolvers/mutations/filters/removeFilter.js +1 -1
  51. package/lib/resolvers/mutations/filters/removeFilter.js.map +1 -1
  52. package/lib/resolvers/mutations/index.d.ts +1 -1
  53. package/lib/resolvers/mutations/pageView.d.ts +1 -1
  54. package/lib/resolvers/mutations/pageView.js +2 -2
  55. package/lib/resolvers/mutations/pageView.js.map +1 -1
  56. package/lib/resolvers/mutations/products/removeProduct.d.ts +1 -1
  57. package/lib/resolvers/mutations/products/removeProduct.js +5 -8
  58. package/lib/resolvers/mutations/products/removeProduct.js.map +1 -1
  59. package/lib/resolvers/type/assortment/assortment-filter-types.d.ts +2 -2
  60. package/lib/resolvers/type/assortment/assortment-link-types.d.ts +2 -2
  61. package/lib/resolvers/type/assortment/assortment-media-types.d.ts +1 -1
  62. package/lib/resolvers/type/assortment/assortment-path-link-types.d.ts +1 -4
  63. package/lib/resolvers/type/assortment/assortment-path-link-types.js +0 -7
  64. package/lib/resolvers/type/assortment/assortment-path-link-types.js.map +1 -1
  65. package/lib/resolvers/type/assortment/assortment-product-types.d.ts +2 -2
  66. package/lib/resolvers/type/assortment/assortment-types.d.ts +1 -1
  67. package/lib/resolvers/type/bookmark-types.d.ts +1 -1
  68. package/lib/resolvers/type/country-types.d.ts +1 -1
  69. package/lib/resolvers/type/delivery-provider-types.d.ts +1 -1
  70. package/lib/resolvers/type/enrollment/enrollment-delivery-types.d.ts +2 -2
  71. package/lib/resolvers/type/enrollment/enrollment-payment-types.d.ts +2 -2
  72. package/lib/resolvers/type/enrollment/enrollment-period-types.d.ts +2 -2
  73. package/lib/resolvers/type/enrollment/enrollment-plan-tyes.d.ts +2 -2
  74. package/lib/resolvers/type/enrollment/enrollment-types.d.ts +2 -2
  75. package/lib/resolvers/type/event-types.d.ts +1 -1
  76. package/lib/resolvers/type/filter/filter-option-types.d.ts +2 -2
  77. package/lib/resolvers/type/filter/filter-types.d.ts +2 -2
  78. package/lib/resolvers/type/order/order-delivery-discount-types.d.ts +1 -1
  79. package/lib/resolvers/type/order/order-delivery-pickup-types.d.ts +1 -1
  80. package/lib/resolvers/type/order/order-delivery-shipping-types.d.ts +1 -1
  81. package/lib/resolvers/type/order/order-discount-types.d.ts +1 -1
  82. package/lib/resolvers/type/order/order-global-discount-types.d.ts +1 -1
  83. package/lib/resolvers/type/order/order-item-discount-types.d.ts +1 -1
  84. package/lib/resolvers/type/order/order-item-types.d.ts +1 -1
  85. package/lib/resolvers/type/order/order-payment-card-types.d.ts +1 -1
  86. package/lib/resolvers/type/order/order-payment-discount-types.d.ts +1 -1
  87. package/lib/resolvers/type/order/order-payment-generic-types.d.ts +1 -1
  88. package/lib/resolvers/type/order/order-payment-invoice-types.d.ts +1 -1
  89. package/lib/resolvers/type/order/order-types.d.ts +1 -1
  90. package/lib/resolvers/type/price-types.d.ts +1 -1
  91. package/lib/resolvers/type/product/product-discount.d.ts +1 -1
  92. package/lib/resolvers/type/product/product-review-types.d.ts +1 -1
  93. package/lib/resolvers/type/quotation-types.d.ts +2 -2
  94. package/lib/resolvers/type/shop-types.d.ts +1 -1
  95. package/lib/resolvers/type/token-types.d.ts +1 -1
  96. package/lib/resolvers/type/user-types.d.ts +1 -3
  97. package/lib/resolvers/type/user-types.js +0 -12
  98. package/lib/resolvers/type/user-types.js.map +1 -1
  99. package/lib/resolvers/type/webauthn-credentials-types.d.ts +1 -1
  100. package/lib/resolvers/type/work-types.d.ts +1 -1
  101. package/lib/schema/{types/log.d.ts → directives.d.ts} +0 -0
  102. package/lib/schema/directives.js +15 -0
  103. package/lib/schema/directives.js.map +1 -0
  104. package/lib/schema/index.js +2 -1
  105. package/lib/schema/index.js.map +1 -1
  106. package/lib/schema/inputTypes.js +0 -15
  107. package/lib/schema/inputTypes.js.map +1 -1
  108. package/lib/schema/mutation.js +4 -23
  109. package/lib/schema/mutation.js.map +1 -1
  110. package/lib/schema/query.js +28 -10
  111. package/lib/schema/query.js.map +1 -1
  112. package/lib/schema/types/assortment.js +9 -10
  113. package/lib/schema/types/assortment.js.map +1 -1
  114. package/lib/schema/types/color.js +1 -1
  115. package/lib/schema/types/country.js +1 -1
  116. package/lib/schema/types/currency.js +1 -1
  117. package/lib/schema/types/delivery.js +3 -2
  118. package/lib/schema/types/delivery.js.map +1 -1
  119. package/lib/schema/types/dimensions.js +1 -1
  120. package/lib/schema/types/discount.js +1 -1
  121. package/lib/schema/types/dispatch.js +1 -0
  122. package/lib/schema/types/dispatch.js.map +1 -1
  123. package/lib/schema/types/filter.js +5 -8
  124. package/lib/schema/types/filter.js.map +1 -1
  125. package/lib/schema/types/geo-position.js +1 -1
  126. package/lib/schema/types/language.js +1 -1
  127. package/lib/schema/types/media.js +1 -1
  128. package/lib/schema/types/payment.js +3 -3
  129. package/lib/schema/types/price.js +2 -2
  130. package/lib/schema/types/product/bundle-product.js +2 -3
  131. package/lib/schema/types/product/bundle-product.js.map +1 -1
  132. package/lib/schema/types/product/configurable-product.js +7 -7
  133. package/lib/schema/types/product/plan-product.js +4 -3
  134. package/lib/schema/types/product/plan-product.js.map +1 -1
  135. package/lib/schema/types/product/product.js +11 -11
  136. package/lib/schema/types/product/simple-product.js +5 -4
  137. package/lib/schema/types/product/simple-product.js.map +1 -1
  138. package/lib/schema/types/product/tokenized-product.js +4 -3
  139. package/lib/schema/types/product/tokenized-product.js.map +1 -1
  140. package/lib/schema/types/search.js +3 -8
  141. package/lib/schema/types/search.js.map +1 -1
  142. package/lib/schema/types/shop.js +2 -2
  143. package/lib/schema/types/stock.js +1 -0
  144. package/lib/schema/types/stock.js.map +1 -1
  145. package/lib/schema/types/types.js +1 -1
  146. package/lib/schema/types/user.js +1 -3
  147. package/lib/schema/types/user.js.map +1 -1
  148. package/lib/schema/types/warehousing.js +3 -3
  149. package/package.json +23 -10
  150. package/src/api-index.ts +5 -26
  151. package/src/context.ts +0 -18
  152. package/src/createGraphQLServer.ts +1 -44
  153. package/src/express/createApolloMiddleware.ts +40 -0
  154. package/src/express/createBulkImportMiddleware.ts +90 -0
  155. package/src/express/createERCMetadataMiddleware.ts +72 -0
  156. package/src/express/createSingleSignOnMiddleware.ts +83 -0
  157. package/src/express/index.ts +48 -0
  158. package/src/resolvers/mutations/accounts/changePassword.ts +2 -4
  159. package/src/resolvers/mutations/accounts/createUser.ts +2 -2
  160. package/src/resolvers/mutations/accounts/enrollUser.ts +1 -1
  161. package/src/resolvers/mutations/accounts/loginWithPassword.ts +3 -4
  162. package/src/resolvers/mutations/accounts/resetPassword.ts +2 -2
  163. package/src/resolvers/mutations/accounts/setPassword.ts +2 -2
  164. package/src/resolvers/mutations/assortments/removeAssortment.ts +1 -1
  165. package/src/resolvers/mutations/assortments/removeAssortmentFilter.ts +1 -1
  166. package/src/resolvers/mutations/assortments/removeAssortmentLink.ts +1 -1
  167. package/src/resolvers/mutations/assortments/removeAssortmentProduct.ts +1 -1
  168. package/src/resolvers/mutations/filters/removeFilter.ts +1 -1
  169. package/src/resolvers/mutations/pageView.ts +2 -2
  170. package/src/resolvers/mutations/products/removeProduct.ts +6 -8
  171. package/src/resolvers/type/assortment/assortment-path-link-types.ts +0 -11
  172. package/src/resolvers/type/user-types.ts +0 -15
  173. package/src/schema/directives.js +14 -0
  174. package/src/schema/index.js +2 -1
  175. package/src/schema/inputTypes.js +0 -15
  176. package/src/schema/mutation.js +4 -23
  177. package/src/schema/query.js +28 -10
  178. package/src/schema/types/assortment.js +9 -10
  179. package/src/schema/types/color.js +1 -1
  180. package/src/schema/types/country.js +1 -1
  181. package/src/schema/types/currency.js +1 -1
  182. package/src/schema/types/delivery.js +3 -2
  183. package/src/schema/types/dimensions.js +1 -1
  184. package/src/schema/types/discount.js +1 -1
  185. package/src/schema/types/dispatch.js +1 -0
  186. package/src/schema/types/filter.js +5 -8
  187. package/src/schema/types/geo-position.js +1 -1
  188. package/src/schema/types/language.js +1 -1
  189. package/src/schema/types/media.js +1 -1
  190. package/src/schema/types/payment.js +3 -3
  191. package/src/schema/types/price.js +2 -2
  192. package/src/schema/types/product/bundle-product.js +2 -3
  193. package/src/schema/types/product/configurable-product.js +7 -7
  194. package/src/schema/types/product/plan-product.js +4 -3
  195. package/src/schema/types/product/product.js +11 -11
  196. package/src/schema/types/product/simple-product.js +5 -4
  197. package/src/schema/types/product/tokenized-product.js +4 -3
  198. package/src/schema/types/search.js +3 -8
  199. package/src/schema/types/shop.js +2 -2
  200. package/src/schema/types/stock.js +1 -0
  201. package/src/schema/types/types.js +1 -1
  202. package/src/schema/types/user.js +1 -3
  203. package/src/schema/types/warehousing.js +3 -3
  204. package/lib/createBulkImportServer.d.ts +0 -2
  205. package/lib/createBulkImportServer.js +0 -81
  206. package/lib/createBulkImportServer.js.map +0 -1
  207. package/lib/createERCMetadataServer.d.ts +0 -2
  208. package/lib/createERCMetadataServer.js +0 -59
  209. package/lib/createERCMetadataServer.js.map +0 -1
  210. package/lib/schema/types/log.js +0 -16
  211. package/lib/schema/types/log.js.map +0 -1
  212. package/src/createBulkImportServer.ts +0 -95
  213. package/src/createERCMetadataServer.ts +0 -78
  214. 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 (expressApp, options) => {
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.newPassword && !params.newPlainPassword) {
14
+ if (!params.newPlainPassword) {
17
15
  throw new Error('New password is required');
18
16
  }
19
- if (!params.oldPassword && !params.oldPlainPassword) {
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.password && !params.plainPassword && !params.webAuthnPublicKeyCredentials) {
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 (!mappedUser.password && mappedUser.plainPassword) {
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 (!mappedUserData.password && mappedUserData.plainPassword) {
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, password: hashedPassword, plainPassword, totpCode } = params;
16
+ const { username, email, plainPassword, totpCode } = params;
18
17
 
19
18
  log('mutation loginWithPassword', { username, email });
20
19
 
21
- if (!hashedPassword && !plainPassword) {
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: hashedPassword || hashPassword(plainPassword),
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: { newPassword?: string; newPlainPassword?: string; token: string },
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.newPassword && !params.newPlainPassword) {
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: { newPassword: string; newPlainPassword: string; userId: string },
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.newPassword && !params.newPlainPassword) {
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 }, userId);
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, userId);
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 }, userId);
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 }, userId);
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 }, userId);
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, ProductWrongStatusError, InvalidIdError } from '../../../errors';
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
- { modules, userId }: Context,
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
- const product = await modules.products.findProduct({ productId });
15
- if (!product) throw new ProductNotFoundError({ productId });
15
+ if (!(await modules.products.productExists({ productId })))
16
+ throw new ProductNotFoundError({ productId });
16
17
 
17
- if (!modules.products.isDraft(product)) throw new ProductWrongStatusError({ status: product.status });
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,