|
@@ -1,11 +1,7 @@
|
|
import config from "config";
|
|
import config from "config";
|
|
-// import { createClient, RedisClientType } from "redis";
|
|
|
|
import mongoose, {
|
|
import mongoose, {
|
|
Connection,
|
|
Connection,
|
|
isObjectIdOrHexString,
|
|
isObjectIdOrHexString,
|
|
- MongooseDefaultQueryMiddleware,
|
|
|
|
- MongooseDistinctQueryMiddleware,
|
|
|
|
- MongooseQueryOrDocumentMiddleware,
|
|
|
|
SchemaTypes,
|
|
SchemaTypes,
|
|
Types
|
|
Types
|
|
} from "mongoose";
|
|
} from "mongoose";
|
|
@@ -20,104 +16,13 @@ import BaseModule, { ModuleStatus } from "@/BaseModule";
|
|
import { UniqueMethods } from "@/types/Modules";
|
|
import { UniqueMethods } from "@/types/Modules";
|
|
import { AnyModel, Models } from "@/types/Models";
|
|
import { AnyModel, Models } from "@/types/Models";
|
|
import { Schemas } from "@/types/Schemas";
|
|
import { Schemas } from "@/types/Schemas";
|
|
-import JobQueue from "@/JobQueue";
|
|
|
|
import EventsModule from "./EventsModule";
|
|
import EventsModule from "./EventsModule";
|
|
|
|
|
|
-/**
|
|
|
|
- * Experimental: function to get all nested keys from a MongoDB query object
|
|
|
|
- */
|
|
|
|
-function getAllKeys(obj: object) {
|
|
|
|
- const keys: string[] = [];
|
|
|
|
-
|
|
|
|
- function processObject(obj: object, parentKey = "") {
|
|
|
|
- let returnChanged = false;
|
|
|
|
-
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- for (let key in obj) {
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- if (obj.hasOwnProperty(key)) {
|
|
|
|
- if (key.startsWith("$")) {
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- // @ts-ignore
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- processNestedObject(obj[key], parentKey); // Process nested keys without including the current key
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- continue; // Skip the current key
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- const currentKey = parentKey ? `${parentKey}.${key}` : key;
|
|
|
|
-
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- // @ts-ignore
|
|
|
|
- if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- // @ts-ignore
|
|
|
|
- if (Array.isArray(obj[key])) {
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- // @ts-ignore
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- if (processArray(obj[key], currentKey)) {
|
|
|
|
- returnChanged = true;
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- // @ts-ignore
|
|
|
|
- else if (processObject(obj[key], currentKey)) {
|
|
|
|
- returnChanged = true;
|
|
|
|
- // eslint-disable-next-line
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- keys.push(currentKey);
|
|
|
|
-
|
|
|
|
- returnChanged = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return returnChanged;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function processArray(arr: Array<any>, parentKey: string) {
|
|
|
|
- let returnChanged = false;
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < arr.length; i += 1) {
|
|
|
|
- const currentKey = parentKey;
|
|
|
|
-
|
|
|
|
- if (typeof arr[i] === "object" && arr[i] !== null) {
|
|
|
|
- if (Array.isArray(arr[i])) {
|
|
|
|
- if (processArray(arr[i], currentKey)) returnChanged = true;
|
|
|
|
- } else if (processObject(arr[i], currentKey))
|
|
|
|
- returnChanged = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return returnChanged;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function processNestedObject(obj: object, parentKey: string) {
|
|
|
|
- if (typeof obj === "object" && obj !== null) {
|
|
|
|
- if (Array.isArray(obj)) {
|
|
|
|
- processArray(obj, parentKey);
|
|
|
|
- } else {
|
|
|
|
- processObject(obj, parentKey);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- processObject(obj);
|
|
|
|
- return keys;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
export class DataModule extends BaseModule {
|
|
export class DataModule extends BaseModule {
|
|
private _models?: Models;
|
|
private _models?: Models;
|
|
|
|
|
|
private _mongoConnection?: Connection;
|
|
private _mongoConnection?: Connection;
|
|
|
|
|
|
- // private _redisClient?: RedisClientType;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Data Module
|
|
* Data Module
|
|
*/
|
|
*/
|
|
@@ -147,31 +52,6 @@ export class DataModule extends BaseModule {
|
|
|
|
|
|
await this._defineModelJobs();
|
|
await this._defineModelJobs();
|
|
|
|
|
|
- // @ts-ignore
|
|
|
|
- // this._redisClient = createClient({ ...config.get("redis") });
|
|
|
|
- //
|
|
|
|
- // await this._redisClient.connect();
|
|
|
|
- //
|
|
|
|
- // const redisConfigResponse = await this._redisClient.sendCommand([
|
|
|
|
- // "CONFIG",
|
|
|
|
- // "GET",
|
|
|
|
- // "notify-keyspace-events"
|
|
|
|
- // ]);
|
|
|
|
- //
|
|
|
|
- // if (
|
|
|
|
- // !(
|
|
|
|
- // Array.isArray(redisConfigResponse) &&
|
|
|
|
- // redisConfigResponse[1] === "xE"
|
|
|
|
- // )
|
|
|
|
- // )
|
|
|
|
- // throw new Error(
|
|
|
|
- // `notify-keyspace-events is NOT configured correctly! It is set to: ${
|
|
|
|
- // (Array.isArray(redisConfigResponse) &&
|
|
|
|
- // redisConfigResponse[1]) ||
|
|
|
|
- // "unknown"
|
|
|
|
- // }`
|
|
|
|
- // );
|
|
|
|
-
|
|
|
|
await super._started();
|
|
await super._started();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -180,7 +60,6 @@ export class DataModule extends BaseModule {
|
|
*/
|
|
*/
|
|
public override async shutdown() {
|
|
public override async shutdown() {
|
|
await super.shutdown();
|
|
await super.shutdown();
|
|
- // if (this._redisClient) await this._redisClient.quit();
|
|
|
|
patchEventEmitter.removeAllListeners();
|
|
patchEventEmitter.removeAllListeners();
|
|
if (this._mongoConnection) await this._mongoConnection.close();
|
|
if (this._mongoConnection) await this._mongoConnection.close();
|
|
await this._stopped();
|
|
await this._stopped();
|
|
@@ -218,95 +97,6 @@ export class DataModule extends BaseModule {
|
|
ModelName extends keyof Models,
|
|
ModelName extends keyof Models,
|
|
SchemaType extends Schemas[keyof ModelName]
|
|
SchemaType extends Schemas[keyof ModelName]
|
|
>(modelName: ModelName, schema: SchemaType) {
|
|
>(modelName: ModelName, schema: SchemaType) {
|
|
- const methods: string[] = [
|
|
|
|
- "aggregate",
|
|
|
|
- "count",
|
|
|
|
- "countDocuments",
|
|
|
|
- "deleteOne",
|
|
|
|
- "deleteMany",
|
|
|
|
- "estimatedDocumentCount",
|
|
|
|
- "find",
|
|
|
|
- "findOne",
|
|
|
|
- "findOneAndDelete",
|
|
|
|
- "findOneAndRemove",
|
|
|
|
- "findOneAndReplace",
|
|
|
|
- "findOneAndUpdate",
|
|
|
|
- // "init",
|
|
|
|
- "insertMany",
|
|
|
|
- "remove",
|
|
|
|
- "replaceOne",
|
|
|
|
- "save",
|
|
|
|
- "update",
|
|
|
|
- "updateOne",
|
|
|
|
- "updateMany"
|
|
|
|
- // "validate"
|
|
|
|
- ];
|
|
|
|
-
|
|
|
|
- methods.forEach(method => {
|
|
|
|
- // NOTE: some Mongo selectors may also search through linked documents. Prevent that
|
|
|
|
- schema.pre(method, async function () {
|
|
|
|
- // console.log(`Pre-${method}! START`);
|
|
|
|
-
|
|
|
|
- if (
|
|
|
|
- this.options?.userContext &&
|
|
|
|
- ["find", "update", "deleteOne", "save"].indexOf(method) ===
|
|
|
|
- -1
|
|
|
|
- )
|
|
|
|
- throw new Error("Method not allowed");
|
|
|
|
-
|
|
|
|
- // console.log(`Pre-${method}!`, this.options?.userContext);
|
|
|
|
-
|
|
|
|
- if (["find", "update", "deleteOne"].indexOf(method) !== -1) {
|
|
|
|
- const filter = this.getFilter();
|
|
|
|
- const filterKeys = getAllKeys(filter);
|
|
|
|
-
|
|
|
|
- filterKeys.forEach(filterKey => {
|
|
|
|
- const splitFilterKeys = filterKey
|
|
|
|
- .split(".")
|
|
|
|
- .reduce(
|
|
|
|
- (keys: string[], key: string) =>
|
|
|
|
- keys.length > 0
|
|
|
|
- ? [
|
|
|
|
- ...keys,
|
|
|
|
- `${
|
|
|
|
- keys[keys.length - 1]
|
|
|
|
- }.${key}`
|
|
|
|
- ]
|
|
|
|
- : [key],
|
|
|
|
- []
|
|
|
|
- );
|
|
|
|
- splitFilterKeys.forEach(splitFilterKey => {
|
|
|
|
- const path = this.schema.path(splitFilterKey);
|
|
|
|
- if (!path) {
|
|
|
|
- throw new Error(
|
|
|
|
- "Attempted to query with non-existant property"
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- if (path.options.restricted) {
|
|
|
|
- throw new Error(
|
|
|
|
- "Attempted to query with restricted property"
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // console.log(`Pre-${method}!`, filterKeys);
|
|
|
|
-
|
|
|
|
- // Here we want to always exclude some properties depending on the model, like passwords/tokens
|
|
|
|
- this.projection({ restrictedName: 0 });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // console.log(`Pre-${method}! END`);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- schema.post(method, async docOrDocs => {
|
|
|
|
- // console.log(`Post-${method} START!`);
|
|
|
|
- // console.log(`Post-${method}!`, docOrDocs);
|
|
|
|
- // console.log(`Post-${method}!`, this);
|
|
|
|
- // console.log(`Post-${method} END!`);
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
const { enabled, eventCreated, eventUpdated, eventDeleted } =
|
|
const { enabled, eventCreated, eventUpdated, eventDeleted } =
|
|
schema.get("patchHistory") ?? {};
|
|
schema.get("patchHistory") ?? {};
|
|
|
|
|