|
|
@@ -1,5 +1,7 @@
|
|
|
import { LanguageCode } from '@vendure/common/lib/generated-types';
|
|
|
import { ID, JsonCompatible } from '@vendure/common/lib/shared-types';
|
|
|
+import { isObject } from '@vendure/common/lib/shared-utils';
|
|
|
+import { Request } from 'express';
|
|
|
import { TFunction } from 'i18next';
|
|
|
|
|
|
import { CachedSession } from '../../config/session-cache/session-cache-strategy';
|
|
|
@@ -8,6 +10,7 @@ import { Channel } from '../../entity/channel/channel.entity';
|
|
|
import { ApiType } from './get-api-type';
|
|
|
|
|
|
export type SerializedRequestContext = {
|
|
|
+ _req?: any;
|
|
|
_session: JsonCompatible<Required<CachedSession>>;
|
|
|
_apiType: ApiType;
|
|
|
_channel: JsonCompatible<Channel>;
|
|
|
@@ -45,11 +48,13 @@ export class RequestContext {
|
|
|
private readonly _authorizedAsOwnerOnly: boolean;
|
|
|
private readonly _translationFn: TFunction;
|
|
|
private readonly _apiType: ApiType;
|
|
|
+ private readonly _req?: Request;
|
|
|
|
|
|
/**
|
|
|
* @internal
|
|
|
*/
|
|
|
constructor(options: {
|
|
|
+ req?: Request;
|
|
|
apiType: ApiType;
|
|
|
channel: Channel;
|
|
|
session?: CachedSession;
|
|
|
@@ -58,7 +63,8 @@ export class RequestContext {
|
|
|
authorizedAsOwnerOnly: boolean;
|
|
|
translationFn?: TFunction;
|
|
|
}) {
|
|
|
- const { apiType, channel, session, languageCode, translationFn } = options;
|
|
|
+ const { req, apiType, channel, session, languageCode, translationFn } = options;
|
|
|
+ this._req = req;
|
|
|
this._apiType = apiType;
|
|
|
this._channel = channel;
|
|
|
this._session = session;
|
|
|
@@ -90,6 +96,7 @@ export class RequestContext {
|
|
|
*/
|
|
|
static deserialize(ctxObject: SerializedRequestContext): RequestContext {
|
|
|
return new RequestContext({
|
|
|
+ req: ctxObject._req as any,
|
|
|
apiType: ctxObject._apiType,
|
|
|
channel: new Channel(ctxObject._channel),
|
|
|
session: {
|
|
|
@@ -109,7 +116,11 @@ export class RequestContext {
|
|
|
* process, e.g. to pass it to the Worker process via the {@link WorkerService}.
|
|
|
*/
|
|
|
serialize(): SerializedRequestContext {
|
|
|
- return JSON.parse(JSON.stringify(this));
|
|
|
+ const serializableThis: any = Object.assign({}, this);
|
|
|
+ if (this._req) {
|
|
|
+ serializableThis._req = this.shallowCloneRequestObject(this._req);
|
|
|
+ }
|
|
|
+ return JSON.parse(JSON.stringify(serializableThis));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -122,10 +133,26 @@ export class RequestContext {
|
|
|
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @description
|
|
|
+ * The raw Express request object.
|
|
|
+ */
|
|
|
+ get req(): Request | undefined {
|
|
|
+ return this._req;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @description
|
|
|
+ * Signals which API this request was received by, e.g. `admin` or `shop`.
|
|
|
+ */
|
|
|
get apiType(): ApiType {
|
|
|
return this._apiType;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @description
|
|
|
+ * The active {@link Channel} of this request.
|
|
|
+ */
|
|
|
get channel(): Channel {
|
|
|
return this._channel;
|
|
|
}
|
|
|
@@ -174,4 +201,33 @@ export class RequestContext {
|
|
|
return `Translation format error: ${e.message}). Original key: ${key}`;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The Express "Request" object is huge and contains many circular
|
|
|
+ * references. We will preserve just a subset of the whole, by preserving
|
|
|
+ * only the serializable properties up to 2 levels deep.
|
|
|
+ * @private
|
|
|
+ */
|
|
|
+ private shallowCloneRequestObject(req: Request) {
|
|
|
+ function copySimpleFieldsToDepth(target: any, maxDepth: number, depth: number = 0) {
|
|
|
+ const result: any = {};
|
|
|
+ // tslint:disable-next-line:forin
|
|
|
+ for (const key in target) {
|
|
|
+ if (key === 'host' && depth === 0) {
|
|
|
+ // avoid Express "deprecated: req.host" warning
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ const val = (target as any)[key];
|
|
|
+ if (!isObject(val) && typeof val !== 'function') {
|
|
|
+ result[key] = val;
|
|
|
+ } else if (depth < maxDepth) {
|
|
|
+ depth++;
|
|
|
+ result[key] = copySimpleFieldsToDepth(val, maxDepth, depth);
|
|
|
+ depth--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ return copySimpleFieldsToDepth(req, 1);
|
|
|
+ }
|
|
|
}
|