diff --git a/app.js b/app.js index 6f195df9..5833d74c 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,5 @@ -const mongodb = require("./models/mongodb/index"); +// const mongodb = require("./models/mongodb/index"); +require("./plugins/sql/init"); const express = require("express"); const { createServer } = require("http"); const app = express(); diff --git a/config-class.js b/config-class.js index 32f14eee..d0c2158c 100644 --- a/config-class.js +++ b/config-class.js @@ -22,18 +22,18 @@ function generateUidFromGuid(iGuid) { return `2.25.${bigInteger.toString()}`; //Output the previus parsed integer as string by adding `2.25.` as prefix } -class MongoDbConfig { - constructor() { - this.dbName = env.get("MONGODB_NAME").default("raccoon").asString(); - this.hosts = env.get("MONGODB_HOSTS").required().asJsonArray(); - this.ports = env.get("MONGODB_PORTS").required().asJsonArray(); - this.user = env.get("MONGODB_USER").default("").asString(); - this.password = env.get("MONGODB_PASSWORD").default("").asString(); - this.authSource = env.get("MONGODB_AUTH_SOURCE").default("admin").asString(); - this.urlOptions = env.get("MONGODB_OPTIONS").default("").asString(); - this.isShardingMode = env.get("MONGODB_IS_SHARDING_MODE").default("false").asBool(); - } -} +/** + * @type {import("sequelize").Options} + */ +const SqlDbConfig = { + host: env.get("SQL_HOST").default("127.0.0.1").asString(), + port: env.get("SQL_PORT").default("5432").asString(), + database: env.get("SQL_DB").default("raccoon").asString(), + dialect: env.get("SQL_TYPE").default("postgres").asString(), + username: env.get("SQL_USERNAME").default("postgres").asString(), + password: env.get("SQL_PASSWORD").default("postgres").asString() +}; + class ServerConfig { constructor() { @@ -53,71 +53,71 @@ class DicomWebConfig { } } -class DicomDimseConfig { - constructor(mongodbConfig, serverConfig, dicomWebConfig) { - /** @type { MongoDbConfig } */ - this.mongodbConfig = mongodbConfig; - /** @type { ServerConfig } */ - this.serverConfig = serverConfig; - /** @type { DicomWebConfig } */ - this.dicomWebConfig = dicomWebConfig; - this.enableDimse = env.get("ENABLE_DIMSE").default("true").asBool(); - - if (this.enableDimse) { - this.dcm4cheQrscpArgv = env.get("DCM4CHE_QRSCP_COMMAND").required().asJsonArray(); - this.generateDimseJsonConfig(); - this.replacePathInArgv(); - } - } - - generateDimseJsonConfig() { - let stowUrlObj = new URL(`http://${this.serverConfig.host}`); - stowUrlObj.port = this.serverConfig.port; - stowUrlObj.pathname = "dicom-web/studies"; - - let dimseConfig = { - mongodb: { - hosts: this.mongodbConfig.hosts, - ports: this.mongodbConfig.ports, - username: this.mongodbConfig.user, - password: this.mongodbConfig.password, - authSource: this.mongodbConfig.authSource, - database: this.mongodbConfig.dbName - }, - raccoon: { - dicomStoreRoot: this.dicomWebConfig.storeRootPath, - raccoonUploadScriptPath: path.join(__dirname, "./local/dicom-uploader-stow.js"), - mode: "STOW", - stowUrl: stowUrlObj.href - } - }; - fs.writeFileSync(path.join(__dirname, "./config/raccoon-dimse-app.json"), JSON.stringify(dimseConfig)); - } +// class DicomDimseConfig { +// constructor(mongodbConfig, serverConfig, dicomWebConfig) { +// /** @type { MongoDbConfig } */ +// this.mongodbConfig = mongodbConfig; +// /** @type { ServerConfig } */ +// this.serverConfig = serverConfig; +// /** @type { DicomWebConfig } */ +// this.dicomWebConfig = dicomWebConfig; +// this.enableDimse = env.get("ENABLE_DIMSE").default("true").asBool(); + +// if (this.enableDimse) { +// this.dcm4cheQrscpArgv = env.get("DCM4CHE_QRSCP_COMMAND").required().asJsonArray(); +// this.generateDimseJsonConfig(); +// this.replacePathInArgv(); +// } +// } + +// generateDimseJsonConfig() { +// let stowUrlObj = new URL(`http://${this.serverConfig.host}`); +// stowUrlObj.port = this.serverConfig.port; +// stowUrlObj.pathname = "dicom-web/studies"; + +// let dimseConfig = { +// mongodb: { +// hosts: this.mongodbConfig.hosts, +// ports: this.mongodbConfig.ports, +// username: this.mongodbConfig.user, +// password: this.mongodbConfig.password, +// authSource: this.mongodbConfig.authSource, +// database: this.mongodbConfig.dbName +// }, +// raccoon: { +// dicomStoreRoot: this.dicomWebConfig.storeRootPath, +// raccoonUploadScriptPath: path.join(__dirname, "./local/dicom-uploader-stow.js"), +// mode: "STOW", +// stowUrl: stowUrlObj.href +// } +// }; +// fs.writeFileSync(path.join(__dirname, "./config/raccoon-dimse-app.json"), JSON.stringify(dimseConfig)); +// } - replacePathInArgv() { - for(let i = 0 ; i < this.dcm4cheQrscpArgv.length ; i++) { - this.dcm4cheQrscpArgv[i] = this.dcm4cheQrscpArgv[i].replace(/{project}/gm, __dirname); - } - } - - getPort() { - let bindArgIndex = this.dcm4cheQrscpArgv.findIndex(v => v === "-b"); - /** @type {string} */ - let bindInfo = this.dcm4cheQrscpArgv[bindArgIndex + 1]; - return bindInfo.split(":").pop(); - } - - getAeTitle() { - let bindArgIndex = this.dcm4cheQrscpArgv.findIndex(v => v === "-b"); - /** @type {string} */ - let bindInfo = this.dcm4cheQrscpArgv[bindArgIndex + 1]; - - let aeTitleAndIp = bindInfo.split(":").shift(); - let aeTitle = aeTitleAndIp.includes("@") ? aeTitleAndIp.split("@").shift() : aeTitleAndIp; - return aeTitle; - } - -} +// replacePathInArgv() { +// for(let i = 0 ; i < this.dcm4cheQrscpArgv.length ; i++) { +// this.dcm4cheQrscpArgv[i] = this.dcm4cheQrscpArgv[i].replace(/{project}/gm, __dirname); +// } +// } + +// getPort() { +// let bindArgIndex = this.dcm4cheQrscpArgv.findIndex(v => v === "-b"); +// /** @type {string} */ +// let bindInfo = this.dcm4cheQrscpArgv[bindArgIndex + 1]; +// return bindInfo.split(":").pop(); +// } + +// getAeTitle() { +// let bindArgIndex = this.dcm4cheQrscpArgv.findIndex(v => v === "-b"); +// /** @type {string} */ +// let bindInfo = this.dcm4cheQrscpArgv[bindArgIndex + 1]; + +// let aeTitleAndIp = bindInfo.split(":").shift(); +// let aeTitle = aeTitleAndIp.includes("@") ? aeTitleAndIp.split("@").shift() : aeTitleAndIp; +// return aeTitle; +// } + +// } class FhirConfig { constructor() { @@ -129,21 +129,23 @@ class FhirConfig { class RaccoonConfig { constructor() { - this.mongoDbConfig = new MongoDbConfig(); + this.sqlDbConfig = SqlDbConfig; this.serverConfig = new ServerConfig(); this.dicomWebConfig = new DicomWebConfig(); - this.dicomDimseConfig = new DicomDimseConfig(this.mongoDbConfig, this.serverConfig, this.dicomWebConfig); + // this.dicomDimseConfig = new DicomDimseConfig(this.mongoDbConfig, this.serverConfig, this.dicomWebConfig); this.fhirConfig = new FhirConfig(); /** @type {string} */ this.mediaStorageUID = generateUidFromGuid( - uuid.v5(this.mongoDbConfig.dbName, NAME_SPACE) + uuid.v5(this.sqlDbConfig.database, NAME_SPACE) ); /** @type {string} */ - this.mediaStorageID = this.mongoDbConfig.dbName; + this.mediaStorageID = this.sqlDbConfig.database; + + this.aeTitle = this.dicomWebConfig.aeTitle; + // this.aeTitle = this.dicomDimseConfig.enableDimse ? this.dicomDimseConfig.getAeTitle() : this.dicomWebConfig.aeTitle; - this.aeTitle = this.dicomDimseConfig.enableDimse ? this.dicomDimseConfig.getAeTitle() : this.dicomWebConfig.aeTitle; if (!this.aeTitle) throw new Error("Missing required config `aeTitle`"); } diff --git a/package-lock.json b/package-lock.json index 9b214fdb..def24372 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "commander": "^10.0.1", "compression": "^1.7.4", "connect-mongo": "^4.6.0", + "connect-session-sequelize": "^7.1.7", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dicom-parser": "^1.8.13", @@ -43,10 +44,13 @@ "passport": "^0.6.0", "passport-local": "^1.0.0", "path-match": "^1.2.4", + "pg": "^8.11.1", + "pg-hstore": "^2.3.4", "regexparam": "^2.0.1", "request-compose": "^2.1.6", "request-multipart": "^1.0.0", "run-script-os": "^1.1.6", + "sequelize": "^6.32.1", "sharp": "^0.30.4", "shorthash2": "^1.0.3", "uuid": "^9.0.0", @@ -1328,6 +1332,14 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -1340,6 +1352,11 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "node_modules/@types/node": { "version": "17.0.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", @@ -1351,6 +1368,11 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, + "node_modules/@types/validator": { + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -1862,6 +1884,14 @@ "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", @@ -2504,6 +2534,20 @@ "mongodb": "^4.1.0" } }, + "node_modules/connect-session-sequelize": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-7.1.7.tgz", + "integrity": "sha512-Wqq7rg0w+9bOVs6jC0nhZnssXJ3+iKNlDVWn2JfBuBPoY7oYaxzxfBKeUYrX6dHt3OWEWbZV6LJvapwi76iBQQ==", + "dependencies": { + "debug": "^4.1.1" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "sequelize": ">= 6.1.0" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -3185,6 +3229,11 @@ "node": ">=4" } }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -4472,6 +4521,14 @@ "node": ">=8" } }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6318,6 +6375,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6543,6 +6605,108 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "node_modules/pg": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.1.tgz", + "integrity": "sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.1", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + }, + "node_modules/pg-hstore": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.4.tgz", + "integrity": "sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA==", + "dependencies": { + "underscore": "^1.13.1" + }, + "engines": { + "node": ">= 0.8.x" + } + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/pgpass/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -6576,6 +6740,41 @@ "node": ">=8" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prebuild-install": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", @@ -7129,6 +7328,11 @@ "node": ">=4" } }, + "node_modules/retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, "node_modules/rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", @@ -7194,9 +7398,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -7248,6 +7452,83 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/sequelize": { + "version": "6.32.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.32.1.tgz", + "integrity": "sha512-3Iv0jruv57Y0YvcxQW7BE56O7DC1BojcfIrqh6my+IQwde+9u/YnuYHzK+8kmZLhLvaziRT1eWu38nh9yVwn/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.4", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.0", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.1", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -8026,6 +8307,11 @@ "node": ">=0.6" } }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -8142,6 +8428,11 @@ "node": ">= 0.8" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -8260,7 +8551,6 @@ "version": "13.9.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "dev": true, "engines": { "node": ">= 0.10" } @@ -8317,6 +8607,14 @@ "node": ">= 0.10.0" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -8423,7 +8721,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } @@ -9694,6 +9991,14 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "requires": { + "@types/ms": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -9706,6 +10011,11 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", "dev": true }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "@types/node": { "version": "17.0.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", @@ -9717,6 +10027,11 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, + "@types/validator": { + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==" + }, "@types/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -10124,6 +10439,11 @@ "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", @@ -10654,6 +10974,14 @@ "kruptein": "^3.0.0" } }, + "connect-session-sequelize": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-7.1.7.tgz", + "integrity": "sha512-Wqq7rg0w+9bOVs6jC0nhZnssXJ3+iKNlDVWn2JfBuBPoY7oYaxzxfBKeUYrX6dHt3OWEWbZV6LJvapwi76iBQQ==", + "requires": { + "debug": "^4.1.1" + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -11164,6 +11492,11 @@ } } }, + "dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -12153,6 +12486,11 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -13532,6 +13870,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13705,6 +14048,83 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "pg": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.1.tgz", + "integrity": "sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-cloudflare": "^1.1.1", + "pg-connection-string": "^2.6.1", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + }, + "pg-hstore": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.4.tgz", + "integrity": "sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA==", + "requires": { + "underscore": "^1.13.1" + } + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "requires": {} + }, + "pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + }, + "dependencies": { + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + } + } + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -13726,6 +14146,29 @@ "find-up": "^4.0.0" } }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "prebuild-install": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", @@ -14162,6 +14605,11 @@ "dev": true, "peer": true }, + "retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, "rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", @@ -14200,9 +14648,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -14249,6 +14697,41 @@ } } }, + "sequelize": { + "version": "6.32.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.32.1.tgz", + "integrity": "sha512-3Iv0jruv57Y0YvcxQW7BE56O7DC1BojcfIrqh6my+IQwde+9u/YnuYHzK+8kmZLhLvaziRT1eWu38nh9yVwn/g==", + "requires": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.4", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.0", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.1", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==" + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -14848,6 +15331,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14931,6 +15419,11 @@ "random-bytes": "~1.0.0" } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -15038,8 +15531,7 @@ "validator": { "version": "13.9.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "dev": true + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" }, "vary": { "version": "1.1.2", @@ -15081,6 +15573,14 @@ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" }, + "wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "requires": { + "@types/node": "*" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -15156,8 +15656,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "3.2.2", diff --git a/package.json b/package.json index e93c3bbf..acfe9916 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "commander": "^10.0.1", "compression": "^1.7.4", "connect-mongo": "^4.6.0", + "connect-session-sequelize": "^7.1.7", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dicom-parser": "^1.8.13", @@ -96,10 +97,13 @@ "passport": "^0.6.0", "passport-local": "^1.0.0", "path-match": "^1.2.4", + "pg": "^8.11.1", + "pg-hstore": "^2.3.4", "regexparam": "^2.0.1", "request-compose": "^2.1.6", "request-multipart": "^1.0.0", "run-script-os": "^1.1.6", + "sequelize": "^6.32.1", "sharp": "^0.30.4", "shorthash2": "^1.0.3", "uuid": "^9.0.0", diff --git a/plugins/sql/init.js b/plugins/sql/init.js new file mode 100644 index 00000000..0c0e3f8b --- /dev/null +++ b/plugins/sql/init.js @@ -0,0 +1,23 @@ +const { PersonNameModel } = require("./models/personName.model"); +const { PatientModel } = require("./models/patient.model"); +const sequelizeInstance = require("./instance"); + +async function init() { + try { + await sequelizeInstance.authenticate(); + + PatientModel.belongsTo(PersonNameModel, { + foreignKey: "x00100010" + }); + + await sequelizeInstance.sync({}); + } catch (e) { + console.error('Unable to connect to the database:', e); + process.exit(1); + } + +} + +module.exports = (() => init())(); + + diff --git a/plugins/sql/instance.js b/plugins/sql/instance.js new file mode 100644 index 00000000..197eb0e7 --- /dev/null +++ b/plugins/sql/instance.js @@ -0,0 +1,9 @@ +const { raccoonConfig } = require("@root/config-class"); +const { Sequelize } = require("sequelize"); + +const sequelize = new Sequelize(raccoonConfig.sqlDbConfig); + +/** + * @type {Sequelize} + */ +module.exports = sequelize; \ No newline at end of file diff --git a/plugins/sql/models/dicom-json-model.js b/plugins/sql/models/dicom-json-model.js new file mode 100644 index 00000000..84218706 --- /dev/null +++ b/plugins/sql/models/dicom-json-model.js @@ -0,0 +1,41 @@ +const _ = require("lodash"); + +const { DicomJsonModel } = require("@models/DICOM/dicom-json-model"); + + +class SqlDicomJsonModel extends DicomJsonModel { + constructor(dicomJson) { + super(dicomJson); + } + + async storeToDb(dicomFileSaveInfo) { + let dicomJsonClone = _.cloneDeep(this.dicomJson); + try { + let mediaStorage = this.getMediaStorageInfo(); + _.merge(dicomJsonClone, this.uidObj); + _.merge(dicomJsonClone, { + studyPath: dicomFileSaveInfo.studyPath, + seriesPath: dicomFileSaveInfo.seriesPath, + instancePath: dicomFileSaveInfo.relativePath + }); + _.merge(dicomJsonClone, mediaStorage); + + delete dicomJsonClone.sopClass; + delete dicomJsonClone.sopInstanceUID; + + await Promise.all([ + this.storePatientCollection(dicomJsonClone) + ]); + } catch(e) { + throw e; + } + } + + async storePatientCollection(dicomJson) { + console.log(dicomJson); + console.log("TODO: Store Patient"); + } +} + + +module.exports.SqlDicomJsonModel = SqlDicomJsonModel; \ No newline at end of file diff --git a/plugins/sql/models/patient.model.js b/plugins/sql/models/patient.model.js new file mode 100644 index 00000000..1cf9d10b --- /dev/null +++ b/plugins/sql/models/patient.model.js @@ -0,0 +1,51 @@ +const { Sequelize, DataTypes, Model } = require("sequelize"); +const sequelizeInstance = require("@root/plugins/sql/instance"); +const { vrTypeMapping } = require("../vrTypeMapping"); + +class PatientModel extends Model {}; + +PatientModel.init({ + "x00100010": { + type: DataTypes.INTEGER + }, + "x00100020": { + type: vrTypeMapping.LO + }, + "x00100021": { + type: vrTypeMapping.LO + }, + "x00100030": { + type: vrTypeMapping.DA + }, + "x00100032": { + type: vrTypeMapping.TM + }, + "x00100040": { + type: vrTypeMapping.CS + }, + "x00101001": { + type: vrTypeMapping.PN + }, + "x00102160": { + type: vrTypeMapping.SH + }, + "x00104000": { + type: vrTypeMapping.LT + }, + "x00880130": { + type: vrTypeMapping.SH + }, + "x00880140": { + type: vrTypeMapping.UI + }, + "json": { + type: DataTypes.JSON + } +}, { + sequelize: sequelizeInstance, + modelName: "Patient", + tableName: "Patient", + freezeTableName: true +}); + +module.exports.PatientModel = PatientModel; diff --git a/plugins/sql/models/personName.model.js b/plugins/sql/models/personName.model.js new file mode 100644 index 00000000..2bdf9bc9 --- /dev/null +++ b/plugins/sql/models/personName.model.js @@ -0,0 +1,31 @@ +const { Sequelize, DataTypes, Model } = require("sequelize"); +const sequelizeInstance = require("@root/plugins/sql/instance"); + + +class PersonNameModel extends Model {} +PersonNameModel.init({ + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true + }, + alphabetic: { + type: DataTypes.STRING + }, + ideographic: { + type: DataTypes.STRING, + allowNull: true + }, + phonetic: { + type: DataTypes.STRING, + allowNull: true + } +}, { + sequelize: sequelizeInstance, + modelName: "PersonName", + tableName: "PersonName", + freezeTableName: true +}); + + +module.exports.PersonNameModel = PersonNameModel; diff --git a/plugins/sql/vrTypeMapping.js b/plugins/sql/vrTypeMapping.js new file mode 100644 index 00000000..65f19dc2 --- /dev/null +++ b/plugins/sql/vrTypeMapping.js @@ -0,0 +1,38 @@ +const { DataTypes } = require("sequelize"); + +const vrTypeMapping = { + "AE": DataTypes.STRING(16+1), + "AS": DataTypes.STRING(4+1), + "CS": DataTypes.STRING(16+1), + "DA": DataTypes.DATEONLY, + "DS": DataTypes.STRING(16+1), + "DT": DataTypes.DATE, + "FT": DataTypes.FLOAT, + "FD": DataTypes.DOUBLE, + "IS": DataTypes.STRING(12+1), + "LO": DataTypes.STRING(64+1), + "LT": DataTypes.STRING(10240+1), + "OB": DataTypes.TEXT, + "OD": DataTypes.TEXT, + "OF": DataTypes.TEXT, + "OL": DataTypes.TEXT, + "OV": DataTypes.TEXT, + "OW": DataTypes.TEXT, + "PN": DataTypes.INTEGER, // foreign key + "SH": DataTypes.STRING(16+1), + "SL": DataTypes.INTEGER, + "SS": DataTypes.SMALLINT, + "ST": DataTypes.STRING(1024+1), + "SV": DataTypes.BIGINT, + "TM": DataTypes.DECIMAL, + "UC": DataTypes.TEXT("long"), + "UI": DataTypes.STRING(64+1), + "UL": DataTypes.INTEGER.UNSIGNED, + "UR": DataTypes.TEXT("long"), + "US": DataTypes.SMALLINT.UNSIGNED, + "UT": DataTypes.TEXT("long"), + "UV": DataTypes.BIGINT.UNSIGNED +}; + + +module.exports.vrTypeMapping = vrTypeMapping; \ No newline at end of file diff --git a/plugins/store-sql/index.js b/plugins/store-sql/index.js new file mode 100644 index 00000000..53df6585 --- /dev/null +++ b/plugins/store-sql/index.js @@ -0,0 +1,55 @@ +const { performance } = require("node:perf_hooks"); +const errorResponseMessage = require("@root/utils/errorResponse/errorResponseMessage"); +const { ApiLogger } = require("@root/utils/logs/api-logger"); +const { StowRsRequestMultipartParser } = require("@root/api/dicom-web/controller/STOW-RS/service/request-multipart-parser"); +const { SqlStowRsService } = require("./service/stow-rs.service"); + + +/** + * To store DICOM instance + * 1. we parse multipart request to get file info that user upload + * 2. parse DICOM to JSON and store DICOM file from step 1 + * 3. parse DICOM json model to FHIR (Patient, Endpoint, ImagingStudy) + * 4. upload FHIR to FHIR server + * @param {import('express').Request} req + * @param {import('express').Response} res + */ +module.exports = async function (req, res) { + let startSTOWTime = performance.now(); + let retCode; + let storeMessage; + let apiLogger = new ApiLogger(req, "STOW-RS"); + apiLogger.addTokenValue(); + + try { + let requestMultipartParser = new StowRsRequestMultipartParser(req); + let multipartParseResult = await requestMultipartParser.parse(); + + if (multipartParseResult.status) { + let stowRsService = new SqlStowRsService(req, multipartParseResult.multipart.files); + let storeInstancesResult = await stowRsService.storeInstances(); + + retCode = storeInstancesResult.code; + storeMessage = storeInstancesResult.responseMessage; + } + let endSTOWTime = performance.now(); + let elapsedTime = (endSTOWTime - startSTOWTime).toFixed(3); + apiLogger.logger.info(`Finished STOW-RS, elapsed time: ${elapsedTime} ms`); + + res.writeHead(retCode, { + "Content-Type": "application/dicom" + }); + + return res.end(JSON.stringify(storeMessage)); + } catch (e) { + let errorStr = JSON.stringify(e, Object.getOwnPropertyNames(e)); + apiLogger.logger.error(errorStr); + + let errorMessage = + errorResponseMessage.getInternalServerErrorMessage(errorStr); + res.writeHead(500, { + "Content-Type": "application/dicom+json" + }); + return res.end(JSON.stringify(errorMessage)); + } +}; diff --git a/plugins/store-sql/service/stow-rs.service.js b/plugins/store-sql/service/stow-rs.service.js new file mode 100644 index 00000000..1541d4f4 --- /dev/null +++ b/plugins/store-sql/service/stow-rs.service.js @@ -0,0 +1,62 @@ +const _ = require("lodash"); + +const { DicomJsonParser } = require("@models/DICOM/dicom-json-parser"); +const { StowRsService } = require("@root/api/dicom-web/controller/STOW-RS/service/stow-rs.service"); +const { DicomFileSaver } = require("@root/api/dicom-web/controller/STOW-RS/service/dicom-file-saver"); +const { SqlDicomJsonModel: DicomJsonModel } = require("../../sql/models/dicom-json-model"); + +class SqlStowRsService extends StowRsService { + /** + * @param {import('express').Request} req + * @param {import('formidable').File[]} uploadFiles + */ + constructor(req, uploadFiles) { + super(req, uploadFiles); + } + + /** + * + * @param {import("formidable").File} file + */ + async storeInstance(file) { + let dicomJsonParser = new DicomJsonParser(); + let dicomJson = await dicomJsonParser.parseFromFilename(file.filepath); + + let dicomJsonModel = new DicomJsonModel(dicomJson); + dicomJsonModel.setMinifyDicomJsonAndTempBigValueTags(); + dicomJsonModel.setUidObj(); + + let isSameStudyIDStatus = this.isSameStudyID_(this.responseMessage); + if (!isSameStudyIDStatus) { + this.responseCode = 409; + } + + // let dicomJsonBinaryDataModel = new DicomJsonBinaryDataModel(dicomJsonModel); + // await dicomJsonBinaryDataModel.storeAllBinaryDataToFileAndDb(); + // dicomJsonBinaryDataModel.replaceAllBinaryToURI(); + + let dicomFileSaver = new DicomFileSaver(file, dicomJsonModel); + let dicomFileSaveInfo = await dicomFileSaver.saveAndGetSaveInfo(); + + await dicomJsonModel.saveMetadataToFile(dicomFileSaveInfo.fullPath); + + await dicomJsonModel.storeToDb(dicomFileSaveInfo); + + let retrieveUrlObj = this.getRetrieveUrl(dicomJsonModel.uidObj); + this.responseMessage["00081190"].Value.push(retrieveUrlObj.study); + this.responseMessage["00081190"].Value = _.uniq(this.responseMessage["00081190"].Value); + + let sopSeq = this.getSOPSeq(dicomJsonModel.uidObj.sopClass, dicomJsonModel.uidObj.sopInstanceUID); + _.set(sopSeq, "00081190.vr", "UT"); + _.set(sopSeq, "00081190.Value", [retrieveUrlObj.instance]); + this.responseMessage["00081199"]["Value"].push(sopSeq); + + return { + dicomJsonModel, + dicomFileSaveInfo + }; + } +} + + +module.exports.SqlStowRsService = SqlStowRsService; \ No newline at end of file diff --git a/server.js b/server.js index e9674c8b..4d5118d9 100644 --- a/server.js +++ b/server.js @@ -8,8 +8,8 @@ const cookieParser = require("cookie-parser"); const compress = require("compression"); const cors = require("cors"); const os = require("os"); -const mongoose = require("mongoose"); -const MongoStore = require("connect-mongo"); +const SequelizeStore = require("connect-session-sequelize")(session.Store); +const sequelizeInstance = require("./plugins/sql/instance"); const passport = require("passport"); const { raccoonConfig } = require("./config-class"); @@ -50,9 +50,8 @@ app.use( httpOnly: true, maxAge: 60 * 60 * 1000 }, - store: MongoStore.create({ - client: mongoose.connection.getClient(), - dbName: raccoonConfig.mongoDbConfig.dbName + store: new SequelizeStore({ + db: sequelizeInstance }) }) ); @@ -85,23 +84,23 @@ if (osPlatform.includes("linux")) { // #region DIMSE -(async () => { - if (raccoonConfig.dicomDimseConfig.enableDimse) { - const { java } = require("./models/DICOM/dcm4che/java-instance"); - let dcmQrScpClass = await java.importClassAsync("org.dcm4che3.tool.dcmqrscp.DcmQRSCP"); - const net = require("net"); - let checkPortServer = net.createServer() - .once("listening", async function () { - checkPortServer.close(); - await dcmQrScpClass.main(raccoonConfig.dicomDimseConfig.dcm4cheQrscpArgv); - }) - .once("error", function (err) { - if (err.code === "EADDRINUSE") { - console.log("QRSCP's port is already in use, please check is QRSCP running or another app running"); - } - }) - .listen(raccoonConfig.dicomDimseConfig.getPort()); - } -})(); +// (async () => { +// if (raccoonConfig.dicomDimseConfig.enableDimse) { +// const { java } = require("./models/DICOM/dcm4che/java-instance"); +// let dcmQrScpClass = await java.importClassAsync("org.dcm4che3.tool.dcmqrscp.DcmQRSCP"); +// const net = require("net"); +// let checkPortServer = net.createServer() +// .once("listening", async function () { +// checkPortServer.close(); +// await dcmQrScpClass.main(raccoonConfig.dicomDimseConfig.dcm4cheQrscpArgv); +// }) +// .once("error", function (err) { +// if (err.code === "EADDRINUSE") { +// console.log("QRSCP's port is already in use, please check is QRSCP running or another app running"); +// } +// }) +// .listen(raccoonConfig.dicomDimseConfig.getPort()); +// } +// })(); // #endregion \ No newline at end of file