@unchainedshop/api 4.4.0 → 4.5.0

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 (92) hide show
  1. package/lib/api-index.js +3 -0
  2. package/lib/context.js +2 -13
  3. package/lib/errors.d.ts +1 -0
  4. package/lib/errors.js +2 -1
  5. package/lib/events.js +0 -2
  6. package/lib/fastify/index.js +9 -3
  7. package/lib/loaders/assortmentMediaTextLoader.js +1 -1
  8. package/lib/loaders/assortmentMediasLoader.js +1 -1
  9. package/lib/loaders/assortmentTextLoader.js +1 -1
  10. package/lib/loaders/countryLoader.js +1 -1
  11. package/lib/loaders/currencyLoader.js +1 -1
  12. package/lib/loaders/deliveryProviderLoader.js +1 -1
  13. package/lib/loaders/fileLoader.js +1 -1
  14. package/lib/loaders/filterTextLoader.js +1 -1
  15. package/lib/loaders/languageLoader.js +1 -1
  16. package/lib/loaders/orderLoader.js +1 -1
  17. package/lib/loaders/paymentProviderLoader.js +1 -1
  18. package/lib/loaders/productBySKULoader.js +2 -4
  19. package/lib/loaders/productLoader.js +1 -3
  20. package/lib/loaders/productMediaTextLoader.js +1 -1
  21. package/lib/loaders/productMediasLoader.js +1 -1
  22. package/lib/loaders/productProxiesLoader.js +2 -10
  23. package/lib/loaders/productTextLoader.js +1 -1
  24. package/lib/loaders/productVariationByKeyLoader.js +1 -1
  25. package/lib/loaders/productVariationLoader.js +1 -1
  26. package/lib/loaders/productVariationTextLoader.js +1 -1
  27. package/lib/loaders/quotationLoader.js +1 -1
  28. package/lib/loaders/userLoader.js +1 -1
  29. package/lib/loaders/warehousingProviderLoader.js +1 -1
  30. package/lib/mcp/tools/order/handlers/getTopCustomers.d.ts +5 -5
  31. package/lib/mcp/tools/order/handlers/getTopCustomers.js +4 -54
  32. package/lib/mcp/tools/order/handlers/getTopProducts.d.ts +3 -3
  33. package/lib/mcp/tools/order/handlers/getTopProducts.js +3 -42
  34. package/lib/mcp/tools/provider/handlers/listProviders.js +4 -8
  35. package/lib/resolvers/mutations/accounts/addWeb3Address.d.ts +2 -2
  36. package/lib/resolvers/mutations/accounts/addWeb3Address.js +1 -14
  37. package/lib/resolvers/mutations/accounts/addWebAuthnCredentials.d.ts +2 -2
  38. package/lib/resolvers/mutations/accounts/addWebAuthnCredentials.js +2 -6
  39. package/lib/resolvers/mutations/accounts/createUser.js +3 -1
  40. package/lib/resolvers/mutations/accounts/createWebAuthnCredentialCreationOptions.d.ts +1 -4
  41. package/lib/resolvers/mutations/accounts/createWebAuthnCredentialRequestOptions.d.ts +1 -4
  42. package/lib/resolvers/mutations/accounts/loginAsGuest.js +1 -3
  43. package/lib/resolvers/mutations/accounts/removeWeb3Address.d.ts +1 -1
  44. package/lib/resolvers/mutations/accounts/removeWeb3Address.js +3 -7
  45. package/lib/resolvers/mutations/accounts/removeWebAuthnCredentials.d.ts +1 -1
  46. package/lib/resolvers/mutations/accounts/removeWebAuthnCredentials.js +1 -5
  47. package/lib/resolvers/mutations/accounts/verifyWeb3Address.d.ts +2 -2
  48. package/lib/resolvers/mutations/accounts/verifyWeb3Address.js +8 -89
  49. package/lib/resolvers/mutations/filters/removeFilter.d.ts +2 -2
  50. package/lib/resolvers/mutations/filters/removeFilter.js +3 -5
  51. package/lib/resolvers/mutations/orders/addMultipleCartProducts.js +23 -31
  52. package/lib/resolvers/mutations/orders/removeCartDiscount.d.ts +2 -2
  53. package/lib/resolvers/mutations/orders/removeCartDiscount.js +9 -14
  54. package/lib/resolvers/mutations/orders/updateCart.js +2 -8
  55. package/lib/resolvers/mutations/users/removeUser.js +1 -1
  56. package/lib/resolvers/mutations/warehousing/exportToken.js +9 -17
  57. package/lib/resolvers/queries/payment/paymentProvidersCount.d.ts +3 -3
  58. package/lib/resolvers/queries/payment/paymentProvidersCount.js +1 -1
  59. package/lib/resolvers/queries/shopInfo.js +4 -36
  60. package/lib/resolvers/type/delivery-provider-interface.d.ts +1 -7
  61. package/lib/resolvers/type/delivery-provider-interface.js +10 -20
  62. package/lib/resolvers/type/delivery-provider-pickup-types.d.ts +1 -7
  63. package/lib/resolvers/type/delivery-provider-shipping-types.d.ts +1 -7
  64. package/lib/resolvers/type/delivery-provider-types.d.ts +1 -7
  65. package/lib/resolvers/type/index.d.ts +33 -54
  66. package/lib/resolvers/type/media-types.d.ts +1 -1
  67. package/lib/resolvers/type/media-types.js +7 -5
  68. package/lib/resolvers/type/order/order-item-types.d.ts +2 -9
  69. package/lib/resolvers/type/order/order-item-types.js +2 -37
  70. package/lib/resolvers/type/order/order-statistics-types.d.ts +10 -10
  71. package/lib/resolvers/type/order/order-statistics-types.js +10 -64
  72. package/lib/resolvers/type/payment/payment-provider-types.d.ts +1 -7
  73. package/lib/resolvers/type/payment/payment-provider-types.js +7 -17
  74. package/lib/resolvers/type/price-types.d.ts +0 -1
  75. package/lib/resolvers/type/product/product-bundle-types.d.ts +1 -1
  76. package/lib/resolvers/type/product/product-configurable-types.d.ts +1 -1
  77. package/lib/resolvers/type/product/product-configurable-types.js +9 -32
  78. package/lib/resolvers/type/product/product-plan-types.d.ts +1 -1
  79. package/lib/resolvers/type/product/product-simple-types.d.ts +1 -1
  80. package/lib/resolvers/type/product/product-tokenized-types.d.ts +1 -1
  81. package/lib/resolvers/type/product/product-types.d.ts +1 -1
  82. package/lib/resolvers/type/product/product-types.js +7 -17
  83. package/lib/resolvers/type/shop-types.js +2 -5
  84. package/lib/resolvers/type/token-types.d.ts +3 -3
  85. package/lib/resolvers/type/token-types.js +4 -25
  86. package/lib/resolvers/type/user-types.js +1 -4
  87. package/lib/resolvers/type/webauthn-credentials-types.d.ts +10 -1
  88. package/lib/roles/index.d.ts +2 -1
  89. package/lib/roles/index.js +7 -1
  90. package/lib/utils/getConfiguredTags.d.ts +1 -0
  91. package/lib/utils/getConfiguredTags.js +9 -0
  92. package/package.json +7 -17
