@unchainedshop/api 5.0.0-alpha.4 → 5.0.0-alpha.5
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/acl.js +4 -0
- package/lib/adminUiPlugins.d.ts +12 -1
- package/lib/adminUiPlugins.js +188 -26
- package/lib/errors.d.ts +2 -0
- package/lib/errors.js +2 -0
- package/lib/events.d.ts +1 -0
- package/lib/events.js +1 -0
- package/lib/express/index.js +44 -17
- package/lib/fastify/index.d.ts +2 -0
- package/lib/fastify/index.js +65 -24
- package/lib/mcp/tools/system/handlers/allocateWork.js +2 -2
- package/lib/mcp/tools/users/index.js +1 -1
- package/lib/middleware/createAuthMiddleware.js +2 -0
- package/lib/resolvers/mutations/accounts/loginWithPassword.js +16 -2
- package/lib/resolvers/mutations/accounts/logoutAllSessions.d.ts +3 -1
- package/lib/resolvers/mutations/accounts/logoutAllSessions.js +10 -4
- package/lib/resolvers/mutations/bulk/bulkAssignProductsToAssortment.d.ts +10 -0
- package/lib/resolvers/mutations/bulk/bulkAssignProductsToAssortment.js +18 -0
- package/lib/resolvers/mutations/bulk/bulkOperation.d.ts +12 -0
- package/lib/resolvers/mutations/bulk/bulkOperation.js +14 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveAssortments.d.ts +9 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveAssortments.js +7 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveFilters.d.ts +9 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveFilters.js +9 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveProducts.d.ts +9 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveProducts.js +9 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveUsers.d.ts +9 -0
- package/lib/resolvers/mutations/bulk/bulkRemoveUsers.js +7 -0
- package/lib/resolvers/mutations/bulk/bulkSetAssortmentActive.d.ts +10 -0
- package/lib/resolvers/mutations/bulk/bulkSetAssortmentActive.js +9 -0
- package/lib/resolvers/mutations/bulk/bulkSetFilterActive.d.ts +10 -0
- package/lib/resolvers/mutations/bulk/bulkSetFilterActive.js +10 -0
- package/lib/resolvers/mutations/bulk/bulkSetProductStatus.d.ts +10 -0
- package/lib/resolvers/mutations/bulk/bulkSetProductStatus.js +17 -0
- package/lib/resolvers/mutations/bulk/bulkSetUserRoles.d.ts +10 -0
- package/lib/resolvers/mutations/bulk/bulkSetUserRoles.js +14 -0
- package/lib/resolvers/mutations/bulk/bulkUpdateAssortmentTags.d.ts +11 -0
- package/lib/resolvers/mutations/bulk/bulkUpdateAssortmentTags.js +10 -0
- package/lib/resolvers/mutations/bulk/bulkUpdateProductTags.d.ts +11 -0
- package/lib/resolvers/mutations/bulk/bulkUpdateProductTags.js +7 -0
- package/lib/resolvers/mutations/bulk/bulkUpdateUserTags.d.ts +11 -0
- package/lib/resolvers/mutations/bulk/bulkUpdateUserTags.js +7 -0
- package/lib/resolvers/mutations/index.d.ts +12 -0
- package/lib/resolvers/mutations/index.js +25 -1
- package/lib/resolvers/mutations/users/removeUser.js +9 -2
- package/lib/resolvers/mutations/users/setRoles.js +9 -2
- package/lib/resolvers/mutations/worker/allocateWork.d.ts +1 -1
- package/lib/resolvers/mutations/worker/allocateWork.js +4 -3
- package/lib/resolvers/queries/index.d.ts +1 -0
- package/lib/resolvers/queries/index.js +2 -0
- package/lib/resolvers/queries/search/globalSearch.d.ts +36 -0
- package/lib/resolvers/queries/search/globalSearch.js +200 -0
- package/lib/resolvers/type/global-search-result-types.d.ts +3 -0
- package/lib/resolvers/type/global-search-result-types.js +15 -0
- package/lib/resolvers/type/index.d.ts +3 -0
- package/lib/resolvers/type/index.js +2 -0
- package/lib/resolvers/type/user-types.d.ts +1 -0
- package/lib/resolvers/type/user-types.js +35 -4
- package/lib/roles/all.js +12 -0
- package/lib/roles/index.js +12 -0
- package/lib/roles/loggedIn.js +1 -1
- package/lib/schema/inputTypes.js +5 -0
- package/lib/schema/mutation.js +74 -1
- package/lib/schema/query.js +15 -0
- package/lib/schema/types/common.js +7 -0
- package/lib/schema/types/search.js +36 -0
- package/lib/schema/types/user.js +4 -0
- package/package.json +25 -25
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { usersManagement, UsersManagementSchema } from "./usersManagement.js";
|
|
2
2
|
export const registerUsersTools = (server, context) => {
|
|
3
3
|
server.registerTool('users_management', {
|
|
4
|
-
description: '
|
|
4
|
+
description: 'User management for Unchained e-commerce. Actions: LIST, COUNT, GET, CREATE, UPDATE, REMOVE, ENROLL (create + send enrollment email), SET_TAGS, SET_USERNAME, ADD_EMAIL, REMOVE_EMAIL, SEND_ENROLLMENT_EMAIL, SEND_VERIFICATION_EMAIL, GET_ORDERS, GET_ENROLLMENTS, GET_QUOTATIONS, GET_BOOKMARKS, GET_PAYMENT_CREDENTIALS, GET_AVATAR, GET_REVIEWS, GET_REVIEWS_COUNT, REMOVE_PRODUCT_REVIEWS, GET_CURRENT_USER. LIST supports pagination, sorting, search, and filters for guests, email verification, and last login; COUNT supports the same search and filter criteria.',
|
|
5
5
|
inputSchema: UsersManagementSchema,
|
|
6
6
|
}, async (params) => usersManagement(context, params));
|
|
7
7
|
};
|
|
@@ -61,7 +61,9 @@ export async function createAuthContext(params, authConfig) {
|
|
|
61
61
|
const tokenObject = {
|
|
62
62
|
_id: crypto.randomUUID(),
|
|
63
63
|
userId: user._id,
|
|
64
|
+
userName: user.username || user.emails?.[0]?.address,
|
|
64
65
|
tokenExpires: expires,
|
|
66
|
+
remoteAddress: params.remoteAddress,
|
|
65
67
|
};
|
|
66
68
|
await emit(API_EVENTS.API_LOGIN_TOKEN_CREATED, tokenObject);
|
|
67
69
|
user._inLoginMethodResponse = true;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { emit } from '@unchainedshop/events';
|
|
2
3
|
import { InvalidCredentialsError, UsernameOrEmailRequiredError } from "../../../errors.js";
|
|
4
|
+
import { API_EVENTS } from "../../../events.js";
|
|
3
5
|
export default async function loginWithPassword(root, params, context) {
|
|
4
6
|
const { username, email, password } = params;
|
|
5
7
|
log('mutation loginWithPassword', { username, email });
|
|
@@ -8,12 +10,24 @@ export default async function loginWithPassword(root, params, context) {
|
|
|
8
10
|
let user = username
|
|
9
11
|
? await context.modules.users.findUserByUsername(username)
|
|
10
12
|
: await context.modules.users.findUserByEmail(email);
|
|
11
|
-
if (!user)
|
|
13
|
+
if (!user) {
|
|
14
|
+
await emit(API_EVENTS.API_LOGIN_FAILED, {
|
|
15
|
+
username,
|
|
16
|
+
email,
|
|
17
|
+
remoteAddress: context.remoteAddress,
|
|
18
|
+
});
|
|
12
19
|
throw new InvalidCredentialsError({ username, email });
|
|
20
|
+
}
|
|
13
21
|
const verified = user.services?.password &&
|
|
14
22
|
(await context.modules.users.verifyPassword(user.services.password, password));
|
|
15
|
-
if (!verified)
|
|
23
|
+
if (!verified) {
|
|
24
|
+
await emit(API_EVENTS.API_LOGIN_FAILED, {
|
|
25
|
+
userId: user._id,
|
|
26
|
+
username: user.username,
|
|
27
|
+
remoteAddress: context.remoteAddress,
|
|
28
|
+
});
|
|
16
29
|
throw new InvalidCredentialsError({ username, email });
|
|
30
|
+
}
|
|
17
31
|
if (user.guest) {
|
|
18
32
|
await context.modules.users.updateGuest(user, false);
|
|
19
33
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
|
-
export default function logoutAllSessions(root: never,
|
|
2
|
+
export default function logoutAllSessions(root: never, params: {
|
|
3
|
+
userId?: string;
|
|
4
|
+
}, context: Context): Promise<{
|
|
3
5
|
success: boolean;
|
|
4
6
|
}>;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
|
|
2
|
+
import { UserNotFoundError } from "../../../errors.js";
|
|
3
|
+
export default async function logoutAllSessions(root, params, context) {
|
|
3
4
|
const { userId, modules } = context;
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
const normalizedUserId = params.userId || userId;
|
|
6
|
+
log(`mutation logoutAllSessions ${normalizedUserId}`, { userId });
|
|
7
|
+
if (!(await modules.users.userExists({ userId: normalizedUserId })))
|
|
8
|
+
throw new UserNotFoundError({ userId: normalizedUserId });
|
|
9
|
+
const result = await modules.users.incrementTokenVersion(normalizedUserId);
|
|
6
10
|
if (!result) {
|
|
7
11
|
throw new Error('Failed to logout all sessions', { cause: 'LOGOUT_FAILED' });
|
|
8
12
|
}
|
|
9
|
-
|
|
13
|
+
if (normalizedUserId === userId) {
|
|
14
|
+
await context.logout();
|
|
15
|
+
}
|
|
10
16
|
return { success: true };
|
|
11
17
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkAssignProductsToAssortment(root: never, { productIds, assortmentId }: {
|
|
3
|
+
productIds: string[];
|
|
4
|
+
assortmentId: string;
|
|
5
|
+
}, { modules, services, userId }: Context): Promise<{
|
|
6
|
+
successIds: string[];
|
|
7
|
+
successCount: number;
|
|
8
|
+
failedIds: string[];
|
|
9
|
+
failedCount: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { InvalidIdError, AssortmentNotFoundError } from "../../../errors.js";
|
|
3
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
4
|
+
export default async function bulkAssignProductsToAssortment(root, { productIds, assortmentId }, { modules, services, userId }) {
|
|
5
|
+
log(`mutation bulkAssignProductsToAssortment ${assortmentId} for ${productIds.length} products`, {
|
|
6
|
+
userId,
|
|
7
|
+
});
|
|
8
|
+
const normalizedProductIds = normalizeBulkIds(productIds);
|
|
9
|
+
if (!assortmentId)
|
|
10
|
+
throw new InvalidIdError({ assortmentId });
|
|
11
|
+
if (!(await modules.assortments.assortmentExists({ assortmentId })))
|
|
12
|
+
throw new AssortmentNotFoundError({ assortmentId });
|
|
13
|
+
const result = await services.assortments.bulkAssignProductsToAssortment({
|
|
14
|
+
assortmentId,
|
|
15
|
+
productIds: normalizedProductIds,
|
|
16
|
+
});
|
|
17
|
+
return createBulkOperationResult(result);
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const MAX_BULK_OPERATION_SIZE = 1000;
|
|
2
|
+
export interface BulkOperationIds {
|
|
3
|
+
successIds: string[];
|
|
4
|
+
failedIds: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare const normalizeBulkIds: (ids: readonly string[]) => string[];
|
|
7
|
+
export declare const createBulkOperationResult: ({ successIds, failedIds }: BulkOperationIds) => {
|
|
8
|
+
successIds: string[];
|
|
9
|
+
successCount: number;
|
|
10
|
+
failedIds: string[];
|
|
11
|
+
failedCount: number;
|
|
12
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BulkOperationTooLargeError } from "../../../errors.js";
|
|
2
|
+
export const MAX_BULK_OPERATION_SIZE = 1000;
|
|
3
|
+
export const normalizeBulkIds = (ids) => {
|
|
4
|
+
if (ids.length > MAX_BULK_OPERATION_SIZE) {
|
|
5
|
+
throw new BulkOperationTooLargeError({ limit: MAX_BULK_OPERATION_SIZE });
|
|
6
|
+
}
|
|
7
|
+
return [...new Set(ids)];
|
|
8
|
+
};
|
|
9
|
+
export const createBulkOperationResult = ({ successIds, failedIds }) => ({
|
|
10
|
+
successIds,
|
|
11
|
+
successCount: successIds.length,
|
|
12
|
+
failedIds,
|
|
13
|
+
failedCount: failedIds.length,
|
|
14
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkRemoveAssortments(root: never, { assortmentIds }: {
|
|
3
|
+
assortmentIds: string[];
|
|
4
|
+
}, { modules, userId }: Context): Promise<{
|
|
5
|
+
successIds: string[];
|
|
6
|
+
successCount: number;
|
|
7
|
+
failedIds: string[];
|
|
8
|
+
failedCount: number;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkRemoveAssortments(root, { assortmentIds }, { modules, userId }) {
|
|
4
|
+
log(`mutation bulkRemoveAssortments for ${assortmentIds.length} assortments`, { userId });
|
|
5
|
+
const result = await modules.assortments.bulkDelete(normalizeBulkIds(assortmentIds));
|
|
6
|
+
return createBulkOperationResult(result);
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkRemoveFilters(root: never, { filterIds }: {
|
|
3
|
+
filterIds: string[];
|
|
4
|
+
}, { services, userId }: Context): Promise<{
|
|
5
|
+
successIds: string[];
|
|
6
|
+
successCount: number;
|
|
7
|
+
failedIds: string[];
|
|
8
|
+
failedCount: number;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkRemoveFilters(root, { filterIds }, { services, userId }) {
|
|
4
|
+
log(`mutation bulkRemoveFilters for ${filterIds.length} filters`, { userId });
|
|
5
|
+
const result = await services.filters.bulkRemoveFilters({
|
|
6
|
+
filterIds: normalizeBulkIds(filterIds),
|
|
7
|
+
});
|
|
8
|
+
return createBulkOperationResult(result);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkRemoveProducts(root: never, { productIds }: {
|
|
3
|
+
productIds: string[];
|
|
4
|
+
}, { services, userId }: Context): Promise<{
|
|
5
|
+
successIds: string[];
|
|
6
|
+
successCount: number;
|
|
7
|
+
failedIds: string[];
|
|
8
|
+
failedCount: number;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkRemoveProducts(root, { productIds }, { services, userId }) {
|
|
4
|
+
log(`mutation bulkRemoveProducts for ${productIds.length} products`, { userId });
|
|
5
|
+
const result = await services.products.bulkRemoveProducts({
|
|
6
|
+
productIds: normalizeBulkIds(productIds),
|
|
7
|
+
});
|
|
8
|
+
return createBulkOperationResult(result);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkRemoveUsers(root: never, { userIds }: {
|
|
3
|
+
userIds: string[];
|
|
4
|
+
}, { services, userId }: Context): Promise<{
|
|
5
|
+
successIds: string[];
|
|
6
|
+
successCount: number;
|
|
7
|
+
failedIds: string[];
|
|
8
|
+
failedCount: number;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkRemoveUsers(root, { userIds }, { services, userId }) {
|
|
4
|
+
log(`mutation bulkRemoveUsers for ${userIds.length} users`, { userId });
|
|
5
|
+
const result = await services.users.bulkDeleteUsers({ userIds: normalizeBulkIds(userIds) });
|
|
6
|
+
return createBulkOperationResult(result);
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkSetAssortmentActive(root: never, { assortmentIds, isActive }: {
|
|
3
|
+
assortmentIds: string[];
|
|
4
|
+
isActive: boolean;
|
|
5
|
+
}, { modules, userId }: Context): Promise<{
|
|
6
|
+
successIds: string[];
|
|
7
|
+
successCount: number;
|
|
8
|
+
failedIds: string[];
|
|
9
|
+
failedCount: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkSetAssortmentActive(root, { assortmentIds, isActive }, { modules, userId }) {
|
|
4
|
+
log(`mutation bulkSetAssortmentActive ${isActive} for ${assortmentIds.length} assortments`, {
|
|
5
|
+
userId,
|
|
6
|
+
});
|
|
7
|
+
const result = await modules.assortments.bulkSetActive(normalizeBulkIds(assortmentIds), isActive);
|
|
8
|
+
return createBulkOperationResult(result);
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkSetFilterActive(root: never, { filterIds, isActive }: {
|
|
3
|
+
filterIds: string[];
|
|
4
|
+
isActive: boolean;
|
|
5
|
+
}, { services, userId }: Context): Promise<{
|
|
6
|
+
successIds: string[];
|
|
7
|
+
successCount: number;
|
|
8
|
+
failedIds: string[];
|
|
9
|
+
failedCount: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkSetFilterActive(root, { filterIds, isActive }, { services, userId }) {
|
|
4
|
+
log(`mutation bulkSetFilterActive ${isActive} for ${filterIds.length} filters`, { userId });
|
|
5
|
+
const result = await services.filters.bulkSetFilterActive({
|
|
6
|
+
filterIds: normalizeBulkIds(filterIds),
|
|
7
|
+
isActive,
|
|
8
|
+
});
|
|
9
|
+
return createBulkOperationResult(result);
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkSetProductStatus(root: never, { productIds, status }: {
|
|
3
|
+
productIds: string[];
|
|
4
|
+
status: string;
|
|
5
|
+
}, { modules, userId }: Context): Promise<{
|
|
6
|
+
successIds: string[];
|
|
7
|
+
successCount: number;
|
|
8
|
+
failedIds: string[];
|
|
9
|
+
failedCount: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkSetProductStatus(root, { productIds, status }, { modules, userId }) {
|
|
4
|
+
log(`mutation bulkSetProductStatus ${status} for ${productIds.length} products`, { userId });
|
|
5
|
+
const normalizedProductIds = normalizeBulkIds(productIds);
|
|
6
|
+
let result;
|
|
7
|
+
if (status === 'ACTIVE') {
|
|
8
|
+
result = await modules.products.bulkPublish(normalizedProductIds);
|
|
9
|
+
}
|
|
10
|
+
else if (status === 'DRAFT') {
|
|
11
|
+
result = await modules.products.bulkUnpublish(normalizedProductIds);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
result = { successIds: [], failedIds: normalizedProductIds };
|
|
15
|
+
}
|
|
16
|
+
return createBulkOperationResult(result);
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkSetUserRoles(root: never, { userIds, roles }: {
|
|
3
|
+
userIds: string[];
|
|
4
|
+
roles: string[];
|
|
5
|
+
}, context: Context): Promise<{
|
|
6
|
+
successIds: string[];
|
|
7
|
+
successCount: number;
|
|
8
|
+
failedIds: string[];
|
|
9
|
+
failedCount: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { getPublicRoles } from "../../../roles/index.js";
|
|
3
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
4
|
+
export default async function bulkSetUserRoles(root, { userIds, roles }, context) {
|
|
5
|
+
const { modules, userId } = context;
|
|
6
|
+
log(`mutation bulkSetUserRoles for ${userIds.length} users`, { userId });
|
|
7
|
+
const publicRoles = getPublicRoles(context.roles);
|
|
8
|
+
const invalidRoles = roles.filter((r) => !publicRoles.includes(r));
|
|
9
|
+
if (invalidRoles.length) {
|
|
10
|
+
throw new Error(`Invalid role names: ${invalidRoles.join(', ')}`);
|
|
11
|
+
}
|
|
12
|
+
const result = await modules.users.bulkUpdateRoles(normalizeBulkIds(userIds), roles);
|
|
13
|
+
return createBulkOperationResult(result);
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkUpdateAssortmentTags(root: never, { assortmentIds, add, remove }: {
|
|
3
|
+
assortmentIds: string[];
|
|
4
|
+
add?: string[];
|
|
5
|
+
remove?: string[];
|
|
6
|
+
}, { modules, userId }: Context): Promise<{
|
|
7
|
+
successIds: string[];
|
|
8
|
+
successCount: number;
|
|
9
|
+
failedIds: string[];
|
|
10
|
+
failedCount: number;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkUpdateAssortmentTags(root, { assortmentIds, add, remove }, { modules, userId }) {
|
|
4
|
+
log(`mutation bulkUpdateAssortmentTags for ${assortmentIds.length} assortments`, { userId });
|
|
5
|
+
const result = await modules.assortments.bulkUpdateTags(normalizeBulkIds(assortmentIds), {
|
|
6
|
+
add,
|
|
7
|
+
remove,
|
|
8
|
+
});
|
|
9
|
+
return createBulkOperationResult(result);
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkUpdateProductTags(root: never, { productIds, add, remove }: {
|
|
3
|
+
productIds: string[];
|
|
4
|
+
add?: string[];
|
|
5
|
+
remove?: string[];
|
|
6
|
+
}, { modules, userId }: Context): Promise<{
|
|
7
|
+
successIds: string[];
|
|
8
|
+
successCount: number;
|
|
9
|
+
failedIds: string[];
|
|
10
|
+
failedCount: number;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkUpdateProductTags(root, { productIds, add, remove }, { modules, userId }) {
|
|
4
|
+
log(`mutation bulkUpdateProductTags for ${productIds.length} products`, { userId });
|
|
5
|
+
const result = await modules.products.bulkUpdateTags(normalizeBulkIds(productIds), { add, remove });
|
|
6
|
+
return createBulkOperationResult(result);
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
export default function bulkUpdateUserTags(root: never, { userIds, add, remove }: {
|
|
3
|
+
userIds: string[];
|
|
4
|
+
add?: string[];
|
|
5
|
+
remove?: string[];
|
|
6
|
+
}, { modules, userId }: Context): Promise<{
|
|
7
|
+
successIds: string[];
|
|
8
|
+
successCount: number;
|
|
9
|
+
failedIds: string[];
|
|
10
|
+
failedCount: number;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { createBulkOperationResult, normalizeBulkIds } from "./bulkOperation.js";
|
|
3
|
+
export default async function bulkUpdateUserTags(root, { userIds, add, remove }, { modules, userId }) {
|
|
4
|
+
log(`mutation bulkUpdateUserTags for ${userIds.length} users`, { userId });
|
|
5
|
+
const result = await modules.users.bulkUpdateTags(normalizeBulkIds(userIds), { add, remove });
|
|
6
|
+
return createBulkOperationResult(result);
|
|
7
|
+
}
|
|
@@ -149,5 +149,17 @@ declare const _default: {
|
|
|
149
149
|
signPaymentProviderForCredentialRegistration: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
150
150
|
signPaymentProviderForCheckout: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
151
151
|
removeUserProductReviews: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
152
|
+
bulkSetProductStatus: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
153
|
+
bulkUpdateProductTags: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
154
|
+
bulkAssignProductsToAssortment: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
155
|
+
bulkRemoveProducts: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
156
|
+
bulkUpdateUserTags: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
157
|
+
bulkRemoveAssortments: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
158
|
+
bulkUpdateAssortmentTags: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
159
|
+
bulkSetAssortmentActive: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
160
|
+
bulkRemoveFilters: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
161
|
+
bulkSetFilterActive: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
162
|
+
bulkRemoveUsers: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
163
|
+
bulkSetUserRoles: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
152
164
|
};
|
|
153
165
|
export default _default;
|
|
@@ -150,6 +150,18 @@ import updateCartDeliveryPickUp from "./orders/updateCartDeliveryPickUp.js";
|
|
|
150
150
|
import updateCartDeliveryShipping from "./orders/updateCartDeliveryShipping.js";
|
|
151
151
|
import updateCartPaymentGeneric from "./orders/updateCartPaymentGeneric.js";
|
|
152
152
|
import updateCartPaymentInvoice from "./orders/updateCartPaymentInvoice.js";
|
|
153
|
+
import bulkSetProductStatus from "./bulk/bulkSetProductStatus.js";
|
|
154
|
+
import bulkUpdateProductTags from "./bulk/bulkUpdateProductTags.js";
|
|
155
|
+
import bulkAssignProductsToAssortment from "./bulk/bulkAssignProductsToAssortment.js";
|
|
156
|
+
import bulkRemoveProducts from "./bulk/bulkRemoveProducts.js";
|
|
157
|
+
import bulkUpdateUserTags from "./bulk/bulkUpdateUserTags.js";
|
|
158
|
+
import bulkRemoveAssortments from "./bulk/bulkRemoveAssortments.js";
|
|
159
|
+
import bulkUpdateAssortmentTags from "./bulk/bulkUpdateAssortmentTags.js";
|
|
160
|
+
import bulkSetAssortmentActive from "./bulk/bulkSetAssortmentActive.js";
|
|
161
|
+
import bulkRemoveFilters from "./bulk/bulkRemoveFilters.js";
|
|
162
|
+
import bulkSetFilterActive from "./bulk/bulkSetFilterActive.js";
|
|
163
|
+
import bulkRemoveUsers from "./bulk/bulkRemoveUsers.js";
|
|
164
|
+
import bulkSetUserRoles from "./bulk/bulkSetUserRoles.js";
|
|
153
165
|
export default {
|
|
154
166
|
logout: acl(actions.logout)(logout),
|
|
155
167
|
logoutAllSessions: acl(actions.logoutAllSessions)(logoutAllSessions),
|
|
@@ -179,7 +191,7 @@ export default {
|
|
|
179
191
|
addEmail: acl(actions.updateUser)(addEmail),
|
|
180
192
|
removeEmail: acl(actions.updateUser)(removeEmail),
|
|
181
193
|
updateUserProfile: acl(actions.updateUser)(updateUserProfile),
|
|
182
|
-
removeUser: acl(actions.
|
|
194
|
+
removeUser: acl(actions.removeUser)(removeUser),
|
|
183
195
|
setPassword: acl(actions.updateUser)(setPassword),
|
|
184
196
|
prepareUserAvatarUpload: acl(actions.uploadUserAvatar)(prepareUserAvatarUpload),
|
|
185
197
|
setUserTags: acl(actions.manageUsers)(setUserTags),
|
|
@@ -301,4 +313,16 @@ export default {
|
|
|
301
313
|
signPaymentProviderForCredentialRegistration: acl(actions.registerPaymentCredentials)(signPaymentProviderForCredentialRegistration),
|
|
302
314
|
signPaymentProviderForCheckout: acl(actions.updateOrderPayment)(signPaymentProviderForCheckout),
|
|
303
315
|
removeUserProductReviews: acl(actions.updateUser)(removeUserProductReviews),
|
|
316
|
+
bulkSetProductStatus: acl(actions.bulkSetProductStatus)(bulkSetProductStatus),
|
|
317
|
+
bulkUpdateProductTags: acl(actions.bulkUpdateProductTags)(bulkUpdateProductTags),
|
|
318
|
+
bulkAssignProductsToAssortment: acl(actions.bulkAssignProductsToAssortment)(bulkAssignProductsToAssortment),
|
|
319
|
+
bulkRemoveProducts: acl(actions.bulkRemoveProducts)(bulkRemoveProducts),
|
|
320
|
+
bulkUpdateUserTags: acl(actions.bulkUpdateUserTags)(bulkUpdateUserTags),
|
|
321
|
+
bulkRemoveAssortments: acl(actions.bulkRemoveAssortments)(bulkRemoveAssortments),
|
|
322
|
+
bulkUpdateAssortmentTags: acl(actions.bulkUpdateAssortmentTags)(bulkUpdateAssortmentTags),
|
|
323
|
+
bulkSetAssortmentActive: acl(actions.bulkSetAssortmentActive)(bulkSetAssortmentActive),
|
|
324
|
+
bulkRemoveFilters: acl(actions.bulkRemoveFilters)(bulkRemoveFilters),
|
|
325
|
+
bulkSetFilterActive: acl(actions.bulkSetFilterActive)(bulkSetFilterActive),
|
|
326
|
+
bulkRemoveUsers: acl(actions.bulkRemoveUsers)(bulkRemoveUsers),
|
|
327
|
+
bulkSetUserRoles: acl(actions.bulkSetUserRoles)(bulkSetUserRoles),
|
|
304
328
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
import { UserNotFoundError } from "../../../errors.js";
|
|
2
|
+
import { LastAdminError, UserNotFoundError } from "../../../errors.js";
|
|
3
3
|
export default async function removeUser(root, params, { modules, services, userId: currentUserId }) {
|
|
4
4
|
const { userId: paramUserId, removeUserReviews = false } = params;
|
|
5
5
|
const normalizedUserId = paramUserId || currentUserId;
|
|
@@ -9,5 +9,12 @@ export default async function removeUser(root, params, { modules, services, user
|
|
|
9
9
|
if (removeUserReviews) {
|
|
10
10
|
await modules.products.reviews.deleteByAuthorId(normalizedUserId);
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
try {
|
|
13
|
+
return await services.users.deleteUser({ userId: normalizedUserId });
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
if (e.cause === 'LAST_ADMIN')
|
|
17
|
+
throw new LastAdminError({ userId: normalizedUserId });
|
|
18
|
+
throw e;
|
|
19
|
+
}
|
|
13
20
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
import { InvalidIdError, UserNotFoundError } from "../../../errors.js";
|
|
2
|
+
import { InvalidIdError, LastAdminError, UserNotFoundError } from "../../../errors.js";
|
|
3
3
|
import { getPublicRoles } from "../../../roles/index.js";
|
|
4
4
|
export default async function setRoles(root, params, context) {
|
|
5
5
|
const { modules, userId } = context;
|
|
@@ -14,5 +14,12 @@ export default async function setRoles(root, params, context) {
|
|
|
14
14
|
if (invalidRoles.length) {
|
|
15
15
|
throw new Error(`Invalid role names: ${invalidRoles.join(', ')}`);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
try {
|
|
18
|
+
return await modules.users.updateRoles(foreignUserId, params.roles);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
if (e.cause === 'LAST_ADMIN')
|
|
22
|
+
throw new LastAdminError({ userId: foreignUserId });
|
|
23
|
+
throw e;
|
|
24
|
+
}
|
|
18
25
|
}
|
|
@@ -2,4 +2,4 @@ import type { Context } from '../../../context.ts';
|
|
|
2
2
|
export default function allocateWork(root: never, { types, worker }: {
|
|
3
3
|
types: string[];
|
|
4
4
|
worker: string;
|
|
5
|
-
},
|
|
5
|
+
}, context: Context): Promise<import("@unchainedshop/core-worker").Work | null>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
|
|
2
|
+
import { WorkerDirector } from '@unchainedshop/core';
|
|
3
|
+
export default async function allocateWork(root, { types, worker }, context) {
|
|
3
4
|
log(`mutation allocateWork ${(types || []).join(',')} ${worker}`, {
|
|
4
|
-
userId,
|
|
5
|
+
userId: context.userId,
|
|
5
6
|
});
|
|
6
|
-
return
|
|
7
|
+
return WorkerDirector.allocateWork(context, { types, worker });
|
|
7
8
|
}
|
|
@@ -56,6 +56,7 @@ declare const _default: {
|
|
|
56
56
|
quotation: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
57
57
|
quotations: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
58
58
|
quotationsCount: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
59
|
+
globalSearch: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
59
60
|
searchProducts: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
60
61
|
searchAssortments: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
61
62
|
workQueue: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
@@ -44,6 +44,7 @@ import productsCount from "./products/productsCount.js";
|
|
|
44
44
|
import quotation from "./quotations/quotation.js";
|
|
45
45
|
import quotations from "./quotations/quotations.js";
|
|
46
46
|
import quotationsCount from "./quotations/quotationsCount.js";
|
|
47
|
+
import globalSearch from "./search/globalSearch.js";
|
|
47
48
|
import searchAssortments from "./filters/searchAssortments.js";
|
|
48
49
|
import searchProducts from "./filters/searchProducts.js";
|
|
49
50
|
import shopInfo from "./shopInfo.js";
|
|
@@ -129,6 +130,7 @@ export default {
|
|
|
129
130
|
quotation: acl(actions.viewQuotation)(quotation),
|
|
130
131
|
quotations: acl(actions.viewQuotations)(quotations),
|
|
131
132
|
quotationsCount: acl(actions.viewQuotations)(quotationsCount),
|
|
133
|
+
globalSearch: acl(actions.search)(globalSearch),
|
|
132
134
|
searchProducts: acl(actions.search)(searchProducts),
|
|
133
135
|
searchAssortments: acl(actions.search)(searchAssortments),
|
|
134
136
|
workQueue: acl(actions.viewWorkQueue)(workQueue),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Context } from '../../../context.ts';
|
|
2
|
+
import type { Product } from '@unchainedshop/core-products';
|
|
3
|
+
import type { User } from '@unchainedshop/core-users';
|
|
4
|
+
import type { Order } from '@unchainedshop/core-orders';
|
|
5
|
+
import type { Assortment } from '@unchainedshop/core-assortments';
|
|
6
|
+
import type { Filter } from '@unchainedshop/core-filters';
|
|
7
|
+
import type { Enrollment } from '@unchainedshop/core-enrollments';
|
|
8
|
+
import type { Quotation } from '@unchainedshop/core-quotations';
|
|
9
|
+
import type { Work } from '@unchainedshop/core-worker';
|
|
10
|
+
type SearchableEntity = 'PRODUCT' | 'USER' | 'ORDER' | 'ASSORTMENT' | 'FILTER' | 'ENROLLMENT' | 'QUOTATION' | 'WORK';
|
|
11
|
+
interface TypeLimitInput {
|
|
12
|
+
type: SearchableEntity;
|
|
13
|
+
limit: number;
|
|
14
|
+
}
|
|
15
|
+
type SearchResultItem = (Product | User | Order | Assortment | Filter | Enrollment | Quotation | Work) & {
|
|
16
|
+
__typename: string;
|
|
17
|
+
};
|
|
18
|
+
export default function globalSearch(root: never, params: {
|
|
19
|
+
query: string;
|
|
20
|
+
types?: SearchableEntity[];
|
|
21
|
+
limit?: number;
|
|
22
|
+
typeLimits?: TypeLimitInput[];
|
|
23
|
+
includeDraftProducts?: boolean;
|
|
24
|
+
includeInactiveAssortments?: boolean;
|
|
25
|
+
includeInactiveFilters?: boolean;
|
|
26
|
+
includeGuestUsers?: boolean;
|
|
27
|
+
includeCarts?: boolean;
|
|
28
|
+
}, context: Context): Promise<{
|
|
29
|
+
results: SearchResultItem[];
|
|
30
|
+
counts: {
|
|
31
|
+
type: SearchableEntity;
|
|
32
|
+
totalCount: number;
|
|
33
|
+
authorized: boolean;
|
|
34
|
+
}[];
|
|
35
|
+
}>;
|
|
36
|
+
export {};
|