From a567551c8a6b03a956d21354a5504b9f9e1f40b1 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Tue, 24 Aug 2021 16:35:04 +0200 Subject: :construction: api --- api/src/util/Channel.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'api/src/util/Channel.ts') diff --git a/api/src/util/Channel.ts b/api/src/util/Channel.ts index fb6f9c8c..a618d2df 100644 --- a/api/src/util/Channel.ts +++ b/api/src/util/Channel.ts @@ -1,10 +1,10 @@ import { ChannelCreateEvent, - ChannelModel, + Channel, ChannelType, emitEvent, getPermission, - GuildModel, + Guild, Snowflake, TextChannel, toObject, @@ -29,7 +29,7 @@ export async function createChannel( case ChannelType.GUILD_TEXT: case ChannelType.GUILD_VOICE: if (channel.parent_id && !opts?.skipExistsCheck) { - const exists = await ChannelModel.findOne({ id: channel.parent_id }, { guild_id: true }).exec(); + const exists = await Channel.findOneOrFail({ id: channel.parent_id }, { guild_id: true }); if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); if (exists.guild_id !== channel.guild_id) throw new HTTPError("The category channel needs to be in the guild"); } @@ -49,7 +49,7 @@ export async function createChannel( if (!channel.permission_overwrites) channel.permission_overwrites = []; // TODO: auto generate position - channel = await new ChannelModel({ + channel = await new Channel({ ...channel, ...(!opts?.keepId && { id: Snowflake.generate() }), created_at: new Date(), @@ -57,7 +57,7 @@ export async function createChannel( recipient_ids: null }).save(); - await emitEvent({ event: "CHANNEL_CREATE", data: toObject(channel), guild_id: channel.guild_id } as ChannelCreateEvent); + await emitEvent({ event: "CHANNEL_CREATE", data: channel), guild_id: channel.guild_id } as ChannelCreateEvent; return channel; } -- cgit 1.5.1 From 0ecc5d8c0e353676e9f5bdbc724bb33572d3b572 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 27 Aug 2021 11:10:42 +0200 Subject: :construction: api --- api/__tests__/routes/auth/login.ts | 1 - api/jest.config.ts | 4 +- api/package-lock.json | 1131 ++++++++++++++++++++++++-- api/package.json | 6 +- api/scripts/setup_test.js | 1 + api/src/middlewares/ErrorHandler.ts | 1 + api/src/routes/auth/login.ts | 12 +- api/src/routes/auth/register.ts | 59 +- api/src/routes/guilds/#guild_id/channels.ts | 12 +- api/src/routes/guilds/#guild_id/templates.ts | 60 +- api/src/routes/guilds/templates/index.ts | 7 +- api/src/routes/invites/index.ts | 4 +- api/src/routes/users/#id/profile.ts | 2 +- api/src/routes/users/@me/relationships.ts | 36 +- api/src/util/Channel.ts | 2 +- api/src/util/User.ts | 16 - api/tests/routes/auth/login.ts | 1 + 17 files changed, 1160 insertions(+), 195 deletions(-) delete mode 100644 api/__tests__/routes/auth/login.ts create mode 100644 api/scripts/setup_test.js delete mode 100644 api/src/util/User.ts create mode 100644 api/tests/routes/auth/login.ts (limited to 'api/src/util/Channel.ts') diff --git a/api/__tests__/routes/auth/login.ts b/api/__tests__/routes/auth/login.ts deleted file mode 100644 index 536e03ee..00000000 --- a/api/__tests__/routes/auth/login.ts +++ /dev/null @@ -1 +0,0 @@ -it("works", () => {}); diff --git a/api/jest.config.ts b/api/jest.config.ts index 153a9b2b..50550d9d 100644 --- a/api/jest.config.ts +++ b/api/jest.config.ts @@ -145,9 +145,9 @@ export default { // The glob patterns Jest uses to detect test files testMatch: [ - "**/__tests__/**/*.[jt]s?(x)", + "**/tests/**/*.[jt]s?(x)" // "**/?(*.)+(spec|test).[tj]s?(x)" - ], + ] // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped // testPathIgnorePatterns: [ diff --git a/api/package-lock.json b/api/package-lock.json index 3221969a..bff4e940 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -34,13 +34,15 @@ "mongoose-autopopulate": "^0.12.3", "mongoose-long": "^0.3.2", "multer": "^1.4.2", - "node-fetch": "^2.6.1" + "node-fetch": "^2.6.1", + "typeorm": "^0.2.37" }, "devDependencies": { "@types/amqplib": "^0.8.1", "@types/bcrypt": "^5.0.0", "@types/express": "^4.17.9", "@types/i18next-node-fs-backend": "^2.1.0", + "@types/jest": "^27.0.1", "@types/jsonwebtoken": "^8.5.0", "@types/mongodb": "^3.6.9", "@types/mongoose": "^5.10.5", @@ -74,9 +76,9 @@ "jsonwebtoken": "^8.5.1", "missing-native-js-functions": "^1.2.10", "node-fetch": "^2.6.1", + "patch-package": "^6.4.7", "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", - "ts-transform-json-schema": "^2.0.3", "typeorm": "^0.2.37", "typescript": "^4.3.5", "typescript-json-schema": "^0.50.1" @@ -1115,6 +1117,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@sqltools/formatter": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz", + "integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==" + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1282,6 +1289,101 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.1.tgz", + "integrity": "sha512-HTLpVXHrY69556ozYkcq47TtQJXpcWAWfkoqz+ZGz2JnmZhzlRjprCIyFnetSy8gpDWwTTGBcRVv1J1I1vBrHw==", + "dev": true, + "dependencies": { + "jest-diff": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/types": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz", + "integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@types/jest/node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/diff-sequences": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz", + "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-diff": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz", + "integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-get-type": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz", + "integrity": "sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz", + "integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "ansi-regex": "^5.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/@types/jsonwebtoken": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", @@ -1425,6 +1527,11 @@ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", "dev": true }, + "node_modules/@types/zen-observable": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz", + "integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==" + }, "node_modules/@zerollup/ts-helpers": { "version": "1.7.18", "resolved": "https://registry.npmjs.org/@zerollup/ts-helpers/-/ts-helpers-1.7.18.tgz", @@ -1667,7 +1774,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, "engines": { "node": ">=8" } @@ -1692,6 +1798,11 @@ "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=", "dev": true }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -1705,6 +1816,14 @@ "node": ">= 8" } }, + "node_modules/app-root-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", + "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==", + "engines": { + "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", @@ -2116,7 +2235,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -2927,6 +3045,111 @@ "node": ">=0.10.0" } }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "node_modules/cli-highlight/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -4114,8 +4337,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -4169,7 +4391,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -4183,7 +4404,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -4693,6 +4913,14 @@ "bser": "2.1.1" } }, + "node_modules/figlet": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", + "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4908,7 +5136,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5035,7 +5262,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -5047,7 +5273,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5174,6 +5399,14 @@ "node": ">=10.0.0" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -5356,7 +5589,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -7394,6 +7626,16 @@ "integrity": "sha512-nU7mOEuaXiQIB/EgTIjYZJ7g8KqMm2D8l4qp+DqA4jxWOb/tnb1KEoqp+tlbdQIDIAiC1i7j7X/3yHDFXLxr9g==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoassert": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", @@ -8004,6 +8246,14 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, + "node_modules/parent-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", + "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/parents": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", @@ -8669,6 +8919,11 @@ "node": ">=8.10.0" } }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -8720,7 +8975,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9190,6 +9444,11 @@ "node": ">=6" } }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "node_modules/saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -9315,7 +9574,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -10119,7 +10377,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.0" }, @@ -10339,6 +10596,25 @@ "node": ">=8" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", @@ -10776,45 +11052,269 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true, + "node_modules/typeorm": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.37.tgz", + "integrity": "sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==", + "dependencies": { + "@sqltools/formatter": "^1.2.2", + "app-root-path": "^3.0.0", + "buffer": "^6.0.3", + "chalk": "^4.1.0", + "cli-highlight": "^2.1.11", + "debug": "^4.3.1", + "dotenv": "^8.2.0", + "glob": "^7.1.6", + "js-yaml": "^4.0.0", + "mkdirp": "^1.0.4", + "reflect-metadata": "^0.1.13", + "sha.js": "^2.4.11", + "tslib": "^2.1.0", + "xml2js": "^0.4.23", + "yargonaut": "^1.1.4", + "yargs": "^17.0.1", + "zen-observable-ts": "^1.0.0" + }, "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "typeorm": "cli.js" }, - "engines": { - "node": ">=4.2.0" + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@sap/hana-client": "*", + "better-sqlite3": "*", + "hdb-pool": "*", + "ioredis": "*", + "mongodb": "^3.6.0", + "mssql": "*", + "mysql2": "*", + "oracledb": "*", + "pg": "*", + "pg-native": "*", + "pg-query-stream": "*", + "redis": "*", + "sql.js": "*", + "sqlite3": "*", + "typeorm-aurora-data-api-driver": "*" + }, + "peerDependenciesMeta": { + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } } }, - "node_modules/umd": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", - "dev": true, - "bin": { - "umd": "bin/cli.js" - } + "node_modules/typeorm/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/undeclared-identifiers": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", - "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", - "dev": true, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "acorn-node": "^1.3.0", - "dash-ast": "^1.0.0", - "get-assigned-identifiers": "^1.2.0", - "simple-concat": "^1.0.0", - "xtend": "^4.0.1" - }, - "bin": { - "undeclared-identifiers": "bin.js" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/union-value": { + "node_modules/typeorm/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/typeorm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/typeorm/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typeorm/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typeorm/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/typeorm/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/typeorm/node_modules/yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/typeorm/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true, + "bin": { + "umd": "bin/cli.js" + } + }, + "node_modules/undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + }, + "bin": { + "undeclared-identifiers": "bin.js" + } + }, + "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", @@ -11274,6 +11774,26 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -11299,6 +11819,66 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yargonaut": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", + "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", + "dependencies": { + "chalk": "^1.1.1", + "figlet": "^1.1.1", + "parent-require": "^1.0.0" + } + }, + "node_modules/yargonaut/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", @@ -11366,6 +11946,20 @@ "node": ">=6" } }, + "node_modules/zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "node_modules/zen-observable-ts": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz", + "integrity": "sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==", + "dependencies": { + "@types/zen-observable": "0.8.3", + "zen-observable": "0.8.15" + } + }, "node_modules/zip-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", @@ -11884,9 +12478,9 @@ "jsonwebtoken": "^8.5.1", "missing-native-js-functions": "^1.2.10", "node-fetch": "^2.6.1", + "patch-package": "^6.4.7", "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", - "ts-transform-json-schema": "^2.0.3", "typeorm": "^0.2.37", "typescript": "^4.3.5", "typescript-json-schema": "^0.50.1" @@ -12227,6 +12821,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "@sqltools/formatter": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz", + "integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==" + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -12391,6 +12990,82 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.1.tgz", + "integrity": "sha512-HTLpVXHrY69556ozYkcq47TtQJXpcWAWfkoqz+ZGz2JnmZhzlRjprCIyFnetSy8gpDWwTTGBcRVv1J1I1vBrHw==", + "dev": true, + "requires": { + "jest-diff": "^27.0.0", + "pretty-format": "^27.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz", + "integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "diff-sequences": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz", + "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==", + "dev": true + }, + "jest-diff": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz", + "integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "jest-get-type": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz", + "integrity": "sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==", + "dev": true + }, + "pretty-format": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz", + "integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==", + "dev": true, + "requires": { + "@jest/types": "^27.0.6", + "ansi-regex": "^5.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + } + } + }, "@types/jsonwebtoken": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", @@ -12534,6 +13209,11 @@ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", "dev": true }, + "@types/zen-observable": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz", + "integrity": "sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==" + }, "@zerollup/ts-helpers": { "version": "1.7.18", "resolved": "https://registry.npmjs.org/@zerollup/ts-helpers/-/ts-helpers-1.7.18.tgz", @@ -12721,8 +13401,7 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { "version": "4.3.0", @@ -12738,6 +13417,11 @@ "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, "anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -12748,6 +13432,11 @@ "picomatch": "^2.0.4" } }, + "app-root-path": { + "version": "3.0.0", + "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", @@ -13102,8 +13791,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bcrypt": { "version": "5.0.1", @@ -13797,6 +14485,85 @@ } } }, + "cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "requires": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "dependencies": { + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -14824,8 +15591,7 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "encodeurl": { "version": "1.0.2", @@ -14869,8 +15635,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -14880,8 +15645,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "2.0.0", @@ -15298,6 +16062,11 @@ "bser": "2.1.1" } }, + "figlet": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", + "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -15470,8 +16239,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-package-type": { "version": "0.1.0", @@ -15562,7 +16330,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" }, @@ -15570,8 +16337,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" } } }, @@ -15676,6 +16442,11 @@ "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -15830,8 +16601,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "5.1.8", @@ -17453,6 +18223,16 @@ "integrity": "sha512-nU7mOEuaXiQIB/EgTIjYZJ7g8KqMm2D8l4qp+DqA4jxWOb/tnb1KEoqp+tlbdQIDIAiC1i7j7X/3yHDFXLxr9g==", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "nanoassert": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", @@ -17936,6 +18716,11 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, + "parent-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", + "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=" + }, "parents": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", @@ -18491,6 +19276,11 @@ "picomatch": "^2.2.1" } }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -18532,8 +19322,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-from-string": { "version": "2.0.2", @@ -18884,6 +19673,11 @@ "sparse-bitfield": "^3.0.3" } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -18994,7 +19788,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -19699,7 +20492,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -19864,6 +20656,22 @@ "minimatch": "^3.0.4" } }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", @@ -20207,6 +21015,118 @@ "is-typedarray": "^1.0.0" } }, + "typeorm": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.37.tgz", + "integrity": "sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==", + "requires": { + "@sqltools/formatter": "^1.2.2", + "app-root-path": "^3.0.0", + "buffer": "^6.0.3", + "chalk": "^4.1.0", + "cli-highlight": "^2.1.11", + "debug": "^4.3.1", + "dotenv": "^8.2.0", + "glob": "^7.1.6", + "js-yaml": "^4.0.0", + "mkdirp": "^1.0.4", + "reflect-metadata": "^0.1.13", + "sha.js": "^2.4.11", + "tslib": "^2.1.0", + "xml2js": "^0.4.23", + "yargonaut": "^1.1.4", + "yargs": "^17.0.1", + "zen-observable-ts": "^1.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, "typescript": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", @@ -20623,6 +21543,20 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", @@ -20645,6 +21579,53 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yargonaut": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz", + "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", + "requires": { + "chalk": "^1.1.1", + "figlet": "^1.1.1", + "parent-require": "^1.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", @@ -20699,6 +21680,20 @@ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, + "zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "zen-observable-ts": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz", + "integrity": "sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==", + "requires": { + "@types/zen-observable": "0.8.3", + "zen-observable": "0.8.15" + } + }, "zip-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", diff --git a/api/package.json b/api/package.json index 7b2de011..1310d577 100644 --- a/api/package.json +++ b/api/package.json @@ -5,7 +5,7 @@ "main": "dist/Server.js", "types": "dist/Server.d.ts", "scripts": { - "test": "jest", + "test": "npm run build && jest", "test:watch": "jest --watch", "start": "npm run build && node dist/start", "build": "npx tsc -b .", @@ -35,6 +35,7 @@ "@types/bcrypt": "^5.0.0", "@types/express": "^4.17.9", "@types/i18next-node-fs-backend": "^2.1.0", + "@types/jest": "^27.0.1", "@types/jsonwebtoken": "^8.5.0", "@types/mongodb": "^3.6.9", "@types/mongoose": "^5.10.5", @@ -79,6 +80,7 @@ "mongoose-autopopulate": "^0.12.3", "mongoose-long": "^0.3.2", "multer": "^1.4.2", - "node-fetch": "^2.6.1" + "node-fetch": "^2.6.1", + "typeorm": "^0.2.37" } } diff --git a/api/scripts/setup_test.js b/api/scripts/setup_test.js new file mode 100644 index 00000000..4b692f7c --- /dev/null +++ b/api/scripts/setup_test.js @@ -0,0 +1 @@ +// TODO: start api diff --git a/api/src/middlewares/ErrorHandler.ts b/api/src/middlewares/ErrorHandler.ts index d080e498..8e2cd923 100644 --- a/api/src/middlewares/ErrorHandler.ts +++ b/api/src/middlewares/ErrorHandler.ts @@ -2,6 +2,7 @@ import { NextFunction, Request, Response } from "express"; import { HTTPError } from "lambert-server"; import { FieldError } from "../util/instanceOf"; +// TODO: update with new body/typorm validation export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) { if (!error) next(); diff --git a/api/src/routes/auth/login.ts b/api/src/routes/auth/login.ts index 579a097e..c0acad4e 100644 --- a/api/src/routes/auth/login.ts +++ b/api/src/routes/auth/login.ts @@ -21,9 +21,6 @@ router.post( async (req: Request, res: Response) => { const { login, password, captcha_key, undelete } = req.body; const email = adjustEmail(login); - const query: any[] = [{ phone: login }]; - if (email) query.push({ email }); - console.log(req.body, email); const config = Config.get(); @@ -41,11 +38,10 @@ router.post( // TODO: check captcha } - const user = await User.findOneOrFail( - { $or: query }, - { "data.hash": true, id: true, disabled: true, deleted: true, "settings.locale": true, "settings.theme": true } - ).catch((e) => { - console.log(e, query); + const user = await User.findOneOrFail({ + where: [{ phone: login }, { email: login }], + select: ["data", "id", "disabled", "deleted", "settings"] + }).catch((e) => { throw FieldErrors({ login: { message: req.t("auth:login.INVALID_LOGIN"), code: "INVALID_LOGIN" } }); }); diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts index 1405e219..62b039d5 100644 --- a/api/src/routes/auth/register.ts +++ b/api/src/routes/auth/register.ts @@ -1,12 +1,12 @@ import { Request, Response, Router } from "express"; -import { trimSpecial, User, Snowflake, User, Config } from "@fosscord/util"; +import { trimSpecial, User, Snowflake, Config } from "@fosscord/util"; import bcrypt from "bcrypt"; import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf"; import "missing-native-js-functions"; import { generateToken } from "./login"; import { getIpAdress, IPAnalysis, isProxy } from "../../util/ipAddress"; import { HTTPError } from "lambert-server"; -import RateLimit from "../../middlewares/RateLimit"; +import { In } from "typeorm"; const router: Router = Router(); @@ -55,13 +55,13 @@ router.post( // TODO: check password strength // adjusted_email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick - let adjusted_email: string | null = adjustEmail(email); + let adjusted_email = adjustEmail(email); // adjusted_password will be the hash of the password - let adjusted_password: string = ""; + let adjusted_password = ""; // trim special uf8 control characters -> Backspace, Newline, ... - let adjusted_username: string = trimSpecial(username); + let adjusted_username = trimSpecial(username); // discriminator will be randomly generated let discriminator = ""; @@ -129,7 +129,7 @@ router.post( if (!register.allowMultipleAccounts) { // TODO: check if fingerprint was eligible generated - const exists = await User.findOneOrFail({ fingerprints: fingerprint }).catch((e) => {}); + const exists = await User.findOne({ where: { fingerprints: In(fingerprint) } }); if (exists) { throw FieldErrors({ @@ -164,12 +164,8 @@ router.post( // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the mongodb database? for (let tries = 0; tries < 5; tries++) { discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); - try { - exists = await User.findOneOrFail({ discriminator, username: adjusted_username }, "id"); - } catch (error) { - // doesn't exist -> break - break; - } + exists = await User.findOne({ where: { discriminator, username: adjusted_username }, select: ["id"] }); + if (!exists) break; } if (exists) { @@ -185,35 +181,26 @@ router.post( // appearently discord doesn't save the date of birth and just calculate if nsfw is allowed // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false - const user: User = { + const user = await new User({ id: Snowflake.generate(), created_at: new Date(), username: adjusted_username, discriminator, - avatar: null, - accent_color: null, - banner: null, + avatar: undefined, + accent_color: undefined, + banner: undefined, bot: false, system: false, desktop: false, mobile: false, premium: true, premium_type: 2, - phone: null, + phone: undefined, bio: "", mfa_enabled: false, verified: false, disabled: false, deleted: false, - presence: { - activities: [], - client_status: { - desktop: undefined, - mobile: undefined, - web: undefined - }, - status: "offline" - }, email: adjusted_email, nsfw_allowed: true, // TODO: depending on age public_flags: 0n, @@ -221,10 +208,7 @@ router.post( guilds: [], data: { hash: adjusted_password, - valid_tokens_since: new Date(), - relationships: [], - connected_accounts: [], - fingerprints: [] + valid_tokens_since: new Date() }, settings: { afk_timeout: 300, @@ -234,10 +218,10 @@ router.post( contact_sync_enabled: false, convert_emoticons: false, custom_status: { - emoji_id: null, - emoji_name: null, - expires_at: null, - text: null + emoji_id: undefined, + emoji_name: undefined, + expires_at: undefined, + text: undefined }, default_guilds_restricted: false, detect_platform_accounts: true, @@ -265,16 +249,13 @@ router.post( timezone_offset: 0 // timezone_offset: // TODO: timezone from request } - }; - - // insert user into database - await new User(user).save(); + }).save(); return res.json({ token: await generateToken(user.id) }); } ); -export function adjustEmail(email: string): string | null { +export function adjustEmail(email: string): string | undefined { // body parser already checked if it is a valid email const parts = email.match(EMAIL_REGEX); // @ts-ignore diff --git a/api/src/routes/guilds/#guild_id/channels.ts b/api/src/routes/guilds/#guild_id/channels.ts index b53c9a5a..7b0e94b6 100644 --- a/api/src/routes/guilds/#guild_id/channels.ts +++ b/api/src/routes/guilds/#guild_id/channels.ts @@ -1,5 +1,5 @@ import { Router, Response, Request } from "express"; -import { Channel, toObject, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util"; +import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { ChannelModifySchema } from "../../../schema/Channel"; @@ -48,15 +48,19 @@ router.patch( if (x.parent_id) { opts.parent_id = x.parent_id; - const parent_channel = await Channel.findOneOrFail({ id: x.parent_id, guild_id }, { permission_overwrites: true }); + const parent_channel = await Channel.findOneOrFail({ + where: { id: x.parent_id, guild_id }, + select: ["permission_overwrites"] + }); if (x.lock_permissions) { opts.permission_overwrites = parent_channel.permission_overwrites; } } - const channel = await Channel.findOneOrFailAndUpdate({ id: x.id, guild_id }, opts, { new: true }); + await Channel.update({ guild_id, id: x.id }, opts); + const channel = await Channel.findOneOrFail({ guild_id, id: x.id }); - await emitEvent({ event: "CHANNEL_UPDATE", data: channel), channel_id: x.id, guild_id } as ChannelUpdateEvent; + await emitEvent({ event: "CHANNEL_UPDATE", data: channel, channel_id: x.id, guild_id } as ChannelUpdateEvent); }) ]); diff --git a/api/src/routes/guilds/#guild_id/templates.ts b/api/src/routes/guilds/#guild_id/templates.ts index 13917dbd..e1d2f5fd 100644 --- a/api/src/routes/guilds/#guild_id/templates.ts +++ b/api/src/routes/guilds/#guild_id/templates.ts @@ -1,5 +1,5 @@ import { Request, Response, Router } from "express"; -import { TemplateModel, Guild, getPermission, toObject, User, Snowflake } from "@fosscord/util"; +import { Guild, getPermission, Template } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { TemplateCreateSchema, TemplateModifySchema } from "../../../schema/Template"; import { check } from "../../../util/instanceOf"; @@ -7,22 +7,22 @@ import { generateCode } from "../../../util/String"; const router: Router = Router(); -const TemplateGuildProjection = { - name: true, - description: true, - region: true, - verification_level: true, - default_message_notifications: true, - explicit_content_filter: true, - preferred_locale: true, - afk_timeout: true, - roles: true, - channels: true, - afk_channel_id: true, - system_channel_id: true, - system_channel_flags: true, - icon_hash: true -}; +const TemplateGuildProjection: (keyof Guild)[] = [ + "name", + "description", + "region", + "verification_level", + "default_message_notifications", + "explicit_content_filter", + "preferred_locale", + "afk_timeout", + "roles", + "channels", + "afk_channel_id", + "system_channel_id", + "system_channel_flags", + "icon" +]; router.get("/", async (req: Request, res: Response) => { const { guild_id } = req.params; @@ -34,14 +34,14 @@ router.get("/", async (req: Request, res: Response) => { router.post("/", check(TemplateCreateSchema), async (req: Request, res: Response) => { const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ id: guild_id }, TemplateGuildProjection); + const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: TemplateGuildProjection }); const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_GUILD"); const exists = await Template.findOneOrFail({ id: guild_id }).catch((e) => {}); if (exists) throw new HTTPError("Template already exists", 400); - const template = await new TemplateModel({ + const template = await new Template({ ...req.body, code: generateCode(), creator_id: req.user_id, @@ -51,7 +51,7 @@ router.post("/", check(TemplateCreateSchema), async (req: Request, res: Response serialized_source_guild: guild }).save(); - res.json(template)).send(; + res.json(template); }); router.delete("/:code", async (req: Request, res: Response) => { @@ -61,37 +61,39 @@ router.delete("/:code", async (req: Request, res: Response) => { const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_GUILD"); - const template = await Template.findOneOrFailAndDelete({ + const template = await Template.delete({ code }); - res.send(template); + res.json(template); }); router.put("/:code", async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const { code } = req.params; + // synchronizes the template + const { code, guild_id } = req.params; - const guild = await Guild.findOneOrFail({ id: guild_id }, TemplateGuildProjection); + const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: TemplateGuildProjection }); const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_GUILD"); - const template = await Template.findOneOrFailAndUpdate({ code }, { serialized_source_guild: guild }, { new: true }); + const template = await new Template({ code, serialized_source_guild: guild }).save(); - res.json(template)).send(; + res.json(template); }); router.patch("/:code", check(TemplateModifySchema), async (req: Request, res: Response) => { + // updates the template description const { guild_id } = req.params; const { code } = req.params; + const { name, description } = req.body; const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_GUILD"); - const template = await Template.findOneOrFailAndUpdate({ code }, { name: req.body.name, description: req.body.description }, { new: true }); + const template = await new Template({ code, name: name, description: description }).save(); - res.json(template)).send(; + res.json(template); }); export default router; diff --git a/api/src/routes/guilds/templates/index.ts b/api/src/routes/guilds/templates/index.ts index ad8b676b..b8c1012d 100644 --- a/api/src/routes/guilds/templates/index.ts +++ b/api/src/routes/guilds/templates/index.ts @@ -1,9 +1,8 @@ import { Request, Response, Router } from "express"; const router: Router = Router(); -import { TemplateModel, Guild, toObject, User, Role, Snowflake, Guild, Config } from "@fosscord/util"; +import { Template, Guild, Role, Snowflake, Config, User } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { GuildTemplateCreateSchema } from "../../../schema/Guild"; -import { getPublicUser } from "../../../util/User"; import { check } from "../../../util/instanceOf"; import { addMember } from "../../../util/Member"; @@ -12,7 +11,7 @@ router.get("/:code", async (req: Request, res: Response) => { const template = await Template.findOneOrFail({ code: code }); - res.json(template)).send(; + res.json(template); }); router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res: Response) => { @@ -20,7 +19,7 @@ router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res const body = req.body as GuildTemplateCreateSchema; const { maxGuilds } = Config.get().limits.user; - const user = await getPublicUser(req.user_id, { guilds: true }); + const user = await User.getPublicUser(req.user_id, { guilds: true }); if (user.guilds.length >= maxGuilds) { throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403); diff --git a/api/src/routes/invites/index.ts b/api/src/routes/invites/index.ts index e871af86..a488c44b 100644 --- a/api/src/routes/invites/index.ts +++ b/api/src/routes/invites/index.ts @@ -1,5 +1,5 @@ import { Router, Request, Response } from "express"; -import { getPermission, Guild, InviteModel, toObject } from "@fosscord/util"; +import { getPermission, Guild, Invite } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { addMember } from "../../util/Member"; const router: Router = Router(); @@ -40,7 +40,7 @@ router.delete("/:code", async (req: Request, res: Response) => { await Guild.update({ vanity_url_code: code }, { $unset: { vanity_url_code: 1 } }).catch((e) => {}); - res.status(200).send({ invite: invite) }; + res.json({ invite: invite }); }); export default router; diff --git a/api/src/routes/users/#id/profile.ts b/api/src/routes/users/#id/profile.ts index 46c96698..9b2e2d5e 100644 --- a/api/src/routes/users/#id/profile.ts +++ b/api/src/routes/users/#id/profile.ts @@ -7,7 +7,7 @@ router.get("/", async (req: Request, res: Response) => { const user = await getPublicUser(req.params.id, { data: true }); res.json({ - connected_accounts: user.data.connected_accounts, + connected_accounts: user.connected_accounts, premium_guild_since: null, // TODO premium_since: null, // TODO user: { diff --git a/api/src/routes/users/@me/relationships.ts b/api/src/routes/users/@me/relationships.ts index 9b8d6199..8c7469ed 100644 --- a/api/src/routes/users/@me/relationships.ts +++ b/api/src/routes/users/@me/relationships.ts @@ -15,14 +15,14 @@ import { check, Length } from "../../../util/instanceOf"; const router = Router(); -const userProjection = { "user_data.relationships": true, ...PublicUserProjection }; +const userProjection = { "data.relationships": true, ...PublicUserProjection }; router.get("/", async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ id: req.user_id }, { user_data: { relationships: true } }).populate({ - path: "user_data.relationships.id", + const user = await User.findOneOrFail({ id: req.user_id }, { data: { relationships: true } }).populate({ + path: "data.relationships.id", model: User }); - return res.json(user.user_data.relationships); + return res.json(user.data.relationships); }); async function addRelationship(req: Request, res: Response, friend: UserDocument, type: RelationshipType) { @@ -30,8 +30,8 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument if (id === req.user_id) throw new HTTPError("You can't add yourself as a friend"); const user = await User.findOneOrFail({ id: req.user_id }, userProjection); - const newUserRelationships = [...user.user_data.relationships]; - const newFriendRelationships = [...friend.user_data.relationships]; + const newUserRelationships = [...user.data.relationships]; + const newFriendRelationships = [...friend.data.relationships]; var relationship = newUserRelationships.find((x) => x.id === id); const friendRequest = newFriendRelationships.find((x) => x.id === req.user_id); @@ -48,7 +48,7 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument if (friendRequest && friendRequest.type !== RelationshipType.blocked) { newFriendRelationships.remove(friendRequest); await Promise.all([ - User.update({ id: friend.id }, { "user_data.relationships": newFriendRelationships }), + User.update({ id: friend.id }, { "data.relationships": newFriendRelationships }), emitEvent({ event: "RELATIONSHIP_REMOVE", data: friendRequest, @@ -58,12 +58,12 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument } await Promise.all([ - User.update({ id: req.user_id }, { "user_data.relationships": newUserRelationships }), + User.update({ id: req.user_id }, { "data.relationships": newUserRelationships }), emitEvent({ event: "RELATIONSHIP_ADD", data: { ...relationship, - user: { ...friend, user_data: undefined } + user: { ...friend, data: undefined } }, user_id: req.user_id } as RelationshipAddEvent) @@ -91,13 +91,13 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument } else newUserRelationships.push(outgoing_relationship); await Promise.all([ - User.update({ id: req.user_id }, { "user_data.relationships": newUserRelationships }), - User.update({ id: friend.id }, { "user_data.relationships": newFriendRelationships }), + User.update({ id: req.user_id }, { "data.relationships": newUserRelationships }), + User.update({ id: friend.id }, { "data.relationships": newFriendRelationships }), emitEvent({ event: "RELATIONSHIP_ADD", data: { ...outgoing_relationship, - user: { ...friend, user_data: undefined } + user: { ...friend, data: undefined } }, user_id: req.user_id } as RelationshipAddEvent), @@ -106,7 +106,7 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument data: { ...incoming_relationship, should_notify: true, - user: { ...user, user_data: undefined } + user: { ...user, data: undefined } }, user_id: id } as RelationshipAddEvent) @@ -138,11 +138,11 @@ router.delete("/:id", async (req: Request, res: Response) => { const friend = await User.findOneOrFail({ id }, userProjection); if (!friend) throw new HTTPError("User not found", 404); - const relationship = user.user_data.relationships.find((x) => x.id === id); - const friendRequest = friend.user_data.relationships.find((x) => x.id === req.user_id); + const relationship = user.data.relationships.find((x) => x.id === id); + const friendRequest = friend.data.relationships.find((x) => x.id === req.user_id); if (relationship?.type === RelationshipType.blocked) { // unblock user - user.user_data.relationships.remove(relationship); + user.data.relationships.remove(relationship); await Promise.all([ user.save(), @@ -153,8 +153,8 @@ router.delete("/:id", async (req: Request, res: Response) => { if (!relationship || !friendRequest) throw new HTTPError("You are not friends with the user", 404); if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you"); - user.user_data.relationships.remove(relationship); - friend.user_data.relationships.remove(friendRequest); + user.data.relationships.remove(relationship); + friend.data.relationships.remove(friendRequest); await Promise.all([ user.save(), diff --git a/api/src/util/Channel.ts b/api/src/util/Channel.ts index a618d2df..f518aefd 100644 --- a/api/src/util/Channel.ts +++ b/api/src/util/Channel.ts @@ -57,7 +57,7 @@ export async function createChannel( recipient_ids: null }).save(); - await emitEvent({ event: "CHANNEL_CREATE", data: channel), guild_id: channel.guild_id } as ChannelCreateEvent; + await emitEvent({ event: "CHANNEL_CREATE", data: channel, guild_id: channel.guild_id } as ChannelCreateEvent); return channel; } diff --git a/api/src/util/User.ts b/api/src/util/User.ts deleted file mode 100644 index 4d9065c4..00000000 --- a/api/src/util/User.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { toObject, User, PublicUserProjection } from "@fosscord/util"; -import { HTTPError } from "lambert-server"; - -export { PublicUserProjection }; - -export async function getPublicUser(user_id: string, additional_fields?: any) { - const user = await User.findOneOrFail( - { id: user_id }, - { - ...PublicUserProjection, - ...additional_fields - } - ); - if (!user) throw new HTTPError("User not found", 404); - return user; -} diff --git a/api/tests/routes/auth/login.ts b/api/tests/routes/auth/login.ts new file mode 100644 index 00000000..e1938b36 --- /dev/null +++ b/api/tests/routes/auth/login.ts @@ -0,0 +1 @@ +test("works", () => {}); -- cgit 1.5.1 From e99008a1a5fe6eb64821235bb56d34d620e08fe7 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 29 Aug 2021 00:03:40 +0200 Subject: :sparkles: typeorm api rewrite done --- api/src/Server.ts | 23 +-- api/src/index.ts | 2 - api/src/middlewares/Authentication.ts | 2 +- api/src/routes/auth/register.ts | 56 ------ api/src/routes/channels/#channel_id/index.ts | 30 +-- api/src/routes/channels/#channel_id/invites.ts | 4 +- .../#channel_id/messages/#message_id/ack.ts | 2 +- .../#channel_id/messages/#message_id/index.ts | 28 +-- .../#channel_id/messages/#message_id/reactions.ts | 45 +++-- .../channels/#channel_id/messages/bulk-delete.ts | 7 +- .../routes/channels/#channel_id/messages/index.ts | 30 ++- api/src/routes/channels/#channel_id/permissions.ts | 29 +-- api/src/routes/channels/#channel_id/pins.ts | 76 +++---- api/src/routes/channels/#channel_id/typing.ts | 4 +- api/src/routes/channels/#channel_id/webhooks.ts | 2 +- api/src/routes/guilds/#guild_id/bans.ts | 71 ++++--- api/src/routes/guilds/#guild_id/delete.ts | 19 +- api/src/routes/guilds/#guild_id/index.ts | 39 ++-- api/src/routes/guilds/#guild_id/invites.ts | 2 +- .../guilds/#guild_id/members/#member_id/index.ts | 27 +-- .../guilds/#guild_id/members/#member_id/nick.ts | 5 +- .../members/#member_id/roles/#role_id/index.ts | 7 +- api/src/routes/guilds/#guild_id/members/index.ts | 21 +- api/src/routes/guilds/#guild_id/roles.ts | 68 +++---- api/src/routes/guilds/#guild_id/vanity-url.ts | 38 ++-- api/src/routes/guilds/#guild_id/welcome_screen.ts | 28 +-- api/src/routes/guilds/#guild_id/widget.json.ts | 95 ++------- api/src/routes/guilds/index.ts | 17 +- api/src/routes/guilds/templates/index.ts | 25 ++- api/src/routes/invites/index.ts | 19 +- api/src/routes/users/#id/index.ts | 5 +- api/src/routes/users/#id/profile.ts | 4 +- api/src/routes/users/@me/channels.ts | 23 +-- api/src/routes/users/@me/delete.ts | 13 +- api/src/routes/users/@me/disable.ts | 9 +- api/src/routes/users/@me/guilds.ts | 19 +- api/src/routes/users/@me/index.ts | 47 +++-- api/src/routes/users/@me/profile.ts | 6 +- api/src/routes/users/@me/relationships.ts | 85 ++++---- api/src/schema/Guild.ts | 80 ++------ api/src/schema/Message.ts | 8 +- api/src/schema/Roles.ts | 2 +- api/src/test/mongo_test.ts | 40 ---- api/src/test/server_benchmark.ts | 39 ---- api/src/test/test.ts | 3 - api/src/util/Channel.ts | 17 +- api/src/util/Member.ts | 220 --------------------- api/src/util/Message.ts | 19 +- 48 files changed, 467 insertions(+), 993 deletions(-) delete mode 100644 api/src/test/mongo_test.ts delete mode 100644 api/src/test/server_benchmark.ts delete mode 100644 api/src/test/test.ts delete mode 100644 api/src/util/Member.ts (limited to 'api/src/util/Channel.ts') diff --git a/api/src/Server.ts b/api/src/Server.ts index adaf7f2c..0f444f86 100644 --- a/api/src/Server.ts +++ b/api/src/Server.ts @@ -2,7 +2,7 @@ import "missing-native-js-functions"; import { Connection } from "mongoose"; import { Server, ServerOptions } from "lambert-server"; import { Authentication, CORS } from "./middlewares/"; -import { Config, db, initEvent } from "@fosscord/util"; +import { Config, initDatabase, initEvent } from "@fosscord/util"; import { ErrorHandler } from "./middlewares/ErrorHandler"; import { BodyParser } from "./middlewares/BodyParser"; import { Router, Request, Response, NextFunction } from "express"; @@ -31,30 +31,13 @@ export class FosscordServer extends Server { super({ ...opts, errorHandler: false, jsonBody: false }); } - async setupSchema() { - return Promise.all([ - db.collection("users").createIndex({ id: 1 }, { unique: true }), - db.collection("messages").createIndex({ id: 1 }, { unique: true }), - db.collection("channels").createIndex({ id: 1 }, { unique: true }), - db.collection("guilds").createIndex({ id: 1 }, { unique: true }), - db.collection("members").createIndex({ id: 1, guild_id: 1 }, { unique: true }), - db.collection("roles").createIndex({ id: 1 }, { unique: true }), - db.collection("emojis").createIndex({ id: 1 }, { unique: true }), - db.collection("invites").createIndex({ code: 1 }, { unique: true }), - db.collection("invites").createIndex({ expires_at: 1 }, { expireAfterSeconds: 0 }), // after 0 seconds of expires_at the invite will get delete - db.collection("ratelimits").createIndex({ expires_at: 1 }, { expireAfterSeconds: 0 }) - ]); - } - async start() { - // @ts-ignore - await (db as Promise); - await this.setupSchema(); + await initDatabase(); await Config.init(); await initEvent(); this.app.use(CORS); - this.app.use(BodyParser({ inflate: true, limit: 1024 * 1024 * 10 })); // 2MB + this.app.use(BodyParser({ inflate: true, limit: 1024 * 1024 * 10 })); // 10MB const app = this.app; const api = Router(); // @ts-ignore diff --git a/api/src/index.ts b/api/src/index.ts index 73b9dbd2..0bba7f5e 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -8,8 +8,6 @@ export * from "./schema/Message"; export * from "./util/Constants"; export * from "./util/instanceOf"; export * from "./util/instanceOf"; -export * from "./util/Member"; export * from "./util/RandomInviteID"; export * from "./util/String"; -export * from "./util/User"; export { check as checkPassword } from "./util/passwordStrength"; diff --git a/api/src/middlewares/Authentication.ts b/api/src/middlewares/Authentication.ts index a8bfe196..34a66a6b 100644 --- a/api/src/middlewares/Authentication.ts +++ b/api/src/middlewares/Authentication.ts @@ -28,7 +28,7 @@ declare global { export async function Authentication(req: Request, res: Response, next: NextFunction) { if (req.method === "OPTIONS") return res.sendStatus(204); const url = req.url.replace(API_PREFIX, ""); - if (url.startsWith("/invites") && req.method === "GET") return next(); // @ts-ignore + if (url.startsWith("/invites") && req.method === "GET") return next(); if ( NO_AUTHORIZATION_ROUTES.some((x) => { if (typeof x === "string") return url.startsWith(x); diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts index 62b039d5..5ad6d6a4 100644 --- a/api/src/routes/auth/register.ts +++ b/api/src/routes/auth/register.ts @@ -182,72 +182,16 @@ router.post( // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false const user = await new User({ - id: Snowflake.generate(), - created_at: new Date(), username: adjusted_username, discriminator, - avatar: undefined, - accent_color: undefined, - banner: undefined, - bot: false, - system: false, - desktop: false, - mobile: false, premium: true, premium_type: 2, - phone: undefined, - bio: "", - mfa_enabled: false, - verified: false, - disabled: false, - deleted: false, email: adjusted_email, nsfw_allowed: true, // TODO: depending on age - public_flags: 0n, - flags: 0n, // TODO: generate default flags guilds: [], data: { hash: adjusted_password, valid_tokens_since: new Date() - }, - settings: { - afk_timeout: 300, - allow_accessibility_detection: true, - animate_emoji: true, - animate_stickers: 0, - contact_sync_enabled: false, - convert_emoticons: false, - custom_status: { - emoji_id: undefined, - emoji_name: undefined, - expires_at: undefined, - text: undefined - }, - default_guilds_restricted: false, - detect_platform_accounts: true, - developer_mode: false, - disable_games_tab: false, - enable_tts_command: true, - explicit_content_filter: 0, - friend_source_flags: { all: true }, - gateway_connected: false, - gif_auto_play: true, - guild_folders: [], - guild_positions: [], - inline_attachment_media: true, - inline_embed_media: true, - locale: req.language, - message_display_compact: false, - native_phone_integration_enabled: true, - render_embeds: true, - render_reactions: true, - restricted_guilds: [], - show_current_game: true, - status: "offline", - stream_notifications_enabled: true, - theme: "dark", - timezone_offset: 0 - // timezone_offset: // TODO: timezone from request } }).save(); diff --git a/api/src/routes/channels/#channel_id/index.ts b/api/src/routes/channels/#channel_id/index.ts index 91534602..4aa5a5b9 100644 --- a/api/src/routes/channels/#channel_id/index.ts +++ b/api/src/routes/channels/#channel_id/index.ts @@ -1,4 +1,4 @@ -import { ChannelDeleteEvent, Channel, ChannelUpdateEvent, emitEvent, getPermission, GuildUpdateEvent, toObject } from "@fosscord/util"; +import { ChannelDeleteEvent, Channel, ChannelUpdateEvent, emitEvent, getPermission } from "@fosscord/util"; import { Router, Response, Request } from "express"; import { HTTPError } from "lambert-server"; import { ChannelModifySchema } from "../../../schema/Channel"; @@ -23,7 +23,7 @@ router.delete("/", async (req: Request, res: Response) => { const channel = await Channel.findOneOrFail({ id: channel_id }); - const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel }); + const permission = await getPermission(req.user_id, channel?.guild_id, channel_id); permission.hasThrow("MANAGE_CHANNELS"); // TODO: Dm channel "close" not delete @@ -31,7 +31,7 @@ router.delete("/", async (req: Request, res: Response) => { await emitEvent({ event: "CHANNEL_DELETE", data, channel_id } as ChannelDeleteEvent); - await Channel.deleteOne({ id: channel_id }); + await Channel.delete({ id: channel_id }); res.send(data); }); @@ -43,17 +43,19 @@ router.patch("/", check(ChannelModifySchema), async (req: Request, res: Response const permission = await getPermission(req.user_id, undefined, channel_id); permission.hasThrow("MANAGE_CHANNELS"); - const channel = await Channel.findOneOrFailAndUpdate({ id: channel_id }, payload, { new: true }); - - const data = channel; - - await emitEvent({ - event: "CHANNEL_UPDATE", - data, - channel_id - } as ChannelUpdateEvent); - - res.send(data); + const channel = await Channel.findOneOrFail({ id: channel_id }); + channel.assign(payload); + + await Promise.all([ + channel.save(), + emitEvent({ + event: "CHANNEL_UPDATE", + data: channel, + channel_id + } as ChannelUpdateEvent) + ]); + + res.send(channel); }); export default router; diff --git a/api/src/routes/channels/#channel_id/invites.ts b/api/src/routes/channels/#channel_id/invites.ts index cc77df26..35006ac4 100644 --- a/api/src/routes/channels/#channel_id/invites.ts +++ b/api/src/routes/channels/#channel_id/invites.ts @@ -6,7 +6,7 @@ import { random } from "../../../util/RandomInviteID"; import { InviteCreateSchema } from "../../../schema/Invite"; -import { getPermission, Channel, InviteModel, InviteCreateEvent, toObject, emitEvent } from "@fosscord/util"; +import { getPermission, Channel, Invite, InviteCreateEvent, emitEvent } from "@fosscord/util"; const router: Router = Router(); @@ -38,7 +38,7 @@ router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) inviter_id: user_id }; - await new InviteModel(invite).save(); + await new Invite(invite).save(); await emitEvent({ event: "INVITE_CREATE", data: invite, guild_id } as InviteCreateEvent); res.status(201).send(invite); diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/ack.ts b/api/src/routes/channels/#channel_id/messages/#message_id/ack.ts index ff576d90..0fd5f2be 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/ack.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/ack.ts @@ -14,7 +14,7 @@ router.post("/", check({ $manual: Boolean, $mention_count: Number }), async (req const permission = await getPermission(req.user_id, undefined, channel_id); permission.hasThrow("VIEW_CHANNEL"); - await ReadState.update({ user_id: req.user_id, channel_id, message_id }, { user_id: req.user_id, channel_id, message_id }); + await ReadState.update({ user_id: req.user_id, channel_id }, { user_id: req.user_id, channel_id, last_message_id: message_id }); await emitEvent({ event: "MESSAGE_ACK", diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts index 058e2390..32478763 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts @@ -1,6 +1,5 @@ -import { Channel, emitEvent, getPermission, MessageDeleteEvent, Message, MessageUpdateEvent, toObject } from "@fosscord/util"; +import { Channel, emitEvent, getPermission, MessageDeleteEvent, Message, MessageUpdateEvent } from "@fosscord/util"; import { Router, Response, Request } from "express"; -import { HTTPError } from "lambert-server"; import { MessageCreateSchema } from "../../../../../schema/Message"; import { check } from "../../../../../util/instanceOf"; @@ -12,7 +11,7 @@ router.patch("/", check(MessageCreateSchema), async (req: Request, res: Response const { message_id, channel_id } = req.params; var body = req.body as MessageCreateSchema; - var message = await Message.findOneOrFail({ id: message_id, channel_id }, { author_id: true, message_reference: true }); + const message = await Message.findOneOrFail({ id: message_id, channel_id }); const permissions = await getPermission(req.user_id, undefined, channel_id); @@ -29,15 +28,16 @@ router.patch("/", check(MessageCreateSchema), async (req: Request, res: Response id: message_id, edited_timestamp: new Date() }); + message.assign(opts); - // @ts-ignore - message = await Message.findOneOrFailAndUpdate({ id: message_id }, opts, { new: true }).populate("author"); - - await emitEvent({ - event: "MESSAGE_UPDATE", - channel_id, - data: { ...message, nonce: undefined } - } as MessageUpdateEvent); + await Promise.all([ + message.save(), + await emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + data: { ...message, nonce: undefined } + } as MessageUpdateEvent) + ]); postHandleMessage(message); @@ -49,13 +49,13 @@ router.patch("/", check(MessageCreateSchema), async (req: Request, res: Response router.delete("/", async (req: Request, res: Response) => { const { message_id, channel_id } = req.params; - const channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true }); - const message = await Message.findOneOrFail({ id: message_id }, { author_id: true }); + const channel = await Channel.findOneOrFail({ id: channel_id }); + const message = await Message.findOneOrFail({ id: message_id }); const permission = await getPermission(req.user_id, channel.guild_id, channel_id); if (message.author_id !== req.user_id) permission.hasThrow("MANAGE_MESSAGES"); - await Message.deleteOne({ id: message_id }); + await Message.delete({ id: message_id }); await emitEvent({ event: "MESSAGE_DELETE", diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/reactions.ts b/api/src/routes/channels/#channel_id/messages/#message_id/reactions.ts index 9a6e4436..f60484b5 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/reactions.ts @@ -1,7 +1,7 @@ import { Channel, emitEvent, - EmojiModel, + Emoji, getPermission, Member, Message, @@ -11,11 +11,11 @@ import { MessageReactionRemoveEvent, PartialEmoji, PublicUserProjection, - toObject, User } from "@fosscord/util"; import { Router, Response, Request } from "express"; import { HTTPError } from "lambert-server"; +import { In } from "typeorm"; const router = Router(); // TODO: check if emoji is really an unicode emoji or a prperly encoded external emoji @@ -38,12 +38,12 @@ function getEmoji(emoji: string): PartialEmoji { router.delete("/", async (req: Request, res: Response) => { const { message_id, channel_id } = req.params; - const channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true }); + const channel = await Channel.findOneOrFail({ id: channel_id }); const permissions = await getPermission(req.user_id, undefined, channel_id); permissions.hasThrow("MANAGE_MESSAGES"); - await Message.findOneOrFailAndUpdate({ id: message_id, channel_id }, { reactions: [] }, { new: true }); + await Message.update({ id: message_id, channel_id }, { reactions: [] }); await emitEvent({ event: "MESSAGE_REACTION_REMOVE_ALL", @@ -62,8 +62,6 @@ router.delete("/:emoji", async (req: Request, res: Response) => { const { message_id, channel_id } = req.params; const emoji = getEmoji(req.params.emoji); - const channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true }); - const permissions = await getPermission(req.user_id, undefined, channel_id); permissions.hasThrow("MANAGE_MESSAGES"); @@ -73,18 +71,19 @@ router.delete("/:emoji", async (req: Request, res: Response) => { if (!already_added) throw new HTTPError("Reaction not found", 404); message.reactions.remove(already_added); - await Message.update({ id: message_id, channel_id }, message); - - await emitEvent({ - event: "MESSAGE_REACTION_REMOVE_EMOJI", - channel_id, - data: { + await Promise.all([ + message.save(), + emitEvent({ + event: "MESSAGE_REACTION_REMOVE_EMOJI", channel_id, - message_id, - guild_id: channel.guild_id, - emoji - } - } as MessageReactionRemoveEmojiEvent); + data: { + channel_id, + message_id, + guild_id: message.guild_id, + emoji + } + } as MessageReactionRemoveEmojiEvent) + ]); res.sendStatus(204); }); @@ -94,14 +93,18 @@ router.get("/:emoji", async (req: Request, res: Response) => { const emoji = getEmoji(req.params.emoji); const message = await Message.findOneOrFail({ id: message_id, channel_id }); - if (!message) throw new HTTPError("Message not found", 404); const reaction = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); if (!reaction) throw new HTTPError("Reaction not found", 404); const permissions = await getPermission(req.user_id, undefined, channel_id); permissions.hasThrow("VIEW_CHANNEL"); - const users = await User.find({ id: { $in: reaction.user_ids } }, PublicUserProjection); + const users = await User.find({ + where: { + id: In(reaction.user_ids) + }, + select: PublicUserProjection + }); res.json(users); }); @@ -111,7 +114,7 @@ router.put("/:emoji/:user_id", async (req: Request, res: Response) => { if (user_id !== "@me") throw new HTTPError("Invalid user"); const emoji = getEmoji(req.params.emoji); - const channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true }); + const channel = await Channel.findOneOrFail({ id: channel_id }); const message = await Message.findOneOrFail({ id: message_id, channel_id }); const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); @@ -156,7 +159,7 @@ router.delete("/:emoji/:user_id", async (req: Request, res: Response) => { const emoji = getEmoji(req.params.emoji); - const channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true }); + const channel = await Channel.findOneOrFail({ id: channel_id }); const message = await Message.findOneOrFail({ id: message_id, channel_id }); const permissions = await getPermission(req.user_id, undefined, channel_id); diff --git a/api/src/routes/channels/#channel_id/messages/bulk-delete.ts b/api/src/routes/channels/#channel_id/messages/bulk-delete.ts index 028a5d73..5c486676 100644 --- a/api/src/routes/channels/#channel_id/messages/bulk-delete.ts +++ b/api/src/routes/channels/#channel_id/messages/bulk-delete.ts @@ -3,6 +3,7 @@ import { Channel, Config, emitEvent, getPermission, MessageDeleteBulkEvent, Mess import { HTTPError } from "lambert-server"; import { check } from "../../../../util/instanceOf"; +import { In } from "typeorm"; const router: Router = Router(); @@ -13,10 +14,10 @@ export default router; // https://discord.com/developers/docs/resources/channel#bulk-delete-messages router.post("/", check({ messages: [String] }), async (req: Request, res: Response) => { const { channel_id } = req.params; - const channel = await Channel.findOneOrFail({ id: channel_id }, { permission_overwrites: true, guild_id: true }); + const channel = await Channel.findOneOrFail({ id: channel_id }); if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); - const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel }); + const permission = await getPermission(req.user_id, channel?.guild_id, channel_id); permission.hasThrow("MANAGE_MESSAGES"); const { maxBulkDelete } = Config.get().limits.message; @@ -25,7 +26,7 @@ router.post("/", check({ messages: [String] }), async (req: Request, res: Respon if (messages.length < 2) throw new HTTPError("You must at least specify 2 messages to bulk delete"); if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`); - await Message.deleteMany({ id: { $in: messages } }); + await Message.delete({ id: In(messages) }); await emitEvent({ event: "MESSAGE_DELETE_BULK", diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index b5c650bb..6307c022 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -1,5 +1,5 @@ import { Router, Response, Request } from "express"; -import { Attachment, Channel, ChannelType, getPermission, MessageDocument, Message, toObject } from "@fosscord/util"; +import { Attachment, Channel, ChannelType, getPermission, Message } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { MessageCreateSchema } from "../../../../schema/Message"; import { check, instanceOf, Length } from "../../../../util/instanceOf"; @@ -7,6 +7,7 @@ import multer from "multer"; import { Query } from "mongoose"; import { sendMessage } from "../../../../util/Message"; import { uploadFile } from "../../../../util/cdn"; +import { FindManyOptions, LessThan, MoreThan } from "typeorm"; const router: Router = Router(); @@ -55,24 +56,21 @@ router.get("/", async (req: Request, res: Response) => { permissions.hasThrow("VIEW_CHANNEL"); if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); - var query: Query; - if (after) query = Message.find({ channel_id, id: { $gt: after } }); - else if (before) query = Message.find({ channel_id, id: { $lt: before } }); - else if (around) - query = Message.find({ - channel_id, - id: { $gt: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() } - }); - else { - query = Message.find({ channel_id }); - } + var query: FindManyOptions & { where: { id?: any } } = { order: { id: "DESC" }, take: limit, where: { channel_id } }; - query = query.sort({ id: -1 }); + if (after) query.where.id = MoreThan(after); + else if (before) query.where.id = LessThan(before); + else if (around) { + query.where.id = [ + MoreThan((BigInt(around) - BigInt(halfLimit)).toString()), + LessThan((BigInt(around) + BigInt(halfLimit)).toString()) + ]; + } - const messages = await query.limit(limit); + const messages = await Message.find(query); return res.json(messages).map((x) => { - (x.reactions || []).forEach((x) => { + (x.reactions || []).forEach((x: any) => { // @ts-ignore if ((x.user_ids || []).includes(req.user_id)) x.me = true; // @ts-ignore @@ -135,7 +133,7 @@ router.post("/", messageUpload.single("file"), async (req: Request, res: Respons embeds, channel_id, attachments, - edited_timestamp: null + edited_timestamp: undefined }); return res.send(data); diff --git a/api/src/routes/channels/#channel_id/permissions.ts b/api/src/routes/channels/#channel_id/permissions.ts index 2990e35b..9c49542b 100644 --- a/api/src/routes/channels/#channel_id/permissions.ts +++ b/api/src/routes/channels/#channel_id/permissions.ts @@ -1,4 +1,4 @@ -import { Channel, ChannelPermissionOverwrite, ChannelUpdateEvent, emitEvent, getPermission, Member, Role, toObject } from "@fosscord/util"; +import { Channel, ChannelPermissionOverwrite, ChannelUpdateEvent, emitEvent, getPermission, Member, Role } from "@fosscord/util"; import { Router, Response, Request } from "express"; import { HTTPError } from "lambert-server"; @@ -11,16 +11,16 @@ router.put("/:overwrite_id", check({ allow: String, deny: String, type: Number, const { channel_id, overwrite_id } = req.params; const body = req.body as { allow: bigint; deny: bigint; type: number; id: string }; - var channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true, permission_overwrites: true }); + var channel = await Channel.findOneOrFail({ id: channel_id }); if (!channel.guild_id) throw new HTTPError("Channel not found", 404); const permissions = await getPermission(req.user_id, channel.guild_id, channel_id); permissions.hasThrow("MANAGE_ROLES"); if (body.type === 0) { - if (!(await Role.exists({ id: overwrite_id }))) throw new HTTPError("role not found", 404); + if (!(await Role.count({ id: overwrite_id }))) throw new HTTPError("role not found", 404); } else if (body.type === 1) { - if (!(await Member.exists({ id: overwrite_id }))) throw new HTTPError("user not found", 404); + if (!(await Member.count({ id: overwrite_id }))) throw new HTTPError("user not found", 404); } else throw new HTTPError("type not supported", 501); // @ts-ignore @@ -57,18 +57,19 @@ router.delete("/:overwrite_id", async (req: Request, res: Response) => { const permissions = await getPermission(req.user_id, undefined, channel_id); permissions.hasThrow("MANAGE_ROLES"); - const channel = await Channel.findOneOrFailAndUpdate( - { id: channel_id }, - { $pull: { permission_overwrites: { id: overwrite_id } } }, - { new: true } - ); + const channel = await Channel.findOneOrFail({ id: channel_id }); if (!channel.guild_id) throw new HTTPError("Channel not found", 404); - await emitEvent({ - event: "CHANNEL_UPDATE", - channel_id, - data: channel - } as ChannelUpdateEvent); + channel.permission_overwrites = channel.permission_overwrites.filter((x) => x.id === overwrite_id); + + await Promise.all([ + channel.save(), + emitEvent({ + event: "CHANNEL_UPDATE", + channel_id, + data: channel + } as ChannelUpdateEvent) + ]); return res.sendStatus(204); }); diff --git a/api/src/routes/channels/#channel_id/pins.ts b/api/src/routes/channels/#channel_id/pins.ts index 3ed42ab4..d83e36ed 100644 --- a/api/src/routes/channels/#channel_id/pins.ts +++ b/api/src/routes/channels/#channel_id/pins.ts @@ -1,4 +1,4 @@ -import { Channel, ChannelPinsUpdateEvent, Config, emitEvent, getPermission, Message, MessageUpdateEvent, toObject } from "@fosscord/util"; +import { Channel, ChannelPinsUpdateEvent, Config, emitEvent, getPermission, Message, MessageUpdateEvent } from "@fosscord/util"; import { Router, Request, Response } from "express"; import { HTTPError } from "lambert-server"; @@ -6,35 +6,36 @@ const router: Router = Router(); router.put("/:message_id", async (req: Request, res: Response) => { const { channel_id, message_id } = req.params; - const channel = await Channel.findOneOrFail({ id: channel_id }); - const permission = await getPermission(req.user_id, channel.guild_id, channel_id); + + const message = await Message.findOneOrFail({ id: message_id }); + const permission = await getPermission(req.user_id, message.guild_id, channel_id); permission.hasThrow("VIEW_CHANNEL"); // * in dm channels anyone can pin messages -> only check for guilds - if (channel.guild_id) permission.hasThrow("MANAGE_MESSAGES"); + if (message.guild_id) permission.hasThrow("MANAGE_MESSAGES"); - const pinned_count = await Messagecount({ channel_id, pinned: true }); + const pinned_count = await Message.count({ channel_id, pinned: true }); const { maxPins } = Config.get().limits.channel; if (pinned_count >= maxPins) throw new HTTPError("Max pin count reached: " + maxPins); - await Message.update({ id: message_id }, { pinned: true }); - const message = await Message.findOneOrFail({ id: message_id }); - - await emitEvent({ - event: "MESSAGE_UPDATE", - channel_id, - data: message - } as MessageUpdateEvent); + await Promise.all([ + Message.update({ id: message_id }, { pinned: true }), + emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + data: message + } as MessageUpdateEvent), - await emitEvent({ - event: "CHANNEL_PINS_UPDATE", - channel_id, - data: { + emitEvent({ + event: "CHANNEL_PINS_UPDATE", channel_id, - guild_id: channel.guild_id, - last_pin_timestamp: undefined - } - } as ChannelPinsUpdateEvent); + data: { + channel_id, + guild_id: message.guild_id, + last_pin_timestamp: undefined + } + } as ChannelPinsUpdateEvent) + ]); res.sendStatus(204); }); @@ -48,23 +49,28 @@ router.delete("/:message_id", async (req: Request, res: Response) => { permission.hasThrow("VIEW_CHANNEL"); if (channel.guild_id) permission.hasThrow("MANAGE_MESSAGES"); - const message = await Message.findOneOrFailAndUpdate({ id: message_id }, { pinned: false }, { new: true }); + const message = await Message.findOneOrFail({ id: message_id }); + message.pinned = false; - await emitEvent({ - event: "MESSAGE_UPDATE", - channel_id, - data: message - } as MessageUpdateEvent); + await Promise.all([ + message.save(), + + emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + data: message + } as MessageUpdateEvent), - await emitEvent({ - event: "CHANNEL_PINS_UPDATE", - channel_id, - data: { + emitEvent({ + event: "CHANNEL_PINS_UPDATE", channel_id, - guild_id: channel.guild_id, - last_pin_timestamp: undefined - } - } as ChannelPinsUpdateEvent); + data: { + channel_id, + guild_id: channel.guild_id, + last_pin_timestamp: undefined + } + } as ChannelPinsUpdateEvent) + ]); res.sendStatus(204); }); diff --git a/api/src/routes/channels/#channel_id/typing.ts b/api/src/routes/channels/#channel_id/typing.ts index 298d2f50..2305e8e8 100644 --- a/api/src/routes/channels/#channel_id/typing.ts +++ b/api/src/routes/channels/#channel_id/typing.ts @@ -1,4 +1,4 @@ -import { Channel, emitEvent, Member, toObject, TypingStartEvent } from "@fosscord/util"; +import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util"; import { Router, Request, Response } from "express"; import { HTTPError } from "lambert-server"; @@ -17,7 +17,7 @@ router.post("/", async (req: Request, res: Response) => { channel_id: channel_id, data: { // this is the paylod - member: member, + member: { ...member, roles: member.role_ids }, channel_id, timestamp, user_id, diff --git a/api/src/routes/channels/#channel_id/webhooks.ts b/api/src/routes/channels/#channel_id/webhooks.ts index 38a8226b..775053ba 100644 --- a/api/src/routes/channels/#channel_id/webhooks.ts +++ b/api/src/routes/channels/#channel_id/webhooks.ts @@ -10,7 +10,7 @@ const router: Router = Router(); // TODO: use Image Data Type for avatar instead of String router.post("/", check({ name: new Length(String, 1, 80), $avatar: String }), async (req: Request, res: Response) => { const channel_id = req.params.channel_id; - const channel = await Channel.findOneOrFail({ id: channel_id }, { guild_id: true, type: true }); + const channel = await Channel.findOneOrFail({ id: channel_id }); isTextChannel(channel.type); if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400); diff --git a/api/src/routes/guilds/#guild_id/bans.ts b/api/src/routes/guilds/#guild_id/bans.ts index cbc0b0fa..b84a68a7 100644 --- a/api/src/routes/guilds/#guild_id/bans.ts +++ b/api/src/routes/guilds/#guild_id/bans.ts @@ -1,22 +1,17 @@ import { Request, Response, Router } from "express"; -import { BanModel, emitEvent, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, Guild, toObject } from "@fosscord/util"; +import { emitEvent, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, Guild, Ban, User, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { getIpAdress } from "../../../util/ipAddress"; import { BanCreateSchema } from "../../../schema/Ban"; import { check } from "../../../util/instanceOf"; -import { removeMember } from "../../../util/Member"; -import { getPublicUser } from "../../../util/User"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { const { guild_id } = req.params; - const guild = await Guild.exists({ id: guild_id }); - if (!guild) throw new HTTPError("Guild not found", 404); - - var bans = await Ban.find({ guild_id: guild_id }, { user_id: true, reason: true }); + var bans = await Ban.find({ guild_id: guild_id }); return res.json(bans); }); @@ -32,29 +27,32 @@ router.put("/:user_id", check(BanCreateSchema), async (req: Request, res: Respon const { guild_id } = req.params; const banned_user_id = req.params.user_id; - const banned_user = await getPublicUser(banned_user_id); + const banned_user = await User.getPublicUser(banned_user_id); const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("BAN_MEMBERS"); if (req.user_id === banned_user_id) throw new HTTPError("You can't ban yourself", 400); + if (perms.cache.guild?.owner_id === banned_user_id) throw new HTTPError("You can't ban the owner", 400); - await removeMember(banned_user_id, guild_id); - - const ban = await new BanModel({ + const ban = new Ban({ user_id: banned_user_id, guild_id: guild_id, ip: getIpAdress(req), executor_id: req.user_id, reason: req.body.reason // || otherwise empty - }).save(); + }); - await emitEvent({ - event: "GUILD_BAN_ADD", - data: { - guild_id: guild_id, - user: banned_user - }, - guild_id: guild_id - } as GuildBanAddEvent); + await Promise.all([ + Member.removeFromGuild(banned_user_id, guild_id), + ban.save(), + emitEvent({ + event: "GUILD_BAN_ADD", + data: { + guild_id: guild_id, + user: banned_user + }, + guild_id: guild_id + } as GuildBanAddEvent) + ]); return res.json(ban); }); @@ -63,26 +61,25 @@ router.delete("/:user_id", async (req: Request, res: Response) => { var { guild_id } = req.params; var banned_user_id = req.params.user_id; - const banned_user = await getPublicUser(banned_user_id); - const guild = await Guild.exists({ id: guild_id }); - if (!guild) throw new HTTPError("Guild not found", 404); - + const banned_user = await User.getPublicUser(banned_user_id); const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("BAN_MEMBERS"); - await Ban.deleteOne({ - user_id: banned_user_id, - guild_id - }); - - await emitEvent({ - event: "GUILD_BAN_REMOVE", - data: { - guild_id, - user: banned_user - }, - guild_id - } as GuildBanRemoveEvent); + await Promise.all([ + Ban.delete({ + user_id: banned_user_id, + guild_id + }), + + emitEvent({ + event: "GUILD_BAN_REMOVE", + data: { + guild_id, + user: banned_user + }, + guild_id + } as GuildBanRemoveEvent) + ]); return res.status(204).send(); }); diff --git a/api/src/routes/guilds/#guild_id/delete.ts b/api/src/routes/guilds/#guild_id/delete.ts index a53271ce..043260e9 100644 --- a/api/src/routes/guilds/#guild_id/delete.ts +++ b/api/src/routes/guilds/#guild_id/delete.ts @@ -1,4 +1,4 @@ -import { Channel, emitEvent, EmojiModel, GuildDeleteEvent, Guild, InviteModel, Member, Message, Role, User } from "@fosscord/util"; +import { Channel, emitEvent, GuildDeleteEvent, Guild, Member, Message, Role, Invite, Emoji } from "@fosscord/util"; import { Router, Request, Response } from "express"; import { HTTPError } from "lambert-server"; @@ -9,7 +9,7 @@ const router = Router(); router.post("/", async (req: Request, res: Response) => { var { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ id: guild_id }, "owner_id"); + const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: ["owner_id"] }); if (guild.owner_id !== req.user_id) throw new HTTPError("You are not the owner of this guild", 401); await emitEvent({ @@ -21,14 +21,13 @@ router.post("/", async (req: Request, res: Response) => { } as GuildDeleteEvent); await Promise.all([ - Guild.deleteOne({ id: guild_id }), - User.updateMany({ guilds: guild_id }, { $pull: { guilds: guild_id } }), - Role.deleteMany({ guild_id }), - Channel.deleteMany({ guild_id }), - Emoji.deleteMany({ guild_id }), - Invite.deleteMany({ guild_id }), - Message.deleteMany({ guild_id }), - Member.deleteMany({ guild_id }) + Guild.delete({ id: guild_id }), + Role.delete({ guild_id }), + Channel.delete({ guild_id }), + Emoji.delete({ guild_id }), + Invite.delete({ guild_id }), + Message.delete({ guild_id }), + Member.delete({ guild_id }) ]); return res.sendStatus(204); diff --git a/api/src/routes/guilds/#guild_id/index.ts b/api/src/routes/guilds/#guild_id/index.ts index af9ea9d6..d205b164 100644 --- a/api/src/routes/guilds/#guild_id/index.ts +++ b/api/src/routes/guilds/#guild_id/index.ts @@ -1,19 +1,5 @@ import { Request, Response, Router } from "express"; -import { - Channel, - emitEvent, - EmojiModel, - getPermission, - GuildDeleteEvent, - Guild, - GuildUpdateEvent, - InviteModel, - Member, - Message, - Role, - toObject, - User -} from "@fosscord/util"; +import { emitEvent, getPermission, Guild, GuildUpdateEvent, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { GuildUpdateSchema } from "../../../schema/Guild"; @@ -26,9 +12,15 @@ const router = Router(); router.get("/", async (req: Request, res: Response) => { const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ id: guild_id }).populate({ path: "joined_at", match: { id: req.user_id } }); - const member = await Member.exists({ guild_id: guild_id, id: req.user_id }); - if (!member) throw new HTTPError("You are not a member of the guild you are trying to access", 401); + const [guild, member_count, member] = await Promise.all([ + Guild.findOneOrFail({ id: guild_id }), + Member.count({ guild_id: guild_id, id: req.user_id }), + Member.findOneOrFail(req.user_id) + ]); + if (!member_count) throw new HTTPError("You are not a member of the guild you are trying to access", 401); + + // @ts-ignore + guild.joined_at = member?.joined_at; return res.json(guild); }); @@ -45,15 +37,12 @@ router.patch("/", check(GuildUpdateSchema), async (req: Request, res: Response) if (body.banner) body.banner = await handleFile(`/banners/${guild_id}`, body.banner); if (body.splash) body.splash = await handleFile(`/splashes/${guild_id}`, body.splash); - const guild = await Guild.findOneOrFailAndUpdate({ id: guild_id }, body, { new: true }).populate({ - path: "joined_at", - match: { id: req.user_id } - }); - const data = guild; + const guild = await Guild.findOneOrFail({ id: guild_id }); + guild.assign(body); - emitEvent({ event: "GUILD_UPDATE", data: data, guild_id } as GuildUpdateEvent); + await Promise.all([guild.save(), emitEvent({ event: "GUILD_UPDATE", data: guild, guild_id } as GuildUpdateEvent)]); - return res.json(data); + return res.json(guild); }); export default router; diff --git a/api/src/routes/guilds/#guild_id/invites.ts b/api/src/routes/guilds/#guild_id/invites.ts index ca72cce8..1843b689 100644 --- a/api/src/routes/guilds/#guild_id/invites.ts +++ b/api/src/routes/guilds/#guild_id/invites.ts @@ -1,4 +1,4 @@ -import { getPermission, InviteModel, toObject } from "@fosscord/util"; +import { getPermission, Invite } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router = Router(); diff --git a/api/src/routes/guilds/#guild_id/members/#member_id/index.ts b/api/src/routes/guilds/#guild_id/members/#member_id/index.ts index 1dacbdad..db29cd08 100644 --- a/api/src/routes/guilds/#guild_id/members/#member_id/index.ts +++ b/api/src/routes/guilds/#guild_id/members/#member_id/index.ts @@ -3,7 +3,6 @@ import { Guild, Member, User, - toObject, GuildMemberAddEvent, getPermission, PermissionResolvable, @@ -12,15 +11,15 @@ import { emitEvent } from "@fosscord/util"; import { HTTPError } from "lambert-server"; -import { addMember, isMember, removeMember } from "../../../../../util/Member"; import { check } from "../../../../../util/instanceOf"; import { MemberChangeSchema } from "../../../../../schema/Member"; +import { In } from "typeorm"; const router = Router(); router.get("/", async (req: Request, res: Response) => { const { guild_id, member_id } = req.params; - await isMember(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); const member = await Member.findOneOrFail({ id: member_id, guild_id }); @@ -31,18 +30,22 @@ router.patch("/", check(MemberChangeSchema), async (req: Request, res: Response) const { guild_id, member_id } = req.params; const body = req.body as MemberChangeSchema; if (body.roles) { - const roles = await Role.find({ id: { $in: body.roles } }); + const roles = await Role.find({ id: In(body.roles) }); if (body.roles.length !== roles.length) throw new HTTPError("Roles not found", 404); // TODO: check if user has permission to add role } - const member = await Member.findOneOrFailAndUpdate({ id: member_id, guild_id }, body, { new: true }); + const member = await Member.findOneOrFail({ id: member_id, guild_id }); + member.assign(req.body); - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - guild_id, - data: member - } as GuildMemberUpdateEvent); + Promise.all([ + member.save(), + emitEvent({ + event: "GUILD_MEMBER_UPDATE", + guild_id, + data: { ...member, roles: member.role_ids } + } as GuildMemberUpdateEvent) + ]); res.json(member); }); @@ -52,7 +55,7 @@ router.put("/", async (req: Request, res: Response) => { throw new HTTPError("Maintenance: Currently you can't add a member", 403); // TODO: only for oauth2 applications - await addMember(member_id, guild_id); + await Member.addToGuild(member_id, guild_id); res.sendStatus(204); }); @@ -62,7 +65,7 @@ router.delete("/", async (req: Request, res: Response) => { const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("KICK_MEMBERS"); - await removeMember(member_id, guild_id); + await Member.removeFromGuild(member_id, guild_id); res.sendStatus(204); }); diff --git a/api/src/routes/guilds/#guild_id/members/#member_id/nick.ts b/api/src/routes/guilds/#guild_id/members/#member_id/nick.ts index e4308364..3f2975e6 100644 --- a/api/src/routes/guilds/#guild_id/members/#member_id/nick.ts +++ b/api/src/routes/guilds/#guild_id/members/#member_id/nick.ts @@ -1,8 +1,7 @@ -import { getPermission, PermissionResolvable } from "@fosscord/util"; +import { getPermission, Member, PermissionResolvable } from "@fosscord/util"; import { Request, Response, Router } from "express"; import { check } from "lambert-server"; import { MemberNickChangeSchema } from "../../../../../schema/Member"; -import { changeNickname } from "../../../../../util/Member"; const router = Router(); @@ -17,7 +16,7 @@ router.patch("/", check(MemberNickChangeSchema), async (req: Request, res: Respo const perms = await getPermission(req.user_id, guild_id); perms.hasThrow(permissionString); - await changeNickname(member_id, guild_id, req.body.nick); + await Member.changeNickname(member_id, guild_id, req.body.nick); res.status(200).send(); }); diff --git a/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts index fad0695e..cb9bad9a 100644 --- a/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts +++ b/api/src/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts @@ -1,6 +1,5 @@ -import { getPermission } from "@fosscord/util"; +import { getPermission, Member } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { addRole, removeRole } from "../../../../../../../util/Member"; const router = Router(); @@ -10,7 +9,7 @@ router.delete("/:member_id/roles/:role_id", async (req: Request, res: Response) const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_ROLES"); - await removeRole(member_id, guild_id, role_id); + await Member.removeRole(member_id, guild_id, role_id); res.sendStatus(204); }); @@ -20,7 +19,7 @@ router.put("/:member_id/roles/:role_id", async (req: Request, res: Response) => const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_ROLES"); - await addRole(member_id, guild_id, role_id); + await Member.addRole(member_id, guild_id, role_id); res.sendStatus(204); }); diff --git a/api/src/routes/guilds/#guild_id/members/index.ts b/api/src/routes/guilds/#guild_id/members/index.ts index 656d3acd..0bfd71cb 100644 --- a/api/src/routes/guilds/#guild_id/members/index.ts +++ b/api/src/routes/guilds/#guild_id/members/index.ts @@ -1,8 +1,7 @@ import { Request, Response, Router } from "express"; -import { Guild, Member, toObject } from "@fosscord/util"; -import { HTTPError } from "lambert-server"; +import { Guild, Member, PublicMemberProjection } from "@fosscord/util"; import { instanceOf, Length } from "../../../../util/instanceOf"; -import { PublicMemberProjection, isMember } from "../../../../util/Member"; +import { MoreThan } from "typeorm"; const router = Router(); @@ -11,7 +10,7 @@ const router = Router(); router.get("/", async (req: Request, res: Response) => { const { guild_id } = req.params; const guild = await Guild.findOneOrFail({ id: guild_id }); - await isMember(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); try { instanceOf({ $limit: new Length(Number, 1, 1000), $after: String }, req.query, { @@ -23,12 +22,16 @@ router.get("/", async (req: Request, res: Response) => { return res.status(400).json({ code: 50035, message: "Invalid Query", success: false, errors: error }); } - // @ts-ignore - if (!req.query.limit) req.query.limit = 1; - const { limit, after } = (req.query) as { limit: number; after: string }; - const query = after ? { id: { $gt: after } } : {}; + const { limit, after } = (req.query) as { limit?: number; after?: string }; + const query = after ? { id: MoreThan(after) } : {}; + + const members = await Member.find({ + where: { guild_id, ...query }, + select: PublicMemberProjection, + take: limit || 1, + order: { id: "ASC" } + }); - var members = await Member.find({ guild_id, ...query }, PublicMemberProjection).limit(limit); return res.json(members); }); diff --git a/api/src/routes/guilds/#guild_id/roles.ts b/api/src/routes/guilds/#guild_id/roles.ts index 5ebc0580..e9e777b9 100644 --- a/api/src/routes/guilds/#guild_id/roles.ts +++ b/api/src/routes/guilds/#guild_id/roles.ts @@ -1,10 +1,7 @@ import { Request, Response, Router } from "express"; import { Role, - Guild, getPermission, - toObject, - User, Snowflake, Member, GuildRoleCreateEvent, @@ -16,15 +13,13 @@ import { HTTPError } from "lambert-server"; import { check } from "../../../util/instanceOf"; import { RoleModifySchema } from "../../../schema/Roles"; -import { getPublicUser } from "../../../util/User"; -import { isMember } from "../../../util/Member"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { const guild_id = req.params.guild_id; - await isMember(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); const roles = await Role.find({ guild_id: guild_id }); @@ -35,12 +30,8 @@ router.post("/", check(RoleModifySchema), async (req: Request, res: Response) => const guild_id = req.params.guild_id; const body = req.body as RoleModifySchema; - const guild = await Guild.findOneOrFail({ id: guild_id }, { id: true }); - const user = await User.findOneOrFail({ id: req.user_id }); - const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_ROLES"); - if (!body.name) throw new HTTPError("You need to specify a name"); const role = await new Role({ ...body, @@ -49,7 +40,7 @@ router.post("/", check(RoleModifySchema), async (req: Request, res: Response) => managed: false, position: 0, tags: null, - permissions: body.permissions || 0n + permissions: perms.bitfield & (body.permissions || 0n) }).save(); await emitEvent({ @@ -72,19 +63,20 @@ router.delete("/:role_id", async (req: Request, res: Response) => { const permissions = await getPermission(req.user_id, guild_id); permissions.hasThrow("MANAGE_ROLES"); - await Role.deleteOne({ - id: role_id, - guild_id: guild_id - }); - - await emitEvent({ - event: "GUILD_ROLE_DELETE", - guild_id, - data: { + await Promise.all([ + Role.delete({ + id: role_id, + guild_id: guild_id + }), + emitEvent({ + event: "GUILD_ROLE_DELETE", guild_id, - role_id - } - } as GuildRoleDeleteEvent); + data: { + guild_id, + role_id + } + } as GuildRoleDeleteEvent) + ]); res.sendStatus(204); }); @@ -96,30 +88,22 @@ router.patch("/:role_id", check(RoleModifySchema), async (req: Request, res: Res const { role_id } = req.params; const body = req.body as RoleModifySchema; - const guild = await Guild.findOneOrFail({ id: guild_id }, { id: true }); - const user = await User.findOneOrFail({ id: req.user_id }); - const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_ROLES"); - const role = await Role.findOneOrFailAndUpdate( - { - id: role_id, - guild_id: guild_id - }, - // @ts-ignore - body, - { new: true } - ); + const role = new Role({ ...body, role_id, guild_id, permissions: perms.bitfield & (body.permissions || 0n) }); - await emitEvent({ - event: "GUILD_ROLE_UPDATE", - guild_id, - data: { + await Promise.all([ + role.save(), + emitEvent({ + event: "GUILD_ROLE_UPDATE", guild_id, - role - } - } as GuildRoleUpdateEvent); + data: { + guild_id, + role + } + } as GuildRoleUpdateEvent) + ]); res.json(role); }); diff --git a/api/src/routes/guilds/#guild_id/vanity-url.ts b/api/src/routes/guilds/#guild_id/vanity-url.ts index 335cea27..58940b42 100644 --- a/api/src/routes/guilds/#guild_id/vanity-url.ts +++ b/api/src/routes/guilds/#guild_id/vanity-url.ts @@ -1,8 +1,7 @@ -import { Channel, ChannelType, getPermission, Guild, InviteModel, trimSpecial } from "@fosscord/util"; +import { Channel, ChannelType, getPermission, Guild, Invite, trimSpecial } from "@fosscord/util"; import { Router, Request, Response } from "express"; import { HTTPError } from "lambert-server"; import { check, Length } from "../../../util/instanceOf"; -import { isMember } from "../../../util/Member"; const router = Router(); @@ -14,42 +13,37 @@ router.get("/", async (req: Request, res: Response) => { const permission = await getPermission(req.user_id, guild_id); permission.hasThrow("MANAGE_GUILD"); - const guild = await Guild.findOneOrFail({ id: guild_id }); - if (!guild.vanity_url_code) return res.json({ code: null }); - const { uses } = await Invite.findOneOrFail({ code: guild.vanity_url_code }); + const guild = await Guild.findOneOrFail({ where: { id: guild_id }, relations: ["vanity_url"] }); + if (!guild.vanity_url) return res.json({ code: null }); - return res.json({ code: guild.vanity_url_code, uses }); + return res.json({ code: guild.vanity_url_code, uses: guild.vanity_url.uses }); }); // TODO: check if guild is elgible for vanity url router.patch("/", check({ code: new Length(String, 0, 20) }), async (req: Request, res: Response) => { const { guild_id } = req.params; - var code = req.body.code.replace(InviteRegex); - if (!code) code = null; + const code = req.body.code.replace(InviteRegex); + + await Invite.findOneOrFail({ code }); const guild = await Guild.findOneOrFail({ id: guild_id }); - const permission = await getPermission(req.user_id, guild_id, undefined, { guild }); + const permission = await getPermission(req.user_id, guild_id); permission.hasThrow("MANAGE_GUILD"); - const alreadyExists = await Promise.all([ - Guild.findOneOrFail({ vanity_url_code: code }).catch(() => null), - Invite.findOneOrFail({ code: code }).catch(() => null) - ]); - if (alreadyExists.some((x) => x)) throw new HTTPError("Vanity url already exists", 400); - - await Guild.update({ id: guild_id }, { vanity_url_code: code }); const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT }); - await Invite.update( - { code: guild.vanity_url_code }, - { + guild.vanity_url_code = code; + + Promise.all([ + guild.save(), + Invite.delete({ code: guild.vanity_url_code }), + new Invite({ code: code, uses: 0, created_at: new Date(), guild_id, channel_id: id - }, - { upsert: true } - ); + }).save() + ]); return res.json({ code: code }); }); diff --git a/api/src/routes/guilds/#guild_id/welcome_screen.ts b/api/src/routes/guilds/#guild_id/welcome_screen.ts index b457efb6..defbcd40 100644 --- a/api/src/routes/guilds/#guild_id/welcome_screen.ts +++ b/api/src/routes/guilds/#guild_id/welcome_screen.ts @@ -1,11 +1,9 @@ import { Request, Response, Router } from "express"; -import { Guild, getPermission, toObject, Snowflake } from "@fosscord/util"; +import { Guild, getPermission, Snowflake, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { check } from "../../../util/instanceOf"; -import { isMember } from "../../../util/Member"; -import { GuildAddChannelToWelcomeScreenSchema } from "../../../schema/Guild"; -import { getPublicUser } from "../../../util/User"; +import { GuildUpdateWelcomeScreenSchema } from "../../../schema/Guild"; const router: Router = Router(); @@ -14,34 +12,24 @@ router.get("/", async (req: Request, res: Response) => { const guild = await Guild.findOneOrFail({ id: guild_id }); - await isMember(req.user_id, guild_id); + await Member.IsInGuildOrFail(req.user_id, guild_id); res.json(guild.welcome_screen); }); -router.post("/", check(GuildAddChannelToWelcomeScreenSchema), async (req: Request, res: Response) => { +router.patch("/", check(GuildUpdateWelcomeScreenSchema), async (req: Request, res: Response) => { const guild_id = req.params.guild_id; - const body = req.body as GuildAddChannelToWelcomeScreenSchema; + const body = req.body as GuildUpdateWelcomeScreenSchema; const guild = await Guild.findOneOrFail({ id: guild_id }); - var channelObject = { - ...body - }; - const perms = await getPermission(req.user_id, guild_id); perms.hasThrow("MANAGE_GUILD"); if (!guild.welcome_screen.enabled) throw new HTTPError("Welcome screen disabled", 400); - if (guild.welcome_screen.welcome_channels.some((channel) => channel.channel_id === body.channel_id)) - throw new Error("Welcome Channel exists"); - - await Guild.findOneOrFailAndUpdate( - { - id: guild_id - }, - { $push: { "welcome_screen.welcome_channels": channelObject } } - ); + if (body.welcome_channels) guild.welcome_screen.welcome_channels = body.welcome_channels; // TODO: check if they exist and are valid + if (body.description) guild.welcome_screen.description = body.description; + if (body.enabled != null) guild.welcome_screen.enabled = body.enabled; res.sendStatus(204); }); diff --git a/api/src/routes/guilds/#guild_id/widget.json.ts b/api/src/routes/guilds/#guild_id/widget.json.ts index 10bc3ac0..ae1f0599 100644 --- a/api/src/routes/guilds/#guild_id/widget.json.ts +++ b/api/src/routes/guilds/#guild_id/widget.json.ts @@ -1,5 +1,5 @@ import { Request, Response, Router } from "express"; -import { Config, Permissions, Guild, InviteModel, Channel, Member } from "@fosscord/util"; +import { Config, Permissions, Guild, Invite, Channel, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { random } from "../../../util/RandomInviteID"; @@ -21,7 +21,8 @@ router.get("/", async (req: Request, res: Response) => { if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404); // Fetch existing widget invite for widget channel - var invite = await Invite.findOneOrFail({ channel_id: guild.widget_channel_id, inviter_id: { $type: 10 } }); + var invite = await Invite.findOne({ channel_id: guild.widget_channel_id }); + if (guild.widget_channel_id && !invite) { // Create invite for channel if none exists // TODO: Refactor invite create code to a shared function @@ -40,85 +41,29 @@ router.get("/", async (req: Request, res: Response) => { inviter_id: null }; - invite = await new InviteModel(body).save(); + invite = await new Invite(body).save(); } // Fetch voice channels, and the @everyone permissions object - let channels: any[] = []; - await Channel.find({ guild_id: guild_id, type: 2 }, { permission_overwrites: { $elemMatch: { id: guild_id } } }) - .select("id name position permission_overwrites") - .sort({ position: 1 }) - .cursor() - .eachAsync((doc) => { - // Only return channels where @everyone has the CONNECT permission - if ( - doc.permission_overwrites === undefined || - Permissions.channelPermission(doc.permission_overwrites, Permissions.FLAGS.CONNECT) === Permissions.FLAGS.CONNECT - ) { - channels.push({ - id: doc.id, - name: doc.name, - position: doc.position - }); - } - }); + const channels = [] as any[]; + + (await Channel.find({ where: { guild_id: guild_id, type: 2 }, order: { position: "ASC" } })).filter((doc) => { + // Only return channels where @everyone has the CONNECT permission + if ( + doc.permission_overwrites === undefined || + Permissions.channelPermission(doc.permission_overwrites, Permissions.FLAGS.CONNECT) === Permissions.FLAGS.CONNECT + ) { + channels.push({ + id: doc.id, + name: doc.name, + position: doc.position + }); + } + }); // Fetch members // TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file) - let members: any[] = []; - await Member.find({ guild_id: guild_id }) - .populate({ path: "user", select: { _id: 0, username: 1, avatar: 1, presence: 1 } }) - .select("id user nick deaf mute") - .cursor() - .eachAsync((doc) => { - const status = doc.user?.presence?.status || "offline"; - if (status == "offline") return; - - let item = {}; - - item = { - ...item, - id: null, // this is updated during the sort outside of the query - username: doc.nick || doc.user?.username, - discriminator: "0000", // intended (https://github.com/discord/discord-api-docs/issues/1287) - avatar: null, // intended, avatar_url below will return a unique guild + user url to the avatar - status: status - }; - - const activity = doc.user?.presence?.activities?.[0]; - if (activity) { - item = { - ...item, - game: { name: activity.name } - }; - } - - // TODO: If the member is in a voice channel, return extra widget details - // Extra fields returned include deaf, mute, self_deaf, self_mute, supress, and channel_id (voice channel connected to) - // Get this from VoiceState - - // TODO: Implement a widget-avatar endpoint on the CDN, and implement logic here to request it - // Get unique avatar url for guild user, cdn to serve the actual avatar image on this url - /* - const avatar = doc.user?.avatar; - if (avatar) { - const CDN_HOST = Config.get().cdn.endpoint || "http://localhost:3003"; - const avatar_url = "/widget-avatars/" + ; - item = { - ...item, - avatar_url: avatar_url - } - } - */ - - members.push(item); - }); - - // Sort members, and update ids (Unable to do under the mongoose query due to https://mongoosejs.com/docs/faq.html#populate_sort_order) - members = members.sort((first, second) => 0 - (first.username > second.username ? -1 : 1)); - members.forEach((x, i) => { - x.id = i; - }); + let members = await Member.find({ where: { guild_id: guild_id } }); // Construct object to respond with const data = { diff --git a/api/src/routes/guilds/index.ts b/api/src/routes/guilds/index.ts index 05be07d9..c158c7d4 100644 --- a/api/src/routes/guilds/index.ts +++ b/api/src/routes/guilds/index.ts @@ -1,10 +1,8 @@ import { Router, Request, Response } from "express"; -import { Role, Guild, Snowflake, Guild, RoleDocument, Config } from "@fosscord/util"; +import { Role, Guild, Snowflake, Config, User, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { check } from "./../../util/instanceOf"; import { GuildCreateSchema } from "../../schema/Guild"; -import { getPublicUser } from "../../util/User"; -import { addMember } from "../../util/Member"; import { createChannel } from "../../util/Channel"; const router: Router = Router(); @@ -15,14 +13,13 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) = const body = req.body as GuildCreateSchema; const { maxGuilds } = Config.get().limits.user; - const user = await getPublicUser(req.user_id, { guilds: true }); - - if (user.guilds.length >= maxGuilds) { + const guild_count = await Member.count({ where: { id: req.user_id } }); + if (guild_count >= maxGuilds) { throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403); } const guild_id = Snowflake.generate(); - const guild: Guild = { + const guild = new Guild({ name: body.name, region: Config.get().regions.default, owner_id: req.user_id, @@ -62,7 +59,7 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) = }, widget_channel_id: undefined, widget_enabled: false - }; + }); const [guild_doc, role] = await Promise.all([ new Guild(guild).save(), @@ -94,14 +91,14 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) = body.channels?.map((x) => { var id = ids.get(x.id) || Snowflake.generate(); - // TODO: should we abort if parent_id is a category? (or not to allow sub category channels) + // TODO: should we abort if parent_id is a category? (to disallow sub category channels) var parent_id = ids.get(x.parent_id); return createChannel({ ...x, guild_id, id, parent_id }, req.user_id, { keepId: true, skipExistsCheck: true }); }) ); - await addMember(req.user_id, guild_id); + await Member.addToGuild(req.user_id, guild_id); res.status(201).json({ id: guild.id }); }); diff --git a/api/src/routes/guilds/templates/index.ts b/api/src/routes/guilds/templates/index.ts index b8c1012d..7a8ac886 100644 --- a/api/src/routes/guilds/templates/index.ts +++ b/api/src/routes/guilds/templates/index.ts @@ -1,10 +1,9 @@ import { Request, Response, Router } from "express"; const router: Router = Router(); -import { Template, Guild, Role, Snowflake, Config, User } from "@fosscord/util"; +import { Template, Guild, Role, Snowflake, Config, User, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { GuildTemplateCreateSchema } from "../../../schema/Guild"; import { check } from "../../../util/instanceOf"; -import { addMember } from "../../../util/Member"; router.get("/:code", async (req: Request, res: Response) => { const { code } = req.params; @@ -19,9 +18,9 @@ router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res const body = req.body as GuildTemplateCreateSchema; const { maxGuilds } = Config.get().limits.user; - const user = await User.getPublicUser(req.user_id, { guilds: true }); - if (user.guilds.length >= maxGuilds) { + const guild_count = await Member.count({ id: req.user_id }); + if (guild_count >= maxGuilds) { throw new HTTPError(`Maximum number of guilds reached ${maxGuilds}`, 403); } @@ -29,15 +28,13 @@ router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res const guild_id = Snowflake.generate(); - const guild: Guild = { - ...body, - ...template.serialized_source_guild, - id: guild_id, - owner_id: req.user_id - }; - - const [guild_doc, role] = await Promise.all([ - new Guild(guild).save(), + const [guild, role] = await Promise.all([ + new Guild({ + ...body, + ...template.serialized_source_guild, + id: guild_id, + owner_id: req.user_id + }).save(), new Role({ id: guild_id, guild_id: guild_id, @@ -52,7 +49,7 @@ router.post("/:code", check(GuildTemplateCreateSchema), async (req: Request, res }).save() ]); - await addMember(req.user_id, guild_id, { guild: guild_doc }); + await Member.addToGuild(req.user_id, guild_id); res.status(201).json({ id: guild.id }); }); diff --git a/api/src/routes/invites/index.ts b/api/src/routes/invites/index.ts index a488c44b..23fa3aec 100644 --- a/api/src/routes/invites/index.ts +++ b/api/src/routes/invites/index.ts @@ -1,14 +1,12 @@ import { Router, Request, Response } from "express"; -import { getPermission, Guild, Invite } from "@fosscord/util"; +import { getPermission, Guild, Invite, Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; -import { addMember } from "../../util/Member"; const router: Router = Router(); router.get("/:code", async (req: Request, res: Response) => { const { code } = req.params; const invite = await Invite.findOneOrFail({ code }); - if (!invite) throw new HTTPError("Unknown Invite", 404); res.status(200).send(invite); }); @@ -16,11 +14,11 @@ router.get("/:code", async (req: Request, res: Response) => { router.post("/:code", async (req: Request, res: Response) => { const { code } = req.params; - const invite = await Invite.findOneOrFailAndUpdate({ code }, { $inc: { uses: 1 } }, { new: true }); - if (!invite) throw new HTTPError("Unknown Invite", 404); - if (invite.uses >= invite.max_uses) await Invite.deleteOne({ code }); + const invite = await Invite.findOneOrFail({ code }); + if (invite.uses++ >= invite.max_uses) await Invite.delete({ code }); + else await invite.save(); - await addMember(req.user_id, invite.guild_id); + await Member.addToGuild(req.user_id, invite.guild_id); res.status(200).send(invite); }); @@ -30,15 +28,12 @@ router.delete("/:code", async (req: Request, res: Response) => { const invite = await Invite.findOneOrFail({ code }); const { guild_id, channel_id } = invite; - const guild = await Guild.findOneOrFail({ id: guild_id }); - const permission = await getPermission(req.user_id, guild_id, channel_id, { guild }); + const permission = await getPermission(req.user_id, guild_id, channel_id); if (!permission.has("MANAGE_GUILD") && !permission.has("MANAGE_CHANNELS")) throw new HTTPError("You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401); - await Invite.deleteOne({ code }); - - await Guild.update({ vanity_url_code: code }, { $unset: { vanity_url_code: 1 } }).catch((e) => {}); + await Promise.all([Invite.delete({ code }), Guild.update({ vanity_url_code: code }, { vanity_url_code: undefined })]); res.json({ invite: invite }); }); diff --git a/api/src/routes/users/#id/index.ts b/api/src/routes/users/#id/index.ts index a2ad3ae6..3841756b 100644 --- a/api/src/routes/users/#id/index.ts +++ b/api/src/routes/users/#id/index.ts @@ -1,13 +1,12 @@ import { Router, Request, Response } from "express"; -import { getPublicUser } from "../../../util/User"; -import { HTTPError } from "lambert-server"; +import { User } from "../../../../../util/dist"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { const { id } = req.params; - res.json(await getPublicUser(id)); + res.json(await User.getPublicUser(id)); }); export default router; diff --git a/api/src/routes/users/#id/profile.ts b/api/src/routes/users/#id/profile.ts index 9b2e2d5e..afccfed5 100644 --- a/api/src/routes/users/#id/profile.ts +++ b/api/src/routes/users/#id/profile.ts @@ -1,10 +1,10 @@ import { Router, Request, Response } from "express"; -import { getPublicUser } from "../../../util/User"; +import { User } from "../../../../../util/dist"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { - const user = await getPublicUser(req.params.id, { data: true }); + const user = await User.getPublicUser(req.params.id, { relations: ["connected_accounts"] }); res.json({ connected_accounts: user.connected_accounts, diff --git a/api/src/routes/users/@me/channels.ts b/api/src/routes/users/@me/channels.ts index 28e77dd9..ab203571 100644 --- a/api/src/routes/users/@me/channels.ts +++ b/api/src/routes/users/@me/channels.ts @@ -1,20 +1,10 @@ import { Router, Request, Response } from "express"; -import { - Channel, - ChannelCreateEvent, - toObject, - ChannelType, - Snowflake, - trimSpecial, - Channel, - DMChannel, - User, - emitEvent -} from "@fosscord/util"; +import { Channel, ChannelCreateEvent, ChannelType, Snowflake, trimSpecial, User, emitEvent } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { DmChannelCreateSchema } from "../../../schema/Channel"; import { check } from "../../../util/instanceOf"; +import { In } from "typeorm"; const router: Router = Router(); @@ -29,8 +19,10 @@ router.post("/", check(DmChannelCreateSchema), async (req: Request, res: Respons body.recipients = body.recipients.filter((x) => x !== req.user_id).unique(); - if (!(await Promise.all(body.recipients.map((x) => User.exists({ id: x })))).every((x) => x)) { - throw new HTTPError("Recipient not found"); + const recipients = await User.find({ id: In(body.recipients) }); + + if (recipients.length !== body.recipients.length) { + throw new HTTPError("Recipient/s not found"); } const type = body.recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM; @@ -40,13 +32,12 @@ router.post("/", check(DmChannelCreateSchema), async (req: Request, res: Respons name, type, owner_id: req.user_id, - id: Snowflake.generate(), created_at: new Date(), last_message_id: null, recipient_ids: [...body.recipients, req.user_id] }).save(); - await emitEvent({ event: "CHANNEL_CREATE", data: channel), user_id: req.user_id } as ChannelCreateEvent; + await emitEvent({ event: "CHANNEL_CREATE", data: channel, user_id: req.user_id } as ChannelCreateEvent); res.json(channel); }); diff --git a/api/src/routes/users/@me/delete.ts b/api/src/routes/users/@me/delete.ts index fa8134cc..6bfe0b93 100644 --- a/api/src/routes/users/@me/delete.ts +++ b/api/src/routes/users/@me/delete.ts @@ -5,12 +5,19 @@ const router = Router(); router.post("/", async (req: Request, res: Response) => { const user = await User.findOneOrFail({ id: req.user_id }); //User object + let correctpass = true; + + if (user.data.hash) { + // guest accounts can delete accounts without password + correctpass = await bcrypt.compare(req.body.password, user.data.hash); //Not sure if user typed right password :/ + } + + // TODO: decrement guild member count - let correctpass = await bcrypt.compare(req.body.password, user!.data.hash); //Not sure if user typed right password :/ if (correctpass) { await Promise.all([ - User.deleteOne({ id: req.user_id }), //Yeetus user deletus - Member.deleteMany({ id: req.user_id }) + User.delete({ id: req.user_id }), //Yeetus user deletus + Member.delete({ id: req.user_id }) ]); res.sendStatus(204); diff --git a/api/src/routes/users/@me/disable.ts b/api/src/routes/users/@me/disable.ts index a40c9e59..ed1dedcc 100644 --- a/api/src/routes/users/@me/disable.ts +++ b/api/src/routes/users/@me/disable.ts @@ -5,9 +5,14 @@ import bcrypt from "bcrypt"; const router = Router(); router.post("/", async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ id: req.user_id }); //User object + const user = await User.findOneOrFail(req.user_id); //User object + let correctpass = true; + + if (user.data.hash) { + // guest accounts can delete accounts without password + correctpass = await bcrypt.compare(req.body.password, user.data.hash); //Not sure if user typed right password :/ + } - let correctpass = await bcrypt.compare(req.body.password, user!.data.hash); //Not sure if user typed right password :/ if (correctpass) { await User.update({ id: req.user_id }, { disabled: true }); diff --git a/api/src/routes/users/@me/guilds.ts b/api/src/routes/users/@me/guilds.ts index e40bfec9..fb88281b 100644 --- a/api/src/routes/users/@me/guilds.ts +++ b/api/src/routes/users/@me/guilds.ts @@ -1,31 +1,26 @@ import { Router, Request, Response } from "express"; -import { Guild, Member, User, GuildDeleteEvent, GuildMemberRemoveEvent, toObject, emitEvent } from "@fosscord/util"; +import { Guild, Member, User, GuildDeleteEvent, GuildMemberRemoveEvent, emitEvent } from "@fosscord/util"; import { HTTPError } from "lambert-server"; - -import { getPublicUser } from "../../../util/User"; +import { In } from "typeorm"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ id: req.user_id }, { guilds: true }); - if (!user) throw new HTTPError("User not found", 404); + const members = await Member.find({ relations: ["guild"], where: { id: req.user_id } }); - var guildIDs = user.guilds || []; - var guild = await Guild.find({ id: { $in: guildIDs } }).populate({ path: "joined_at", match: { id: req.user_id } }); - res.json(guild); + res.json(members.map((x) => x.guild)); }); // user send to leave a certain guild router.delete("/:id", async (req: Request, res: Response) => { const guild_id = req.params.id; - const guild = await Guild.findOneOrFail({ id: guild_id }, { guild_id: true }); + const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: ["owner_id"] }); if (!guild) throw new HTTPError("Guild doesn't exist", 404); if (guild.owner_id === req.user_id) throw new HTTPError("You can't leave your own guild", 400); await Promise.all([ - Member.deleteOne({ id: req.user_id, guild_id: guild_id }), - User.update({ id: req.user_id }, { $pull: { guilds: guild_id } }), + Member.delete({ id: req.user_id, guild_id: guild_id }), emitEvent({ event: "GUILD_DELETE", data: { @@ -35,7 +30,7 @@ router.delete("/:id", async (req: Request, res: Response) => { } as GuildDeleteEvent) ]); - const user = await getPublicUser(req.user_id); + const user = await User.getPublicUser(req.user_id); await emitEvent({ event: "GUILD_MEMBER_REMOVE", diff --git a/api/src/routes/users/@me/index.ts b/api/src/routes/users/@me/index.ts index 903b24aa..274cfb24 100644 --- a/api/src/routes/users/@me/index.ts +++ b/api/src/routes/users/@me/index.ts @@ -1,6 +1,5 @@ import { Router, Request, Response } from "express"; -import { User, toObject, PublicUserProjection } from "@fosscord/util"; -import { getPublicUser } from "../../../util/User"; +import { User } from "@fosscord/util"; import { UserModifySchema } from "../../../schema/User"; import { check } from "../../../util/instanceOf"; import { handleFile } from "../../../util/cdn"; @@ -8,29 +7,29 @@ import { handleFile } from "../../../util/cdn"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { - res.json(await getPublicUser(req.user_id)); + res.json(await User.getPublicUser(req.user_id)); }); -const UserUpdateProjection = { - accent_color: true, - avatar: true, - banner: true, - bio: true, - bot: true, - discriminator: true, - email: true, - flags: true, - id: true, - locale: true, - mfa_enabled: true, - nsfw_alllowed: true, - phone: true, - public_flags: true, - purchased_flags: true, - // token: true, // this isn't saved in the db and needs to be set manually - username: true, - verified: true -}; +const UserUpdateProjection = [ + "accent_color", + "avatar", + "banner", + "bio", + "bot", + "discriminator", + "email", + "flags", + "id", + "locale", + "mfa_enabled", + "nsfw_alllowed", + "phone", + "public_flags", + "purchased_flags", + // "token", // this isn't saved in the db and needs to be set manually + "username", + "verified" +]; router.patch("/", check(UserModifySchema), async (req: Request, res: Response) => { const body = req.body as UserModifySchema; @@ -38,7 +37,7 @@ router.patch("/", check(UserModifySchema), async (req: Request, res: Response) = if (body.avatar) body.avatar = await handleFile(`/avatars/${req.user_id}`, body.avatar as string); if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string); - const user = await User.findOneOrFailAndUpdate({ id: req.user_id }, body, { projection: UserUpdateProjection, new: true }); + const user = await new User({ ...body }, { id: req.user_id }).save(); // TODO: dispatch user update event res.json(user); diff --git a/api/src/routes/users/@me/profile.ts b/api/src/routes/users/@me/profile.ts index fdb969dc..5ba03c68 100644 --- a/api/src/routes/users/@me/profile.ts +++ b/api/src/routes/users/@me/profile.ts @@ -1,13 +1,13 @@ import { Router, Request, Response } from "express"; -import { getPublicUser } from "../../../util/User"; +import { User } from "../../../../../util/dist"; const router: Router = Router(); router.get("/", async (req: Request, res: Response) => { - const user = await getPublicUser(req.user_id, { data: true }); + const user = await User.getPublicUser(req.user_id, { relations: ["connected_accounts"] }); res.json({ - connected_accounts: user.data.connected_accounts, + connected_accounts: user.connected_accounts, premium_guild_since: null, // TODO premium_since: null, // TODO user: { diff --git a/api/src/routes/users/@me/relationships.ts b/api/src/routes/users/@me/relationships.ts index 8c7469ed..1a89b110 100644 --- a/api/src/routes/users/@me/relationships.ts +++ b/api/src/routes/users/@me/relationships.ts @@ -2,11 +2,10 @@ import { RelationshipAddEvent, User, PublicUserProjection, - toObject, RelationshipType, RelationshipRemoveEvent, - UserDocument, - emitEvent + emitEvent, + Relationship } from "@fosscord/util"; import { Router, Response, Request } from "express"; import { HTTPError } from "lambert-server"; @@ -15,40 +14,36 @@ import { check, Length } from "../../../util/instanceOf"; const router = Router(); -const userProjection = { "data.relationships": true, ...PublicUserProjection }; +const userProjection: (keyof User)[] = ["relationships", ...PublicUserProjection]; router.get("/", async (req: Request, res: Response) => { - const user = await User.findOneOrFail({ id: req.user_id }, { data: { relationships: true } }).populate({ - path: "data.relationships.id", - model: User - }); - return res.json(user.data.relationships); + const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["relationships"] }); + + return res.json(user.relationships); }); -async function addRelationship(req: Request, res: Response, friend: UserDocument, type: RelationshipType) { +async function updateRelationship(req: Request, res: Response, friend: User, type: RelationshipType) { const id = friend.id; if (id === req.user_id) throw new HTTPError("You can't add yourself as a friend"); - const user = await User.findOneOrFail({ id: req.user_id }, userProjection); - const newUserRelationships = [...user.data.relationships]; - const newFriendRelationships = [...friend.data.relationships]; + const user = await User.findOneOrFail(req.user_id, { relations: ["relationships"], select: userProjection }); - var relationship = newUserRelationships.find((x) => x.id === id); - const friendRequest = newFriendRelationships.find((x) => x.id === req.user_id); + var relationship = user.relationships.find((x) => x.id === id); + const friendRequest = friend.relationships.find((x) => x.id === req.user_id); if (type === RelationshipType.blocked) { if (relationship) { if (relationship.type === RelationshipType.blocked) throw new HTTPError("You already blocked the user"); relationship.type = RelationshipType.blocked; } else { - relationship = { id, type: RelationshipType.blocked }; - newUserRelationships.push(relationship); + relationship = new Relationship({ id, type: RelationshipType.blocked }); + user.relationships.push(relationship); } if (friendRequest && friendRequest.type !== RelationshipType.blocked) { - newFriendRelationships.remove(friendRequest); + friend.relationships.remove(friendRequest); await Promise.all([ - User.update({ id: friend.id }, { "data.relationships": newFriendRelationships }), + user.save(), emitEvent({ event: "RELATIONSHIP_REMOVE", data: friendRequest, @@ -58,12 +53,12 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument } await Promise.all([ - User.update({ id: req.user_id }, { "data.relationships": newUserRelationships }), + user.save(), emitEvent({ event: "RELATIONSHIP_ADD", data: { ...relationship, - user: { ...friend, data: undefined } + user: { ...friend } }, user_id: req.user_id } as RelationshipAddEvent) @@ -72,32 +67,31 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument return res.sendStatus(204); } - var incoming_relationship = { id: req.user_id, nickname: undefined, type: RelationshipType.incoming }; - var outgoing_relationship = { id, nickname: undefined, type: RelationshipType.outgoing }; + var incoming_relationship = new Relationship({ nickname: undefined, type: RelationshipType.incoming }, { id: req.user_id }); + var outgoing_relationship = new Relationship({ nickname: undefined, type: RelationshipType.outgoing }, { id }); if (friendRequest) { if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you"); // accept friend request - // @ts-ignore incoming_relationship = friendRequest; incoming_relationship.type = RelationshipType.friends; outgoing_relationship.type = RelationshipType.friends; - } else newFriendRelationships.push(incoming_relationship); + } else friend.relationships.push(incoming_relationship); if (relationship) { if (relationship.type === RelationshipType.outgoing) throw new HTTPError("You already sent a friend request"); if (relationship.type === RelationshipType.blocked) throw new HTTPError("Unblock the user before sending a friend request"); if (relationship.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user"); - } else newUserRelationships.push(outgoing_relationship); + } else user.relationships.push(outgoing_relationship); await Promise.all([ - User.update({ id: req.user_id }, { "data.relationships": newUserRelationships }), - User.update({ id: friend.id }, { "data.relationships": newFriendRelationships }), + user.save(), + friend.save(), emitEvent({ event: "RELATIONSHIP_ADD", data: { ...outgoing_relationship, - user: { ...friend, data: undefined } + user: { ...friend } }, user_id: req.user_id } as RelationshipAddEvent), @@ -106,7 +100,7 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument data: { ...incoming_relationship, should_notify: true, - user: { ...user, data: undefined } + user: { ...user } }, user_id: id } as RelationshipAddEvent) @@ -116,14 +110,23 @@ async function addRelationship(req: Request, res: Response, friend: UserDocument } router.put("/:id", check({ $type: new Length(Number, 1, 4) }), async (req: Request, res: Response) => { - return await addRelationship(req, res, await User.findOneOrFail({ id: req.params.id }), req.body.type); + return await updateRelationship( + req, + res, + await User.findOneOrFail(req.params.id, { relations: ["relationships"], select: userProjection }), + req.body.type + ); }); router.post("/", check({ discriminator: String, username: String }), async (req: Request, res: Response) => { - return await addRelationship( + return await updateRelationship( req, res, - await User.findOneOrFail(req.body as { discriminator: string; username: string }), + await User.findOneOrFail({ + relations: ["relationships"], + select: userProjection, + where: req.body as { discriminator: string; username: string } + }), req.body.type ); }); @@ -132,17 +135,15 @@ router.delete("/:id", async (req: Request, res: Response) => { const { id } = req.params; if (id === req.user_id) throw new HTTPError("You can't remove yourself as a friend"); - const user = await User.findOneOrFail({ id: req.user_id }); - if (!user) throw new HTTPError("Invalid token", 400); + const user = await User.findOneOrFail(req.user_id, { select: userProjection, relations: ["relationships"] }); + const friend = await User.findOneOrFail(id, { select: userProjection, relations: ["relationships"] }); - const friend = await User.findOneOrFail({ id }, userProjection); - if (!friend) throw new HTTPError("User not found", 404); + const relationship = user.relationships.find((x) => x.id === id); + const friendRequest = friend.relationships.find((x) => x.id === req.user_id); - const relationship = user.data.relationships.find((x) => x.id === id); - const friendRequest = friend.data.relationships.find((x) => x.id === req.user_id); if (relationship?.type === RelationshipType.blocked) { // unblock user - user.data.relationships.remove(relationship); + user.relationships.remove(relationship); await Promise.all([ user.save(), @@ -153,8 +154,8 @@ router.delete("/:id", async (req: Request, res: Response) => { if (!relationship || !friendRequest) throw new HTTPError("You are not friends with the user", 404); if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you"); - user.data.relationships.remove(relationship); - friend.data.relationships.remove(friendRequest); + user.relationships.remove(relationship); + friend.relationships.remove(friendRequest); await Promise.all([ user.save(), diff --git a/api/src/schema/Guild.ts b/api/src/schema/Guild.ts index e5971baf..01690ae9 100644 --- a/api/src/schema/Guild.ts +++ b/api/src/schema/Guild.ts @@ -1,4 +1,4 @@ -import { ChannelSchema, GuildChannel } from "@fosscord/util"; +import { Channel } from "@fosscord/util"; import { Length } from "../util/instanceOf"; import { ChannelModifySchema } from "./Channel"; @@ -59,54 +59,6 @@ export interface GuildUpdateSchema extends Omit { preferred_locale?: string; } -export const GuildGetSchema = { - id: true, - name: true, - icon: true, - splash: true, - discovery_splash: true, - owner: true, - owner_id: true, - permissions: true, - region: true, - afk_channel_id: true, - afk_timeout: true, - widget_enabled: true, - widget_channel_id: true, - verification_level: true, - default_message_notifications: true, - explicit_content_filter: true, - roles: true, - emojis: true, - features: true, - mfa_level: true, - application_id: true, - system_channel_id: true, - system_channel_flags: true, - rules_channel_id: true, - joined_at: true, - // large: true, - // unavailable: true, - member_count: true, - // voice_states: true, - // members: true, - // channels: true, - // presences: true, - max_presences: true, - max_members: true, - vanity_url_code: true, - description: true, - banner: true, - premium_tier: true, - premium_subscription_count: true, - preferred_locale: true, - public_updates_channel_id: true, - max_video_channel_users: true, - approximate_member_count: true, - approximate_presence_count: true - // welcome_screen: true, -}; - export const GuildTemplateCreateSchema = { name: String, $avatar: String @@ -117,16 +69,26 @@ export interface GuildTemplateCreateSchema { avatar?: string; } -export const GuildAddChannelToWelcomeScreenSchema = { - channel_id: String, - description: String, - $emoji_id: String, - emoji_name: String +export const GuildUpdateWelcomeScreenSchema = { + $welcome_channels: [ + { + channel_id: String, + description: String, + $emoji_id: String, + emoji_name: String + } + ], + $enabled: Boolean, + $description: new Length(String, 0, 140) }; -export interface GuildAddChannelToWelcomeScreenSchema { - channel_id: string; - description: string; - emoji_id?: string; - emoji_name: string; +export interface GuildUpdateWelcomeScreenSchema { + welcome_channels?: { + channel_id: string; + description: string; + emoji_id?: string; + emoji_name: string; + }[]; + enabled?: boolean; + description?: string; } diff --git a/api/src/schema/Message.ts b/api/src/schema/Message.ts index 2dd54f0c..f9bfcc67 100644 --- a/api/src/schema/Message.ts +++ b/api/src/schema/Message.ts @@ -1,6 +1,12 @@ -import { Embed, EmbedImage } from "@fosscord/util"; +import { Embed } from "@fosscord/util"; import { Length } from "../util/instanceOf"; +export const EmbedImage = { + $url: String, + $width: Number, + $height: Number +}; + export const MessageCreateSchema = { $content: new Length(String, 0, 2000), $nonce: String, diff --git a/api/src/schema/Roles.ts b/api/src/schema/Roles.ts index f662e61b..1e5f560f 100644 --- a/api/src/schema/Roles.ts +++ b/api/src/schema/Roles.ts @@ -9,7 +9,7 @@ export const RoleModifySchema = { export interface RoleModifySchema { name?: string; - permissions?: BigInt; + permissions?: bigint; color?: number; hoist?: boolean; // whether the role should be displayed separately in the sidebar mentionable?: boolean; // whether the role should be mentionable diff --git a/api/src/test/mongo_test.ts b/api/src/test/mongo_test.ts deleted file mode 100644 index bf203ea6..00000000 --- a/api/src/test/mongo_test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import mongoose, { Schema, Types } from "mongoose"; -require("mongoose-long")(mongoose); - -const userSchema = new Schema({ - id: String -}); - -const messageSchema = new Schema({ - id: String, - content: String -}); -const message = mongoose.model("message", messageSchema, "messages"); -const user = mongoose.model("user", userSchema, "users"); - -messageSchema.virtual("u", { - ref: user, - localField: "id", - foreignField: "id", - justOne: true -}); - -messageSchema.set("toObject", { virtuals: true }); -messageSchema.set("toJSON", { virtuals: true }); - -async function main() { - const conn = await mongoose.connect("mongodb://localhost:27017/lambert?readPreference=secondaryPreferred", { - useNewUrlParser: true, - useUnifiedTopology: false - }); - console.log("connected"); - - // const u = await new user({ name: "test" }).save(); - // await new message({ user: u._id, content: "test" }).save(); - - const test = await message.findOneOrFail({}).populate("u"); - // @ts-ignore - console.log(test?.toJSON()); -} - -main(); diff --git a/api/src/test/server_benchmark.ts b/api/src/test/server_benchmark.ts deleted file mode 100644 index c582ee89..00000000 --- a/api/src/test/server_benchmark.ts +++ /dev/null @@ -1,39 +0,0 @@ -// @ts-nocheck -import "missing-native-js-functions"; -import { config } from "dotenv"; -config(); -import { DiscordServer } from "../Server"; -import fetch from "node-fetch"; -import { promises } from "fs"; -const count = 100; - -async function main() { - const server = new DiscordServer({ port: 3000 }); - await server.start(); - - const tasks = []; - for (let i = 0; i < count; i++) { - tasks.push(test()); - } - - await Promise.all(tasks); - - console.log("logging in 5secs"); - setTimeout(async () => { - await test(); - - process.exit(); - }, 5000); -} -main(); - -async function test() { - const res = await fetch("http://localhost:3000/api/v8/guilds/813524615463698433/members/813524464300982272", { - headers: { - authorization: - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjgxMzUyNDQ2NDMwMDk4MjI3MiIsImlhdCI6MTYxNDAyOTc0Nn0.6WQiU4D5HHRi3sliHOQe1hsW-hZTEttvdtZuNIdviNI", - }, - }); - - return await res.text(); -} diff --git a/api/src/test/test.ts b/api/src/test/test.ts deleted file mode 100644 index c37c762b..00000000 --- a/api/src/test/test.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Snowflake } from "@fosscord/util"; - -console.log(Snowflake.deconstruct("0")); diff --git a/api/src/util/Channel.ts b/api/src/util/Channel.ts index f518aefd..bc9217ce 100644 --- a/api/src/util/Channel.ts +++ b/api/src/util/Channel.ts @@ -1,20 +1,9 @@ -import { - ChannelCreateEvent, - Channel, - ChannelType, - emitEvent, - getPermission, - Guild, - Snowflake, - TextChannel, - toObject, - VoiceChannel -} from "@fosscord/util"; +import { ChannelCreateEvent, Channel, ChannelType, emitEvent, getPermission, Snowflake } from "@fosscord/util"; import { HTTPError } from "lambert-server"; // TODO: DM channel export async function createChannel( - channel: Partial, + channel: Partial, user_id: string = "0", opts?: { keepId?: boolean; @@ -29,7 +18,7 @@ export async function createChannel( case ChannelType.GUILD_TEXT: case ChannelType.GUILD_VOICE: if (channel.parent_id && !opts?.skipExistsCheck) { - const exists = await Channel.findOneOrFail({ id: channel.parent_id }, { guild_id: true }); + const exists = await Channel.findOneOrFail({ id: channel.parent_id }); if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); if (exists.guild_id !== channel.guild_id) throw new HTTPError("The category channel needs to be in the guild"); } diff --git a/api/src/util/Member.ts b/api/src/util/Member.ts deleted file mode 100644 index 6cb14d71..00000000 --- a/api/src/util/Member.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { - Guild, - GuildCreateEvent, - GuildDeleteEvent, - GuildMemberAddEvent, - GuildMemberRemoveEvent, - GuildMemberUpdateEvent, - Guild, - Member, - Role, - toObject, - User, - GuildDocument, - Config, - emitEvent -} from "@fosscord/util"; - -import { HTTPError } from "lambert-server"; - -import { getPublicUser } from "./User"; - -export const PublicMemberProjection = { - id: true, - guild_id: true, - nick: true, - roles: true, - joined_at: true, - pending: true, - deaf: true, - mute: true, - premium_since: true -}; - -export async function isMember(user_id: string, guild_id: string) { - const exists = await Member.exists({ id: user_id, guild_id }); - if (!exists) throw new HTTPError("You are not a member of this guild", 403); - return exists; -} - -export async function addMember(user_id: string, guild_id: string, cache?: { guild?: GuildDocument }) { - const user = await getPublicUser(user_id, { guilds: true }); - - const { maxGuilds } = Config.get().limits.user; - if (user.guilds.length >= maxGuilds) { - throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403); - } - - const guild = cache?.guild || (await Guild.findOneOrFail({ id: guild_id })); - - if (!guild) throw new HTTPError("Guild not found", 404); - - if (await Member.exists({ id: user.id, guild_id })) throw new HTTPError("You are already a member of this guild", 400); - - const member = { - id: user_id, - guild_id: guild_id, - nick: undefined, - roles: [guild_id], // @everyone role - joined_at: new Date(), - premium_since: undefined, - deaf: false, - mute: false, - pending: false - }; - - await Promise.all([ - new Member({ - ...member, - read_state: {}, - settings: { - channel_overrides: [], - message_notifications: 0, - mobile_push: true, - mute_config: null, - muted: false, - suppress_everyone: false, - suppress_roles: false, - version: 0 - } - }).save(), - - User.update({ id: user_id }, { $push: { guilds: guild_id } }), - Guild.update({ id: guild_id }, { $inc: { member_count: 1 } }), - - emitEvent({ - event: "GUILD_MEMBER_ADD", - data: { - ...member, - user, - guild_id: guild_id - }, - guild_id: guild_id - } as GuildMemberAddEvent) - ]); - - await emitEvent({ - event: "GUILD_CREATE", - data: await guild - .populate({ path: "members", match: { guild_id } }) - .populate({ path: "joined_at", match: { id: user.id } }) - .execPopulate(), - user_id - } as GuildCreateEvent); -} - -export async function removeMember(user_id: string, guild_id: string) { - const user = await getPublicUser(user_id); - - const guild = await Guild.findOneOrFail({ id: guild_id }, { owner_id: true }); - if (!guild) throw new HTTPError("Guild not found", 404); - if (guild.owner_id === user_id) throw new Error("The owner cannot be removed of the guild"); - if (!(await Member.exists({ id: user.id, guild_id }))) throw new HTTPError("Is not member of this guild", 404); - - // use promise all to execute all promises at the same time -> save time - return Promise.all([ - Member.deleteOne({ - id: user_id, - guild_id: guild_id - }), - User.update({ id: user.id }, { $pull: { guilds: guild_id } }), - Guild.update({ id: guild_id }, { $inc: { member_count: -1 } }), - - emitEvent({ - event: "GUILD_DELETE", - data: { - id: guild_id - }, - user_id: user_id - } as GuildDeleteEvent), - emitEvent({ - event: "GUILD_MEMBER_REMOVE", - data: { - guild_id: guild_id, - user: user - }, - guild_id: guild_id - } as GuildMemberRemoveEvent) - ]); -} - -export async function addRole(user_id: string, guild_id: string, role_id: string) { - const user = await getPublicUser(user_id); - - const role = await Role.findOneOrFail({ id: role_id, guild_id: guild_id }); - if (!role) throw new HTTPError("role not found", 404); - - var memberObj = await Member.findOneOrFailAndUpdate( - { - id: user_id, - guild_id: guild_id - }, - { $push: { roles: role_id } }, - { new: true } - ); - - if (!memberObj) throw new HTTPError("Member not found", 404); - - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { - guild_id: guild_id, - user: user, - roles: memberObj.roles - }, - guild_id: guild_id - } as GuildMemberUpdateEvent); -} - -export async function removeRole(user_id: string, guild_id: string, role_id: string) { - const user = await getPublicUser(user_id); - - const role = await Role.findOneOrFail({ id: role_id, guild_id: guild_id }); - if (!role) throw new HTTPError("role not found", 404); - - var memberObj = await Member.findOneOrFailAndUpdate( - { - id: user_id, - guild_id: guild_id - }, - { $pull: { roles: role_id } }, - { new: true } - ); - - if (!memberObj) throw new HTTPError("Member not found", 404); - - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { - guild_id: guild_id, - user: user, - roles: memberObj.roles - }, - guild_id: guild_id - } as GuildMemberUpdateEvent); -} - -export async function changeNickname(user_id: string, guild_id: string, nickname: string) { - const user = await getPublicUser(user_id); - - var memberObj = await Member.findOneOrFailAndUpdate( - { - id: user_id, - guild_id: guild_id - }, - { nick: nickname }, - { new: true } - ); - - if (!memberObj) throw new HTTPError("Member not found", 404); - - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { - guild_id: guild_id, - user: user, - nick: nickname - }, - guild_id: guild_id - } as GuildMemberUpdateEvent); -} diff --git a/api/src/util/Message.ts b/api/src/util/Message.ts index 5561904b..70989301 100644 --- a/api/src/util/Message.ts +++ b/api/src/util/Message.ts @@ -8,18 +8,17 @@ import { getPermission, CHANNEL_MENTION, Snowflake, - PublicMemberProjection, USER_MENTION, ROLE_MENTION, Role, EVERYONE_MENTION, - HERE_MENTION + HERE_MENTION, + MessageType } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import fetch from "node-fetch"; import cheerio from "cheerio"; -import { MessageType } from "@fosscord/util/dist/util/Constants"; // TODO: check webhook, application, system author const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g; @@ -61,7 +60,7 @@ export async function handleMessage(opts: Partial): Promise { } var content = opts.content; - var mention_channels_ids = [] as string[]; + var mention_channel_ids = [] as string[]; var mention_role_ids = [] as string[]; var mention_user_ids = [] as string[]; var mention_everyone = false; @@ -70,7 +69,7 @@ export async function handleMessage(opts: Partial): Promise { if (content) { content = content.trim(); for (const [_, mention] of content.matchAll(CHANNEL_MENTION)) { - if (!mention_channels_ids.includes(mention)) mention_channels_ids.push(mention); + if (!mention_channel_ids.includes(mention)) mention_channel_ids.push(mention); } for (const [_, mention] of content.matchAll(USER_MENTION)) { @@ -92,11 +91,12 @@ export async function handleMessage(opts: Partial): Promise { } // TODO: check and put it all in the body + return { ...opts, guild_id: channel.guild_id, channel_id: opts.channel_id, - mention_channels_ids, + mention_channel_ids, mention_role_ids, mention_user_ids, mention_everyone, @@ -104,7 +104,7 @@ export async function handleMessage(opts: Partial): Promise { embeds: opts.embeds || [], reactions: opts.reactions || [], type: opts.type ?? 0 - }; + } as Message; } // TODO: cache link result in db @@ -163,10 +163,7 @@ export async function postHandleMessage(message: Message) { export async function sendMessage(opts: Partial) { const message = await handleMessage({ ...opts, id: Snowflake.generate(), timestamp: new Date() }); - const data = await new Message(message) - .populate({ path: "member", select: PublicMemberProjection }) - .populate("referenced_message") - .save(); + const data = await new Message(message).save(); await emitEvent({ event: "MESSAGE_CREATE", channel_id: opts.channel_id, data } as MessageCreateEvent); -- cgit 1.5.1 From 9400fc8fb809942669283fbe46c8634cdb9a8298 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Tue, 31 Aug 2021 17:57:44 +0200 Subject: Channel utility methods in entity --- api/src/routes/guilds/#guild_id/channels.ts | 3 +- api/src/util/Channel.ts | 52 ----------------------------- 2 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 api/src/util/Channel.ts (limited to 'api/src/util/Channel.ts') diff --git a/api/src/routes/guilds/#guild_id/channels.ts b/api/src/routes/guilds/#guild_id/channels.ts index 7b0e94b6..5aa1d33d 100644 --- a/api/src/routes/guilds/#guild_id/channels.ts +++ b/api/src/routes/guilds/#guild_id/channels.ts @@ -4,7 +4,6 @@ import { HTTPError } from "lambert-server"; import { ChannelModifySchema } from "../../../schema/Channel"; import { check } from "../../../util/instanceOf"; -import { createChannel } from "../../../util/Channel"; const router = Router(); router.get("/", async (req: Request, res: Response) => { @@ -22,7 +21,7 @@ router.post("/", check(ChannelModifySchema), async (req: Request, res: Response) const { guild_id } = req.params; const body = req.body as ChannelModifySchema; - const channel = await createChannel({ ...body, guild_id }, req.user_id); + const channel = await Channel.createChannel({ ...body, guild_id }, req.user_id); res.status(201).json(channel); }); diff --git a/api/src/util/Channel.ts b/api/src/util/Channel.ts deleted file mode 100644 index bc9217ce..00000000 --- a/api/src/util/Channel.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ChannelCreateEvent, Channel, ChannelType, emitEvent, getPermission, Snowflake } from "@fosscord/util"; -import { HTTPError } from "lambert-server"; - -// TODO: DM channel -export async function createChannel( - channel: Partial, - user_id: string = "0", - opts?: { - keepId?: boolean; - skipExistsCheck?: boolean; - } -) { - // Always check if user has permission first - const permissions = await getPermission(user_id, channel.guild_id); - permissions.hasThrow("MANAGE_CHANNELS"); - - switch (channel.type) { - case ChannelType.GUILD_TEXT: - case ChannelType.GUILD_VOICE: - if (channel.parent_id && !opts?.skipExistsCheck) { - const exists = await Channel.findOneOrFail({ id: channel.parent_id }); - if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); - if (exists.guild_id !== channel.guild_id) throw new HTTPError("The category channel needs to be in the guild"); - } - break; - case ChannelType.GUILD_CATEGORY: - break; - case ChannelType.DM: - case ChannelType.GROUP_DM: - throw new HTTPError("You can't create a dm channel in a guild"); - // TODO: check if guild is community server - case ChannelType.GUILD_STORE: - case ChannelType.GUILD_NEWS: - default: - throw new HTTPError("Not yet supported"); - } - - if (!channel.permission_overwrites) channel.permission_overwrites = []; - // TODO: auto generate position - - channel = await new Channel({ - ...channel, - ...(!opts?.keepId && { id: Snowflake.generate() }), - created_at: new Date(), - // @ts-ignore - recipient_ids: null - }).save(); - - await emitEvent({ event: "CHANNEL_CREATE", data: channel, guild_id: channel.guild_id } as ChannelCreateEvent); - - return channel; -} -- cgit 1.5.1