@@ -2,7 +2,4 @@ import type { Context } from '../../../context.ts';
2
2
  export default function createWebAuthnCredentialCreationOptions(root: never, { extensionOptions, username }: {
3
3
  username: string;
4
4
  extensionOptions: any;
5
- }, { modules, userId, getHeader }: Context): Promise<(Omit<import("fido2-lib").PublicKeyCredentialCreationOptions, "challenge" | "requestId"> & {
6
- challenge: string;
7
- requestId: number;
8
- }) | null>;
5
+ }, { modules, userId, getHeader }: Context): Promise<import("@unchainedshop/core-users").WebAuthnCredentialCreationOptions>;
@@ -2,7 +2,4 @@ import type { Context } from '../../../context.ts';
2
2
  export default function createWebAuthnCredentialRequestOptions(root: never, { extensionOptions, username }: {
3
3
  username: string;
4
4
  extensionOptions: any;
5
- }, { modules, userId, getHeader }: Context): Promise<(Omit<import("fido2-lib").PublicKeyCredentialRequestOptions, "challenge" | "requestId"> & {
6
- challenge: string;
7
- requestId: number;
8
- }) | null>;
5
+ }, { modules, userId, getHeader }: Context): Promise<import("@unchainedshop/core-users").WebAuthnCredentialRequestOptions>;
@@ -1,9 +1,7 @@
1
1
  import { log } from '@unchainedshop/logger';
