@unchainedshop/api 4.8.4 → 5.0.0-alpha.1
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.d.ts +2 -2
- package/lib/acl.js +36 -8
- package/lib/api-index.d.ts +3 -1
- package/lib/chat/generateImageHandler.js +6 -5
- package/lib/context.d.ts +4 -1
- package/lib/context.js +9 -3
- package/lib/events.d.ts +2 -0
- package/lib/events.js +2 -0
- package/lib/express/createMCPMiddleware.js +1 -1
- package/lib/express/index.d.ts +6 -9
- package/lib/express/index.js +52 -125
- package/lib/fastify/index.d.ts +6 -9
- package/lib/fastify/index.js +51 -117
- package/lib/fastify/mcpHandler.js +1 -1
- package/lib/loaders/buildTextMap.js +6 -3
- package/lib/locale-context.d.ts +4 -2
- package/lib/locale-context.js +2 -6
- package/lib/mcp/tools/assortment/assortmentManagement.d.ts +2 -2
- package/lib/mcp/tools/assortment/assortmentManagement.js +2 -2
- package/lib/mcp/tools/assortment/handlers/getAssortment.js +1 -1
- package/lib/mcp/tools/assortment/schemas.d.ts +322 -141
- package/lib/mcp/tools/assortment/schemas.js +179 -96
- package/lib/mcp/tools/filter/filterManagement.d.ts +2 -2
- package/lib/mcp/tools/filter/filterManagement.js +2 -2
- package/lib/mcp/tools/filter/schemas.d.ts +184 -75
- package/lib/mcp/tools/filter/schemas.js +87 -51
- package/lib/mcp/tools/localization/getLocalizationsConfig.d.ts +1 -1
- package/lib/mcp/tools/localization/localizationManagement.d.ts +2 -2
- package/lib/mcp/tools/localization/localizationManagement.js +2 -2
- package/lib/mcp/tools/localization/schemas.d.ts +144 -47
- package/lib/mcp/tools/localization/schemas.js +51 -36
- package/lib/mcp/tools/order/handlers/getTopCustomers.d.ts +2 -0
- package/lib/mcp/tools/order/orderManagement.d.ts +2 -2
- package/lib/mcp/tools/order/orderManagement.js +2 -2
- package/lib/mcp/tools/order/schemas.d.ts +184 -75
- package/lib/mcp/tools/order/schemas.js +113 -51
- package/lib/mcp/tools/product/handlers/getProduct.js +1 -1
- package/lib/mcp/tools/product/productManagement.d.ts +2 -2
- package/lib/mcp/tools/product/productManagement.js +2 -2
- package/lib/mcp/tools/product/schemas.d.ts +642 -317
- package/lib/mcp/tools/product/schemas.js +287 -191
- package/lib/mcp/tools/provider/providerManagement.d.ts +2 -2
- package/lib/mcp/tools/provider/providerManagement.js +2 -2
- package/lib/mcp/tools/provider/schemas.d.ts +142 -47
- package/lib/mcp/tools/provider/schemas.js +67 -32
- package/lib/mcp/tools/quotation/handlers/getQuotation.d.ts +2 -0
- package/lib/mcp/tools/quotation/handlers/listQuotations.d.ts +2 -0
- package/lib/mcp/tools/quotation/handlers/makeQuotationProposal.d.ts +2 -0
- package/lib/mcp/tools/quotation/handlers/rejectQuotation.d.ts +2 -0
- package/lib/mcp/tools/quotation/handlers/verifyQuotation.d.ts +2 -0
- package/lib/mcp/tools/quotation/quotationManagement.d.ts +2 -2
- package/lib/mcp/tools/quotation/quotationManagement.js +2 -2
- package/lib/mcp/tools/quotation/schemas.d.ts +111 -40
- package/lib/mcp/tools/quotation/schemas.js +54 -29
- package/lib/mcp/tools/system/handlers/countEvents.d.ts +8 -1
- package/lib/mcp/tools/system/handlers/countWork.d.ts +10 -1
- package/lib/mcp/tools/system/handlers/countWork.js +1 -0
- package/lib/mcp/tools/system/handlers/index.d.ts +4 -4
- package/lib/mcp/tools/system/handlers/listEvents.d.ts +12 -1
- package/lib/mcp/tools/system/handlers/listWork.d.ts +14 -1
- package/lib/mcp/tools/system/handlers/listWork.js +1 -0
- package/lib/mcp/tools/system/schemas.d.ts +179 -84
- package/lib/mcp/tools/system/schemas.js +188 -67
- package/lib/mcp/tools/system/systemManagement.d.ts +1 -1
- package/lib/mcp/tools/system/systemManagement.js +1 -1
- package/lib/mcp/tools/users/handlers/addUserEmail.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/createUser.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/enrollUser.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/getCurrentUser.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/getUser.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/listUsers.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/setUserTags.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/setUserUsername.d.ts +2 -0
- package/lib/mcp/tools/users/handlers/updateUser.d.ts +2 -0
- package/lib/mcp/tools/users/schemas.d.ts +296 -163
- package/lib/mcp/tools/users/schemas.js +152 -71
- package/lib/mcp/tools/users/usersManagement.d.ts +2 -2
- package/lib/mcp/tools/users/usersManagement.js +2 -2
- package/lib/mcp/utils/getNormalizedQuotationDetails.d.ts +2 -0
- package/lib/mcp/utils/getNormalizedUserDetails.d.ts +2 -0
- package/lib/mcp/utils/normalizeMediaUrl.js +1 -1
- package/lib/mcp/utils/sanitizeLocalizationEntityData.d.ts +1 -1
- package/lib/mcp/utils/sharedSchemas.d.ts +28 -39
- package/lib/mcp/utils/sharedSchemas.js +28 -47
- package/lib/mcp/utils/validateIsoCode.d.ts +1 -1
- package/lib/resolvers/mutations/accounts/changePassword.js +1 -2
- package/lib/resolvers/mutations/index.d.ts +150 -155
- package/lib/resolvers/mutations/index.js +2 -12
- package/lib/resolvers/mutations/orders/updateCart.js +7 -1
- package/lib/resolvers/mutations/users/setRoles.d.ts +1 -1
- package/lib/resolvers/mutations/users/setRoles.js +8 -1
- package/lib/resolvers/queries/events/registeredEventTypes.d.ts +1 -1
- package/lib/resolvers/queries/events/registeredEventTypes.js +1 -1
- package/lib/resolvers/queries/index.d.ts +71 -71
- package/lib/resolvers/queries/index.js +1 -1
- package/lib/resolvers/type/index.d.ts +0 -1
- package/lib/resolvers/type/login-method-response-types.js +2 -1
- package/lib/resolvers/type/order/order-delivery-pickup-types.d.ts +0 -1
- package/lib/resolvers/type/order/order-delivery-pickup-types.js +0 -7
- package/lib/resolvers/type/shop-types.js +1 -1
- package/lib/resolvers/type/user-types.js +3 -3
- package/lib/roles/all.js +5 -3
- package/lib/roles/index.d.ts +4 -11
- package/lib/roles/index.js +16 -11
- package/lib/roles/loggedIn.js +2 -0
- package/lib/schema/mutation.js +6 -44
- package/lib/schema/query.js +5 -5
- package/lib/schema/types/index.js +2 -2
- package/lib/schema/types/order/delivery.js +0 -2
- package/package.json +13 -23
package/lib/acl.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type { CheckPermissionArgs } from '@unchainedshop/roles';
|
|
|
2
2
|
export declare const ensureActionExists: (action: any, userOptions: any) => void;
|
|
3
3
|
export declare const ensureIsFunction: (fn: any, action: any, options: any, key: any) => void;
|
|
4
4
|
declare const checkAction: (context: any, action: any, args?: CheckPermissionArgs, options?: any) => Promise<void>;
|
|
5
|
-
declare const checkResolver: (action: any, userOptions?: any) => (fn: any, name?: string) => (root: any, params: any, context: any,
|
|
5
|
+
declare const checkResolver: (action: any, userOptions?: any) => (fn: any, name?: string) => (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
6
6
|
declare const checkTypeResolver: (action: any, key: any) => (obj: any, params: any, context: any) => Promise<any>;
|
|
7
7
|
declare const resolverDecorator: (action: any, userOptions?: any) => (target: any, key: any, descriptor: any) => {
|
|
8
8
|
configurable: boolean;
|
|
9
|
-
get(): (root: any, params: any, context: any,
|
|
9
|
+
get(): (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
10
10
|
};
|
|
11
11
|
export default resolverDecorator;
|
|
12
12
|
export { resolverDecorator, checkResolver, checkTypeResolver, checkAction };
|
package/lib/acl.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { emit } from '@unchainedshop/events';
|
|
2
2
|
import { NoPermissionError, PermissionSystemError } from "./errors.js";
|
|
3
|
+
import { API_EVENTS } from "./events.js";
|
|
4
|
+
const SENSITIVE_ACTION_PREFIXES = [
|
|
5
|
+
'manageUser',
|
|
6
|
+
'login',
|
|
7
|
+
'logout',
|
|
8
|
+
'reset',
|
|
9
|
+
'forgot',
|
|
10
|
+
'impersonate',
|
|
11
|
+
'updateUser',
|
|
12
|
+
'createUser',
|
|
13
|
+
];
|
|
3
14
|
const defaultOptions = {
|
|
4
15
|
showKey: true,
|
|
5
16
|
mapArgs: (...args) => args,
|
|
@@ -19,11 +30,27 @@ export const ensureIsFunction = (fn, action, options, key) => {
|
|
|
19
30
|
});
|
|
20
31
|
}
|
|
21
32
|
};
|
|
33
|
+
const isSensitiveAction = (action) => {
|
|
34
|
+
return SENSITIVE_ACTION_PREFIXES.some((prefix) => action.startsWith(prefix));
|
|
35
|
+
};
|
|
22
36
|
const checkAction = async (context, action, args = emptyArray, options = emptyObject) => {
|
|
23
37
|
const { key } = options || emptyObject;
|
|
24
|
-
const hasPermission = await
|
|
25
|
-
if (hasPermission)
|
|
38
|
+
const hasPermission = await context.roles.userHasPermission(context, action, args);
|
|
39
|
+
if (hasPermission) {
|
|
40
|
+
if (isSensitiveAction(action)) {
|
|
41
|
+
await emit(API_EVENTS.ACL_GRANTED_SENSITIVE, {
|
|
42
|
+
userId: context.userId,
|
|
43
|
+
action,
|
|
44
|
+
key,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
26
47
|
return;
|
|
48
|
+
}
|
|
49
|
+
await emit(API_EVENTS.ACL_DENIED, {
|
|
50
|
+
userId: context.userId,
|
|
51
|
+
action,
|
|
52
|
+
key,
|
|
53
|
+
});
|
|
27
54
|
const keyText = key && key !== '' ? ` in "${key}"` : '';
|
|
28
55
|
throw new NoPermissionError({
|
|
29
56
|
userId: context.userId,
|
|
@@ -36,11 +63,12 @@ const wrapFunction = (fn, name, action, userOptions) => {
|
|
|
36
63
|
const key = name || fn.name;
|
|
37
64
|
const options = { ...defaultOptions, ...userOptions };
|
|
38
65
|
ensureIsFunction(fn, action, options, key);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
66
|
+
return async (root, params, context, ...other) => {
|
|
67
|
+
const args = options.mapArgs(root, params, ...other);
|
|
68
|
+
await checkAction(context, action, args, {
|
|
69
|
+
key: options.showKey ? key : '',
|
|
70
|
+
});
|
|
71
|
+
return fn(root, params, context, ...other);
|
|
44
72
|
};
|
|
45
73
|
};
|
|
46
74
|
const checkResolver = (action, userOptions) => {
|
package/lib/api-index.d.ts
CHANGED
|
@@ -8,9 +8,11 @@ export * from './loaders/index.ts';
|
|
|
8
8
|
export * from './errors.ts';
|
|
9
9
|
export * as acl from './acl.ts';
|
|
10
10
|
export * as roles from './roles/index.ts';
|
|
11
|
+
export type { OIDCProviderConfig, AuthConfig } from './auth.ts';
|
|
11
12
|
export { createContextResolver, getCurrentContextResolver, setCurrentContextResolver };
|
|
13
|
+
import type { RolesInterface } from '@unchainedshop/roles';
|
|
12
14
|
export type UnchainedServerOptions = {
|
|
13
|
-
roles?:
|
|
15
|
+
roles?: RolesInterface;
|
|
14
16
|
adminUiConfig?: AdminUiConfig;
|
|
15
17
|
unchainedAPI: UnchainedCore;
|
|
16
18
|
context?: (defaultResolver: UnchainedContextResolver) => UnchainedContextResolver;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
import mime from 'mime/lite';
|
|
3
3
|
import { createLogger } from '@unchainedshop/logger';
|
|
4
4
|
const logger = createLogger('unchained:api:mcp');
|
|
@@ -12,9 +12,9 @@ try {
|
|
|
12
12
|
catch {
|
|
13
13
|
}
|
|
14
14
|
const inputSchema = z.object({
|
|
15
|
-
prompt: z.string().
|
|
15
|
+
prompt: z.string().describe('The prompt to generate the image from'),
|
|
16
16
|
size: z
|
|
17
|
-
.
|
|
17
|
+
.enum([
|
|
18
18
|
'512x512',
|
|
19
19
|
'768x768',
|
|
20
20
|
'1024x1024',
|
|
@@ -26,8 +26,9 @@ const inputSchema = z.object({
|
|
|
26
26
|
'1120x640',
|
|
27
27
|
'1344x768',
|
|
28
28
|
'1792x1024',
|
|
29
|
-
])
|
|
30
|
-
.
|
|
29
|
+
])
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('The size of the image (default is 1024x1024).'),
|
|
31
32
|
});
|
|
32
33
|
const generateImageHandler = (req) => ({ model, uploadUrl = `${process.env.ROOT_URL}/temp-upload`, }) => {
|
|
33
34
|
return tool({
|
package/lib/context.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { UnchainedCore } from '@unchainedshop/core';
|
|
2
|
+
import type { RolesInterface } from '@unchainedshop/roles';
|
|
2
3
|
import { type UnchainedLoaders } from './loaders/index.ts';
|
|
3
4
|
import { type UnchainedLocaleContext } from './locale-context.ts';
|
|
4
5
|
import type { UnchainedServerOptions } from './api-index.ts';
|
|
5
6
|
import type { User } from '@unchainedshop/core-users';
|
|
7
|
+
export declare const IN_LOGIN_RESPONSE: unique symbol;
|
|
6
8
|
export type LoginFn = (user: User, options?: {
|
|
7
9
|
impersonator?: User;
|
|
8
10
|
}) => Promise<{
|
|
@@ -16,6 +18,7 @@ export interface UnchainedUserContext {
|
|
|
16
18
|
userId?: string;
|
|
17
19
|
impersonatorId?: string;
|
|
18
20
|
accessToken?: string;
|
|
21
|
+
tokenVersion?: number;
|
|
19
22
|
user?: User;
|
|
20
23
|
}
|
|
21
24
|
export interface CustomAdminUiProperties {
|
|
@@ -37,7 +40,7 @@ export interface UnchainedHTTPServerContext {
|
|
|
37
40
|
}
|
|
38
41
|
export type Context = UnchainedCore & {
|
|
39
42
|
version?: string;
|
|
40
|
-
roles?:
|
|
43
|
+
roles?: RolesInterface;
|
|
41
44
|
adminUiConfig?: AdminUiConfig;
|
|
42
45
|
loaders: UnchainedLoaders;
|
|
43
46
|
} & UnchainedUserContext & UnchainedLocaleContext & UnchainedHTTPServerContext;
|
package/lib/context.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import instantiateLoaders, {} from "./loaders/index.js";
|
|
2
2
|
import { getLocaleContext } from "./locale-context.js";
|
|
3
|
+
export const IN_LOGIN_RESPONSE = Symbol('inLoginMethodResponse');
|
|
3
4
|
const { npm_package_version, UNCHAINED_API_VERSION } = process.env;
|
|
4
5
|
let context;
|
|
5
6
|
export const getCurrentContextResolver = () => context;
|
|
6
7
|
export const setCurrentContextResolver = (newContext) => {
|
|
7
8
|
context = newContext;
|
|
8
9
|
};
|
|
9
|
-
export const createContextResolver = (unchainedAPI, unchainedConfig) => async ({ getHeader, setHeader, remoteAddress, remotePort, userId, impersonatorId, accessToken, login, logout, }) => {
|
|
10
|
+
export const createContextResolver = (unchainedAPI, unchainedConfig) => async ({ getHeader, setHeader, remoteAddress, remotePort, userId, impersonatorId, accessToken, tokenVersion, login, logout, }) => {
|
|
10
11
|
const abstractHttpServerContext = { remoteAddress, remotePort, getHeader, setHeader };
|
|
11
12
|
const loaders = instantiateLoaders(unchainedAPI);
|
|
12
13
|
const localeContext = await getLocaleContext(abstractHttpServerContext, unchainedAPI);
|
|
@@ -21,8 +22,13 @@ export const createContextResolver = (unchainedAPI, unchainedConfig) => async ({
|
|
|
21
22
|
if (userId && !userContext.userId) {
|
|
22
23
|
const user = await unchainedAPI.modules.users.findUserById(userId);
|
|
23
24
|
if (user) {
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const userTokenVersion = user.tokenVersion ?? 1;
|
|
26
|
+
if (tokenVersion !== undefined && tokenVersion !== userTokenVersion) {
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
userContext.user = user;
|
|
30
|
+
userContext.userId = user._id;
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
34
|
return {
|
package/lib/events.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const API_EVENTS: {
|
|
2
2
|
readonly API_LOGIN_TOKEN_CREATED: "API_LOGIN_TOKEN_CREATED";
|
|
3
3
|
readonly API_LOGOUT: "API_LOGOUT";
|
|
4
|
+
readonly ACL_DENIED: "ACL_DENIED";
|
|
5
|
+
readonly ACL_GRANTED_SENSITIVE: "ACL_GRANTED_SENSITIVE";
|
|
4
6
|
};
|
|
5
7
|
export type API_EVENTS = (typeof API_EVENTS)[keyof typeof API_EVENTS];
|
package/lib/events.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import initMCPServer from "../mcp/index.js";
|
|
1
2
|
import { createLogger } from '@unchainedshop/logger';
|
|
2
3
|
const logger = createLogger('unchained:api:mcp');
|
|
3
4
|
let StreamableHTTPServerTransport;
|
|
@@ -34,7 +35,6 @@ const handlePostRequest = async (req, res) => {
|
|
|
34
35
|
}
|
|
35
36
|
};
|
|
36
37
|
const roles = req.unchainedContext.user?.roles || [];
|
|
37
|
-
const { default: initMCPServer } = await import("../mcp/index.js");
|
|
38
38
|
const server = initMCPServer(new McpServer({
|
|
39
39
|
name: 'Unchained MCP Server',
|
|
40
40
|
version: '1.0.0',
|
package/lib/express/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import e from 'express';
|
|
2
2
|
import type { YogaServerInstance } from 'graphql-yoga';
|
|
3
|
-
import { mongodb } from '@unchainedshop/mongodb';
|
|
4
3
|
import type { UnchainedCore } from '@unchainedshop/core';
|
|
4
|
+
import type { AuthConfig } from '../auth.ts';
|
|
5
5
|
import type { ChatConfiguration } from '../chat/utils.ts';
|
|
6
6
|
import { connectChat } from './chatHandler.ts';
|
|
7
7
|
export interface AdminUIRouterOptions {
|
|
@@ -9,17 +9,14 @@ export interface AdminUIRouterOptions {
|
|
|
9
9
|
enabled?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export declare const adminUIRouter: (enabled?: boolean) => import("express-serve-static-core").Router;
|
|
12
|
-
export declare const connect: (expressApp: e.Express, { graphqlHandler,
|
|
12
|
+
export declare const connect: (expressApp: e.Express, { graphqlHandler, unchainedAPI, }: {
|
|
13
13
|
graphqlHandler: YogaServerInstance<any, any>;
|
|
14
|
-
db: mongodb.Db;
|
|
15
14
|
unchainedAPI: UnchainedCore;
|
|
16
|
-
}, { allowRemoteToLocalhostSecureCookies, adminUI, chat,
|
|
15
|
+
}, { allowRemoteToLocalhostSecureCookies, adminUI, chat, authConfig, trustProxy, }?: {
|
|
17
16
|
allowRemoteToLocalhostSecureCookies?: boolean;
|
|
18
17
|
adminUI?: boolean | Omit<AdminUIRouterOptions, "enabled">;
|
|
19
18
|
chat?: ChatConfiguration;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}) => void;
|
|
24
|
-
export declare const expressRouter: (enabled?: boolean) => import("express-serve-static-core").Router;
|
|
19
|
+
authConfig?: AuthConfig;
|
|
20
|
+
trustProxy?: boolean;
|
|
21
|
+
}) => Promise<void>;
|
|
25
22
|
export { connectChat };
|
package/lib/express/index.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import e from 'express';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import MongoStore from "../mongo-store.js";
|
|
5
|
-
import { Passport } from 'passport';
|
|
6
|
-
import { mongodb } from '@unchainedshop/mongodb';
|
|
7
|
-
import { emit } from '@unchainedshop/events';
|
|
2
|
+
import cookieParser from 'cookie-parser';
|
|
3
|
+
import { pluginRegistry } from '@unchainedshop/core';
|
|
8
4
|
import { getCurrentContextResolver } from "../context.js";
|
|
9
|
-
import
|
|
10
|
-
import createERCMetadataMiddleware from "./createERCMetadataMiddleware.js";
|
|
11
|
-
import createTempUploadMiddleware from "./createTempUploadMiddleware.js";
|
|
5
|
+
import { createAuthContext } from "../middleware/createAuthMiddleware.js";
|
|
12
6
|
import createMCPMiddleware from "./createMCPMiddleware.js";
|
|
13
|
-
import { API_EVENTS } from "../events.js";
|
|
14
7
|
import { connectChat } from "./chatHandler.js";
|
|
8
|
+
import { mountRoutes } from "./mountRoutes.js";
|
|
9
|
+
import { createBackchannelLogoutRoute } from "../handlers/createBackchannelLogoutHandler.js";
|
|
15
10
|
export const adminUIRouter = (enabled = true) => {
|
|
16
11
|
const router = e.Router();
|
|
17
12
|
const staticURL = import.meta.resolve('@unchainedshop/admin-ui');
|
|
@@ -24,87 +19,53 @@ export const adminUIRouter = (enabled = true) => {
|
|
|
24
19
|
}
|
|
25
20
|
return router;
|
|
26
21
|
};
|
|
27
|
-
const resolveUserRemoteAddress = (req) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
req.
|
|
22
|
+
const resolveUserRemoteAddress = (req, trustProxy = false) => {
|
|
23
|
+
let remoteAddress;
|
|
24
|
+
if (trustProxy) {
|
|
25
|
+
const forwardedFor = req.headers['x-forwarded-for'];
|
|
26
|
+
const forwardedIps = forwardedFor?.split(',').map((ip) => ip.trim());
|
|
27
|
+
remoteAddress =
|
|
28
|
+
req.headers['x-real-ip'] ||
|
|
29
|
+
forwardedIps?.[forwardedIps.length - 1] ||
|
|
30
|
+
req.socket?.remoteAddress;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
remoteAddress = req.socket?.remoteAddress;
|
|
34
|
+
}
|
|
31
35
|
const remotePort = req.socket?.remotePort;
|
|
32
36
|
return { remoteAddress, remotePort };
|
|
33
37
|
};
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const { BULK_IMPORT_API_PATH = '/bulk-import', ERC_METADATA_API_PATH = '/erc-metadata', TEMP_UPLOAD_API_PATH = '/temp-upload', MCP_API_PATH = '/mcp', GRAPHQL_API_PATH = '/graphql', UNCHAINED_COOKIE_NAME = 'unchained_token', UNCHAINED_COOKIE_PATH = '/', UNCHAINED_COOKIE_DOMAIN, UNCHAINED_COOKIE_SAMESITE = 'none', UNCHAINED_COOKIE_INSECURE, } = process.env;
|
|
37
|
-
const addContext = async function middlewareWithContext(req, res, next) {
|
|
38
|
+
const { MCP_API_PATH = '/mcp' } = process.env;
|
|
39
|
+
const createAddContextMiddleware = (authConfig, trustProxy = false) => async function middlewareWithContext(req, res, next) {
|
|
38
40
|
try {
|
|
39
41
|
const setHeader = (key, value) => res.setHeader(key, value);
|
|
40
42
|
const getHeader = (key) => req.headers[key];
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
const tokenObject = {
|
|
55
|
-
_id: req.sessionID,
|
|
56
|
-
userId: user._id,
|
|
57
|
-
tokenExpires: new Date(req.session?.cookie._expires),
|
|
58
|
-
};
|
|
59
|
-
await emit(API_EVENTS.API_LOGIN_TOKEN_CREATED, tokenObject);
|
|
60
|
-
user._inLoginMethodResponse = true;
|
|
61
|
-
return { user, ...tokenObject };
|
|
62
|
-
};
|
|
63
|
-
const logout = async (sessionId) => {
|
|
64
|
-
const { user } = req;
|
|
65
|
-
if (!user)
|
|
66
|
-
return false;
|
|
67
|
-
const currentSessionId = req.sessionID;
|
|
68
|
-
const targetSessionId = sessionId || currentSessionId;
|
|
69
|
-
const tokenObject = {
|
|
70
|
-
_id: targetSessionId,
|
|
71
|
-
userId: user._id,
|
|
72
|
-
};
|
|
73
|
-
if (sessionId && sessionId !== currentSessionId) {
|
|
74
|
-
await new Promise((resolve, reject) => {
|
|
75
|
-
req.sessionStore.destroy(sessionId, (error) => {
|
|
76
|
-
if (error) {
|
|
77
|
-
return reject(error);
|
|
78
|
-
}
|
|
79
|
-
return resolve();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
await new Promise((resolve, reject) => {
|
|
85
|
-
req.logout((error, result) => {
|
|
86
|
-
if (error) {
|
|
87
|
-
return reject(error);
|
|
88
|
-
}
|
|
89
|
-
req.session.impersonatorId = null;
|
|
90
|
-
return resolve(result);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
await emit(API_EVENTS.API_LOGOUT, tokenObject);
|
|
95
|
-
return true;
|
|
43
|
+
const getCookie = (name) => req.cookies?.[name];
|
|
44
|
+
const setCookie = (name, value, options) => res.cookie(name, value, options);
|
|
45
|
+
const clearCookie = (name, options) => res.clearCookie(name, { ...options, maxAge: 0 });
|
|
46
|
+
const { remoteAddress, remotePort } = resolveUserRemoteAddress(req, trustProxy);
|
|
47
|
+
const authContextParams = {
|
|
48
|
+
setHeader,
|
|
49
|
+
getHeader,
|
|
50
|
+
getCookie,
|
|
51
|
+
setCookie,
|
|
52
|
+
clearCookie,
|
|
53
|
+
remoteAddress,
|
|
54
|
+
remotePort,
|
|
96
55
|
};
|
|
97
|
-
const
|
|
56
|
+
const authContext = await createAuthContext(authContextParams, authConfig);
|
|
57
|
+
const context = getCurrentContextResolver();
|
|
98
58
|
req.unchainedContext = await context({
|
|
99
59
|
setHeader,
|
|
100
60
|
getHeader,
|
|
101
61
|
remoteAddress,
|
|
102
62
|
remotePort,
|
|
103
|
-
login,
|
|
104
|
-
logout,
|
|
105
|
-
accessToken,
|
|
106
|
-
userId:
|
|
107
|
-
impersonatorId:
|
|
63
|
+
login: authContext.login,
|
|
64
|
+
logout: authContext.logout,
|
|
65
|
+
accessToken: authContext.accessToken,
|
|
66
|
+
userId: authContext.userId,
|
|
67
|
+
impersonatorId: authContext.impersonatorId,
|
|
68
|
+
tokenVersion: authContext.tokenVersion,
|
|
108
69
|
}, req, res);
|
|
109
70
|
next();
|
|
110
71
|
}
|
|
@@ -112,8 +73,12 @@ const addContext = async function middlewareWithContext(req, res, next) {
|
|
|
112
73
|
next(error);
|
|
113
74
|
}
|
|
114
75
|
};
|
|
115
|
-
export const connect = (expressApp, { graphqlHandler,
|
|
76
|
+
export const connect = async (expressApp, { graphqlHandler, unchainedAPI, }, { allowRemoteToLocalhostSecureCookies = false, adminUI = false, chat, authConfig, trustProxy = false, } = {}) => {
|
|
116
77
|
if (allowRemoteToLocalhostSecureCookies) {
|
|
78
|
+
if (process.env.NODE_ENV === 'production') {
|
|
79
|
+
throw new Error('allowRemoteToLocalhostSecureCookies is not allowed in production. ' +
|
|
80
|
+
'Configure a proper CORS policy with specific allowed origins instead.');
|
|
81
|
+
}
|
|
117
82
|
expressApp.set('trust proxy', 1);
|
|
118
83
|
expressApp.use((req, res, next) => {
|
|
119
84
|
req.headers['x-forwarded-proto'] = 'https';
|
|
@@ -125,59 +90,21 @@ export const connect = (expressApp, { graphqlHandler, db, unchainedAPI, }, { all
|
|
|
125
90
|
next();
|
|
126
91
|
});
|
|
127
92
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
132
|
-
passport.deserializeUser(function deserialize(_id, done) {
|
|
133
|
-
done(null, { _id });
|
|
134
|
-
});
|
|
135
|
-
const name = UNCHAINED_COOKIE_NAME;
|
|
136
|
-
const domain = UNCHAINED_COOKIE_DOMAIN;
|
|
137
|
-
const path = UNCHAINED_COOKIE_PATH;
|
|
138
|
-
const secure = UNCHAINED_COOKIE_INSECURE ? false : true;
|
|
139
|
-
const sameSite = ({
|
|
140
|
-
none: 'none',
|
|
141
|
-
lax: 'lax',
|
|
142
|
-
strict: 'strict',
|
|
143
|
-
'1': true,
|
|
144
|
-
'0': false,
|
|
145
|
-
}[UNCHAINED_COOKIE_SAMESITE?.trim()?.toLowerCase()] || false);
|
|
146
|
-
expressApp.use(session({
|
|
147
|
-
secret: process.env.UNCHAINED_TOKEN_SECRET,
|
|
148
|
-
store: MongoStore.create({
|
|
149
|
-
client: db.client,
|
|
150
|
-
dbName: db.databaseName,
|
|
151
|
-
collectionName: 'sessions',
|
|
152
|
-
touchAfter: 24 * 3600,
|
|
153
|
-
}),
|
|
154
|
-
name,
|
|
155
|
-
saveUninitialized: false,
|
|
156
|
-
resave: false,
|
|
157
|
-
cookie: {
|
|
158
|
-
domain,
|
|
159
|
-
path,
|
|
160
|
-
sameSite,
|
|
161
|
-
secure,
|
|
162
|
-
httpOnly: true,
|
|
163
|
-
maxAge: 1000 * 60 * 60 * 24 * 7,
|
|
164
|
-
},
|
|
165
|
-
}), passport.initialize(), passport.session(), addContext);
|
|
166
|
-
expressApp.use(GRAPHQL_API_PATH, graphqlHandler.handle);
|
|
167
|
-
expressApp.use(ERC_METADATA_API_PATH, createERCMetadataMiddleware);
|
|
168
|
-
expressApp.use(BULK_IMPORT_API_PATH, createBulkImportMiddleware);
|
|
169
|
-
expressApp.use(TEMP_UPLOAD_API_PATH, upload.any(), createTempUploadMiddleware);
|
|
93
|
+
expressApp.use(cookieParser());
|
|
94
|
+
expressApp.use(createAddContextMiddleware(authConfig, trustProxy || allowRemoteToLocalhostSecureCookies));
|
|
95
|
+
expressApp.use(graphqlHandler.graphqlEndpoint, graphqlHandler.handle);
|
|
170
96
|
expressApp.use(MCP_API_PATH, e.json({ limit: '10mb' }));
|
|
171
97
|
expressApp.use(MCP_API_PATH, createMCPMiddleware);
|
|
172
98
|
if (chat) {
|
|
173
99
|
connectChat(expressApp, chat);
|
|
174
100
|
}
|
|
175
|
-
|
|
176
|
-
|
|
101
|
+
const routes = pluginRegistry.getRoutes();
|
|
102
|
+
if (authConfig?.oidcProviders?.length) {
|
|
103
|
+
routes.push(createBackchannelLogoutRoute(authConfig.oidcProviders));
|
|
177
104
|
}
|
|
105
|
+
mountRoutes(expressApp, unchainedAPI, routes);
|
|
178
106
|
if (adminUI) {
|
|
179
107
|
expressApp.use(typeof adminUI === 'object' ? adminUI.prefix : '/', adminUIRouter(true));
|
|
180
108
|
}
|
|
181
109
|
};
|
|
182
|
-
export const expressRouter = adminUIRouter;
|
|
183
110
|
export { connectChat };
|
package/lib/fastify/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { AuthConfig } from '../auth.ts';
|
|
1
2
|
import type { YogaServerInstance } from 'graphql-yoga';
|
|
2
|
-
import type { mongodb } from '@unchainedshop/mongodb';
|
|
3
3
|
import type { UnchainedCore } from '@unchainedshop/core';
|
|
4
4
|
import type { FastifyBaseLogger, FastifyInstance, FastifyPluginAsync } from 'fastify';
|
|
5
5
|
import { connectChat } from './chatHandler.ts';
|
|
@@ -9,18 +9,15 @@ export interface AdminUIRouterOptions {
|
|
|
9
9
|
enabled?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export declare const unchainedLogger: (prefix: string) => FastifyBaseLogger;
|
|
12
|
-
export declare const connect: (fastify: FastifyInstance, { graphqlHandler,
|
|
12
|
+
export declare const connect: (fastify: FastifyInstance, { graphqlHandler, unchainedAPI, }: {
|
|
13
13
|
graphqlHandler: YogaServerInstance<any, any>;
|
|
14
|
-
db: mongodb.Db;
|
|
15
14
|
unchainedAPI: UnchainedCore;
|
|
16
|
-
}, { allowRemoteToLocalhostSecureCookies, adminUI, chat,
|
|
15
|
+
}, { allowRemoteToLocalhostSecureCookies, adminUI, chat, authConfig, trustProxy, }?: {
|
|
17
16
|
allowRemoteToLocalhostSecureCookies?: boolean;
|
|
18
17
|
adminUI?: boolean | Omit<AdminUIRouterOptions, "enabled">;
|
|
19
18
|
chat?: ChatConfiguration;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}) => void;
|
|
19
|
+
authConfig?: AuthConfig;
|
|
20
|
+
trustProxy?: boolean;
|
|
21
|
+
}) => Promise<void>;
|
|
24
22
|
export declare const adminUIRouter: FastifyPluginAsync<AdminUIRouterOptions>;
|
|
25
|
-
export declare const fastifyRouter: FastifyPluginAsync<AdminUIRouterOptions>;
|
|
26
23
|
export { connectChat };
|