diff --git a/util/package-lock.json b/util/package-lock.json
index 4b0f1f15..f4129614 100644
--- a/util/package-lock.json
+++ b/util/package-lock.json
@@ -18,6 +18,7 @@
"jsonwebtoken": "^8.5.1",
"lambert-server": "^1.2.10",
"missing-native-js-functions": "^1.2.15",
+ "multer": "^1.4.3",
"node-fetch": "^2.6.1",
"patch-package": "^6.4.7",
"pg": "^8.7.1",
@@ -32,6 +33,7 @@
"@types/amqplib": "^0.8.1",
"@types/jsonwebtoken": "^8.5.0",
"@types/mongoose-autopopulate": "^0.10.1",
+ "@types/multer": "^1.4.7",
"@types/node": "^14.17.9",
"@types/node-fetch": "^2.5.12",
"jest": "^27.0.6"
@@ -224,19 +226,19 @@
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.15.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.4.tgz",
- "integrity": "sha512-9fHHSGE9zTC++KuXLZcB5FKgvlV83Ox+NLUmQTawovwlJ85+QMhk1CnVk406CQVj97LaWod6KVjl2Sfgw9Aktw==",
+ "version": "7.15.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz",
+ "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==",
"dev": true,
"dependencies": {
"@babel/helper-module-imports": "^7.15.4",
"@babel/helper-replace-supers": "^7.15.4",
"@babel/helper-simple-access": "^7.15.4",
"@babel/helper-split-export-declaration": "^7.15.4",
- "@babel/helper-validator-identifier": "^7.14.9",
+ "@babel/helper-validator-identifier": "^7.15.7",
"@babel/template": "^7.15.4",
"@babel/traverse": "^7.15.4",
- "@babel/types": "^7.15.4"
+ "@babel/types": "^7.15.6"
},
"engines": {
"node": ">=6.9.0"
@@ -303,9 +305,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.14.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz",
- "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==",
+ "version": "7.15.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
+ "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -420,9 +422,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.15.6",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.6.tgz",
- "integrity": "sha512-S/TSCcsRuCkmpUuoWijua0Snt+f3ewU/8spLo+4AXJCZfT0bVCzLD5MuOKdrx0mlAptbKzn5AdgEIIKXxXkz9Q==",
+ "version": "7.15.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz",
+ "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -1063,6 +1065,48 @@
"integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==",
"dev": true
},
+ "node_modules/@types/body-parser": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz",
+ "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==",
+ "dev": true,
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "dev": true,
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.24",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz",
+ "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
"node_modules/@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@@ -1115,6 +1159,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+ "dev": true
+ },
"node_modules/@types/mongodb": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-4.0.7.tgz",
@@ -1144,10 +1194,19 @@
"@types/mongoose": "5.10.5"
}
},
+ "node_modules/@types/multer": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
+ "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==",
+ "dev": true,
+ "dependencies": {
+ "@types/express": "*"
+ }
+ },
"node_modules/@types/node": {
- "version": "14.17.16",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.16.tgz",
- "integrity": "sha512-WiFf2izl01P1CpeY8WqFAeKWwByMueBEkND38EcN8N68qb0aDG3oIS1P5MhAX5kUdr469qRyqsY/MjanLjsFbQ=="
+ "version": "14.17.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.17.tgz",
+ "integrity": "sha512-niAjcewgEYvSPCZm3OaM9y6YQrL2SEPH9PymtE6fuZAvFiP6ereCcvApGl2jKTq7copTIguX3PBvfP08LN4LvQ=="
},
"node_modules/@types/node-fetch": {
"version": "2.5.12",
@@ -1165,6 +1224,28 @@
"integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==",
"dev": true
},
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/stack-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@@ -1396,6 +1477,11 @@
"node": ">= 6.0.0"
}
},
+ "node_modules/append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
+ },
"node_modules/aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -1816,6 +1902,18 @@
"node": ">=4"
}
},
+ "node_modules/busboy": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+ "dependencies": {
+ "dicer": "0.2.5",
+ "readable-stream": "1.1.x"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -1843,9 +1941,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001257",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz",
- "integrity": "sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA==",
+ "version": "1.0.30001258",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz",
+ "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==",
"dev": true,
"funding": {
"type": "opencollective",
@@ -2028,6 +2126,52 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "engines": [
+ "node >= 0.8"
+ ],
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/concat-stream/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "node_modules/concat-stream/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/concat-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/concat-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.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",
@@ -2258,6 +2402,18 @@
"node": ">=8"
}
},
+ "node_modules/dicer": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+ "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+ "dependencies": {
+ "readable-stream": "1.1.x",
+ "streamsearch": "0.1.2"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -2342,9 +2498,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"node_modules/electron-to-chromium": {
- "version": "1.3.840",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.840.tgz",
- "integrity": "sha512-yRoUmTLDJnkIJx23xLY7GbSvnmDCq++NSuxHDQ0jiyDJ9YZBUGJcrdUqm+ZwZFzMbCciVzfem2N2AWiHJcWlbw==",
+ "version": "1.3.843",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.843.tgz",
+ "integrity": "sha512-OWEwAbzaVd1Lk9MohVw8LxMXFlnYd9oYTYxfX8KS++kLLjDfbovLOcEEXwRhG612dqGQ6+44SZvim0GXuBRiKg==",
"dev": true
},
"node_modules/emittery": {
@@ -4619,6 +4775,24 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "node_modules/multer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
+ "integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
+ "dependencies": {
+ "append-field": "^1.0.0",
+ "busboy": "^0.2.11",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.4",
+ "object-assign": "^4.1.1",
+ "on-finished": "^2.3.0",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@@ -6075,13 +6249,12 @@
}
},
"node_modules/stack-utils": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.4.tgz",
- "integrity": "sha512-ERg+H//lSSYlZhBIUu+wJnqg30AbyBbpZlIhcshpn7BNzpoRODZgfyr9J+8ERf3ooC6af3u7Lcl01nleau7MrA==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz",
+ "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==",
"dev": true,
"dependencies": {
- "escape-string-regexp": "^2.0.0",
- "source-map-support": "^0.5.20"
+ "escape-string-regexp": "^2.0.0"
},
"engines": {
"node": ">=10"
@@ -6095,6 +6268,14 @@
"node": ">= 0.6"
}
},
+ "node_modules/streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
@@ -6448,6 +6629,11 @@
"node": ">= 0.6"
}
},
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+ },
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
@@ -7348,19 +7534,19 @@
}
},
"@babel/helper-module-transforms": {
- "version": "7.15.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.4.tgz",
- "integrity": "sha512-9fHHSGE9zTC++KuXLZcB5FKgvlV83Ox+NLUmQTawovwlJ85+QMhk1CnVk406CQVj97LaWod6KVjl2Sfgw9Aktw==",
+ "version": "7.15.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz",
+ "integrity": "sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.15.4",
"@babel/helper-replace-supers": "^7.15.4",
"@babel/helper-simple-access": "^7.15.4",
"@babel/helper-split-export-declaration": "^7.15.4",
- "@babel/helper-validator-identifier": "^7.14.9",
+ "@babel/helper-validator-identifier": "^7.15.7",
"@babel/template": "^7.15.4",
"@babel/traverse": "^7.15.4",
- "@babel/types": "^7.15.4"
+ "@babel/types": "^7.15.6"
}
},
"@babel/helper-optimise-call-expression": {
@@ -7409,9 +7595,9 @@
}
},
"@babel/helper-validator-identifier": {
- "version": "7.14.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz",
- "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==",
+ "version": "7.15.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
+ "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"dev": true
},
"@babel/helper-validator-option": {
@@ -7501,9 +7687,9 @@
}
},
"@babel/parser": {
- "version": "7.15.6",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.6.tgz",
- "integrity": "sha512-S/TSCcsRuCkmpUuoWijua0Snt+f3ewU/8spLo+4AXJCZfT0bVCzLD5MuOKdrx0mlAptbKzn5AdgEIIKXxXkz9Q==",
+ "version": "7.15.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz",
+ "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==",
"dev": true
},
"@babel/plugin-syntax-async-generators": {
@@ -8011,6 +8197,48 @@
"integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==",
"dev": true
},
+ "@types/body-parser": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz",
+ "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==",
+ "dev": true,
+ "requires": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/connect": {
+ "version": "3.4.35",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
+ "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/express": {
+ "version": "4.17.13",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+ "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+ "dev": true,
+ "requires": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "@types/express-serve-static-core": {
+ "version": "4.17.24",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz",
+ "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
"@types/graceful-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
@@ -8063,6 +8291,12 @@
"@types/node": "*"
}
},
+ "@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+ "dev": true
+ },
"@types/mongodb": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-4.0.7.tgz",
@@ -8091,10 +8325,19 @@
"@types/mongoose": "5.10.5"
}
},
+ "@types/multer": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
+ "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==",
+ "dev": true,
+ "requires": {
+ "@types/express": "*"
+ }
+ },
"@types/node": {
- "version": "14.17.16",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.16.tgz",
- "integrity": "sha512-WiFf2izl01P1CpeY8WqFAeKWwByMueBEkND38EcN8N68qb0aDG3oIS1P5MhAX5kUdr469qRyqsY/MjanLjsFbQ=="
+ "version": "14.17.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.17.tgz",
+ "integrity": "sha512-niAjcewgEYvSPCZm3OaM9y6YQrL2SEPH9PymtE6fuZAvFiP6ereCcvApGl2jKTq7copTIguX3PBvfP08LN4LvQ=="
},
"@types/node-fetch": {
"version": "2.5.12",
@@ -8112,6 +8355,28 @@
"integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==",
"dev": true
},
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "@types/serve-static": {
+ "version": "1.13.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+ "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+ "dev": true,
+ "requires": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
"@types/stack-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
@@ -8290,6 +8555,11 @@
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz",
"integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw=="
},
+ "append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY="
+ },
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -8647,6 +8917,15 @@
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
},
+ "busboy": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
+ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
+ "requires": {
+ "dicer": "0.2.5",
+ "readable-stream": "1.1.x"
+ }
+ },
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -8665,9 +8944,9 @@
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30001257",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz",
- "integrity": "sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA==",
+ "version": "1.0.30001258",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz",
+ "integrity": "sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==",
"dev": true
},
"caseless": {
@@ -8815,6 +9094,51 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -9006,6 +9330,15 @@
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
"dev": true
},
+ "dicer": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
+ "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
+ "requires": {
+ "readable-stream": "1.1.x",
+ "streamsearch": "0.1.2"
+ }
+ },
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -9071,9 +9404,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"electron-to-chromium": {
- "version": "1.3.840",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.840.tgz",
- "integrity": "sha512-yRoUmTLDJnkIJx23xLY7GbSvnmDCq++NSuxHDQ0jiyDJ9YZBUGJcrdUqm+ZwZFzMbCciVzfem2N2AWiHJcWlbw==",
+ "version": "1.3.843",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.843.tgz",
+ "integrity": "sha512-OWEwAbzaVd1Lk9MohVw8LxMXFlnYd9oYTYxfX8KS++kLLjDfbovLOcEEXwRhG612dqGQ6+44SZvim0GXuBRiKg==",
"dev": true
},
"emittery": {
@@ -10830,6 +11163,21 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
+ "multer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz",
+ "integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==",
+ "requires": {
+ "append-field": "^1.0.0",
+ "busboy": "^0.2.11",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.4",
+ "object-assign": "^4.1.1",
+ "on-finished": "^2.3.0",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ }
+ },
"mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@@ -11951,13 +12299,12 @@
}
},
"stack-utils": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.4.tgz",
- "integrity": "sha512-ERg+H//lSSYlZhBIUu+wJnqg30AbyBbpZlIhcshpn7BNzpoRODZgfyr9J+8ERf3ooC6af3u7Lcl01nleau7MrA==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz",
+ "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==",
"dev": true,
"requires": {
- "escape-string-regexp": "^2.0.0",
- "source-map-support": "^0.5.20"
+ "escape-string-regexp": "^2.0.0"
}
},
"statuses": {
@@ -11965,6 +12312,11 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
+ "streamsearch": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
+ "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
+ },
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
@@ -12237,6 +12589,11 @@
"mime-types": "~2.1.24"
}
},
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+ },
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
diff --git a/util/package.json b/util/package.json
index 751484af..2fa93f9b 100644
--- a/util/package.json
+++ b/util/package.json
@@ -31,6 +31,7 @@
"@types/amqplib": "^0.8.1",
"@types/jsonwebtoken": "^8.5.0",
"@types/mongoose-autopopulate": "^0.10.1",
+ "@types/multer": "^1.4.7",
"@types/node": "^14.17.9",
"@types/node-fetch": "^2.5.12",
"jest": "^27.0.6"
@@ -44,6 +45,7 @@
"jsonwebtoken": "^8.5.1",
"lambert-server": "^1.2.10",
"missing-native-js-functions": "^1.2.15",
+ "multer": "^1.4.3",
"node-fetch": "^2.6.1",
"patch-package": "^6.4.7",
"pg": "^8.7.1",
diff --git a/util/src/dtos/DmChannelDTO.ts b/util/src/dtos/DmChannelDTO.ts
new file mode 100644
index 00000000..8b7a18fd
--- /dev/null
+++ b/util/src/dtos/DmChannelDTO.ts
@@ -0,0 +1,35 @@
+import { MinimalPublicUserDTO } from "./UserDTO";
+import { Channel, PublicUserProjection, User } from "../entities";
+
+export class DmChannelDTO {
+ icon: string | null;
+ id: string;
+ last_message_id: string | null;
+ name: string | null;
+ origin_channel_id: string | null;
+ owner_id?: string;
+ recipients: MinimalPublicUserDTO[];
+ type: number;
+
+ static async from(channel: Channel, excluded_recipients: string[] = [], origin_channel_id?: string) {
+ const obj = new DmChannelDTO()
+ obj.icon = channel.icon || null
+ obj.id = channel.id
+ obj.last_message_id = channel.last_message_id || null
+ obj.name = channel.name || null
+ obj.origin_channel_id = origin_channel_id || null
+ obj.owner_id = channel.owner_id
+ obj.type = channel.type
+ obj.recipients = (await Promise.all(channel.recipients!.filter(r => !excluded_recipients.includes(r.user_id)).map(async r => {
+ return await User.findOneOrFail({ where: { id: r.user_id }, select: PublicUserProjection })
+ }))).map(u => new MinimalPublicUserDTO(u))
+ return obj
+ }
+
+ excludedRecipients(excluded_recipients: string[]): DmChannelDTO {
+ return {
+ ...this,
+ recipients: this.recipients.filter(r => !excluded_recipients.includes(r.id))
+ }
+ }
+}
\ No newline at end of file
diff --git a/util/src/dtos/UserDTO.ts b/util/src/dtos/UserDTO.ts
new file mode 100644
index 00000000..f09b5f4e
--- /dev/null
+++ b/util/src/dtos/UserDTO.ts
@@ -0,0 +1,17 @@
+import { User } from "../entities";
+
+export class MinimalPublicUserDTO {
+ avatar?: string | null;
+ discriminator: string;
+ id: string;
+ public_flags: number;
+ username: string;
+
+ constructor(user: User) {
+ this.avatar = user.avatar
+ this.discriminator = user.discriminator
+ this.id = user.id
+ this.public_flags = user.public_flags
+ this.username = user.username
+ }
+}
\ No newline at end of file
diff --git a/util/src/dtos/index.ts b/util/src/dtos/index.ts
new file mode 100644
index 00000000..13702342
--- /dev/null
+++ b/util/src/dtos/index.ts
@@ -0,0 +1,2 @@
+export * from "./DmChannelDTO";
+export * from "./UserDTO";
\ No newline at end of file
diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts
index 2092cd4e..fab3d93f 100644
--- a/util/src/entities/Application.ts
+++ b/util/src/entities/Application.ts
@@ -41,7 +41,9 @@ export class Application extends BaseClass {
verify_key: string;
@JoinColumn({ name: "team_id" })
- @ManyToOne(() => Team)
+ @ManyToOne(() => Team, {
+ onDelete: "CASCADE",
+ })
team?: Team;
@JoinColumn({ name: "guild_id" })
diff --git a/util/src/entities/Attachment.ts b/util/src/entities/Attachment.ts
index ca893400..7b4b17eb 100644
--- a/util/src/entities/Attachment.ts
+++ b/util/src/entities/Attachment.ts
@@ -1,4 +1,6 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { URL } from "url";
+import { deleteFile } from "../util/cdn";
import { BaseClass } from "./BaseClass";
@Entity("attachments")
@@ -29,6 +31,13 @@ export class Attachment extends BaseClass {
message_id: string;
@JoinColumn({ name: "message_id" })
- @ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments)
+ @ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments, {
+ onDelete: "CASCADE",
+ })
message: import("./Message").Message;
+
+ @BeforeRemove()
+ onDelete() {
+ return deleteFile(new URL(this.url).pathname);
+ }
}
diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts
index e8a6d648..9504bd8e 100644
--- a/util/src/entities/Ban.ts
+++ b/util/src/entities/Ban.ts
@@ -10,7 +10,9 @@ export class Ban extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
@Column({ nullable: true })
@@ -18,7 +20,9 @@ export class Ban extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column({ nullable: true })
diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts
index 9b2ce058..d18757f2 100644
--- a/util/src/entities/BaseClass.ts
+++ b/util/src/entities/BaseClass.ts
@@ -1,5 +1,15 @@
import "reflect-metadata";
-import { BaseEntity, BeforeInsert, BeforeUpdate, EntityMetadata, FindConditions, PrimaryColumn } from "typeorm";
+import {
+ BaseEntity,
+ BeforeInsert,
+ BeforeUpdate,
+ EntityMetadata,
+ FindConditions,
+ getConnection,
+ getManager,
+ PrimaryColumn,
+ RemoveOptions,
+} from "typeorm";
import { Snowflake } from "../util/Snowflake";
import "missing-native-js-functions";
@@ -69,6 +79,42 @@ export class BaseClassWithoutId extends BaseEntity {
const repository = this.getRepository();
return repository.decrement(conditions, propertyPath, value);
}
+
+ // static async delete<T>(criteria: FindConditions<T>, options?: RemoveOptions) {
+ // if (!criteria) throw new Error("You need to specify delete criteria");
+
+ // const repository = this.getRepository();
+ // const promises = repository.metadata.relations.map(async (x) => {
+ // if (x.orphanedRowAction !== "delete") return;
+
+ // const foreignKey =
+ // x.foreignKeys.find((key) => key.entityMetadata === repository.metadata) ||
+ // x.inverseRelation?.foreignKeys[0]; // find foreign key for this entity
+ // if (!foreignKey) {
+ // throw new Error(
+ // `Foreign key not found for entity ${repository.metadata.name} in relation ${x.propertyName}`
+ // );
+ // }
+ // const id = (criteria as any)[foreignKey.referencedColumnNames[0]];
+ // if (!id) throw new Error("id missing in criteria options " + foreignKey.referencedColumnNames);
+
+ // if (x.relationType === "many-to-many") {
+ // return getConnection()
+ // .createQueryBuilder()
+ // .relation(this, x.propertyName)
+ // .of(id)
+ // .remove({ [foreignKey.columnNames[0]]: id });
+ // } else if (
+ // x.relationType === "one-to-one" ||
+ // x.relationType === "many-to-one" ||
+ // x.relationType === "one-to-many"
+ // ) {
+ // return (x.inverseEntityMetadata.target as any).delete({ [foreignKey.columnNames[0]]: id });
+ // }
+ // });
+ // await Promise.all(promises);
+ // return super.delete(criteria, options);
+ // }
}
export class BaseClass extends BaseClassWithoutId {
diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts
index fc954f63..ece82bd0 100644
--- a/util/src/entities/Channel.ts
+++ b/util/src/entities/Channel.ts
@@ -1,12 +1,17 @@
-import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
-import { Message } from "./Message";
-import { User } from "./User";
+import { PublicUserProjection, User } from "./User";
import { HTTPError } from "lambert-server";
-import { emitEvent, getPermission, Snowflake } from "../util";
-import { ChannelCreateEvent } from "../interfaces";
+import { containsAll, emitEvent, getPermission, Snowflake, trimSpecial } from "../util";
+import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces";
import { Recipient } from "./Recipient";
+import { Message } from "./Message";
+import { ReadState } from "./ReadState";
+import { Invite } from "./Invite";
+import { VoiceState } from "./VoiceState";
+import { Webhook } from "./Webhook";
+import { DmChannelDTO } from "../dtos";
export enum ChannelType {
GUILD_TEXT = 0, // a text channel within a server
@@ -31,26 +36,29 @@ export class Channel extends BaseClass {
@Column({ nullable: true })
name?: string;
+ @Column({ type: "text", nullable: true })
+ icon?: string | null;
+
@Column({ type: "simple-enum", enum: ChannelType })
type: ChannelType;
- @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, { cascade: true })
+ @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
recipients?: Recipient[];
@Column({ nullable: true })
- @RelationId((channel: Channel) => channel.last_message)
last_message_id: string;
- @JoinColumn({ name: "last_message_id" })
- @ManyToOne(() => Message)
- last_message?: Message;
-
@Column({ nullable: true })
@RelationId((channel: Channel) => channel.guild)
guild_id?: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column({ nullable: true })
@@ -100,6 +108,36 @@ export class Channel extends BaseClass {
@Column({ nullable: true })
topic?: string;
+ @OneToMany(() => Invite, (invite: Invite) => invite.channel, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
+ invites?: Invite[];
+
+ @OneToMany(() => Message, (message: Message) => message.channel, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
+ messages?: Message[];
+
+ @OneToMany(() => VoiceState, (voice_state: VoiceState) => voice_state.channel, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
+ voice_states?: VoiceState[];
+
+ @OneToMany(() => ReadState, (read_state: ReadState) => read_state.channel, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
+ read_states?: ReadState[];
+
+ @OneToMany(() => Webhook, (webhook: Webhook) => webhook.channel, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
+ webhooks?: Webhook[];
+
// TODO: DM channel
static async createChannel(
channel: Partial<Channel>,
@@ -162,6 +200,123 @@ export class Channel extends BaseClass {
return channel;
}
+
+ static async createDMChannel(recipients: string[], creator_user_id: string, name?: string) {
+ recipients = recipients.unique().filter((x) => x !== creator_user_id);
+ const otherRecipientsUsers = await User.find({ where: recipients.map((x) => ({ id: x })) });
+
+ // TODO: check config for max number of recipients
+ if (otherRecipientsUsers.length !== recipients.length) {
+ throw new HTTPError("Recipient/s not found");
+ }
+
+ const type = recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM;
+
+ let channel = null;
+
+ const channelRecipients = [...recipients, creator_user_id];
+
+ const userRecipients = await Recipient.find({
+ where: { user_id: creator_user_id },
+ relations: ["channel", "channel.recipients"],
+ });
+
+ for (let ur of userRecipients) {
+ let re = ur.channel.recipients!.map((r) => r.user_id);
+ if (re.length === channelRecipients.length) {
+ if (containsAll(re, channelRecipients)) {
+ if (channel == null) {
+ channel = ur.channel;
+ await ur.assign({ closed: false }).save();
+ }
+ }
+ }
+ }
+
+ if (channel == null) {
+ name = trimSpecial(name);
+
+ channel = await new Channel({
+ name,
+ type,
+ owner_id: type === ChannelType.DM ? undefined : creator_user_id,
+ created_at: new Date(),
+ last_message_id: null,
+ recipients: channelRecipients.map(
+ (x) =>
+ new Recipient({ user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })
+ ),
+ }).save();
+ }
+
+ const channel_dto = await DmChannelDTO.from(channel);
+
+ if (type === ChannelType.GROUP_DM) {
+ for (let recipient of channel.recipients!) {
+ await emitEvent({
+ event: "CHANNEL_CREATE",
+ data: channel_dto.excludedRecipients([recipient.user_id]),
+ user_id: recipient.user_id,
+ });
+ }
+ } else {
+ await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id });
+ }
+
+ return channel_dto.excludedRecipients([creator_user_id]);
+ }
+
+ static async removeRecipientFromChannel(channel: Channel, user_id: string) {
+ await Recipient.delete({ channel_id: channel.id, user_id: user_id });
+ channel.recipients = channel.recipients?.filter((r) => r.user_id !== user_id);
+
+ if (channel.recipients?.length === 0) {
+ await Channel.deleteChannel(channel);
+ await emitEvent({
+ event: "CHANNEL_DELETE",
+ data: await DmChannelDTO.from(channel, [user_id]),
+ user_id: user_id,
+ });
+ return;
+ }
+
+ await emitEvent({
+ event: "CHANNEL_DELETE",
+ data: await DmChannelDTO.from(channel, [user_id]),
+ user_id: user_id,
+ });
+
+ //If the owner leave we make the first recipient in the list the new owner
+ if (channel.owner_id === user_id) {
+ channel.owner_id = channel.recipients!.find((r) => r.user_id !== user_id)!.user_id; //Is there a criteria to choose the new owner?
+ await emitEvent({
+ event: "CHANNEL_UPDATE",
+ data: await DmChannelDTO.from(channel, [user_id]),
+ channel_id: channel.id,
+ });
+ }
+
+ await channel.save();
+
+ await emitEvent({
+ event: "CHANNEL_RECIPIENT_REMOVE",
+ data: {
+ channel_id: channel.id,
+ user: await User.findOneOrFail({ where: { id: user_id }, select: PublicUserProjection }),
+ },
+ channel_id: channel.id,
+ } as ChannelRecipientRemoveEvent);
+ }
+
+ static async deleteChannel(channel: Channel) {
+ await Message.delete({ channel_id: channel.id }); //TODO we should also delete the attachments from the cdn but to do that we need to move cdn.ts in util
+ //TODO before deleting the channel we should check and delete other relations
+ await Channel.delete({ id: channel.id });
+ }
+
+ isDm() {
+ return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM;
+ }
}
export interface ChannelPermissionOverwrite {
diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts
index fd830db8..f969b6bb 100644
--- a/util/src/entities/Config.ts
+++ b/util/src/entities/Config.ts
@@ -110,13 +110,13 @@ export interface ConfigValue {
};
register: {
email: {
- necessary: boolean; // we have to use necessary instead of required as the cli tool uses json schema and can't use required
+ required: boolean;
allowlist: boolean;
blocklist: boolean;
domains: string[];
};
dateOfBirth: {
- necessary: boolean;
+ required: boolean;
minimum: number; // in years
};
requireCaptcha: boolean;
@@ -125,6 +125,7 @@ export interface ConfigValue {
allowMultipleAccounts: boolean;
blockProxies: boolean;
password: {
+ required: boolean;
minLength: number;
minNumbers: number;
minUpperCase: number;
@@ -246,14 +247,14 @@ export const DefaultConfigOptions: ConfigValue = {
},
register: {
email: {
- necessary: true,
+ required: false,
allowlist: false,
blocklist: true,
domains: [], // TODO: efficiently save domain blocklist in database
// domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"),
},
dateOfBirth: {
- necessary: true,
+ required: false,
minimum: 13,
},
requireInvite: false,
@@ -262,6 +263,7 @@ export const DefaultConfigOptions: ConfigValue = {
allowMultipleAccounts: true,
blockProxies: true,
password: {
+ required: false,
minLength: 8,
minNumbers: 2,
minUpperCase: 2,
diff --git a/util/src/entities/ConnectedAccount.ts b/util/src/entities/ConnectedAccount.ts
index 59a8f423..b8aa2889 100644
--- a/util/src/entities/ConnectedAccount.ts
+++ b/util/src/entities/ConnectedAccount.ts
@@ -11,7 +11,9 @@ export class ConnectedAccount extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
@Column({ select: false })
diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts
index 181aff2c..a252d9f4 100644
--- a/util/src/entities/Emoji.ts
+++ b/util/src/entities/Emoji.ts
@@ -15,7 +15,9 @@ export class Emoji extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column()
diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts
index 7b5d2908..e107937d 100644
--- a/util/src/entities/Guild.ts
+++ b/util/src/entities/Guild.ts
@@ -81,7 +81,10 @@ export class Guild extends BaseClass {
// application?: string;
@JoinColumn({ name: "ban_ids" })
- @OneToMany(() => Ban, (ban: Ban) => ban.guild)
+ @OneToMany(() => Ban, (ban: Ban) => ban.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
bans: Ban[];
@Column({ nullable: true })
@@ -124,15 +127,26 @@ export class Guild extends BaseClass {
@Column({ nullable: true })
presence_count?: number; // users online
- @OneToMany(() => Member, (member: Member) => member.guild)
+ @OneToMany(() => Member, (member: Member) => member.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
members: Member[];
@JoinColumn({ name: "role_ids" })
- @OneToMany(() => Role, (role: Role) => role.guild)
+ @OneToMany(() => Role, (role: Role) => role.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
roles: Role[];
@JoinColumn({ name: "channel_ids" })
- @OneToMany(() => Channel, (channel: Channel) => channel.guild)
+ @OneToMany(() => Channel, (channel: Channel) => channel.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
channels: Channel[];
@Column({ nullable: true })
@@ -144,23 +158,43 @@ export class Guild extends BaseClass {
template: Template;
@JoinColumn({ name: "emoji_ids" })
- @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild)
+ @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
emojis: Emoji[];
@JoinColumn({ name: "sticker_ids" })
- @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild)
+ @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
stickers: Sticker[];
@JoinColumn({ name: "invite_ids" })
- @OneToMany(() => Invite, (invite: Invite) => invite.guild)
+ @OneToMany(() => Invite, (invite: Invite) => invite.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
invites: Invite[];
@JoinColumn({ name: "voice_state_ids" })
- @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild)
+ @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
voice_states: VoiceState[];
@JoinColumn({ name: "webhook_ids" })
- @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild)
+ @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ onDelete: "CASCADE",
+ })
webhooks: Webhook[];
@Column({ nullable: true })
diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts
index afad9c02..78545b02 100644
--- a/util/src/entities/Invite.ts
+++ b/util/src/entities/Invite.ts
@@ -1,4 +1,5 @@
import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm";
+import { Member } from ".";
import { BaseClass } from "./BaseClass";
import { Channel } from "./Channel";
import { Guild } from "./Guild";
@@ -34,7 +35,9 @@ export class Invite extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column({ nullable: true })
@@ -42,7 +45,9 @@ export class Invite extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
- @ManyToOne(() => Channel)
+ @ManyToOne(() => Channel, {
+ onDelete: "CASCADE",
+ })
channel: Channel;
@Column({ nullable: true })
@@ -58,9 +63,20 @@ export class Invite extends BaseClass {
target_user_id: string;
@JoinColumn({ name: "target_user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62
@Column({ nullable: true })
target_user_type?: number;
+
+ static async joinGuild(user_id: string, code: string) {
+ const invite = await Invite.findOneOrFail({ code });
+ if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code });
+ else await invite.save();
+
+ await Member.addToGuild(user_id, invite.guild_id);
+ return invite;
+ }
}
diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts
index 66f5d9a1..feb9c069 100644
--- a/util/src/entities/Member.ts
+++ b/util/src/entities/Member.ts
@@ -39,7 +39,9 @@ export class Member extends BaseClassWithoutId {
id: string;
@JoinColumn({ name: "id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
@Column()
@@ -47,7 +49,9 @@ export class Member extends BaseClassWithoutId {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column({ nullable: true })
@@ -55,7 +59,6 @@ export class Member extends BaseClassWithoutId {
@JoinTable({
name: "member_roles",
-
joinColumn: { name: "index", referencedColumnName: "index" },
inverseJoinColumn: {
name: "role_id",
diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts
index 506db71a..c4901693 100644
--- a/util/src/entities/Message.ts
+++ b/util/src/entities/Message.ts
@@ -8,12 +8,14 @@ import {
Column,
CreateDateColumn,
Entity,
+ FindConditions,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
RelationId,
+ RemoveOptions,
UpdateDateColumn,
} from "typeorm";
import { BaseClass } from "./BaseClass";
@@ -52,7 +54,9 @@ export class Message extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
- @ManyToOne(() => Channel)
+ @ManyToOne(() => Channel, {
+ onDelete: "CASCADE",
+ })
channel: Channel;
@Column({ nullable: true })
@@ -60,7 +64,9 @@ export class Message extends BaseClass {
guild_id?: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild?: Guild;
@Column({ nullable: true })
@@ -68,7 +74,9 @@ export class Message extends BaseClass {
author_id: string;
@JoinColumn({ name: "author_id", referencedColumnName: "id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
author?: User;
@Column({ nullable: true })
@@ -112,7 +120,7 @@ export class Message extends BaseClass {
mention_everyone?: boolean;
@JoinTable({ name: "message_user_mentions" })
- @ManyToMany(() => User)
+ @ManyToMany(() => User, { orphanedRowAction: "delete", onDelete: "CASCADE", cascade: true })
mentions: User[];
@JoinTable({ name: "message_role_mentions" })
@@ -127,8 +135,10 @@ export class Message extends BaseClass {
@ManyToMany(() => Sticker)
sticker_items?: Sticker[];
- @JoinColumn({ name: "attachment_ids" })
- @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, { cascade: true })
+ @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
attachments?: Attachment[];
@Column({ type: "simple-json" })
diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts
index 8dd05b21..68e867a0 100644
--- a/util/src/entities/ReadState.ts
+++ b/util/src/entities/ReadState.ts
@@ -15,7 +15,9 @@ export class ReadState extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
- @ManyToOne(() => Channel)
+ @ManyToOne(() => Channel, {
+ onDelete: "CASCADE",
+ })
channel: Channel;
@Column({ nullable: true })
@@ -23,7 +25,9 @@ export class ReadState extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
@Column({ nullable: true })
diff --git a/util/src/entities/Recipient.ts b/util/src/entities/Recipient.ts
index 2a27b29f..a945f938 100644
--- a/util/src/entities/Recipient.ts
+++ b/util/src/entities/Recipient.ts
@@ -8,7 +8,9 @@ export class Recipient extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
- @ManyToOne(() => require("./Channel").Channel)
+ @ManyToOne(() => require("./Channel").Channel, {
+ onDelete: "CASCADE",
+ })
channel: import("./Channel").Channel;
@Column()
@@ -16,8 +18,13 @@ export class Recipient extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => require("./User").User)
+ @ManyToOne(() => require("./User").User, {
+ onDelete: "CASCADE",
+ })
user: import("./User").User;
+ @Column({ default: false })
+ closed: boolean;
+
// TODO: settings/mute/nick/added at/encryption keys/read_state
}
diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts
index 61b3ac82..e016b36b 100644
--- a/util/src/entities/Relationship.ts
+++ b/util/src/entities/Relationship.ts
@@ -17,7 +17,9 @@ export class Relationship extends BaseClass {
from_id: string;
@JoinColumn({ name: "from_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
from: User;
@Column({})
@@ -25,7 +27,9 @@ export class Relationship extends BaseClass {
to_id: string;
@JoinColumn({ name: "to_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
to: User;
@Column({ nullable: true })
diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts
index 33c8d272..9fca99a5 100644
--- a/util/src/entities/Role.ts
+++ b/util/src/entities/Role.ts
@@ -10,7 +10,9 @@ export class Role extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column()
diff --git a/util/src/entities/Session.ts b/util/src/entities/Session.ts
index d42a8f98..7cc325f5 100644
--- a/util/src/entities/Session.ts
+++ b/util/src/entities/Session.ts
@@ -11,7 +11,9 @@ export class Session extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
//TODO check, should be 32 char long hex string
diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts
index 7730a86a..ab224d1d 100644
--- a/util/src/entities/Sticker.ts
+++ b/util/src/entities/Sticker.ts
@@ -31,7 +31,9 @@ export class Sticker extends BaseClass {
guild_id?: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild?: Guild;
@Column({ type: "simple-enum", enum: StickerType })
diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts
index beb8bf68..22140b7f 100644
--- a/util/src/entities/Team.ts
+++ b/util/src/entities/Team.ts
@@ -9,7 +9,9 @@ export class Team extends BaseClass {
icon?: string;
@JoinColumn({ name: "member_ids" })
- @OneToMany(() => TeamMember, (member: TeamMember) => member.team)
+ @OneToMany(() => TeamMember, (member: TeamMember) => member.team, {
+ orphanedRowAction: "delete",
+ })
members: TeamMember[];
@Column()
diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts
index 6b184d08..bdfdccf0 100644
--- a/util/src/entities/TeamMember.ts
+++ b/util/src/entities/TeamMember.ts
@@ -20,7 +20,9 @@ export class TeamMember extends BaseClass {
team_id: string;
@JoinColumn({ name: "team_id" })
- @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members)
+ @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members, {
+ onDelete: "CASCADE",
+ })
team: import("./Team").Team;
@Column({ nullable: true })
@@ -28,6 +30,8 @@ export class TeamMember extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
}
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
index 736704f8..4c86b2d8 100644
--- a/util/src/entities/User.ts
+++ b/util/src/entities/User.ts
@@ -124,14 +124,20 @@ export class User extends BaseClass {
flags: string; // UserFlags
@Column()
- public_flags: string;
+ public_flags: number;
@JoinColumn({ name: "relationship_ids" })
- @OneToMany(() => Relationship, (relationship: Relationship) => relationship.from)
+ @OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
relationships: Relationship[];
@JoinColumn({ name: "connected_account_ids" })
- @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user)
+ @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ })
connected_accounts: ConnectedAccount[];
@Column({ type: "simple-json", select: false })
diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts
index 56eb244e..75748a01 100644
--- a/util/src/entities/VoiceState.ts
+++ b/util/src/entities/VoiceState.ts
@@ -13,7 +13,9 @@ export class VoiceState extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild?: Guild;
@Column({ nullable: true })
@@ -21,7 +23,9 @@ export class VoiceState extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
- @ManyToOne(() => Channel)
+ @ManyToOne(() => Channel, {
+ onDelete: "CASCADE",
+ })
channel: Channel;
@Column({ nullable: true })
@@ -29,11 +33,15 @@ export class VoiceState extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
// @JoinColumn([{ name: "user_id", referencedColumnName: "id" },{ name: "guild_id", referencedColumnName: "guild_id" }])
- // @ManyToOne(() => Member)
+ // @ManyToOne(() => Member, {
+ // onDelete: "CASCADE",
+ // })
//TODO find a way to make it work without breaking Guild.voice_states
member: Member;
diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts
index 12ba0d08..8382435f 100644
--- a/util/src/entities/Webhook.ts
+++ b/util/src/entities/Webhook.ts
@@ -32,7 +32,9 @@ export class Webhook extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
guild: Guild;
@Column({ nullable: true })
@@ -40,7 +42,9 @@ export class Webhook extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
- @ManyToOne(() => Channel)
+ @ManyToOne(() => Channel, {
+ onDelete: "CASCADE",
+ })
channel: Channel;
@Column({ nullable: true })
@@ -48,7 +52,9 @@ export class Webhook extends BaseClass {
application_id: string;
@JoinColumn({ name: "application_id" })
- @ManyToOne(() => Application)
+ @ManyToOne(() => Application, {
+ onDelete: "CASCADE",
+ })
application: Application;
@Column({ nullable: true })
@@ -56,7 +62,9 @@ export class Webhook extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE",
+ })
user: User;
@Column({ nullable: true })
@@ -64,6 +72,8 @@ export class Webhook extends BaseClass {
source_guild_id: string;
@JoinColumn({ name: "source_guild_id" })
- @ManyToOne(() => Guild)
+ @ManyToOne(() => Guild, {
+ onDelete: "CASCADE",
+ })
source_guild: Guild;
}
diff --git a/util/src/index.ts b/util/src/index.ts
index f3bd9e9b..fc00d46b 100644
--- a/util/src/index.ts
+++ b/util/src/index.ts
@@ -4,6 +4,7 @@ import "reflect-metadata";
export * from "./util/index";
export * from "./interfaces/index";
export * from "./entities/index";
+export * from "./dtos/index";
// import Config from "../util/Config";
// import db, { MongooseCache, toObject } from "./util/Database";
diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts
index aff50300..03099bbb 100644
--- a/util/src/interfaces/Event.ts
+++ b/util/src/interfaces/Event.ts
@@ -127,6 +127,22 @@ export interface ChannelPinsUpdateEvent extends Event {
};
}
+export interface ChannelRecipientAddEvent extends Event {
+ event: "CHANNEL_RECIPIENT_ADD";
+ data: {
+ channel_id: string;
+ user: User;
+ };
+}
+
+export interface ChannelRecipientRemoveEvent extends Event {
+ event: "CHANNEL_RECIPIENT_REMOVE";
+ data: {
+ channel_id: string;
+ user: User;
+ };
+}
+
export interface GuildCreateEvent extends Event {
event: "GUILD_CREATE";
data: Guild & {
@@ -436,6 +452,8 @@ export type EventData =
| ChannelUpdateEvent
| ChannelDeleteEvent
| ChannelPinsUpdateEvent
+ | ChannelRecipientAddEvent
+ | ChannelRecipientRemoveEvent
| GuildCreateEvent
| GuildUpdateEvent
| GuildDeleteEvent
@@ -482,6 +500,8 @@ export enum EVENTEnum {
ChannelUpdate = "CHANNEL_UPDATE",
ChannelDelete = "CHANNEL_DELETE",
ChannelPinsUpdate = "CHANNEL_PINS_UPDATE",
+ ChannelRecipientAdd = "CHANNEL_RECIPIENT_ADD",
+ ChannelRecipientRemove = "CHANNEL_RECIPIENT_REMOVE",
GuildCreate = "GUILD_CREATE",
GuildUpdate = "GUILD_UPDATE",
GuildDelete = "GUILD_DELETE",
@@ -525,6 +545,8 @@ export type EVENT =
| "CHANNEL_UPDATE"
| "CHANNEL_DELETE"
| "CHANNEL_PINS_UPDATE"
+ | "CHANNEL_RECIPIENT_ADD"
+ | "CHANNEL_RECIPIENT_REMOVE"
| "GUILD_CREATE"
| "GUILD_UPDATE"
| "GUILD_DELETE"
diff --git a/util/src/util/Array.ts b/util/src/util/Array.ts
new file mode 100644
index 00000000..27f7c961
--- /dev/null
+++ b/util/src/util/Array.ts
@@ -0,0 +1,3 @@
+export function containsAll(arr: any[], target: any[]) {
+ return target.every(v => arr.includes(v));
+}
\ No newline at end of file
diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts
index d3844cd9..c22d8abd 100644
--- a/util/src/util/Database.ts
+++ b/util/src/util/Database.ts
@@ -21,7 +21,7 @@ export function initDatabase() {
//
entities: Object.values(Models).filter((x) => x.constructor.name !== "Object"),
synchronize: true,
- logging: false,
+ logging: true,
cache: {
duration: 1000 * 3, // cache all find queries for 3 seconds
},
diff --git a/util/src/util/Permissions.ts b/util/src/util/Permissions.ts
index 9d87253a..44852f1e 100644
--- a/util/src/util/Permissions.ts
+++ b/util/src/util/Permissions.ts
@@ -92,6 +92,7 @@ export class Permissions extends BitField {
}
overwriteChannel(overwrites: ChannelPermissionOverwrite[]) {
+ if (!overwrites) return this
if (!this.cache) throw new Error("permission chache not available");
overwrites = overwrites.filter((x) => {
if (x.type === 0 && this.cache.roles?.some((r) => r.id === x.id)) return true;
diff --git a/util/src/util/cdn.ts b/util/src/util/cdn.ts
new file mode 100644
index 00000000..754d6244
--- /dev/null
+++ b/util/src/util/cdn.ts
@@ -0,0 +1,54 @@
+import FormData from "form-data";
+import { HTTPError } from "lambert-server";
+import fetch from "node-fetch";
+import { Config } from "./Config";
+import multer from "multer";
+
+export async function uploadFile(path: string, file: Express.Multer.File) {
+ const form = new FormData();
+ form.append("file", file.buffer, {
+ contentType: file.mimetype,
+ filename: file.originalname,
+ });
+
+ const response = await fetch(`${Config.get().cdn.endpoint || "http://localhost:3003"}${path}`, {
+ headers: {
+ signature: Config.get().security.requestSignature,
+ ...form.getHeaders(),
+ },
+ method: "POST",
+ body: form,
+ });
+ const result = await response.json();
+
+ if (response.status !== 200) throw result;
+ return result;
+}
+
+export async function handleFile(path: string, body?: string): Promise<string | undefined> {
+ if (!body || !body.startsWith("data:")) return body;
+ try {
+ const mimetype = body.split(":")[1].split(";")[0];
+ const buffer = Buffer.from(body.split(",")[1], "base64");
+
+ // @ts-ignore
+ const { id } = await uploadFile(path, { buffer, mimetype, originalname: "banner" });
+ return id;
+ } catch (error) {
+ console.error(error);
+ throw new HTTPError("Invalid " + path);
+ }
+}
+
+export async function deleteFile(path: string) {
+ const response = await fetch(`${Config.get().cdn.endpoint || "http://localhost:3003"}${path}`, {
+ headers: {
+ signature: Config.get().security.requestSignature,
+ },
+ method: "DELETE",
+ });
+ const result = await response.json();
+
+ if (response.status !== 200) throw result;
+ return result;
+}
diff --git a/util/src/util/index.ts b/util/src/util/index.ts
index 4e92f017..3160380f 100644
--- a/util/src/util/index.ts
+++ b/util/src/util/index.ts
@@ -1,6 +1,7 @@
export * from "./ApiError";
export * from "./BitField";
export * from "./checkToken";
+export * from "./cdn";
export * from "./Config";
export * from "./Constants";
export * from "./Database";
@@ -12,3 +13,4 @@ export * from "./RabbitMQ";
export * from "./Regex";
export * from "./Snowflake";
export * from "./String";
+export * from "./Array";
|