2
- import moniker from 'moniker';
3
- import { generateDbObjectId } from '@unchainedshop/mongodb';
4
2
  export default async function loginAsGuest(root, _, context) {
5
3
  log('mutation loginAsGuest');
6
- const guestname = `${moniker.choose()}-${generateDbObjectId(5)}`;
4
+ const guestname = `guest-${crypto.randomUUID()}`;
7
5
  const guestUserId = await context.modules.users.createUser({
8
6
  email: `${guestname}@unchained.local`,
9
7
  guest: true,
@@ -1,4 +1,4 @@
1
1
  import type { Context } from '../../../context.ts';
2
2
  export default function removeWeb3Address(root: never, { address }: {
3
3
  address: string;
4
- }, { modules, userId, user }: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-users").User> | null>;
4
+ }, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User>;
@@ -4,13 +4,9 @@ export default async function removeWeb3Address(root, { address }, { modules, us
4
4
  log(`mutation removeWeb3Address ${address} ${user?.username}`, {
5
5
  userId,
6
6
  });
7
- const foundCredentials = user?.services?.web3?.find((service) => service.address.toLowerCase() === address.toLowerCase());
8
- if (!foundCredentials) {
7
+ const updatedUser = await modules.users.removeWeb3Address(userId, address);
8
+ if (!updatedUser) {
9
9
  throw new UserWeb3AddressNotFoundError({ userId, address });
10
10
  }
11
- return modules.users.updateUser({ _id: userId }, {
12
- $pull: {
13
- 'services.web3': { address: foundCredentials.address },
14
- },
15
- }, {});
11
+ return updatedUser;
16
12
  }
@@ -1,4 +1,4 @@
1
1
  import type { Context } from '../../../context.ts';
2
2
  export default function removeWebAuthnCredentials(root: never, { credentialsId }: {
3
3
  credentialsId: string;
4
- }, { modules, userId, user }: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-users").User> | null>;
4
+ }, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User | null>;
@@ -8,9 +8,5 @@ export default async function removeWebAuthnCredentials(root, { credentialsId },
8
8
  if (!foundCredentials) {
9
9
  throw new UserWebAuthnCredentialsNotFoundError({ userId, credentialsId });
10
10
  }
11
- return modules.users.updateUser({ _id: userId }, {
12
- $pull: {
13
- 'services.webAuthn': { id: credentialsId },
14
- },
15
- }, {});
11
+ return modules.users.removeWebAuthnCredential(userId, credentialsId);
16
12
  }
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '../../../context.ts';
2
- export default function verifyWeb3Address(root: never, { address, hash }: {
2
+ export default function verifyWeb3Address(_root: never, { address, hash }: {
3
3
  address: string;
4
4
  hash: `0x${string}`;
5
- }, { modules, userId, user }: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-users").User> | null>;
5
+ }, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User>;
@@ -1,100 +1,19 @@
1
1
  import { log } from '@unchainedshop/logger';
2
2
  import { UserWeb3AddressNotFoundError, UserWeb3AddressSignatureError } from "../../../errors.js";
3
- let secp256k1;
4
- let keccak_256;
5
- let bytesToHex;
6
- let hexToBytes;
7
- async function loadNoblePackages() {
8
- try {
9
- const curves = await import('@noble/curves/secp256k1.js');
10
- const hashes = await import('@noble/hashes/sha3.js');
11
- const utils = await import('@noble/hashes/utils.js');
12
- if (!curves || !hashes || !utils) {
13
- throw new Error('Missing required @noble packages for Web3 signature verification');
14
- }
15
- secp256k1 = curves.secp256k1;
16
- keccak_256 = hashes.keccak_256;
17
- bytesToHex = utils.bytesToHex;
18
- hexToBytes = utils.hexToBytes;
19
- return true;
20
- }
21
- catch (error) {
22
- log('Failed to load @noble packages for Web3 verification', { error: error.message });
23
- return false;
24
- }
25
- }
26
- function fromRPCSig(sig) {
27
- const bytes = hexToBytes(sig.startsWith('0x') ? sig.slice(2) : sig);
28
- if (bytes.length !== 65)
29
- throw new Error('Invalid signature length');
30
- const r = bytes.slice(0, 32);
31
- const s = bytes.slice(32, 64);
32
- let v = BigInt(bytes[64]);
33
- if (v >= 35n) {
34
- v = v - 35n - 2n * 1n;
35
- }
36
- else if (v >= 27n) {
37
- v = v - 27n;
38
- }
39
- return { v, r, s };
40
- }
41
- function hashPersonalMessage(message) {
42
- const prefix = new TextEncoder().encode('\x19Ethereum Signed Message:\n');
43
- const lengthBytes = new TextEncoder().encode(message.length.toString());
44
- const combined = new Uint8Array(prefix.length + lengthBytes.length + message.length);
45
- combined.set(prefix, 0);
46
- combined.set(lengthBytes, prefix.length);
47
- combined.set(message, prefix.length + lengthBytes.length);
48
- return keccak_256(combined);
49
- }
50
- function ecrecover(msgHash, v, r, s) {
51
- const recovery = Number(v);
52
- const signature = new Uint8Array(64);
53
- signature.set(r, 0);
54
- signature.set(s, 32);
55
- const publicKey = secp256k1.Signature.fromBytes(signature)
56
- .addRecoveryBit(recovery)
57
- .recoverPublicKey(msgHash);
58
- return publicKey.toBytes(false).slice(1);
59
- }
60
- function publicToAddress(publicKey) {
61
- const hash = keccak_256(publicKey);
62
- return hash.slice(-20);
63
- }
64
- export default async function verifyWeb3Address(root, { address, hash }, { modules, userId, user }) {
3
+ export default async function verifyWeb3Address(_root, { address, hash }, { modules, userId, user }) {
65
4
  log(`mutation verifyWeb3Address ${address} ${user?.username}`, {
66
5
  userId,
67
6
  });
68
- const packagesLoaded = await loadNoblePackages();
69
- if (!packagesLoaded) {
70
- throw new Error('Web3 signature verification is not available. Please install the required @noble packages: @noble/curves, @noble/hashes');
71
- }
72
- const foundCredentials = user?.services?.web3?.find((service) => service.address.toLowerCase() === address.toLowerCase());
7
+ const foundCredentials = modules.users.findWeb3Address(user, address);
73
8
  if (!foundCredentials) {
74
9
  throw new UserWeb3AddressNotFoundError({ userId, address });
75
10
  }
76
- const messageHash = hashPersonalMessage(hexToBytes(Buffer.from(foundCredentials.nonce, 'utf8').toString('hex')));
77
- const sigParams = fromRPCSig(hash);
78
- const publicKey = ecrecover(messageHash, sigParams.v, sigParams.r, sigParams.s);
79
- const sender = publicToAddress(publicKey);
80
- const recoveredAddr = `0x${bytesToHex(sender)}`;
81
- const isSignatureCorrectForAddress = recoveredAddr.toLowerCase() === foundCredentials.address.toLowerCase();
82
- if (!isSignatureCorrectForAddress) {
11
+ if (!foundCredentials.nonce) {
83
12
  throw new UserWeb3AddressSignatureError({ userId, address: foundCredentials.address });
84
13
  }
85
- const web3Services = user?.services?.web3.map((service) => {
86
- if (foundCredentials.address === service.address) {
87
- return {
88
- ...service,
89
- nonce: undefined,
90
- verified: true,
91
- };
92
- }
93
- return service;
94
- });
95
- return modules.users.updateUser({ _id: userId }, {
96
- $set: {
97
- 'services.web3': web3Services,
98
- },
99
- }, {});
14
+ const updatedUser = await modules.users.verifyWeb3SignatureAndUpdate(user, { address: foundCredentials.address, nonce: foundCredentials.nonce }, hash);
15
+ if (!updatedUser) {
16
+ throw new UserWeb3AddressSignatureError({ userId, address: foundCredentials.address });
17
+ }
18
+ return updatedUser;
100
19
  }
@@ -1,4 +1,4 @@
1
1
  import type { Context } from '../../../context.ts';
2
- export default function removeFilter(root: never, { filterId }: {
2
+ export default function removeFilter(_root: never, { filterId }: {
3
3
  filterId: string;
4
- }, context: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-filters").Filter>>;
4
+ }, context: Context): Promise<import("@unchainedshop/core-filters").Filter | null>;
@@ -1,14 +1,12 @@
1
1
  import { log } from '@unchainedshop/logger';
2
2
  import { FilterNotFoundError, InvalidIdError } from "../../../errors.js";
3
- export default async function removeFilter(root, { filterId }, context) {
4
- const { modules, userId } = context;
3
+ export default async function removeFilter(_root, { filterId }, context) {
4
+ const { modules, services, userId } = context;
5
5
  log(`mutation removeFilter ${filterId}`, { userId });
6
6
  if (!filterId)
7
7
  throw new InvalidIdError({ filterId });
8
8
  const filter = await modules.filters.findFilter({ filterId });
9
9
  if (!filter)
10
10
  throw new FilterNotFoundError({ filterId });
11
- await modules.assortments.filters.deleteMany({ filterId });
12
- await modules.filters.delete(filterId);
13
- return filter;
11
+ return services.filters.removeFilter({ filter });
14
12
  }
@@ -1,53 +1,45 @@
1
1
  import { log } from '@unchainedshop/logger';
2
2
  import { ProductNotFoundError, OrderQuantityTooLowError, OrderWrongStatusError, OrderNotFoundError, } from "../../../errors.js";
3
- import { ordersSettings } from '@unchainedshop/core-orders';
4
3
  export default async function addMultipleCartProducts(root, params, context) {
5
- const { modules, services, userId, user } = context;
4
+ const { modules, services, userId, user, locale, countryCode } = context;
6
5
  const { orderId, items } = params;
7
6
  log(`mutation addMultipleCartProducts ${JSON.stringify(items)}`, {
8
7
  userId,
9
8
  orderId,
10
9
  });
11
- const itemsWithProducts = await Promise.all(items.map(async ({ productId, ...item }) => {
12
- const originalProduct = await modules.products.findProduct({ productId });
13
- if (!originalProduct)
10
+ for (const { productId, quantity } of items) {
11
+ const product = await modules.products.findProduct({ productId });
12
+ if (!product)
14
13
  throw new ProductNotFoundError({ productId });
15
- return {
16
- ...item,
17
- originalProduct,
18
- };
19
- }));
14
+ if (quantity < 1)
15
+ throw new OrderQuantityTooLowError({ quantity, productId });
16
+ }
20
17
  const order = await services.orders.findOrInitCart({
21
18
  orderId,
22
19
  user: user,
23
- countryCode: context.countryCode,
20
+ countryCode,
24
21
  });
25
22
  if (!order)
26
23
  throw new OrderNotFoundError({ orderId });
27
24
  if (!modules.orders.isCart(order))
28
25
  throw new OrderWrongStatusError({ status: order.status });
29
- for (const { originalProduct, quantity, configuration } of itemsWithProducts) {
30
- if (quantity < 1)
31
- throw new OrderQuantityTooLowError({
32
- quantity,
33
- productId: originalProduct._id,
34
- });
35
- const product = await modules.products.resolveOrderableProduct(originalProduct, {
36
- configuration,
37
- });
38
- await ordersSettings.validateOrderPosition({
39
- order,
40
- product,
41
- configuration,
42
- quantityDiff: quantity,
43
- }, context);
44
- await modules.orders.positions.addProductItem({
45
- quantity,
46
- configuration,
47
- originalProductId: originalProduct._id,
48
- productId: product._id,
26
+ try {
27
+ await services.orders.addMultipleCartProducts({
49
28
  orderId: order._id,
29
+ items,
30
+ context: { localeContext: locale, userId, countryCode },
50
31
  });
51
32
  }
33
+ catch (error) {
34
+ if (error.message?.startsWith('Product not found:')) {
35
+ const productId = error.message.split(': ')[1];
36
+ throw new ProductNotFoundError({ productId });
37
+ }
38
+ if (error.message?.startsWith('Invalid quantity')) {
39
+ const productId = error.message.split(': ')[1];
40
+ throw new OrderQuantityTooLowError({ quantity: 0, productId });
41
+ }
42
+ throw error;
43
+ }
52
44
  return services.orders.updateCalculation(order._id);
53
45
  }
@@ -1,4 +1,4 @@
1
1
  import type { Context } from '../../../context.ts';
2
- export default function removeCartDiscount(root: never, { discountId }: {
2
+ export default function removeCartDiscount(_root: never, { discountId }: {
3
3
  discountId: string;
4
- }, requestContext: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-orders").OrderDiscount> | null>;
4
+ }, requestContext: Context): Promise<import("@unchainedshop/core-orders").OrderDiscount>;
@@ -1,9 +1,7 @@
1
1
  import { log } from '@unchainedshop/logger';
2
2
  import { OrderDiscountNotFoundError, OrderWrongStatusError, InvalidIdError, OrderNotFoundError, } from "../../../errors.js";
3
- import { OrderDiscountTrigger } from '@unchainedshop/core-orders';
4
- import { OrderDiscountDirector } from '@unchainedshop/core';
5
- export default async function removeCartDiscount(root, { discountId }, requestContext) {
6
- const { modules, services, userId } = requestContext;
3
+ export default async function removeCartDiscount(_root, { discountId }, requestContext) {
4
+ const { modules, services, userId, locale } = requestContext;
7
5
  log(`mutation removeCartDiscount ${discountId}`, { userId });
8
6
  if (!discountId)
9
7
  throw new InvalidIdError({ discountId });
@@ -20,16 +18,13 @@ export default async function removeCartDiscount(root, { discountId }, requestCo
20
18
  if (!modules.orders.isCart(order)) {
21
19
  throw new OrderWrongStatusError({ status: order.status });
22
20
  }
23
- if (orderDiscount.trigger === OrderDiscountTrigger.USER) {
24
- const Adapter = OrderDiscountDirector.getAdapter(orderDiscount.discountKey);
25
- if (Adapter) {
26
- const adapter = await Adapter.actions({
27
- context: { order, orderDiscount, code: orderDiscount.code, ...requestContext },
28
- });
29
- await adapter.release();
30
- }
31
- }
32
- const deletedDiscount = await modules.orders.discounts.delete(discountId);
21
+ const deletedDiscount = await services.orders.removeCartDiscount({
22
+ order,
23
+ orderDiscount,
24
+ requestContext: { localeContext: locale, userId },
25
+ });
26
+ if (!deletedDiscount)
27
+ throw new OrderDiscountNotFoundError({ orderDiscount });
33
28
  await services.orders.updateCalculation(order._id);
34
29
  return deletedDiscount;
35
30
  }
@@ -13,14 +13,8 @@ export default async function updateCart(root, params, context) {
13
13
  throw new OrderNotFoundError({ orderId });
14
14
  if (!modules.orders.isCart(order))
15
15
  throw new OrderWrongStatusError({ status: order.status });
16
- if (meta) {
17
- await modules.orders.updateContext(order._id, meta);
18
- }
19
- if (billingAddress) {
20
- await modules.orders.updateBillingAddress(order._id, billingAddress);
21
- }
22
- if (contact) {
23
- await modules.orders.updateContact(order._id, contact);
16
+ if (meta || billingAddress || contact) {
17
+ await modules.orders.updateCartFields(order._id, { meta, billingAddress, contact });
24
18
  }
25
19
  if (paymentProviderId) {
26
20
  await modules.orders.setPaymentProvider(order._id, paymentProviderId);
@@ -8,7 +8,7 @@ export default async function removeUser(root, params, unchainedAPI) {
8
8
  if (!(await modules.users.userExists({ userId: normalizedUserId })))
9
9
  throw new UserNotFoundError({ userId: normalizedUserId });
10
10
  if (removeUserReviews) {
11
- await modules.products.reviews.deleteMany({ authorId: normalizedUserId });
11
+ await modules.products.reviews.deleteByAuthorId(normalizedUserId);
12
12
  }
13
13
  return services.users.deleteUser({ userId: normalizedUserId });
14
14
  }
@@ -1,5 +1,4 @@
1
1
  import { log } from '@unchainedshop/logger';
2
- import { WorkStatus } from '@unchainedshop/core-worker';
3
2
  import { InvalidIdError, TokenNotFoundError, TokenWrongStatusError } from "../../../errors.js";
4
3
  export default async function exportToken(root, { tokenId, quantity, recipientWalletAddress, }, { modules, userId }) {
5
4
  log(`mutation exportToken ${tokenId}`, {
@@ -13,21 +12,14 @@ export default async function exportToken(root, { tokenId, quantity, recipientWa
13
12
  if (token.walletAddress && !token.userId) {
14
13
  throw new TokenWrongStatusError({ tokenId });
15
14
  }
16
- const workItems = await modules.worker.findWorkQueue({
17
- types: ['EXPORT_TOKEN'],
18
- status: [WorkStatus.NEW, WorkStatus.ALLOCATED],
19
- });
20
- const existingWork = workItems.find((item) => item.input?.token?._id === token._id);
21
- if (!existingWork) {
22
- await modules.worker.addWork({
23
- type: 'EXPORT_TOKEN',
24
- retries: 5,
25
- input: {
26
- token,
27
- quantity,
28
- recipientWalletAddress,
29
- },
30
- });
31
- }
15
+ await modules.worker.addWorkIfNotExists({
16
+ type: 'EXPORT_TOKEN',
17
+ retries: 5,
18
+ input: {
19
+ token,
20
+ quantity,
21
+ recipientWalletAddress,
22
+ },
23
+ }, (item) => item.input?.token?._id === token._id);
32
24
  return modules.warehousing.findToken({ tokenId });
33
25
  }
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '../../../context.ts';
2
- import type { PaymentProvider } from '@unchainedshop/core-payment';
3
- export default function paymentProvidersCount(root: never, params: {
4
- type?: PaymentProvider | null;
2
+ import type { PaymentProviderType } from '@unchainedshop/core-payment';
3
+ export default function paymentProvidersCount(_root: never, params: {
4
+ type?: PaymentProviderType | null;
5
5
  }, { modules, userId }: Context): Promise<number>;
@@ -1,5 +1,5 @@
1
1
  import { log } from '@unchainedshop/logger';
2
- export default async function paymentProvidersCount(root, params, { modules, userId }) {
2
+ export default async function paymentProvidersCount(_root, params, { modules, userId }) {
3
3
  log(`query paymentProvidersCount ${params.type}`, { userId });
4
4
  return modules.payment.paymentProviders.count(params.type
5
5
  ? {
@@ -1,5 +1,6 @@
1
1
  import { readFile } from 'fs/promises';
2
2
  import { log } from '@unchainedshop/logger';
3
+ import { getConfiguredTags } from "../../utils/getConfiguredTags.js";
3
4
  export default function shopInfo(root, _, context) {
4
5
  const { adminUiConfig, modules } = context;
5
6
  log('query shopInfo', { userId: context.userId });
@@ -30,42 +31,9 @@ export default function shopInfo(root, _, context) {
30
31
  return [];
31
32
  }
32
33
  },
33
- productTags: async () => {
34
- const existingProductTags = await modules.products.existingTags();
35
- const envTags = (process.env.UNCHAINED_ADMIN_UI_DEFAULT_PRODUCT_TAGS || '')
36
- .split(',')
37
- .map((t) => t.trim())
38
- .filter(Boolean);
39
- const normalizedDefaultTags = envTags?.length
40
- ? envTags
41
- : (adminUiConfig?.defaultProductTags || []).filter(Boolean);
42
- const normalizedTags = Array.from(new Set(normalizedDefaultTags.concat(existingProductTags)));
43
- return normalizedTags;
44
- },
45
- assortmentTags: async () => {
46
- const existingAssortmentTags = await modules.assortments.existingTags();
47
- const envTags = (process.env.UNCHAINED_ADMIN_UI_DEFAULT_ASSORTMENT_TAGS || '')
48
- .split(',')
49
- .map((t) => t.trim())
50
- .filter(Boolean);
51
- const normalizedDefaultTags = envTags?.length
52
- ? envTags
53
- : (adminUiConfig?.defaultAssortmentTags || []).filter(Boolean);
54
- const normalizedTags = Array.from(new Set(normalizedDefaultTags.concat(existingAssortmentTags)));
55
- return normalizedTags;
56
- },
57
- userTags: async () => {
58
- const existingUserTags = await modules.users.existingTags();
59
- const envTags = (process.env.UNCHAINED_ADMIN_UI_DEFAULT_USER_TAGS || '')
60
- .split(',')
61
- .map((t) => t.trim())
62
- .filter(Boolean);
63
- const normalizedDefaultTags = envTags?.length
64
- ? envTags
65
- : (adminUiConfig?.defaultUserTags || []).filter(Boolean);
66
- const normalizedTags = Array.from(new Set(normalizedDefaultTags.concat(existingUserTags)));
67
- return normalizedTags;
68
- },
34
+ productTags: () => getConfiguredTags(() => modules.products.existingTags(), 'UNCHAINED_ADMIN_UI_DEFAULT_PRODUCT_TAGS', adminUiConfig?.defaultProductTags),
35
+ assortmentTags: () => getConfiguredTags(() => modules.assortments.existingTags(), 'UNCHAINED_ADMIN_UI_DEFAULT_ASSORTMENT_TAGS', adminUiConfig?.defaultAssortmentTags),
36
+ userTags: () => getConfiguredTags(() => modules.users.existingTags(), 'UNCHAINED_ADMIN_UI_DEFAULT_USER_TAGS', adminUiConfig?.defaultUserTags),
69
37
  },
70
38
  vapidPublicKey: process.env?.PUSH_NOTIFICATION_PUBLIC_KEY,
71
39
  };
@@ -7,11 +7,5 @@ export declare const DeliveryProviderInterface: {
7
7
  label: string;
8
8
  version: string;
9
9
  } | null;
10
- simulatedPrice(deliveryProvider: any, args: any, context: Context): Promise<{
11
- amount: number;
12
- currencyCode: string;
13
- countryCode: string;
14
- isTaxable: boolean;
15
- isNetPrice: any;
16
- } | null>;
10
+ simulatedPrice(deliveryProvider: any, args: any, requestContext: Context): Promise<import("@unchainedshop/core/lib/services/simulateDeliveryPricing.js").SimulatedDeliveryPrice | null>;
17
11
  };
@@ -1,4 +1,4 @@
1
- import { DeliveryDirector, DeliveryPricingDirector } from '@unchainedshop/core';
1
+ import { DeliveryDirector } from '@unchainedshop/core';
2
2
  export const DeliveryProviderInterface = {
3
3
  async isActive(deliveryProvider, _, context) {
4
4
  const director = await DeliveryDirector.actions(deliveryProvider, {}, context);
@@ -18,30 +18,20 @@ export const DeliveryProviderInterface = {
18
18
  version: Adapter.version,
19
19
  };
20
20
  },
21
- async simulatedPrice(deliveryProvider, args, context) {
22
- const { loaders, countryCode, user } = context;
21
+ async simulatedPrice(deliveryProvider, args, requestContext) {
22
+ const { loaders, services, countryCode, user } = requestContext;
23
23
  const { currencyCode: forcedCurrencyCode, orderId, useNetPrice, context: providerContext } = args;
24
- const order = await loaders.orderLoader.load({ orderId });
25
- const currencyCode = forcedCurrencyCode || order?.currencyCode || context.currencyCode;
26
- const pricingContext = {
24
+ if (!user)
25
+ return null;
26
+ const order = orderId ? await loaders.orderLoader.load({ orderId }) : undefined;
27
+ const currencyCode = forcedCurrencyCode || order?.currencyCode || requestContext.currencyCode;
28
+ return services.delivery.simulateDeliveryPricing({
27
29
  countryCode,
28
30
  currencyCode,
29
31
  provider: deliveryProvider,
30
32
  order,
31
33
  providerContext,
32
- user: user,
33
- };
34
- const calculated = await DeliveryPricingDirector.rebuildCalculation(pricingContext, context);
35
- if (!calculated?.length)
36
- return null;
37
- const pricing = DeliveryPricingDirector.calculationSheet(pricingContext, calculated);
38
- const orderPrice = pricing.total({ useNetPrice });
39
- return {
40
- amount: orderPrice.amount,
41
- currencyCode: orderPrice.currencyCode,
42
- countryCode,
43
- isTaxable: pricing.taxSum() > 0,
44
- isNetPrice: useNetPrice,
45
- };
34
+ user,
35
+ }, { useNetPrice });
46
36
  },
47
37
  };
@@ -9,11 +9,5 @@ export declare const DeliveryProviderPickUp: {
9
9
  label: string;
10
10
  version: string;
11
11
  } | null;
12
- simulatedPrice(deliveryProvider: any, args: any, context: Context): Promise<{
13
- amount: number;
14
- currencyCode: string;
15
- countryCode: string;
16
- isTaxable: boolean;
17
- isNetPrice: any;
18
- } | null>;
12
+ simulatedPrice(deliveryProvider: any, args: any, requestContext: Context): Promise<import("@unchainedshop/core/lib/services/simulateDeliveryPricing.js").SimulatedDeliveryPrice | null>;
19
13
  };
@@ -6,11 +6,5 @@ export declare const DeliveryProviderShipping: {
6
6
  label: string;
7
7
  version: string;
8
8
  } | null;
9
- simulatedPrice(deliveryProvider: any, args: any, context: import("../../context.ts").Context): Promise<{
10
- amount: number;
11
- currencyCode: string;
12
- countryCode: string;
13
- isTaxable: boolean;
14
- isNetPrice: any;
15
- } | null>;
9
+ simulatedPrice(deliveryProvider: any, args: any, requestContext: import("../../context.ts").Context): Promise<import("@unchainedshop/core/lib/services/simulateDeliveryPricing.js").SimulatedDeliveryPrice | null>;
16
10
  };
@@ -7,12 +7,6 @@ export declare const DeliveryProvider: {
7
7
  label: string;
8
8
  version: string;
9
9
  } | null;
10
- simulatedPrice(deliveryProvider: any, args: any, context: import("../../context.ts").Context): Promise<{
11
- amount: number;
12
- currencyCode: string;
13
- countryCode: string;
14
- isTaxable: boolean;
15
- isNetPrice: any;
16
- } | null>;
10
+ simulatedPrice(deliveryProvider: any, args: any, requestContext: import("../../context.ts").Context): Promise<import("@unchainedshop/core/lib/services/simulateDeliveryPricing.js").SimulatedDeliveryPrice | null>;
17
11
  __resolveType: (provider: DeliveryProviderActual) => Promise<"DeliveryProviderPickUp" | "DeliveryProviderShipping">;
18
12
  };