From 90af904661ac5af8db83dc2e34fb4f6f2d0c9275 Mon Sep 17 00:00:00 2001 From: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com> Date: Fri, 8 Oct 2021 23:45:12 -0300 Subject: (api): inital emojis route implementation --- api/src/routes/guilds/#guild_id/emojis.ts | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 api/src/routes/guilds/#guild_id/emojis.ts (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/emojis.ts b/api/src/routes/guilds/#guild_id/emojis.ts new file mode 100644 index 00000000..f1e427e8 --- /dev/null +++ b/api/src/routes/guilds/#guild_id/emojis.ts @@ -0,0 +1,78 @@ +import { Router, Request, Response } from "express"; +import { + Config, + emitEvent, + Emoji, + GuildEmojiUpdateEvent, + Member, + User +} from "@fosscord/util"; +import { HTTPError } from "lambert-server"; +import { route } from "@fosscord/api"; + +const router = Router(); + +export interface EmojiCreateSchema { + name?: string; + image?: string; + roles?: string[]; +} + +router.get("/", route({}), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + + await Member.IsInGuildOrFail(req.user_id, guild_id); + + const emojis = await Emoji.find({ guild_id: guild_id }); + + return res.json(emojis); +}); + +router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + const emoji_id = req.params.emoji_id; + + await Member.IsInGuildOrFail(req.user_id, guild_id); + + const emoji = await Emoji.findOneOrFail({ guild_id: guild_id, id: emoji_id }); + + return res.json(emoji); +}); + +/** WIP +router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + const body = req.body as EmojiCreateSchema; + + const emoji_count = await Emoji.count({ guild_id: guild_id }); + const { maxEmojis } = Config.get().limits.guild; + + if (emoji_count >= maxEmojis) { + throw new HTTPError("Max emojis reached", 400); + } +}); +*/ + +router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + const { emoji_id } = req.params; + + await Promise.all([ + Emoji.delete({ + id: emoji_id, + guild_id: guild_id + }), + emitEvent({ + event: "GUILD_EMOJI_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojiUpdateEvent) + ]) + + res.sendStatus(204); +}); + +export default router; -- cgit 1.5.1 From 23827dfc774b619c1f19512e199584785e8ff525 Mon Sep 17 00:00:00 2001 From: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com> Date: Fri, 8 Oct 2021 23:49:45 -0300 Subject: (api): fix route name --- api/src/routes/guilds/#guild_id/emoji.ts | 78 +++++++++++++++++++++++++++++++ api/src/routes/guilds/#guild_id/emojis.ts | 78 ------------------------------- 2 files changed, 78 insertions(+), 78 deletions(-) create mode 100644 api/src/routes/guilds/#guild_id/emoji.ts delete mode 100644 api/src/routes/guilds/#guild_id/emojis.ts (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/emoji.ts b/api/src/routes/guilds/#guild_id/emoji.ts new file mode 100644 index 00000000..f1e427e8 --- /dev/null +++ b/api/src/routes/guilds/#guild_id/emoji.ts @@ -0,0 +1,78 @@ +import { Router, Request, Response } from "express"; +import { + Config, + emitEvent, + Emoji, + GuildEmojiUpdateEvent, + Member, + User +} from "@fosscord/util"; +import { HTTPError } from "lambert-server"; +import { route } from "@fosscord/api"; + +const router = Router(); + +export interface EmojiCreateSchema { + name?: string; + image?: string; + roles?: string[]; +} + +router.get("/", route({}), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + + await Member.IsInGuildOrFail(req.user_id, guild_id); + + const emojis = await Emoji.find({ guild_id: guild_id }); + + return res.json(emojis); +}); + +router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + const emoji_id = req.params.emoji_id; + + await Member.IsInGuildOrFail(req.user_id, guild_id); + + const emoji = await Emoji.findOneOrFail({ guild_id: guild_id, id: emoji_id }); + + return res.json(emoji); +}); + +/** WIP +router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + const body = req.body as EmojiCreateSchema; + + const emoji_count = await Emoji.count({ guild_id: guild_id }); + const { maxEmojis } = Config.get().limits.guild; + + if (emoji_count >= maxEmojis) { + throw new HTTPError("Max emojis reached", 400); + } +}); +*/ + +router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const guild_id = req.params.guild_id; + const { emoji_id } = req.params; + + await Promise.all([ + Emoji.delete({ + id: emoji_id, + guild_id: guild_id + }), + emitEvent({ + event: "GUILD_EMOJI_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojiUpdateEvent) + ]) + + res.sendStatus(204); +}); + +export default router; diff --git a/api/src/routes/guilds/#guild_id/emojis.ts b/api/src/routes/guilds/#guild_id/emojis.ts deleted file mode 100644 index f1e427e8..00000000 --- a/api/src/routes/guilds/#guild_id/emojis.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Router, Request, Response } from "express"; -import { - Config, - emitEvent, - Emoji, - GuildEmojiUpdateEvent, - Member, - User -} from "@fosscord/util"; -import { HTTPError } from "lambert-server"; -import { route } from "@fosscord/api"; - -const router = Router(); - -export interface EmojiCreateSchema { - name?: string; - image?: string; - roles?: string[]; -} - -router.get("/", route({}), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - - await Member.IsInGuildOrFail(req.user_id, guild_id); - - const emojis = await Emoji.find({ guild_id: guild_id }); - - return res.json(emojis); -}); - -router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const emoji_id = req.params.emoji_id; - - await Member.IsInGuildOrFail(req.user_id, guild_id); - - const emoji = await Emoji.findOneOrFail({ guild_id: guild_id, id: emoji_id }); - - return res.json(emoji); -}); - -/** WIP -router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const body = req.body as EmojiCreateSchema; - - const emoji_count = await Emoji.count({ guild_id: guild_id }); - const { maxEmojis } = Config.get().limits.guild; - - if (emoji_count >= maxEmojis) { - throw new HTTPError("Max emojis reached", 400); - } -}); -*/ - -router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const { emoji_id } = req.params; - - await Promise.all([ - Emoji.delete({ - id: emoji_id, - guild_id: guild_id - }), - emitEvent({ - event: "GUILD_EMOJI_UPDATE", - guild_id: guild_id, - data: { - guild_id: guild_id, - emojis: await Emoji.find({ guild_id: guild_id }) - } - } as GuildEmojiUpdateEvent) - ]) - - res.sendStatus(204); -}); - -export default router; -- cgit 1.5.1 From 8d5911736ad41cdcd17654a269e95777a3219385 Mon Sep 17 00:00:00 2001 From: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com> Date: Sat, 9 Oct 2021 10:52:43 -0300 Subject: (api): add patch to emoji route --- api/src/routes/guilds/#guild_id/emoji.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/emoji.ts b/api/src/routes/guilds/#guild_id/emoji.ts index f1e427e8..f14790ed 100644 --- a/api/src/routes/guilds/#guild_id/emoji.ts +++ b/api/src/routes/guilds/#guild_id/emoji.ts @@ -18,6 +18,11 @@ export interface EmojiCreateSchema { roles?: string[]; } +export interface EmojiModifySchema { + name?: string; + roles?: string[]; +} + router.get("/", route({}), async (req: Request, res: Response) => { const guild_id = req.params.guild_id; @@ -53,6 +58,27 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A }); */ +router.patch("/:emoji_id", route({ body: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const { emoji_id, guild_id } = req.params; + const body = req.body as EmojiModifySchema; + + const emoji = new Emoji({ ...body, id: emoji_id, guild_id: guild_id }); + + await Promise.all([ + emoji.save(), + emitEvent({ + event: "GUILD_EMOJI_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojiUpdateEvent) + ]) + + return res.json(emoji); +}); + router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { const guild_id = req.params.guild_id; const { emoji_id } = req.params; -- cgit 1.5.1 From 4e684e2cd45b12184422ec9933476e553d037016 Mon Sep 17 00:00:00 2001 From: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com> Date: Sat, 9 Oct 2021 11:36:58 -0300 Subject: (api): implement post on emoji route --- api/src/routes/guilds/#guild_id/emoji.ts | 42 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/emoji.ts b/api/src/routes/guilds/#guild_id/emoji.ts index f14790ed..6b194f7b 100644 --- a/api/src/routes/guilds/#guild_id/emoji.ts +++ b/api/src/routes/guilds/#guild_id/emoji.ts @@ -1,10 +1,13 @@ import { Router, Request, Response } from "express"; import { Config, + DiscordApiErrors, emitEvent, Emoji, GuildEmojiUpdateEvent, + handleFile, Member, + Snowflake, User } from "@fosscord/util"; import { HTTPError } from "lambert-server"; @@ -15,6 +18,7 @@ const router = Router(); export interface EmojiCreateSchema { name?: string; image?: string; + require_colons?: boolean | null; roles?: string[]; } @@ -44,7 +48,6 @@ router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { return res.json(emoji); }); -/** WIP router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { const guild_id = req.params.guild_id; const body = req.body as EmojiCreateSchema; @@ -52,11 +55,42 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A const emoji_count = await Emoji.count({ guild_id: guild_id }); const { maxEmojis } = Config.get().limits.guild; - if (emoji_count >= maxEmojis) { - throw new HTTPError("Max emojis reached", 400); + if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis); + + const id = Snowflake.generate(); + + if (!body.image) { + throw DiscordApiErrors.GENERAL_ERROR.withParams("No image provided"); } + + if (body.require_colons === null) body.require_colons = true; + + const user = await User.findOneOrFail({ id: req.user_id }); + + body.image = await handleFile(`/emojis/${id}`, body.image); + + const emoji = new Emoji({ + id: id, + guild_id: guild_id, + ...body, + user: user, + managed: false, + animated: false, // TODO: Add support animated emojis + available: true + }); + + await Promise.all([ + emoji.save(), + emitEvent({ + event: "GUILD_EMOJI_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojiUpdateEvent) + ]); }); -*/ router.patch("/:emoji_id", route({ body: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { const { emoji_id, guild_id } = req.params; -- cgit 1.5.1 From bfd74459782148f166461356d5ee9291f496b68c Mon Sep 17 00:00:00 2001 From: Lobo Metalúrgico <43734867+LoboMetalurgico@users.noreply.github.com> Date: Sat, 9 Oct 2021 11:43:27 -0300 Subject: (api): fix some issues --- api/src/routes/guilds/#guild_id/emoji.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/emoji.ts b/api/src/routes/guilds/#guild_id/emoji.ts index 6b194f7b..000c9949 100644 --- a/api/src/routes/guilds/#guild_id/emoji.ts +++ b/api/src/routes/guilds/#guild_id/emoji.ts @@ -10,7 +10,6 @@ import { Snowflake, User } from "@fosscord/util"; -import { HTTPError } from "lambert-server"; import { route } from "@fosscord/api"; const router = Router(); @@ -75,7 +74,7 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A ...body, user: user, managed: false, - animated: false, // TODO: Add support animated emojis + animated: false, // TODO: Add support animated emojis available: true }); @@ -108,7 +107,7 @@ router.patch("/:emoji_id", route({ body: "EmojiModifySchema", permission: "MANAG emojis: await Emoji.find({ guild_id: guild_id }) } } as GuildEmojiUpdateEvent) - ]) + ]); return res.json(emoji); }); -- cgit 1.5.1 From 74bd98737acf89d9d56ee66a68694d82f591d591 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 10 Oct 2021 11:03:32 +0200 Subject: :art: clean up imports + classes --- .gitignore | 4 +- api/src/Server.ts | 3 -- api/src/routes/channels/#channel_id/invites.ts | 2 +- .../routes/channels/#channel_id/messages/index.ts | 3 +- api/src/test/jwt.ts | 37 -------------- api/src/test/jwt2.ts | 13 ----- api/src/test/password_test.ts | 12 ----- gateway/src/events/Connection.ts | 3 +- gateway/src/opcodes/LazyRequest.ts | 10 +++- util/src/entities/BaseClass.ts | 58 ++-------------------- util/src/entities/Message.ts | 3 -- util/src/entities/RateLimit.ts | 3 -- util/src/entities/User.ts | 9 ++-- util/src/entities/Webhook.ts | 3 -- util/src/index.ts | 6 --- 15 files changed, 23 insertions(+), 146 deletions(-) delete mode 100644 api/src/test/jwt.ts delete mode 100644 api/src/test/jwt2.ts delete mode 100644 api/src/test/password_test.ts (limited to 'api/src') diff --git a/.gitignore b/.gitignore index f67723af..a09d215a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ node_modules api/assets/*.js api/assets/*.css database.db -tsconfig.tsbuildinfo \ No newline at end of file +tsconfig.tsbuildinfo +files/ +.env \ No newline at end of file diff --git a/api/src/Server.ts b/api/src/Server.ts index 12c1d6b4..19e3d245 100644 --- a/api/src/Server.ts +++ b/api/src/Server.ts @@ -1,13 +1,10 @@ -import { OptionsJson } from "body-parser"; import "missing-native-js-functions"; -import { Connection } from "mongoose"; import { Server, ServerOptions } from "lambert-server"; import { Authentication, CORS } from "./middlewares/"; import { Config, initDatabase, initEvent } from "@fosscord/util"; import { ErrorHandler } from "./middlewares/ErrorHandler"; import { BodyParser } from "./middlewares/BodyParser"; import { Router, Request, Response, NextFunction } from "express"; -import mongoose from "mongoose"; import path from "path"; import { initRateLimits } from "./middlewares/RateLimit"; import TestClient from "./middlewares/TestClient"; diff --git a/api/src/routes/channels/#channel_id/invites.ts b/api/src/routes/channels/#channel_id/invites.ts index 22420983..6d2c625d 100644 --- a/api/src/routes/channels/#channel_id/invites.ts +++ b/api/src/routes/channels/#channel_id/invites.ts @@ -2,7 +2,7 @@ import { Router, Request, Response } from "express"; import { HTTPError } from "lambert-server"; import { route } from "@fosscord/api"; import { random } from "@fosscord/api"; -import { getPermission, Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util"; +import { Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util"; import { isTextChannel } from "./messages"; const router: Router = Router(); diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 4ec31417..26bb9e5d 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -22,7 +22,7 @@ const router: Router = Router(); export default router; -function isTextChannel(type: ChannelType): boolean { +export function isTextChannel(type: ChannelType): boolean { switch (type) { case ChannelType.GUILD_STORE: case ChannelType.GUILD_VOICE: @@ -39,7 +39,6 @@ function isTextChannel(type: ChannelType): boolean { return true; } } -module.exports.isTextChannel = isTextChannel; export interface MessageCreateSchema { content?: string; diff --git a/api/src/test/jwt.ts b/api/src/test/jwt.ts deleted file mode 100644 index bdad513b..00000000 --- a/api/src/test/jwt.ts +++ /dev/null @@ -1,37 +0,0 @@ -const jwa = require("jwa"); - -var STR64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".split(""); - -function base64url(string: string, encoding: string) { - // @ts-ignore - return Buffer.from(string, encoding).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); -} - -function to64String(input: number, current = ""): string { - if (input < 0 && current.length == 0) { - input = input * -1; - } - var modify = input % 64; - var remain = Math.floor(input / 64); - var result = STR64[modify] + current; - return remain <= 0 ? result : to64String(remain, result); -} - -function to64Parse(input: string) { - var result = 0; - var toProc = input.split(""); - var e; - for (e in toProc) { - result = result * 64 + STR64.indexOf(toProc[e]); - } - return result; -} - -// @ts-ignore -const start = `${base64url("311129357362135041")}.${to64String(Date.now())}`; -const signature = jwa("HS256").sign(start, `test`); -const token = `${start}.${signature}`; -console.log(token); - -// MzExMTI5MzU3MzYyMTM1MDQx.XdQb_rA.907VgF60kocnOTl32MSUWGSSzbAytQ0jbt36KjLaxuY -// MzExMTI5MzU3MzYyMTM1MDQx.XdQbaPy.4vGx4L7IuFJGsRe6IL3BeybLIvbx4Vauvx12pwNsy2U diff --git a/api/src/test/jwt2.ts b/api/src/test/jwt2.ts deleted file mode 100644 index e231233d..00000000 --- a/api/src/test/jwt2.ts +++ /dev/null @@ -1,13 +0,0 @@ -import jwt from "jsonwebtoken"; - -const algorithm = "HS256"; -const iat = Math.floor(Date.now() / 1000); - -// @ts-ignore -const token = jwt.sign({ id: "311129357362135041" }, "secret", { - algorithm, -}); -console.log(token); - -const decoded = jwt.verify(token, "secret", { algorithms: [algorithm] }); -console.log(decoded); diff --git a/api/src/test/password_test.ts b/api/src/test/password_test.ts deleted file mode 100644 index 983b18ae..00000000 --- a/api/src/test/password_test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { checkPassword } from "@fosscord/api"; - -console.log(checkPassword("123456789012345")); -// -> 0.25 -console.log(checkPassword("ABCDEFGHIJKLMOPQ")); -// -> 0.25 -console.log(checkPassword("ABC123___...123")); -// -> -console.log(checkPassword("")); -// -> -// console.log(checkPassword("")); -// // -> diff --git a/gateway/src/events/Connection.ts b/gateway/src/events/Connection.ts index c1a6b618..2cf22f7d 100644 --- a/gateway/src/events/Connection.ts +++ b/gateway/src/events/Connection.ts @@ -24,6 +24,7 @@ export async function Connection( request: IncomingMessage ) { try { + // @ts-ignore socket.on("close", Close); // @ts-ignore socket.on("message", Message); @@ -68,12 +69,10 @@ export async function Connection( }); socket.readyTimeout = setTimeout(() => { - Session.delete({ session_id: socket.session_id }); //should we await? return socket.close(CLOSECODES.Session_timed_out); }, 1000 * 30); } catch (error) { console.error(error); - Session.delete({ session_id: socket.session_id }); //should we await? return socket.close(CLOSECODES.Unknown_error); } } diff --git a/gateway/src/opcodes/LazyRequest.ts b/gateway/src/opcodes/LazyRequest.ts index d37e32da..f5fd561a 100644 --- a/gateway/src/opcodes/LazyRequest.ts +++ b/gateway/src/opcodes/LazyRequest.ts @@ -41,6 +41,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { const items = []; for (const role of roles) { + // @ts-ignore const [role_members, other_members] = partition(members, (m: Member) => m.roles.find((r) => r.id === role.id) ); @@ -53,9 +54,12 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { groups.push(group); for (const member of role_members) { - member.roles = member.roles.filter((x) => x.id !== guild_id); + member.roles = member.roles.filter((x: Role) => x.id !== guild_id); items.push({ - member: { ...member, roles: member.roles.map((x) => x.id) }, + member: { + ...member, + roles: member.roles.map((x: Role) => x.id), + }, }); } members = other_members; @@ -84,7 +88,9 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { } function partition(array: T[], isValid: Function) { + // @ts-ignore return array.reduce( + // @ts-ignore ([pass, fail], elem) => { return isValid(elem) ? [[...pass, elem], fail] diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts index beccf04b..43b69c76 100644 --- a/util/src/entities/BaseClass.ts +++ b/util/src/entities/BaseClass.ts @@ -1,19 +1,8 @@ import "reflect-metadata"; -import { - BaseEntity, - BeforeInsert, - BeforeUpdate, - EntityMetadata, - FindConditions, - ObjectIdColumn, - PrimaryColumn, -} from "typeorm"; +import { BaseEntity, EntityMetadata, FindConditions, ObjectIdColumn, PrimaryColumn } from "typeorm"; import { Snowflake } from "../util/Snowflake"; import "missing-native-js-functions"; -// TODO use class-validator https://typeorm.io/#/validation with class annotators (isPhone/isEmail) combined with types from typescript-json-schema -// btw. we don't use class-validator for everything, because we need to explicitly set the type instead of deriving it from typescript also it doesn't easily support nested objects - export class BaseClassWithoutId extends BaseEntity { constructor(props?: any) { super(); @@ -38,11 +27,12 @@ export class BaseClassWithoutId extends BaseEntity { .concat(this.metadata.relations.map((x) => x.propertyName)) ); // will not include relational properties + console.log(properties); for (const key in props) { if (!properties.has(key)) continue; // @ts-ignore - const setter = this[`set${key.capitalize()}`]; + const setter = this[`set${key.capitalize()}`]; // use setter function if it exists if (setter) { setter.call(this, props[key]); @@ -53,12 +43,6 @@ export class BaseClassWithoutId extends BaseEntity { } } - @BeforeUpdate() - @BeforeInsert() - validate() { - return this; - } - toJSON(): any { return Object.fromEntries( this.metadata.columns // @ts-ignore @@ -76,42 +60,6 @@ export class BaseClassWithoutId extends BaseEntity { const repository = this.getRepository(); return repository.decrement(conditions, propertyPath, value); } - - // static async delete(criteria: FindConditions, options?: RemoveOptions) { - // if (!criteria) throw new Error("You need to specify delete criteria"); - - // const repository = this.getRepository(); - // const promises = repository.metadata.relations.map(async (x) => { - // if (x.orphanedRowAction !== "delete") return; - - // const foreignKey = - // x.foreignKeys.find((key) => key.entityMetadata === repository.metadata) || - // x.inverseRelation?.foreignKeys[0]; // find foreign key for this entity - // if (!foreignKey) { - // throw new Error( - // `Foreign key not found for entity ${repository.metadata.name} in relation ${x.propertyName}` - // ); - // } - // const id = (criteria as any)[foreignKey.referencedColumnNames[0]]; - // if (!id) throw new Error("id missing in criteria options " + foreignKey.referencedColumnNames); - - // if (x.relationType === "many-to-many") { - // return getConnection() - // .createQueryBuilder() - // .relation(this, x.propertyName) - // .of(id) - // .remove({ [foreignKey.columnNames[0]]: id }); - // } else if ( - // x.relationType === "one-to-one" || - // x.relationType === "many-to-one" || - // x.relationType === "one-to-many" - // ) { - // return (x.inverseEntityMetadata.target as any).delete({ [foreignKey.columnNames[0]]: id }); - // } - // }); - // await Promise.all(promises); - // return super.delete(criteria, options); - // } } export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryColumn; diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 04c3c7aa..c7f7a1d1 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -46,9 +46,6 @@ export enum MessageType { @Entity("messages") export class Message extends BaseClass { - @Column() - id: string; - @Column({ nullable: true }) @RelationId((message: Message) => message.channel) channel_id: string; diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts index fa9c32c1..f5916f6b 100644 --- a/util/src/entities/RateLimit.ts +++ b/util/src/entities/RateLimit.ts @@ -3,9 +3,6 @@ import { BaseClass } from "./BaseClass"; @Entity("rate_limits") export class RateLimit extends BaseClass { - @Column() - id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498 - @Column() // no relation as it also executor_id: string; diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 97564af3..662ab031 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -198,7 +198,7 @@ export class User extends BaseClass { // randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists // if it all five times already exists, abort with USERNAME_TOO_MANY_USERS error // else just continue - // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the mongodb database? + // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the database? for (let tries = 0; tries < 5; tries++) { discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); exists = await User.findOne({ where: { discriminator, username: username }, select: ["id"] }); @@ -219,7 +219,7 @@ export class User extends BaseClass { // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false const language = req.language === "en" ? "en-US" : req.language || "en-US"; - const user = await new User({ + const user = new User({ created_at: new Date(), username: username, discriminator, @@ -246,7 +246,10 @@ export class User extends BaseClass { }, settings: { ...defaultSettings, locale: language }, fingerprints: [], - }).save(); + }); + + console.log(user); + await user.save(); if (Config.get().guild.autoJoin.enabled) { for (const guild of Config.get().guild.autoJoin.guilds || []) { diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts index 8382435f..9d8609ae 100644 --- a/util/src/entities/Webhook.ts +++ b/util/src/entities/Webhook.ts @@ -12,9 +12,6 @@ export enum WebhookType { @Entity("webhooks") export class Webhook extends BaseClass { - @Column() - id: string; - @Column({ type: "simple-enum", enum: WebhookType }) type: WebhookType; diff --git a/util/src/index.ts b/util/src/index.ts index fc00d46b..ae0f7e54 100644 --- a/util/src/index.ts +++ b/util/src/index.ts @@ -1,12 +1,6 @@ import "reflect-metadata"; -// export * as Constants from "../util/Constants"; export * from "./util/index"; export * from "./interfaces/index"; export * from "./entities/index"; export * from "./dtos/index"; - -// import Config from "../util/Config"; -// import db, { MongooseCache, toObject } from "./util/Database"; - -// export { Config }; -- cgit 1.5.1 From 57c9813f2fca9b8f4e02f09d71515bc7f59b4010 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 10 Oct 2021 11:08:55 +0200 Subject: :bug: convert bigint literal to object --- api/src/routes/channels/#channel_id/permissions.ts | 4 ++-- api/src/routes/guilds/#guild_id/roles.ts | 9 +++++++-- api/src/routes/guilds/templates/index.ts | 2 +- bundle/package.json | 2 +- gateway/src/opcodes/Identify.ts | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/channels/#channel_id/permissions.ts b/api/src/routes/channels/#channel_id/permissions.ts index 6ebf721a..2eded853 100644 --- a/api/src/routes/channels/#channel_id/permissions.ts +++ b/api/src/routes/channels/#channel_id/permissions.ts @@ -44,8 +44,8 @@ router.put( }; channel.permission_overwrites!.push(overwrite); } - overwrite.allow = String(req.permission!.bitfield & (BigInt(body.allow) || 0n)); - overwrite.deny = String(req.permission!.bitfield & (BigInt(body.deny) || 0n)); + overwrite.allow = String(req.permission!.bitfield & (BigInt(body.allow) || BigInt("0"))); + overwrite.deny = String(req.permission!.bitfield & (BigInt(body.deny) || BigInt("0"))); await Promise.all([ channel.save(), diff --git a/api/src/routes/guilds/#guild_id/roles.ts b/api/src/routes/guilds/#guild_id/roles.ts index d1d60906..0a57c6a2 100644 --- a/api/src/routes/guilds/#guild_id/roles.ts +++ b/api/src/routes/guilds/#guild_id/roles.ts @@ -57,7 +57,7 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }) ...body, guild_id: guild_id, managed: false, - permissions: String(req.permission!.bitfield & (body.permissions || 0n)), + permissions: String(req.permission!.bitfield & (body.permissions || BigInt("0"))), tags: undefined }); @@ -105,7 +105,12 @@ router.patch("/:role_id", route({ body: "RoleModifySchema", permission: "MANAGE_ const { role_id, guild_id } = req.params; const body = req.body as RoleModifySchema; - const role = new Role({ ...body, id: role_id, guild_id, permissions: String(req.permission!.bitfield & (body.permissions || 0n)) }); + const role = new Role({ + ...body, + id: role_id, + guild_id, + permissions: String(req.permission!.bitfield & (body.permissions || BigInt("0"))) + }); await Promise.all([ role.save(), diff --git a/api/src/routes/guilds/templates/index.ts b/api/src/routes/guilds/templates/index.ts index b5e243e9..86316d23 100644 --- a/api/src/routes/guilds/templates/index.ts +++ b/api/src/routes/guilds/templates/index.ts @@ -47,7 +47,7 @@ router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req: managed: true, mentionable: true, name: "@everyone", - permissions: 2251804225n, + permissions: BigInt("2251804225"), position: 0, tags: null }).save() diff --git a/bundle/package.json b/bundle/package.json index 05cefaab..3bed5b07 100644 --- a/bundle/package.json +++ b/bundle/package.json @@ -94,4 +94,4 @@ "ws": "^7.4.2", "cheerio": "^1.0.0-rc.10" } -} +} \ No newline at end of file diff --git a/gateway/src/opcodes/Identify.ts b/gateway/src/opcodes/Identify.ts index b81c7bf4..5d5b72d1 100644 --- a/gateway/src/opcodes/Identify.ts +++ b/gateway/src/opcodes/Identify.ts @@ -41,7 +41,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { return this.close(CLOSECODES.Authentication_failed); } this.user_id = decoded.id; - if (!identify.intents) identify.intents = 0b11111111111111n; + if (!identify.intents) identify.intents = BigInt("0b11111111111111"); this.intents = new Intents(identify.intents); if (identify.shard) { this.shard_id = identify.shard[0]; -- cgit 1.5.1 From 3b4e026eadd1334f3f075774613ed7e8c4203b40 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 10 Oct 2021 12:35:46 +0200 Subject: :bug: fix tsc compiler --- api/src/Server.ts | 3 ++- bundle/scripts/build.js | 36 +++++++++++++++++++++++++----------- bundle/tsconfig.json | 21 ++++++--------------- cdn/src/Server.ts | 14 ++++++++++---- util/src/util/TraverseDirectory.ts | 10 ++++++++++ util/src/util/index.ts | 1 + 6 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 util/src/util/TraverseDirectory.ts (limited to 'api/src') diff --git a/api/src/Server.ts b/api/src/Server.ts index 19e3d245..1f11a295 100644 --- a/api/src/Server.ts +++ b/api/src/Server.ts @@ -11,6 +11,7 @@ import TestClient from "./middlewares/TestClient"; import { initTranslation } from "./middlewares/Translation"; import morgan from "morgan"; import { initInstance } from "./util/Instance"; +import { registerRoutes } from "@fosscord/util"; export interface FosscordServerOptions extends ServerOptions {} @@ -72,7 +73,7 @@ export class FosscordServer extends Server { await initRateLimits(api); await initTranslation(api); - this.routes = await this.registerRoutes(path.join(__dirname, "routes", "/")); + this.routes = await registerRoutes(this, path.join(__dirname, "routes", "/")); api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => { if (error) return next(error); diff --git a/bundle/scripts/build.js b/bundle/scripts/build.js index 2a82ec57..69111c88 100644 --- a/bundle/scripts/build.js +++ b/bundle/scripts/build.js @@ -2,18 +2,32 @@ const { execSync } = require("child_process"); const path = require("path"); const fse = require("fs-extra"); -const api = path.join(__dirname, "..", "..", "api"); -const dist = path.join(__dirname, "..", "dist"); +fse.copySync(path.join(__dirname, "..", "..", "api", "assets"), path.join(__dirname, "..", "dist", "api", "assets")); +fse.copySync( + path.join(__dirname, "..", "..", "api", "client_test"), + path.join(__dirname, "..", "dist", "api", "client_test") +); +fse.copySync(path.join(__dirname, "..", "..", "api", "locales"), path.join(__dirname, "..", "dist", "api", "locales")); +fse.copySync(path.join(__dirname, "..", "..", "api", "src"), path.join(__dirname, "..", "dist", "api", "src")); +fse.copySync(path.join(__dirname, "..", "..", "util", "src"), path.join(__dirname, "..", "dist", "util", "src")); +fse.copySync(path.join(__dirname, "..", "..", "cdn", "src"), path.join(__dirname, "..", "dist", "cdn", "src")); +fse.copySync(path.join(__dirname, "..", "..", "gateway", "src"), path.join(__dirname, "..", "dist", "gateway", "src")); +fse.copySync(path.join(__dirname, "..", "..", "bundle", "src"), path.join(__dirname, "..", "dist", "bundle", "src")); -fse.copySync(path.join(api, "assets"), path.join(dist, "api", "assets")); -fse.copySync(path.join(api, "client_test"), path.join(dist, "api", "client_test")); -fse.copySync(path.join(api, "locales"), path.join(dist, "api", "locales")); +console.log("Copying src files done"); +console.log("Compiling src files ..."); console.log( - execSync("node " + path.join(__dirname, "..", "node_modules", "typescript", "lib", "tsc.js") + " -p .", { - cwd: path.join(__dirname, ".."), - shell: true, - env: process.env, - encoding: "utf8", - }) + execSync( + "node " + + path.join(__dirname, "..", "node_modules", "typescript", "lib", "tsc.js") + + " -p " + + path.join(__dirname, ".."), + { + cwd: path.join(__dirname, ".."), + shell: true, + env: process.env, + encoding: "utf8", + } + ) ); diff --git a/bundle/tsconfig.json b/bundle/tsconfig.json index 1b2949da..4e8db342 100644 --- a/bundle/tsconfig.json +++ b/bundle/tsconfig.json @@ -1,16 +1,11 @@ { - "include": [ - "../api/src/**/*.ts", - "../gateway/src/**/*.ts", - "../cdn/src/**/*.ts", - "../util/src/**/*.ts", - "src/**/*.ts" - ], + "include": ["dist/**/*.ts"], + "exclude": [], "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ /* Basic Options */ - "incremental": true /* Enable incremental compilation */, + "incremental": false /* Enable incremental compilation */, "target": "ES6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, "lib": ["ES2021"] /* Specify library files to be included in the compilation. */, @@ -22,7 +17,7 @@ "sourceMap": false /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist/" /* Redirect output structure to the directory. */, - "rootDir": "../" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + "rootDir": "./dist/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ @@ -73,16 +68,12 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "resolveJsonModule": true, - "baseUrl": "..", + "baseUrl": "./dist/", "paths": { "@fosscord/api": ["api/src/index"], - "@fosscord/api/*": ["api/src/*"], "@fosscord/gateway": ["gateway/src/index"], - "@fosscord/gateway/*": ["gateway/src/*"], "@fosscord/cdn": ["cdn/src/index"], - "@fosscord/cdn/*": ["cdn/src/*"], - "@fosscord/util": ["util/src/index"], - "@fosscord/util/*": ["util/src/*"] + "@fosscord/util": ["util/src/index"] }, "plugins": [{ "transform": "@zerollup/ts-transform-paths" }] } diff --git a/cdn/src/Server.ts b/cdn/src/Server.ts index 590eda6f..cac34a80 100644 --- a/cdn/src/Server.ts +++ b/cdn/src/Server.ts @@ -1,5 +1,5 @@ import { Server, ServerOptions } from "lambert-server"; -import { Config, initDatabase } from "@fosscord/util"; +import { Config, initDatabase, registerRoutes } from "@fosscord/util"; import path from "path"; import avatarsRoute from "./routes/avatars"; import bodyParser from "body-parser"; @@ -23,13 +23,19 @@ export class CDNServer extends Server { "Content-security-policy", "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';" ); - res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*"); - res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*"); + res.set( + "Access-Control-Allow-Headers", + req.header("Access-Control-Request-Headers") || "*" + ); + res.set( + "Access-Control-Allow-Methods", + req.header("Access-Control-Request-Methods") || "*" + ); next(); }); this.app.use(bodyParser.json({ inflate: true, limit: "10mb" })); - await this.registerRoutes(path.join(__dirname, "routes/")); + await registerRoutes(this, path.join(__dirname, "routes/")); this.app.use("/icons/", avatarsRoute); this.log("verbose", "[Server] Route /icons registered"); diff --git a/util/src/util/TraverseDirectory.ts b/util/src/util/TraverseDirectory.ts new file mode 100644 index 00000000..275b7dcc --- /dev/null +++ b/util/src/util/TraverseDirectory.ts @@ -0,0 +1,10 @@ +import { Server, traverseDirectory } from "lambert-server"; + +const DEFAULT_FILTER = /^([^\.].*)(? Date: Sun, 10 Oct 2021 14:10:27 +0200 Subject: :bug: fix emojis route --- api/assets/schemas.json | 346 ++++++++++++++++++++++++++---- api/src/routes/guilds/#guild_id/emoji.ts | 137 ------------ api/src/routes/guilds/#guild_id/emojis.ts | 121 +++++++++++ 3 files changed, 431 insertions(+), 173 deletions(-) delete mode 100644 api/src/routes/guilds/#guild_id/emoji.ts create mode 100644 api/src/routes/guilds/#guild_id/emojis.ts (limited to 'api/src') diff --git a/api/assets/schemas.json b/api/assets/schemas.json index c2149836..4f1ab9a8 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -2887,47 +2887,321 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmojiListResponse": { - "type": "array", - "items": { - "type": "object", - "properties": { - "animated": { - "type": "boolean" - }, - "available": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "require_colons": { - "type": "boolean" - }, - "guild_id": { + "EmojiCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "image": { + "type": "string" + }, + "require_colons": { + "type": [ + "null", + "boolean" + ] + }, + "roles": { + "type": "array", + "items": { "type": "string" - }, - "roles": { - "type": "array", - "items": { + } + } + }, + "definitions": { + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1 + ], + "type": "number" + }, + "Embed": { + "type": "object", + "properties": { + "title": { "type": "string" + }, + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "color": { + "type": "integer" + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/definitions/EmbedImage" + }, + "thumbnail": { + "$ref": "#/definitions/EmbedImage" + }, + "video": { + "$ref": "#/definitions/EmbedImage" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + } + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "required": [ + "name", + "value" + ] + } } } }, - "required": [ - "animated", - "available", - "id", - "managed", - "name", - "require_colons" - ] + "EmbedImage": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "proxy_url": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + } + } + }, + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 2, + 3, + 4, + 5, + 6 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + } + } + }, + "UserPublic": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "id": { + "type": "string" + }, + "public_flags": { + "type": "integer" + }, + "avatar": { + "type": "string" + }, + "accent_color": { + "type": "integer" + }, + "banner": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "bot": { + "type": "boolean" + } + }, + "required": [ + "bio", + "bot", + "discriminator", + "id", + "public_flags", + "username" + ] + }, + "PublicConnectedAccount": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "verifie": { + "type": "boolean" + } + }, + "required": [ + "name", + "type", + "verifie" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "EmojiModifySchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + } }, "definitions": { "ChannelPermissionOverwriteType": { diff --git a/api/src/routes/guilds/#guild_id/emoji.ts b/api/src/routes/guilds/#guild_id/emoji.ts deleted file mode 100644 index 000c9949..00000000 --- a/api/src/routes/guilds/#guild_id/emoji.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { Router, Request, Response } from "express"; -import { - Config, - DiscordApiErrors, - emitEvent, - Emoji, - GuildEmojiUpdateEvent, - handleFile, - Member, - Snowflake, - User -} from "@fosscord/util"; -import { route } from "@fosscord/api"; - -const router = Router(); - -export interface EmojiCreateSchema { - name?: string; - image?: string; - require_colons?: boolean | null; - roles?: string[]; -} - -export interface EmojiModifySchema { - name?: string; - roles?: string[]; -} - -router.get("/", route({}), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - - await Member.IsInGuildOrFail(req.user_id, guild_id); - - const emojis = await Emoji.find({ guild_id: guild_id }); - - return res.json(emojis); -}); - -router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const emoji_id = req.params.emoji_id; - - await Member.IsInGuildOrFail(req.user_id, guild_id); - - const emoji = await Emoji.findOneOrFail({ guild_id: guild_id, id: emoji_id }); - - return res.json(emoji); -}); - -router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const body = req.body as EmojiCreateSchema; - - const emoji_count = await Emoji.count({ guild_id: guild_id }); - const { maxEmojis } = Config.get().limits.guild; - - if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis); - - const id = Snowflake.generate(); - - if (!body.image) { - throw DiscordApiErrors.GENERAL_ERROR.withParams("No image provided"); - } - - if (body.require_colons === null) body.require_colons = true; - - const user = await User.findOneOrFail({ id: req.user_id }); - - body.image = await handleFile(`/emojis/${id}`, body.image); - - const emoji = new Emoji({ - id: id, - guild_id: guild_id, - ...body, - user: user, - managed: false, - animated: false, // TODO: Add support animated emojis - available: true - }); - - await Promise.all([ - emoji.save(), - emitEvent({ - event: "GUILD_EMOJI_UPDATE", - guild_id: guild_id, - data: { - guild_id: guild_id, - emojis: await Emoji.find({ guild_id: guild_id }) - } - } as GuildEmojiUpdateEvent) - ]); -}); - -router.patch("/:emoji_id", route({ body: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { - const { emoji_id, guild_id } = req.params; - const body = req.body as EmojiModifySchema; - - const emoji = new Emoji({ ...body, id: emoji_id, guild_id: guild_id }); - - await Promise.all([ - emoji.save(), - emitEvent({ - event: "GUILD_EMOJI_UPDATE", - guild_id: guild_id, - data: { - guild_id: guild_id, - emojis: await Emoji.find({ guild_id: guild_id }) - } - } as GuildEmojiUpdateEvent) - ]); - - return res.json(emoji); -}); - -router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { - const guild_id = req.params.guild_id; - const { emoji_id } = req.params; - - await Promise.all([ - Emoji.delete({ - id: emoji_id, - guild_id: guild_id - }), - emitEvent({ - event: "GUILD_EMOJI_UPDATE", - guild_id: guild_id, - data: { - guild_id: guild_id, - emojis: await Emoji.find({ guild_id: guild_id }) - } - } as GuildEmojiUpdateEvent) - ]) - - res.sendStatus(204); -}); - -export default router; diff --git a/api/src/routes/guilds/#guild_id/emojis.ts b/api/src/routes/guilds/#guild_id/emojis.ts new file mode 100644 index 00000000..ff565cd4 --- /dev/null +++ b/api/src/routes/guilds/#guild_id/emojis.ts @@ -0,0 +1,121 @@ +import { Router, Request, Response } from "express"; +import { Config, DiscordApiErrors, emitEvent, Emoji, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User } from "@fosscord/util"; +import { route } from "@fosscord/api"; + +const router = Router(); + +export interface EmojiCreateSchema { + name?: string; + image: string; + require_colons?: boolean | null; + roles?: string[]; +} + +export interface EmojiModifySchema { + name?: string; + roles?: string[]; +} + +router.get("/", route({}), async (req: Request, res: Response) => { + const { guild_id } = req.params; + + await Member.IsInGuildOrFail(req.user_id, guild_id); + + const emojis = await Emoji.find({ where: { guild_id: guild_id }, relations: ["user"] }); + + return res.json(emojis); +}); + +router.get("/:emoji_id", route({}), async (req: Request, res: Response) => { + const { guild_id, emoji_id } = req.params; + + await Member.IsInGuildOrFail(req.user_id, guild_id); + + const emoji = await Emoji.findOneOrFail({ where: { guild_id: guild_id, id: emoji_id }, relations: ["user"] }); + + return res.json(emoji); +}); + +router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const { guild_id } = req.params; + const body = req.body as EmojiCreateSchema; + + const emoji_count = await Emoji.count({ guild_id: guild_id }); + const { maxEmojis } = Config.get().limits.guild; + + if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis); + + const id = Snowflake.generate(); + + if (body.require_colons == null) body.require_colons = true; + + const user = await User.findOneOrFail({ id: req.user_id }); + + body.image = (await handleFile(`/emojis/${id}`, body.image)) as string; + + const emoji = await new Emoji({ + id: id, + guild_id: guild_id, + ...body, + user: user, + managed: false, + animated: false, // TODO: Add support animated emojis + available: true, + roles: [] + }).save(); + + await emitEvent({ + event: "GUILD_EMOJIS_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojisUpdateEvent); + + return res.status(201).json(emoji); +}); + +router.patch( + "/:emoji_id", + route({ body: "EmojiModifySchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), + async (req: Request, res: Response) => { + const { emoji_id, guild_id } = req.params; + const body = req.body as EmojiModifySchema; + + const emoji = await new Emoji({ ...body, id: emoji_id, guild_id: guild_id }).save(); + + await emitEvent({ + event: "GUILD_EMOJIS_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojisUpdateEvent); + + return res.json(emoji); + } +); + +router.delete("/:emoji_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const { emoji_id, guild_id } = req.params; + + await Emoji.delete({ + id: emoji_id, + guild_id: guild_id + }); + + await emitEvent({ + event: "GUILD_EMOJIS_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + emojis: await Emoji.find({ guild_id: guild_id }) + } + } as GuildEmojisUpdateEvent); + + res.sendStatus(204); +}); + +export default router; -- cgit 1.5.1 From 79aee5145b6b289a001caae516477c8ab641bb8f Mon Sep 17 00:00:00 2001 From: The Arcane Brony Date: Sun, 10 Oct 2021 17:55:48 +0200 Subject: Fix duplicate key --- api/assets/schemas.json | 2 +- api/src/routes/guilds/#guild_id/vanity-url.ts | 9 +++------ api/src/routes/invites/index.ts | 1 - bundle/scripts/build.js | 27 +++++++++++++++++++++------ util/src/entities/Guild.ts | 8 -------- util/src/entities/Invite.ts | 3 +++ 6 files changed, 28 insertions(+), 22 deletions(-) (limited to 'api/src') diff --git a/api/assets/schemas.json b/api/assets/schemas.json index 4f1ab9a8..a12925e0 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -4744,7 +4744,7 @@ "type": "string" }, "permissions": { - "type": "bigint" + "type": "array" }, "color": { "type": "integer" diff --git a/api/src/routes/guilds/#guild_id/vanity-url.ts b/api/src/routes/guilds/#guild_id/vanity-url.ts index 7f2cea9e..061b317c 100644 --- a/api/src/routes/guilds/#guild_id/vanity-url.ts +++ b/api/src/routes/guilds/#guild_id/vanity-url.ts @@ -10,10 +10,10 @@ const InviteRegex = /\W/g; router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { const { guild_id } = req.params; - const guild = await Guild.findOneOrFail({ where: { id: guild_id }, relations: ["vanity_url"] }); - if (!guild.vanity_url) return res.json({ code: null }); + const invite = await Invite.findOne({ where: {guild_id: guild_id, vanity_url: true} }); + if (!invite) return res.json({ code: null }); - return res.json({ code: guild.vanity_url_code, uses: guild.vanity_url.uses }); + return res.json({ code: invite.code, uses: invite.uses }); }); export interface VanityUrlSchema { @@ -33,12 +33,9 @@ router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }) const invite = await Invite.findOne({ code }); if (invite) throw new HTTPError("Invite already exists"); - const guild = await Guild.findOneOrFail({ id: guild_id }); const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT }); Promise.all([ - Guild.update({ id: guild_id }, { vanity_url_code: code }), - Invite.delete({ code: guild.vanity_url_code }), new Invite({ code: code, uses: 0, diff --git a/api/src/routes/invites/index.ts b/api/src/routes/invites/index.ts index 0fcf7c86..185311bc 100644 --- a/api/src/routes/invites/index.ts +++ b/api/src/routes/invites/index.ts @@ -33,7 +33,6 @@ router.delete("/:code", route({}), async (req: Request, res: Response) => { await Promise.all([ Invite.delete({ code }), - Guild.update({ vanity_url_code: code }, { vanity_url_code: undefined }), emitEvent({ event: "INVITE_DELETE", guild_id: guild_id, diff --git a/bundle/scripts/build.js b/bundle/scripts/build.js index 69111c88..a9798eff 100644 --- a/bundle/scripts/build.js +++ b/bundle/scripts/build.js @@ -1,6 +1,22 @@ const { execSync } = require("child_process"); const path = require("path"); const fse = require("fs-extra"); +const { getSystemErrorMap } = require("util"); +const { argv } = require("process"); + +const dirs = ["api", "util", "cdn", "gateway", "bundle"]; + +const verbose = argv.includes("verbose") || argv.includes("v"); + +if(argv.includes("clean")){ + dirs.forEach(a=>{ + var d = "../"+a+"/dist"; + if(fse.existsSync(d)) { + fse.rmSync(d,{recursive: true}); + if(verbose) console.log(`Deleted ${d}!`); + } + }); +} fse.copySync(path.join(__dirname, "..", "..", "api", "assets"), path.join(__dirname, "..", "dist", "api", "assets")); fse.copySync( @@ -8,13 +24,12 @@ fse.copySync( path.join(__dirname, "..", "dist", "api", "client_test") ); fse.copySync(path.join(__dirname, "..", "..", "api", "locales"), path.join(__dirname, "..", "dist", "api", "locales")); -fse.copySync(path.join(__dirname, "..", "..", "api", "src"), path.join(__dirname, "..", "dist", "api", "src")); -fse.copySync(path.join(__dirname, "..", "..", "util", "src"), path.join(__dirname, "..", "dist", "util", "src")); -fse.copySync(path.join(__dirname, "..", "..", "cdn", "src"), path.join(__dirname, "..", "dist", "cdn", "src")); -fse.copySync(path.join(__dirname, "..", "..", "gateway", "src"), path.join(__dirname, "..", "dist", "gateway", "src")); -fse.copySync(path.join(__dirname, "..", "..", "bundle", "src"), path.join(__dirname, "..", "dist", "bundle", "src")); +dirs.forEach(a=>{ + fse.copySync("../"+a+"/src", "dist/"+a+"/src"); + if(verbose) console.log(`Copied ${"../"+a+"/dist"} -> ${"dist/"+a+"/src"}!`); +}); -console.log("Copying src files done"); +console.log("Copying src files done"); console.log("Compiling src files ..."); console.log( diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 35595191..157f0921 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -257,14 +257,6 @@ export class Guild extends BaseClass { @Column({ nullable: true }) unavailable?: boolean; - @Column({ nullable: true }) - @RelationId((guild: Guild) => guild.vanity_url) - vanity_url_code?: string; - - @JoinColumn({ name: "vanity_url_code" }) - @ManyToOne(() => Invite) - vanity_url?: Invite; - @Column({ nullable: true }) verification_level?: number; diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts index 82556fab..d6b8f2f8 100644 --- a/util/src/entities/Invite.ts +++ b/util/src/entities/Invite.ts @@ -71,6 +71,9 @@ export class Invite extends BaseClass { @Column({ nullable: true }) target_user_type?: number; + @Column({ nullable: true}) + vanity_url?: boolean; + static async joinGuild(user_id: string, code: string) { const invite = await Invite.findOneOrFail({ code }); if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code }); -- cgit 1.5.1 From a54711f4aebd8715c768092396e5b9a7f1f20362 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 10 Oct 2021 18:28:38 +0200 Subject: :bug: fix vanity url --- api/src/routes/guilds/#guild_id/vanity-url.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/vanity-url.ts b/api/src/routes/guilds/#guild_id/vanity-url.ts index 061b317c..63173345 100644 --- a/api/src/routes/guilds/#guild_id/vanity-url.ts +++ b/api/src/routes/guilds/#guild_id/vanity-url.ts @@ -10,7 +10,7 @@ const InviteRegex = /\W/g; router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { const { guild_id } = req.params; - const invite = await Invite.findOne({ where: {guild_id: guild_id, vanity_url: true} }); + const invite = await Invite.findOne({ where: { guild_id: guild_id, vanity_url: true } }); if (!invite) return res.json({ code: null }); return res.json({ code: invite.code, uses: invite.uses }); @@ -35,15 +35,7 @@ router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }) const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT }); - Promise.all([ - new Invite({ - code: code, - uses: 0, - created_at: new Date(), - guild_id, - channel_id: id - }).save() - ]); + await Invite.update({ vanity_url: true, guild_id }, { code: code, channel_id: id }); return res.json({ code: code }); }); -- cgit 1.5.1 From aa25dac99f9ab2c595d9deeb4067f753796438a1 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 10 Oct 2021 19:00:50 +0200 Subject: :bug: fix modify role.permissions --- api/assets/schemas.json | 5 +- api/src/routes/guilds/#guild_id/roles.ts | 6 +- util/src/migrations/migrate_db_engine.js | 104 ++++++++++++++++++++++++ util/src/migrations/migrate_db_engine.ts | 131 ------------------------------- 4 files changed, 111 insertions(+), 135 deletions(-) create mode 100644 util/src/migrations/migrate_db_engine.js delete mode 100644 util/src/migrations/migrate_db_engine.ts (limited to 'api/src') diff --git a/api/assets/schemas.json b/api/assets/schemas.json index 4f1ab9a8..2ceaa923 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -2909,6 +2909,9 @@ } } }, + "required": [ + "image" + ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -4744,7 +4747,7 @@ "type": "string" }, "permissions": { - "type": "bigint" + "type": "string" }, "color": { "type": "integer" diff --git a/api/src/routes/guilds/#guild_id/roles.ts b/api/src/routes/guilds/#guild_id/roles.ts index 0a57c6a2..b1875598 100644 --- a/api/src/routes/guilds/#guild_id/roles.ts +++ b/api/src/routes/guilds/#guild_id/roles.ts @@ -17,7 +17,7 @@ const router: Router = Router(); export interface RoleModifySchema { name?: string; - permissions?: bigint; + permissions?: string; color?: number; hoist?: boolean; // whether the role should be displayed separately in the sidebar mentionable?: boolean; // whether the role should be mentionable @@ -57,7 +57,7 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }) ...body, guild_id: guild_id, managed: false, - permissions: String(req.permission!.bitfield & (body.permissions || BigInt("0"))), + permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0")), tags: undefined }); @@ -109,7 +109,7 @@ router.patch("/:role_id", route({ body: "RoleModifySchema", permission: "MANAGE_ ...body, id: role_id, guild_id, - permissions: String(req.permission!.bitfield & (body.permissions || BigInt("0"))) + permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0")) }); await Promise.all([ diff --git a/util/src/migrations/migrate_db_engine.js b/util/src/migrations/migrate_db_engine.js new file mode 100644 index 00000000..7b8b5784 --- /dev/null +++ b/util/src/migrations/migrate_db_engine.js @@ -0,0 +1,104 @@ +const { config } = require("dotenv"); +config(); +const { createConnection } = require("typeorm"); +const { initDatabase } = require("../../dist/util/Database"); +require("missing-native-js-functions"); +const { + Application, + Attachment, + Ban, + Channel, + ConnectedAccount, + Emoji, + Guild, + Invite, + Member, + Message, + ReadState, + Recipient, + Relationship, + Role, + Sticker, + Team, + TeamMember, + Template, + User, + VoiceState, + Webhook, +} = require("../../dist/entities/index"); + +async function main() { + if (!process.env.TO) throw new Error("TO database env connection string not set"); + + // manually arrange them because of foreign key + const entities = [ + User, + Guild, + Channel, + Invite, + Role, + Ban, + Application, + Emoji, + ConnectedAccount, + Member, + ReadState, + Recipient, + Relationship, + Sticker, + Team, + TeamMember, + Template, + VoiceState, + Webhook, + Message, + Attachment, + ]; + + const newDB = await initDatabase(); + + const type = process.env.TO.includes("://") ? process.env.TO.split(":")[0]?.replace("+srv", "") : "sqlite"; + const isSqlite = type.includes("sqlite"); + + // @ts-ignore + const oldDB = await createConnection({ + type, + url: isSqlite ? undefined : process.env.TO, + database: isSqlite ? process.env.TO : undefined, + entities, + name: "old", + }); + let i = 0; + + try { + for (const entity of entities) { + const entries = await oldDB.manager.find(entity); + // @ts-ignore + console.log("migrating " + entries.length + " " + entity.name + " ..."); + + for (const entry of entries) { + console.log(i++); + + try { + await newDB.manager.insert(entity, entry); + } catch (error) { + try { + if (!entry.id) throw new Error("object doesn't have a unique id: " + entry); + await newDB.manager.update(entity, { id: entry.id }, entry); + } catch (error) { + console.error("couldn't migrate " + i + " " + entity.name, error); + } + } + } + // @ts-ignore + console.log("migrated " + entries.length + " " + entity.name); + } + } catch (error) { + console.error(error.message); + } + + console.log("SUCCESS migrated all data"); + await newDB.close(); +} + +main().caught(); diff --git a/util/src/migrations/migrate_db_engine.ts b/util/src/migrations/migrate_db_engine.ts deleted file mode 100644 index 527e7b53..00000000 --- a/util/src/migrations/migrate_db_engine.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { config } from "dotenv"; -config(); -import { BaseEntity, createConnection, EntityTarget } from "typeorm"; -import { initDatabase } from "../util/Database"; -import "missing-native-js-functions"; -import { - Application, - Attachment, - Ban, - Channel, - ConnectedAccount, - defaultSettings, - Emoji, - Guild, - Invite, - Member, - Message, - ReadState, - Recipient, - Relationship, - Role, - Sticker, - Team, - TeamMember, - Template, - User, - VoiceState, - Webhook, -} from ".."; - -async function main() { - if (!process.env.TO) throw new Error("TO database env connection string not set"); - - // manually arrange them because of foreign key - const entities = [ - User, - Guild, - Channel, - Invite, - Role, - Ban, - Application, - Emoji, - ConnectedAccount, - Member, - ReadState, - Recipient, - Relationship, - Sticker, - Team, - TeamMember, - Template, - VoiceState, - Webhook, - Message, - Attachment, - ]; - - const newDB = await initDatabase(); - - const type = process.env.TO.includes("://") ? process.env.TO.split(":")[0]?.replace("+srv", "") : "sqlite"; - const isSqlite = type.includes("sqlite"); - - // @ts-ignore - const oldDB = await createConnection({ - type, - url: isSqlite ? undefined : process.env.TO, - database: isSqlite ? process.env.TO : undefined, - entities, - name: "old", - }); - let i = 0; - - try { - for (const e of entities) { - const entity = e as EntityTarget; - const entries = await oldDB.manager.find(entity); - // @ts-ignore - console.log("migrating " + entries.length + " " + entity.name + " ..."); - - for (const entry of entries) { - console.log(i++); - - if (entry instanceof User) { - if (entry.bio == null) entry.bio = ""; - if (entry.rights == null) entry.rights = "0"; - if (entry.disabled == null) entry.disabled = false; - if (entry.fingerprints == null) entry.fingerprints = []; - if (entry.deleted == null) entry.deleted = false; - if (entry.data == null) { - entry.data = { - valid_tokens_since: new Date(0), - hash: undefined, - }; - // @ts-ignore - if (entry.user_data) { - // TODO: relationships - entry.data = { - // @ts-ignore - valid_tokens_since: entry.user_data.valid_tokens_since, // @ts-ignore - hash: entry.user_data.hash, - }; - } - } - // @ts-ignore - if (entry.settings == null) { - entry.settings = defaultSettings; - // @ts-ignore - if (entry.user_data) entry.settings = entry.user_settings; - } - } - - // try { - await newDB.manager.insert(entity, entry); - // } catch (error) { - // if (!entry.id) throw new Error("object doesn't have a unique id: " + entry); - // await newDB.manager.update(entity, { id: entry.id }, entry); - // } - } - // @ts-ignore - console.log("migrated " + entries.length + " " + entity.name); - } - } catch (error) { - console.error((error as any).message); - } - - console.log("SUCCESS migrated all data"); - await newDB.close(); -} - -main().caught(); -- cgit 1.5.1 From dce8657557b336de656b7b53e4bacba3a1014d74 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Mon, 11 Oct 2021 15:21:11 +0200 Subject: :bug: fix #371 --- api/src/routes/guilds/#guild_id/channels.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/channels.ts b/api/src/routes/guilds/#guild_id/channels.ts index a36e5448..a921fa21 100644 --- a/api/src/routes/guilds/#guild_id/channels.ts +++ b/api/src/routes/guilds/#guild_id/channels.ts @@ -31,10 +31,10 @@ router.patch("/", route({ body: "ChannelReorderSchema", permission: "MANAGE_CHAN await Promise.all([ body.map(async (x) => { - if (!x.position && !x.parent_id) throw new HTTPError(`You need to at least specify position or parent_id`, 400); + if (x.position == null && !x.parent_id) throw new HTTPError(`You need to at least specify position or parent_id`, 400); const opts: any = {}; - if (x.position) opts.position = x.position; + if (x.position != null) opts.position = x.position; if (x.parent_id) { opts.parent_id = x.parent_id; -- cgit 1.5.1 From 7053d9a8b8db54b349f39e3c19a38cee8e4fdb18 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:34:18 +0200 Subject: :art: gifs --- api/src/routes/gifs/search.ts | 27 ++++--------- api/src/routes/gifs/trending-gifs.ts | 27 ++++--------- api/src/routes/gifs/trending.ts | 73 ++++++++++++++++++++---------------- api/src/util/Message.ts | 1 + util/src/entities/Config.ts | 21 +++++------ 5 files changed, 66 insertions(+), 83 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/gifs/search.ts b/api/src/routes/gifs/search.ts index 3cbff64f..45b3ddca 100644 --- a/api/src/routes/gifs/search.ts +++ b/api/src/routes/gifs/search.ts @@ -1,37 +1,24 @@ import { Router, Response, Request } from "express"; import fetch from "node-fetch"; import { route } from "@fosscord/api"; +import { getGifApiKey, parseGifResult } from "./trending"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - // TODO: Custom providers and code quality - const { q, media_format, locale, provider } = req.query; + // TODO: Custom providers + const { q, media_format, locale } = req.query; - const parseResult = (result: any) => { - return { - id: result.id, - title: result.title, - url: result.itemurl, - src: result.media[0].mp4.url, - gif_src: result.media[0].gif.url, - width: result.media[0].mp4.dims[0], - height: result.media[0].mp4.dims[1], - preview: result.media[0].mp4.preview - }; - }; + const apiKey = getGifApiKey(); - const response = await fetch(`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=LIVDSRZULELA`, { + const response = await fetch(`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`, { method: "get", headers: { "Content-Type": "application/json" } }); const { results } = await response.json(); - let cache = new Array() as any[]; - results.forEach((result: any) => { - cache.push(parseResult(result)); - }); - res.json(cache).status(200); + + res.json(results.map(parseGifResult)).status(200); }); export default router; diff --git a/api/src/routes/gifs/trending-gifs.ts b/api/src/routes/gifs/trending-gifs.ts index 76e791fa..b5f87222 100644 --- a/api/src/routes/gifs/trending-gifs.ts +++ b/api/src/routes/gifs/trending-gifs.ts @@ -1,37 +1,24 @@ import { Router, Response, Request } from "express"; import fetch from "node-fetch"; import { route } from "@fosscord/api"; +import { getGifApiKey, parseGifResult } from "./trending"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - // TODO: Custom providers and code quality - const { media_format, locale, provider } = req.query; + // TODO: Custom providers + const { media_format, locale } = req.query; - const parseResult = (result: any) => { - return { - id: result.id, - title: result.title, - url: result.itemurl, - src: result.media[0].mp4.url, - gif_src: result.media[0].gif.url, - width: result.media[0].mp4.dims[0], - height: result.media[0].mp4.dims[1], - preview: result.media[0].mp4.preview - }; - }; + const apiKey = getGifApiKey(); - const response = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=LIVDSRZULELA`, { + const response = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`, { method: "get", headers: { "Content-Type": "application/json" } }); const { results } = await response.json(); - let cache = new Array() as any[]; - results.forEach((result: any) => { - cache.push(parseResult(result)); - }); - res.json(cache).status(200); + + res.json(results.map(parseGifResult)).status(200); }); export default router; diff --git a/api/src/routes/gifs/trending.ts b/api/src/routes/gifs/trending.ts index 3b91eb12..7ee9337e 100644 --- a/api/src/routes/gifs/trending.ts +++ b/api/src/routes/gifs/trending.ts @@ -1,48 +1,57 @@ import { Router, Response, Request } from "express"; import fetch from "node-fetch"; import { route } from "@fosscord/api"; +import { Config } from "@fosscord/util"; +import { HTTPError } from "lambert-server"; const router = Router(); -router.get("/", route({}), async (req: Request, res: Response) => { - // TODO: Custom providers and code quality - const { media_format, locale, provider } = req.query; - - const parseResult = (result: any) => { - return { - id: result.id, - title: result.title, - url: result.itemurl, - src: result.media[0].mp4.url, - gif_src: result.media[0].gif.url, - width: result.media[0].mp4.dims[0], - height: result.media[0].mp4.dims[1], - preview: result.media[0].mp4.preview - }; +export function parseGifResult(result: any) { + return { + id: result.id, + title: result.title, + url: result.itemurl, + src: result.media[0].mp4.url, + gif_src: result.media[0].gif.url, + width: result.media[0].mp4.dims[0], + height: result.media[0].mp4.dims[1], + preview: result.media[0].mp4.preview }; +} + +export function getGifApiKey() { + const { enabled, provider, apiKey } = Config.get().gif; + if (!enabled) throw new HTTPError(`Gifs are disabled`); + if (provider !== "tenor" || !apiKey) throw new HTTPError(`${provider} gif provider not supported`); - const responseSource = await fetch(`https://g.tenor.com/v1/categories?media_format=${media_format}&locale=${locale}&key=LIVDSRZULELA`, { - method: "get", - headers: { "Content-Type": "application/json" } - }); + return apiKey; +} - const trendGifSource = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=LIVDSRZULELA`, { - method: "get", - headers: { "Content-Type": "application/json" } - }); +router.get("/", route({}), async (req: Request, res: Response) => { + // TODO: Custom providers + // TODO: return gifs as mp4 + const { media_format, locale } = req.query; + + const apiKey = getGifApiKey(); + + const [responseSource, trendGifSource] = await Promise.all([ + fetch(`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`, { + method: "get", + headers: { "Content-Type": "application/json" } + }), + fetch(`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`, { + method: "get", + headers: { "Content-Type": "application/json" } + }) + ]); const { tags } = await responseSource.json(); const { results } = await trendGifSource.json(); - let cache = new Array() as any[]; - - tags.forEach((result: any) => { - cache.push({ - name: result.searchterm, - src: result.image - }); - }); - res.json({ categories: [cache], gifs: [parseResult(results[0])] }).status(200); + res.json({ + categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })), + gifs: [parseGifResult(results[0])] + }).status(200); }); export default router; diff --git a/api/src/util/Message.ts b/api/src/util/Message.ts index f8230124..40d96b42 100644 --- a/api/src/util/Message.ts +++ b/api/src/util/Message.ts @@ -25,6 +25,7 @@ import cheerio from "cheerio"; import { MessageCreateSchema } from "../routes/channels/#channel_id/messages"; // TODO: check webhook, application, system author +// TODO: embed gifs/videos/images const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g; diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index 6760187f..b3167ac7 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -51,11 +51,6 @@ export interface ConfigValue { general: { instanceId: string; }; - permissions: { - user: { - createGuilds: boolean; - }; - }; limits: { user: { maxGuilds: number; @@ -154,6 +149,11 @@ export interface ConfigValue { canLeave: boolean; }; }; + gif: { + enabled: boolean; + provider: "tenor"; // more coming soon + apiKey?: string; + }; rabbitmq: { host: string | null; }; @@ -176,11 +176,6 @@ export const DefaultConfigOptions: ConfigValue = { general: { instanceId: Snowflake.generate(), }, - permissions: { - user: { - createGuilds: true, - }, - }, limits: { user: { maxGuilds: 100, @@ -307,7 +302,6 @@ export const DefaultConfigOptions: ConfigValue = { }, ], }, - guild: { showAllGuildsInDiscovery: false, autoJoin: { @@ -316,6 +310,11 @@ export const DefaultConfigOptions: ConfigValue = { guilds: [], }, }, + gif: { + enabled: true, + provider: "tenor", + apiKey: "LIVDSRZULELA", + }, rabbitmq: { host: null, }, -- cgit 1.5.1 From 808bf0a74c974d365fb83d31de370dfebb6d0367 Mon Sep 17 00:00:00 2001 From: Maddy Date: Wed, 13 Oct 2021 16:07:33 +1100 Subject: added route /guilds/:id/prune GET count and POST prune. Edited Member entity to include last_message_id to avoid searching for every members last message when pruning When a message is sent through /channels/:id/messages last_message_id of sending Member is updated. --- .../routes/channels/#channel_id/messages/index.ts | 10 ++- api/src/routes/guilds/#guild_id/prune.ts | 74 ++++++++++++++++++++++ util/src/entities/Member.ts | 4 ++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 api/src/routes/guilds/#guild_id/prune.ts (limited to 'api/src') diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 26bb9e5d..399fa7bb 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -10,7 +10,8 @@ import { getPermission, Message, MessageCreateEvent, - uploadFile + uploadFile, + Member, } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { handleMessage, postHandleMessage, route } from "@fosscord/api"; @@ -188,6 +189,13 @@ router.post( await channel.assign({ last_message_id: message.id }).save(); + //gosh + var member = await Member.findOneOrFail({ + where: { id: req.user_id }, + }); + await member.assign({ last_message_id: message.id }) + await member.save(); //why does member.assign here return void? + if (channel.isDm()) { const channel_dto = await DmChannelDTO.from(channel); diff --git a/api/src/routes/guilds/#guild_id/prune.ts b/api/src/routes/guilds/#guild_id/prune.ts new file mode 100644 index 00000000..cddbcee7 --- /dev/null +++ b/api/src/routes/guilds/#guild_id/prune.ts @@ -0,0 +1,74 @@ +import { Router, Request, Response } from "express"; +import { Guild, Member, Snowflake } from "@fosscord/util"; +import { HTTPError } from "lambert-server"; +import { LessThan } from "typeorm"; +import { route } from "@fosscord/api"; +const router = Router(); + +//Returns all inactive members, respecting role hierarchy +export const inactiveMembers = async (guild_id: string, user_id: string, days: number, roles: string[] = []) => { + var date = new Date(); + date.setDate(date.getDate() - days); + //Snowflake should have `generateFromTime` method? Or similar? + var minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22); + + var members = await Member.find({ + where: [ + { + guild_id, + last_message_id: LessThan(minId.toString()), + }, + ], + relations: ["roles"] + }); + if (!members.length) return []; + + //I'm sure I can do this in the above db query ( and it would probably be better to do so ), but oh well. + if (roles.length && members.length) + members = members.filter(user => user.roles?.some(role => roles.includes(role.id))); + + const me = await Member.findOne({ id: user_id, guild_id }, { relations: ["roles"] }); + if (!me) throw new HTTPError("You are not member of this guild", 403); + const myHighestRole = Math.max(...(me.roles?.map(x => x.position) || [])) + + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }) + + members = members.filter(member => + member.id !== guild.owner_id && //can't kick owner + member.roles?.some(role => + role.position < myHighestRole || //roles higher than me can't be kicked + me.id === guild.owner_id //owner can kick anyone + ) + ) + + return members; +} + +router.get("/", async (req: Request, res: Response) => { + const days = parseInt(req.query.days as string); + + var roles = req.query.include_roles; + if (typeof roles === 'string') roles = [roles]; //express will return array otherwise + + const members = await inactiveMembers(req.params.guild_id, req.user_id, days, roles as string[]); + + res.send({ pruned: members.length }); +}); + +router.post("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => { + const days = parseInt(req.body.days); + + var roles = req.query.include_roles; + if (typeof roles === 'string') roles = [roles]; + + const { guild_id } = req.params; + const members = await inactiveMembers(guild_id, req.user_id, days, roles as string[]); + + for (var curr of members) { + await Member.removeFromGuild(curr.id, guild_id); + } + + res.send({ purged: members.length }); +}) + +export default router; diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index 7d7ac40a..19747318 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -84,6 +84,9 @@ export class Member extends BaseClassWithoutId { @Column({ type: "simple-json" }) settings: UserGuildSettings; + @Column() + last_message_id: string; + // TODO: update // @Column({ type: "simple-json" }) // read_state: ReadState; @@ -230,6 +233,7 @@ export class Member extends BaseClassWithoutId { deaf: false, mute: false, pending: false, + last_message_id: "", }; await Promise.all([ -- cgit 1.5.1 From 8257e2631ae5c4237d08b48b365ce7f367de74c6 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 14 Oct 2021 00:32:15 +0200 Subject: :bug: fix migration + autojoin --- api/src/util/Instance.ts | 2 +- util/src/migrations/migrate_db_engine.js | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'api/src') diff --git a/api/src/util/Instance.ts b/api/src/util/Instance.ts index a7b3205a..d1d9e1ab 100644 --- a/api/src/util/Instance.ts +++ b/api/src/util/Instance.ts @@ -8,7 +8,7 @@ export async function initInstance() { // TODO: check if any current user is not part of autoJoinGuilds const { autoJoin } = Config.get().guild; - if (autoJoin.enabled && autoJoin.guilds?.length) { + if (autoJoin.enabled && !autoJoin.guilds?.length) { let guild = await Guild.findOne({}); if (!guild) guild = await Guild.createGuild({}); diff --git a/util/src/migrations/migrate_db_engine.js b/util/src/migrations/migrate_db_engine.js index eab30bc4..79e9d86f 100644 --- a/util/src/migrations/migrate_db_engine.js +++ b/util/src/migrations/migrate_db_engine.js @@ -8,6 +8,7 @@ const { Attachment, Ban, Channel, + ConfigEntity, ConnectedAccount, Emoji, Guild, @@ -32,6 +33,7 @@ async function main() { // manually arrange them because of foreign keys const entities = [ + ConfigEntity, User, Guild, Channel, @@ -61,12 +63,13 @@ async function main() { const isSqlite = type.includes("sqlite"); // @ts-ignore - const oldDB = await createConnection({ + const newDB = await createConnection({ type, url: isSqlite ? undefined : process.env.TO, database: isSqlite ? process.env.TO : undefined, entities, - name: "old", + name: "new", + synchronize: true, }); let i = 0; -- cgit 1.5.1 From a20279c41ffc7da15cff99c33f92dcbffb5a764f Mon Sep 17 00:00:00 2001 From: Hayden Young Date: Thu, 14 Oct 2021 18:27:19 +0100 Subject: feat: initial addition of a healthz, readyz and metrics route --- api/src/routes/-/healthz.ts | 10 ++++++++++ api/src/routes/-/metrics.ts | 10 ++++++++++ api/src/routes/-/readyz.ts | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 api/src/routes/-/healthz.ts create mode 100644 api/src/routes/-/metrics.ts create mode 100644 api/src/routes/-/readyz.ts (limited to 'api/src') diff --git a/api/src/routes/-/healthz.ts b/api/src/routes/-/healthz.ts new file mode 100644 index 00000000..bfd98a22 --- /dev/null +++ b/api/src/routes/-/healthz.ts @@ -0,0 +1,10 @@ +import { Router, Response, Request } from "express"; +import { route } from "@fosscord/api"; + +const router = Router(); + +router.get("/", route({}), (req: Request, res: Response) => { + res.sendStatus(200); +}); + +export default router; diff --git a/api/src/routes/-/metrics.ts b/api/src/routes/-/metrics.ts new file mode 100644 index 00000000..bfd98a22 --- /dev/null +++ b/api/src/routes/-/metrics.ts @@ -0,0 +1,10 @@ +import { Router, Response, Request } from "express"; +import { route } from "@fosscord/api"; + +const router = Router(); + +router.get("/", route({}), (req: Request, res: Response) => { + res.sendStatus(200); +}); + +export default router; diff --git a/api/src/routes/-/readyz.ts b/api/src/routes/-/readyz.ts new file mode 100644 index 00000000..bfd98a22 --- /dev/null +++ b/api/src/routes/-/readyz.ts @@ -0,0 +1,10 @@ +import { Router, Response, Request } from "express"; +import { route } from "@fosscord/api"; + +const router = Router(); + +router.get("/", route({}), (req: Request, res: Response) => { + res.sendStatus(200); +}); + +export default router; -- cgit 1.5.1 From 36a29054ec1be722dc576b40c76f191efce97a71 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 14 Oct 2021 19:46:45 +0200 Subject: :sparkles: stickers --- api/src/routes/guilds/#guild_id/stickers.ts | 74 ++++++++++++++++++++++++++++ api/src/routes/stickers/#sticker_id/index.ts | 12 +++++ 2 files changed, 86 insertions(+) create mode 100644 api/src/routes/guilds/#guild_id/stickers.ts create mode 100644 api/src/routes/stickers/#sticker_id/index.ts (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/stickers.ts b/api/src/routes/guilds/#guild_id/stickers.ts new file mode 100644 index 00000000..c122b72d --- /dev/null +++ b/api/src/routes/guilds/#guild_id/stickers.ts @@ -0,0 +1,74 @@ +import { Member, Sticker } from "@fosscord/util"; +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +import multer from "multer"; +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + const { guild_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); + + res.json(await Sticker.find({ guild_id })); +}); + +const bodyParser = multer({ + limits: { + fileSize: 1024 * 1024 * 100, + fields: 10, + files: 1 + }, + storage: multer.memoryStorage() +}).single("file"); + +router.post("/", bodyParser, route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const { guild_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); + + res.json(await Sticker.find({ guild_id })); +}); + +router.get("/:sticker_id", route({}), async (req: Request, res: Response) => { + const { guild_id, sticker_id } = req.params; + await Member.IsInGuildOrFail(req.user_id, guild_id); + + res.json(await Sticker.findOneOrFail({ guild_id, id: sticker_id })); +}); + +export interface ModifyGuildStickerSchema { + /** + * @minLength 2 + * @maxLength 30 + */ + name: string; + /** + * @minLength 2 + * @maxLength 100 + */ + description?: string; + /** + * @maxLength 200 + */ + tags: string; +} + +router.patch( + "/:sticker_id", + route({ body: "ModifyGuildStickerSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }), + async (req: Request, res: Response) => { + const { guild_id, sticker_id } = req.params; + const body = req.body as ModifyGuildStickerSchema; + + const sticker = await new Sticker({ ...body, guild_id, id: sticker_id }).save(); + return res.json(sticker); + } +); + +router.delete("/:sticker_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { + const { guild_id, sticker_id } = req.params; + + await Sticker.delete({ guild_id, id: sticker_id }); + + return res.sendStatus(204); +}); + +export default router; diff --git a/api/src/routes/stickers/#sticker_id/index.ts b/api/src/routes/stickers/#sticker_id/index.ts new file mode 100644 index 00000000..293ca089 --- /dev/null +++ b/api/src/routes/stickers/#sticker_id/index.ts @@ -0,0 +1,12 @@ +import { Sticker } from "@fosscord/util"; +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + const { sticker_id } = req.params; + + res.json(await Sticker.find({ id: sticker_id })); +}); + +export default router; -- cgit 1.5.1 From 6cf6204d0664d3626d7922ec23147d610201aaf9 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 14 Oct 2021 19:46:53 +0200 Subject: :sparkles: sticker packs --- api/src/routes/sticker-packs/#id/index.ts | 19 ------------------- api/src/routes/sticker-packs/index.ts | 4 ++-- 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 api/src/routes/sticker-packs/#id/index.ts (limited to 'api/src') diff --git a/api/src/routes/sticker-packs/#id/index.ts b/api/src/routes/sticker-packs/#id/index.ts deleted file mode 100644 index 7f723e97..00000000 --- a/api/src/routes/sticker-packs/#id/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; - -const router: Router = Router(); - -router.get("/", route({}), async (req: Request, res: Response) => { - //TODO - res.json({ - id: "", - stickers: [], - name: "", - sku_id: "", - cover_sticker_id: "", - description: "", - banner_asset_id: "" - }).status(200); -}); - -export default router; diff --git a/api/src/routes/sticker-packs/index.ts b/api/src/routes/sticker-packs/index.ts index d671c161..89b91bdb 100644 --- a/api/src/routes/sticker-packs/index.ts +++ b/api/src/routes/sticker-packs/index.ts @@ -1,11 +1,11 @@ import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { StickerPack } from "@fosscord/util/src/entities/StickerPack"; const router: Router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - //TODO - res.json({ sticker_packs: [] }).status(200); + res.json(await StickerPack.find({})); }); export default router; -- cgit 1.5.1 From 4009e9d2dd95bda111f8ee007171c929c1653432 Mon Sep 17 00:00:00 2001 From: Hayden Young Date: Thu, 14 Oct 2021 18:47:05 +0100 Subject: feat: implement a database check on the healthz and readyz probes, remove /-/metrics --- api/src/routes/-/healthz.ts | 9 ++++++++- api/src/routes/-/metrics.ts | 10 ---------- api/src/routes/-/readyz.ts | 9 ++++++++- 3 files changed, 16 insertions(+), 12 deletions(-) delete mode 100644 api/src/routes/-/metrics.ts (limited to 'api/src') diff --git a/api/src/routes/-/healthz.ts b/api/src/routes/-/healthz.ts index bfd98a22..a42575f8 100644 --- a/api/src/routes/-/healthz.ts +++ b/api/src/routes/-/healthz.ts @@ -1,10 +1,17 @@ import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { getConnection } from "typeorm"; const router = Router(); router.get("/", route({}), (req: Request, res: Response) => { - res.sendStatus(200); + try { + // test that the database is alive & responding + getConnection(); + return res.sendStatus(200); + } catch(e) { + res.sendStatus(503); + } }); export default router; diff --git a/api/src/routes/-/metrics.ts b/api/src/routes/-/metrics.ts deleted file mode 100644 index bfd98a22..00000000 --- a/api/src/routes/-/metrics.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - res.sendStatus(200); -}); - -export default router; diff --git a/api/src/routes/-/readyz.ts b/api/src/routes/-/readyz.ts index bfd98a22..a42575f8 100644 --- a/api/src/routes/-/readyz.ts +++ b/api/src/routes/-/readyz.ts @@ -1,10 +1,17 @@ import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { getConnection } from "typeorm"; const router = Router(); router.get("/", route({}), (req: Request, res: Response) => { - res.sendStatus(200); + try { + // test that the database is alive & responding + getConnection(); + return res.sendStatus(200); + } catch(e) { + res.sendStatus(503); + } }); export default router; -- cgit 1.5.1 From e5ed599b483a91a058d4ccd46220dea8ef3fcee1 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 14 Oct 2021 19:47:10 +0200 Subject: :art: restructure --- api/src/routes/template.ts.disabled | 5 +++-- gateway/client.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/template.ts.disabled b/api/src/routes/template.ts.disabled index ad785f10..fcc59ef4 100644 --- a/api/src/routes/template.ts.disabled +++ b/api/src/routes/template.ts.disabled @@ -1,10 +1,11 @@ //TODO: this is a template for a generic route import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; const router = Router(); -router.get("/", async (req: Request, res: Response) => { - res.send({}); +router.get("/",route({}), async (req: Request, res: Response) => { + res.json({}); }); export default router; diff --git a/gateway/client.js b/gateway/client.js index 34a59458..c841c6a0 100644 --- a/gateway/client.js +++ b/gateway/client.js @@ -3,7 +3,7 @@ const WebSocket = require("ws"); const Constants = require("./dist/util/Constants"); // const ws = new WebSocket("ws://127.0.0.1:8080"); -const ws = new WebSocket("wss://gateway.discord.gg"); +const ws = new WebSocket("wss://dev.fosscord.com"); ws.on("open", () => { // ws.send(JSON.stringify({ req_type: "new_auth" })); -- cgit 1.5.1 From d4b6840faefed2c73b9939dafc4aff9d3665c6d5 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 14 Oct 2021 21:25:17 +0200 Subject: :art: do not automatically create default guild --- api/src/util/Instance.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'api/src') diff --git a/api/src/util/Instance.ts b/api/src/util/Instance.ts index d1d9e1ab..7dcd126e 100644 --- a/api/src/util/Instance.ts +++ b/api/src/util/Instance.ts @@ -10,9 +10,9 @@ export async function initInstance() { if (autoJoin.enabled && !autoJoin.guilds?.length) { let guild = await Guild.findOne({}); - if (!guild) guild = await Guild.createGuild({}); - - // @ts-ignore - await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } }); + if (guild) { + // @ts-ignore + await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } }); + } } } -- cgit 1.5.1 From d2df6f99bef7db6f5c20b822b274c6bcf664c7df Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:02:23 +0200 Subject: :sparkles: sticker upload --- api/assets/schemas.json | 308 ++++++++++++++++++++++++++++ api/src/routes/guilds/#guild_id/stickers.ts | 49 ++++- util/src/entities/Sticker.ts | 13 +- 3 files changed, 359 insertions(+), 11 deletions(-) (limited to 'api/src') diff --git a/api/assets/schemas.json b/api/assets/schemas.json index 2ceaa923..eb97112c 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -514,6 +514,12 @@ "attachments": { "type": "array", "items": {} + }, + "sticker_ids": { + "type": "array", + "items": { + "type": "string" + } } }, "definitions": { @@ -5341,6 +5347,308 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, + "ModifyGuildStickerSchema": { + "type": "object", + "properties": { + "name": { + "minLength": 2, + "maxLength": 30, + "type": "string" + }, + "description": { + "maxLength": 100, + "type": "string" + }, + "tags": { + "maxLength": 200, + "type": "string" + } + }, + "required": [ + "name", + "tags" + ], + "definitions": { + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1 + ], + "type": "number" + }, + "Embed": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "color": { + "type": "integer" + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/definitions/EmbedImage" + }, + "thumbnail": { + "$ref": "#/definitions/EmbedImage" + }, + "video": { + "$ref": "#/definitions/EmbedImage" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + } + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "required": [ + "name", + "value" + ] + } + } + } + }, + "EmbedImage": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "proxy_url": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + } + } + }, + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 2, + 3, + 4, + 5, + 6 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + } + } + }, + "UserPublic": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "id": { + "type": "string" + }, + "public_flags": { + "type": "integer" + }, + "avatar": { + "type": "string" + }, + "accent_color": { + "type": "integer" + }, + "banner": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "bot": { + "type": "boolean" + } + }, + "required": [ + "bio", + "bot", + "discriminator", + "id", + "public_flags", + "username" + ] + }, + "PublicConnectedAccount": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "verifie": { + "type": "boolean" + } + }, + "required": [ + "name", + "type", + "verifie" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, "TemplateCreateSchema": { "type": "object", "properties": { diff --git a/api/src/routes/guilds/#guild_id/stickers.ts b/api/src/routes/guilds/#guild_id/stickers.ts index c122b72d..39095dc2 100644 --- a/api/src/routes/guilds/#guild_id/stickers.ts +++ b/api/src/routes/guilds/#guild_id/stickers.ts @@ -1,7 +1,8 @@ -import { Member, Sticker } from "@fosscord/util"; +import { handleFile, Member, Snowflake, Sticker, StickerFormatType, StickerType, uploadFile } from "@fosscord/util"; import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import multer from "multer"; +import { HTTPError } from "lambert-server"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { @@ -20,12 +21,47 @@ const bodyParser = multer({ storage: multer.memoryStorage() }).single("file"); -router.post("/", bodyParser, route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { - const { guild_id } = req.params; - await Member.IsInGuildOrFail(req.user_id, guild_id); +router.post( + "/", + bodyParser, + route({ permission: "MANAGE_EMOJIS_AND_STICKERS", body: "ModifyGuildStickerSchema" }), + async (req: Request, res: Response) => { + if (!req.file) throw new HTTPError("missing file"); - res.json(await Sticker.find({ guild_id })); -}); + const { guild_id } = req.params; + const body = req.body as ModifyGuildStickerSchema; + const id = Snowflake.generate(); + + const [sticker] = await Promise.all([ + new Sticker({ + ...body, + guild_id, + id, + type: StickerType.GUILD, + format_type: getStickerFormat(req.file.mimetype), + available: true + }).save(), + uploadFile(`/stickers/${id}`, req.file) + ]); + + res.json(sticker); + } +); + +export function getStickerFormat(mime_type: string) { + switch (mime_type) { + case "image/apng": + return StickerFormatType.APNG; + case "application/json": + return StickerFormatType.LOTTIE; + case "image/png": + return StickerFormatType.PNG; + case "image/gif": + return StickerFormatType.GIF; + default: + throw new HTTPError("invalid sticker format: must be png, apng or lottie"); + } +} router.get("/:sticker_id", route({}), async (req: Request, res: Response) => { const { guild_id, sticker_id } = req.params; @@ -41,7 +77,6 @@ export interface ModifyGuildStickerSchema { */ name: string; /** - * @minLength 2 * @maxLength 100 */ description?: string; diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts index 94761f60..37bc6fbe 100644 --- a/util/src/entities/Sticker.ts +++ b/util/src/entities/Sticker.ts @@ -9,6 +9,7 @@ export enum StickerType { } export enum StickerFormatType { + GIF = 0, // gif is a custom format type and not in discord spec PNG = 1, APNG = 2, LOTTIE = 3, @@ -22,16 +23,20 @@ export class Sticker extends BaseClass { @Column({ nullable: true }) description?: string; - @Column() - tags: string; + @Column({ nullable: true }) + available?: boolean; - @Column() + @Column({ nullable: true }) + tags?: string; + + @Column({ nullable: true }) @RelationId((sticker: Sticker) => sticker.pack) - pack_id: string; + pack_id?: string; @JoinColumn({ name: "pack_id" }) @ManyToOne(() => require("./StickerPack").StickerPack, { onDelete: "CASCADE", + nullable: true, }) pack: import("./StickerPack").StickerPack; -- cgit 1.5.1 From 0ccc478c54f6e6ae8c8e6574e24ab6796738c83a Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:03:05 +0200 Subject: :bug: fix message sticker sending --- api/client_test/index.html | 4 +++- api/src/routes/channels/#channel_id/messages/index.ts | 1 + api/src/routes/guilds/#guild_id/premium.ts | 10 ++++++++++ api/src/util/Message.ts | 5 +++-- util/src/entities/Message.ts | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 api/src/routes/guilds/#guild_id/premium.ts (limited to 'api/src') diff --git a/api/client_test/index.html b/api/client_test/index.html index 20b431b8..5a795253 100644 --- a/api/client_test/index.html +++ b/api/client_test/index.html @@ -37,6 +37,7 @@ HTML_TIMESTAMP: Date.now(), ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0" }; + GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST; const localStorage = window.localStorage; // TODO: remote auth // window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, ""); @@ -105,7 +106,8 @@ } const settings = JSON.parse(localStorage.getItem("UserSettingsStore")); - if (settings && settings.locale === "en") { + if (settings && settings.locale.length <= 2) { + // fix client locale wrong and client not loading at all settings.locale = "en-US"; localStorage.setItem("UserSettingsStore", JSON.stringify(settings)); } diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 26bb9e5d..20c102ef 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -63,6 +63,7 @@ export interface MessageCreateSchema { payload_json?: string; file?: any; attachments?: any[]; //TODO we should create an interface for attachments + sticker_ids?: string[]; } // https://discord.com/developers/docs/resources/channel#create-message diff --git a/api/src/routes/guilds/#guild_id/premium.ts b/api/src/routes/guilds/#guild_id/premium.ts new file mode 100644 index 00000000..75361ac6 --- /dev/null +++ b/api/src/routes/guilds/#guild_id/premium.ts @@ -0,0 +1,10 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +const router = Router(); + +router.get("/subscriptions", route({}), async (req: Request, res: Response) => { + // TODO: + res.json([]); +}); + +export default router; diff --git a/api/src/util/Message.ts b/api/src/util/Message.ts index 40d96b42..d14d3aa2 100644 --- a/api/src/util/Message.ts +++ b/api/src/util/Message.ts @@ -24,7 +24,7 @@ import fetch from "node-fetch"; import cheerio from "cheerio"; import { MessageCreateSchema } from "../routes/channels/#channel_id/messages"; -// TODO: check webhook, application, system author +// TODO: check webhook, application, system author, stickers // TODO: embed gifs/videos/images const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g; @@ -46,6 +46,7 @@ export async function handleMessage(opts: MessageOptions): Promise { const message = new Message({ ...opts, + sticker_items: opts.sticker_ids?.map((x) => ({ id: x })), guild_id: channel.guild_id, channel_id: opts.channel_id, attachments: opts.attachments || [], @@ -82,7 +83,7 @@ export async function handleMessage(opts: MessageOptions): Promise { } // TODO: stickers/activity - if (!opts.content && !opts.embeds?.length && !opts.attachments?.length) { + if (!opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length) { throw new HTTPError("Empty messages are not allowed", 50006); } diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 63cd6ad3..a4d38315 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -127,7 +127,7 @@ export class Message extends BaseClass { mention_channels: Channel[]; @JoinTable({ name: "message_stickers" }) - @ManyToMany(() => Sticker) + @ManyToMany(() => Sticker, { cascade: true, onDelete: "CASCADE" }) sticker_items?: Sticker[]; @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, { -- cgit 1.5.1 From c1d786b6e1a9b8562e7ea05ae08934d92abff270 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:03:19 +0200 Subject: :bug: fix sticker packs --- api/src/routes/sticker-packs/index.ts | 6 ++++-- gateway/src/opcodes/Identify.ts | 2 ++ util/src/entities/StickerPack.ts | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/sticker-packs/index.ts b/api/src/routes/sticker-packs/index.ts index 89b91bdb..e6560d12 100644 --- a/api/src/routes/sticker-packs/index.ts +++ b/api/src/routes/sticker-packs/index.ts @@ -1,11 +1,13 @@ import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { StickerPack } from "@fosscord/util/src/entities/StickerPack"; +import { StickerPack } from "@fosscord/util"; const router: Router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - res.json(await StickerPack.find({})); + const sticker_packs = await StickerPack.find({ relations: ["stickers"] }); + + res.json({ sticker_packs }); }); export default router; diff --git a/gateway/src/opcodes/Identify.ts b/gateway/src/opcodes/Identify.ts index c91ca5dd..2f9d4632 100644 --- a/gateway/src/opcodes/Identify.ts +++ b/gateway/src/opcodes/Identify.ts @@ -179,6 +179,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { x.guild_hashes = {}; // @ts-ignore x.guild_scheduled_events = []; // @ts-ignore x.threads = []; + x.premium_subscription_count = 30; + x.premium_tier = 3; return x; }), guild_experiments: [], // TODO diff --git a/util/src/entities/StickerPack.ts b/util/src/entities/StickerPack.ts index b80fa9c7..ec8c69a2 100644 --- a/util/src/entities/StickerPack.ts +++ b/util/src/entities/StickerPack.ts @@ -1,8 +1,8 @@ -import { Column, Entity, JoinColumn, OneToMany, OneToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm"; import { Sticker } from "."; import { BaseClass } from "./BaseClass"; -@Entity("stickers") +@Entity("sticker_packs") export class StickerPack extends BaseClass { @Column() name: string; @@ -13,7 +13,7 @@ export class StickerPack extends BaseClass { @Column({ nullable: true }) banner_asset_id?: string; - @OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack_id, { + @OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack, { cascade: true, orphanedRowAction: "delete", }) @@ -25,7 +25,7 @@ export class StickerPack extends BaseClass { @RelationId((pack: StickerPack) => pack.cover_sticker) cover_sticker_id?: string; - @OneToOne(() => Sticker, { nullable: true }) + @ManyToOne(() => Sticker, { nullable: true }) @JoinColumn() cover_sticker?: Sticker; } -- cgit 1.5.1 From 88425f87ecdc5e0d8e64a8e0ce44fea19daf4652 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:03:35 +0200 Subject: :art: reformat --- api/src/Server.ts | 2 +- api/src/routes/guilds/#guild_id/emojis.ts | 5 +---- api/tsconfig.json | 3 +-- util/src/entities/ReadState.ts | 5 ----- util/src/entities/index.ts | 1 + util/src/util/cdn.ts | 4 +++- 6 files changed, 7 insertions(+), 13 deletions(-) (limited to 'api/src') diff --git a/api/src/Server.ts b/api/src/Server.ts index 1f11a295..a6887fd4 100644 --- a/api/src/Server.ts +++ b/api/src/Server.ts @@ -78,7 +78,7 @@ export class FosscordServer extends Server { api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => { if (error) return next(error); res.status(404).json({ - message: "404: Not Found", + message: "404 endpoint not found", code: 0 }); next(); diff --git a/api/src/routes/guilds/#guild_id/emojis.ts b/api/src/routes/guilds/#guild_id/emojis.ts index ff565cd4..85d7ac05 100644 --- a/api/src/routes/guilds/#guild_id/emojis.ts +++ b/api/src/routes/guilds/#guild_id/emojis.ts @@ -40,17 +40,14 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A const { guild_id } = req.params; const body = req.body as EmojiCreateSchema; + const id = Snowflake.generate(); const emoji_count = await Emoji.count({ guild_id: guild_id }); const { maxEmojis } = Config.get().limits.guild; if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis); - - const id = Snowflake.generate(); - if (body.require_colons == null) body.require_colons = true; const user = await User.findOneOrFail({ id: req.user_id }); - body.image = (await handleFile(`/emojis/${id}`, body.image)) as string; const emoji = await new Emoji({ diff --git a/api/tsconfig.json b/api/tsconfig.json index 2cf4e4c1..80d7251f 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -67,8 +67,7 @@ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "baseUrl": ".", "paths": { - "@fosscord/api": ["src/index"], - "@fosscord/api/*": ["src/*"] + "@fosscord/api": ["src/index"] }, "plugins": [{ "transform": "@zerollup/ts-transform-paths" }], "experimentalDecorators": true diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 89480e83..ebef89be 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -32,13 +32,8 @@ export class ReadState extends BaseClass { user: User; @Column({ nullable: true }) - @RelationId((read_state: ReadState) => read_state.last_message) last_message_id: string; - @JoinColumn({ name: "last_message_id" }) - @ManyToOne(() => Message, { nullable: true }) - last_message?: Message; - @Column({ nullable: true }) last_pin_timestamp?: Date; diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index 7b1c9750..31afed88 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -18,6 +18,7 @@ export * from "./Relationship"; export * from "./Role"; export * from "./Session"; export * from "./Sticker"; +export * from "./StickerPack"; export * from "./Team"; export * from "./TeamMember"; export * from "./Template"; diff --git a/util/src/util/cdn.ts b/util/src/util/cdn.ts index 4dd0078a..ea950cd1 100644 --- a/util/src/util/cdn.ts +++ b/util/src/util/cdn.ts @@ -4,7 +4,9 @@ import fetch from "node-fetch"; import { Config } from "./Config"; import multer from "multer"; -export async function uploadFile(path: string, file: Express.Multer.File) { +export async function uploadFile(path: string, file?: Express.Multer.File) { + if (!file?.buffer) throw new HTTPError("Missing file in body"); + const form = new FormData(); form.append("file", file.buffer, { contentType: file.mimetype, -- cgit 1.5.1 From f38f79b70c3daa2ed45b27803fb01e6bc55adef9 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:35:01 +0200 Subject: :zap: speed up message query --- .../routes/channels/#channel_id/messages/index.ts | 48 ++++++++++------------ 1 file changed, 21 insertions(+), 27 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 399fa7bb..b5a2d334 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -11,7 +11,7 @@ import { Message, MessageCreateEvent, uploadFile, - Member, + Member } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { handleMessage, postHandleMessage, route } from "@fosscord/api"; @@ -187,40 +187,34 @@ router.post( message = await message.save(); - await channel.assign({ last_message_id: message.id }).save(); - - //gosh - var member = await Member.findOneOrFail({ - where: { id: req.user_id }, - }); - await member.assign({ last_message_id: message.id }) - await member.save(); //why does member.assign here return void? - if (channel.isDm()) { const channel_dto = await DmChannelDTO.from(channel); - for (let recipient of channel.recipients!) { - if (recipient.closed) { - await emitEvent({ - event: "CHANNEL_CREATE", - data: channel_dto.excludedRecipients([recipient.user_id]), - user_id: recipient.user_id - }); - } - } - //Only one recipients should be closed here, since in group DMs the recipient is deleted not closed + await Promise.all( - channel - .recipients!.filter((r) => r.closed) - .map(async (r) => { - r.closed = false; - return await r.save(); - }) + channel.recipients!.map((recipient) => { + if (recipient.closed) { + recipient.closed = false; + return Promise.all([ + recipient.save(), + emitEvent({ + event: "CHANNEL_CREATE", + data: channel_dto.excludedRecipients([recipient.user_id]), + user_id: recipient.user_id + }) + ]); + } + }) ); } - await emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent); + await Promise.all([ + channel.assign({ last_message_id: message.id }).save(), + new Member({ id: req.user_id, last_message_id: message.id }).save(), + emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent) + ]); + postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error return res.json(message); -- cgit 1.5.1 From 0994b47a6702c8957ba8ffcfe1720d3c70fabebe Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:35:18 +0200 Subject: :bug: fix prune --- api/src/routes/guilds/#guild_id/prune.ts | 56 ++++++++++++++++++-------------- util/src/entities/Member.ts | 5 ++- util/src/util/Snowflake.ts | 2 +- 3 files changed, 35 insertions(+), 28 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/prune.ts b/api/src/routes/guilds/#guild_id/prune.ts index cddbcee7..92809985 100644 --- a/api/src/routes/guilds/#guild_id/prune.ts +++ b/api/src/routes/guilds/#guild_id/prune.ts @@ -1,7 +1,6 @@ import { Router, Request, Response } from "express"; import { Guild, Member, Snowflake } from "@fosscord/util"; -import { HTTPError } from "lambert-server"; -import { LessThan } from "typeorm"; +import { LessThan, IsNull } from "typeorm"; import { route } from "@fosscord/api"; const router = Router(); @@ -16,59 +15,68 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n where: [ { guild_id, - last_message_id: LessThan(minId.toString()), + last_message_id: LessThan(minId.toString()) }, + { + last_message_id: IsNull() + } ], relations: ["roles"] }); + console.log(members); if (!members.length) return []; //I'm sure I can do this in the above db query ( and it would probably be better to do so ), but oh well. - if (roles.length && members.length) - members = members.filter(user => user.roles?.some(role => roles.includes(role.id))); + if (roles.length && members.length) members = members.filter((user) => user.roles?.some((role) => roles.includes(role.id))); - const me = await Member.findOne({ id: user_id, guild_id }, { relations: ["roles"] }); - if (!me) throw new HTTPError("You are not member of this guild", 403); - const myHighestRole = Math.max(...(me.roles?.map(x => x.position) || [])) + const me = await Member.findOneOrFail({ id: user_id, guild_id }, { relations: ["roles"] }); + const myHighestRole = Math.max(...(me.roles?.map((x) => x.position) || [])); - const guild = await Guild.findOneOrFail({ where: { id: guild_id } }) + const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - members = members.filter(member => - member.id !== guild.owner_id && //can't kick owner - member.roles?.some(role => - role.position < myHighestRole || //roles higher than me can't be kicked - me.id === guild.owner_id //owner can kick anyone - ) - ) + members = members.filter( + (member) => + member.id !== guild.owner_id && //can't kick owner + member.roles?.some( + (role) => + role.position < myHighestRole || //roles higher than me can't be kicked + me.id === guild.owner_id //owner can kick anyone + ) + ); return members; -} +}; -router.get("/", async (req: Request, res: Response) => { +router.get("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => { const days = parseInt(req.query.days as string); var roles = req.query.include_roles; - if (typeof roles === 'string') roles = [roles]; //express will return array otherwise + if (typeof roles === "string") roles = [roles]; //express will return array otherwise const members = await inactiveMembers(req.params.guild_id, req.user_id, days, roles as string[]); res.send({ pruned: members.length }); }); +export interface PruneSchema { + /** + * @min 0 + */ + days: number; +} + router.post("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => { const days = parseInt(req.body.days); var roles = req.query.include_roles; - if (typeof roles === 'string') roles = [roles]; + if (typeof roles === "string") roles = [roles]; const { guild_id } = req.params; const members = await inactiveMembers(guild_id, req.user_id, days, roles as string[]); - for (var curr of members) { - await Member.removeFromGuild(curr.id, guild_id); - } + await Promise.all(members.map((x) => Member.removeFromGuild(x.id, guild_id))); res.send({ purged: members.length }); -}) +}); export default router; diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index 19747318..12b0b49a 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -84,8 +84,8 @@ export class Member extends BaseClassWithoutId { @Column({ type: "simple-json" }) settings: UserGuildSettings; - @Column() - last_message_id: string; + @Column({ nullable: true }) + last_message_id?: string; // TODO: update // @Column({ type: "simple-json" }) @@ -233,7 +233,6 @@ export class Member extends BaseClassWithoutId { deaf: false, mute: false, pending: false, - last_message_id: "", }; await Promise.all([ diff --git a/util/src/util/Snowflake.ts b/util/src/util/Snowflake.ts index f7a13388..3f6e3c63 100644 --- a/util/src/util/Snowflake.ts +++ b/util/src/util/Snowflake.ts @@ -84,7 +84,7 @@ export class Snowflake { } static generate() { - var time = BigInt(Date.now() - Snowflake.EPOCH) << 22n; + var time = BigInt(Date.now() - Snowflake.EPOCH) << BigInt(22); var worker = Snowflake.workerId << 17n; var process = Snowflake.processId << 12n; var increment = Snowflake.INCREMENT++; -- cgit 1.5.1 From 0924f73a89f63c0da5f2469d2d1f12b2c0fed03f Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 00:46:09 +0200 Subject: :art: exclude healthz/readyz from auth --- api/src/middlewares/Authentication.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'api/src') diff --git a/api/src/middlewares/Authentication.ts b/api/src/middlewares/Authentication.ts index 5a082751..59a181e6 100644 --- a/api/src/middlewares/Authentication.ts +++ b/api/src/middlewares/Authentication.ts @@ -9,6 +9,8 @@ export const NO_AUTHORIZATION_ROUTES = [ "/ping", "/gateway", "/experiments", + "/-/readyz", + "/-/healthz", /\/guilds\/\d+\/widget\.(json|png)/ ]; -- cgit 1.5.1 From 125f91632f0607903596fff3aeaf67d3df593ec7 Mon Sep 17 00:00:00 2001 From: Hayden Young Date: Fri, 15 Oct 2021 11:05:51 +0000 Subject: feat: add tests to gateway routes, split into own routers --- api/src/routes/gateway.ts | 26 -------------------------- api/src/routes/gateway/bot.ts | 40 ++++++++++++++++++++++++++++++++++++++++ api/src/routes/gateway/index.ts | 24 ++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 26 deletions(-) delete mode 100644 api/src/routes/gateway.ts create mode 100644 api/src/routes/gateway/bot.ts create mode 100644 api/src/routes/gateway/index.ts (limited to 'api/src') diff --git a/api/src/routes/gateway.ts b/api/src/routes/gateway.ts deleted file mode 100644 index 4b3a0ea6..00000000 --- a/api/src/routes/gateway.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Config } from "@fosscord/util"; -import { Router, Response, Request } from "express"; -import { route } from "@fosscord/api"; - -const router = Router(); - -router.get("/", route({}), (req: Request, res: Response) => { - const { endpointPublic } = Config.get().gateway; - res.json({ url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002" }); -}); - -router.get("/bot", route({}), (req: Request, res: Response) => { - const { endpointPublic } = Config.get().gateway; - res.json({ - url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002", - shards: 1, - session_start_limit: { - total: 1000, - remaining: 999, - reset_after: 14400000, - max_concurrency: 1 - } - }); -}); - -export default router; diff --git a/api/src/routes/gateway/bot.ts b/api/src/routes/gateway/bot.ts new file mode 100644 index 00000000..3a038276 --- /dev/null +++ b/api/src/routes/gateway/bot.ts @@ -0,0 +1,40 @@ +import { Config } from "@fosscord/util"; +import { Router, Response, Request } from "express"; +import { route, RouteOptions } from "@fosscord/api"; + +const router = Router(); + +export interface GatewayBotResponse { + url: string; + shards: number; + session_start_limit: { + total: number; + remaining: number; + reset_after: number; + max_concurrency: number; + } +} + +const options: RouteOptions = { + test: { + response: { + body: "GatewayBotResponse" + } + } +}; + +router.get("/bot", route(options), (req: Request, res: Response) => { + const { endpointPublic } = Config.get().gateway; + res.json({ + url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002", + shards: 1, + session_start_limit: { + total: 1000, + remaining: 999, + reset_after: 14400000, + max_concurrency: 1 + } + }); +}); + +export default router; diff --git a/api/src/routes/gateway/index.ts b/api/src/routes/gateway/index.ts new file mode 100644 index 00000000..9bad7478 --- /dev/null +++ b/api/src/routes/gateway/index.ts @@ -0,0 +1,24 @@ +import { Config } from "@fosscord/util"; +import { Router, Response, Request } from "express"; +import { route, RouteOptions } from "@fosscord/api"; + +const router = Router(); + +export interface GatewayResponse { + url: string; +} + +const options: RouteOptions = { + test: { + response: { + body: "GatewayResponse" + } + } +}; + +router.get("/", route(options), (req: Request, res: Response) => { + const { endpointPublic } = Config.get().gateway; + res.json({ url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002" }); +}); + +export default router; -- cgit 1.5.1 From a64f79afda319f7e75df1b705fa476131dfb5bbb Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:39:28 +0200 Subject: :sparkles: sticker events --- api/src/routes/guilds/#guild_id/stickers.ts | 28 +++++++++++++++++++++++++++- util/src/interfaces/Event.ts | 10 ++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'api/src') diff --git a/api/src/routes/guilds/#guild_id/stickers.ts b/api/src/routes/guilds/#guild_id/stickers.ts index 39095dc2..4ea1dce1 100644 --- a/api/src/routes/guilds/#guild_id/stickers.ts +++ b/api/src/routes/guilds/#guild_id/stickers.ts @@ -1,4 +1,14 @@ -import { handleFile, Member, Snowflake, Sticker, StickerFormatType, StickerType, uploadFile } from "@fosscord/util"; +import { + emitEvent, + GuildStickersUpdateEvent, + handleFile, + Member, + Snowflake, + Sticker, + StickerFormatType, + StickerType, + uploadFile +} from "@fosscord/util"; import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import multer from "multer"; @@ -44,6 +54,8 @@ router.post( uploadFile(`/stickers/${id}`, req.file) ]); + await sendStickerUpdateEvent(guild_id); + res.json(sticker); } ); @@ -94,14 +106,28 @@ router.patch( const body = req.body as ModifyGuildStickerSchema; const sticker = await new Sticker({ ...body, guild_id, id: sticker_id }).save(); + await sendStickerUpdateEvent(guild_id); + return res.json(sticker); } ); +async function sendStickerUpdateEvent(guild_id: string) { + return emitEvent({ + event: "GUILD_STICKERS_UPDATE", + guild_id: guild_id, + data: { + guild_id: guild_id, + stickers: await Sticker.find({ guild_id: guild_id }) + } + } as GuildStickersUpdateEvent); +} + router.delete("/:sticker_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => { const { guild_id, sticker_id } = req.params; await Sticker.delete({ guild_id, id: sticker_id }); + await sendStickerUpdateEvent(guild_id); return res.sendStatus(204); }); diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts index 3c8ab8ab..13fd4b8b 100644 --- a/util/src/interfaces/Event.ts +++ b/util/src/interfaces/Event.ts @@ -12,6 +12,7 @@ import { Interaction } from "./Interaction"; import { ConnectedAccount } from "../entities/ConnectedAccount"; import { Relationship, RelationshipType } from "../entities/Relationship"; import { Presence } from "./Presence"; +import { Sticker } from ".."; export interface Event { guild_id?: string; @@ -193,6 +194,14 @@ export interface GuildEmojisUpdateEvent extends Event { }; } +export interface GuildStickersUpdateEvent extends Event { + event: "GUILD_STICKERS_UPDATE"; + data: { + guild_id: string; + stickers: Sticker[]; + }; +} + export interface GuildIntegrationUpdateEvent extends Event { event: "GUILD_INTEGRATIONS_UPDATE"; data: { @@ -553,6 +562,7 @@ export type EVENT = | "GUILD_BAN_ADD" | "GUILD_BAN_REMOVE" | "GUILD_EMOJIS_UPDATE" + | "GUILD_STICKERS_UPDATE" | "GUILD_INTEGRATIONS_UPDATE" | "GUILD_MEMBER_ADD" | "GUILD_MEMBER_REMOVE" -- cgit 1.5.1 From 618b5e6509c28c3f4c43ec7daaec21562c3dbb6c Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:55:26 +0200 Subject: :bug: fix message sending --- api/src/routes/channels/#channel_id/messages/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'api/src') diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 3e26e930..296bec9a 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -212,7 +212,7 @@ router.post( await Promise.all([ channel.assign({ last_message_id: message.id }).save(), - new Member({ id: req.user_id, last_message_id: message.id }).save(), + message.guild_id ? Member.update({ id: req.user_id, guild_id: message.guild_id }, { last_message_id: message.id }) : null, emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent) ]); -- cgit 1.5.1 From d6188037700db5b7bceeff3a0da084bff0dfcd73 Mon Sep 17 00:00:00 2001 From: Chris Chrome Date: Sat, 16 Oct 2021 09:43:21 -0400 Subject: Fix /gateway/bot https://canary.discord.com/channels/806142446094385153/806142446529806367/898928295771521125 --- api/src/routes/gateway/bot.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'api/src') diff --git a/api/src/routes/gateway/bot.ts b/api/src/routes/gateway/bot.ts index 3a038276..72313700 100644 --- a/api/src/routes/gateway/bot.ts +++ b/api/src/routes/gateway/bot.ts @@ -23,7 +23,7 @@ const options: RouteOptions = { } }; -router.get("/bot", route(options), (req: Request, res: Response) => { +router.get("/", route(options), (req: Request, res: Response) => { const { endpointPublic } = Config.get().gateway; res.json({ url: endpointPublic || process.env.GATEWAY || "ws://localhost:3002", -- cgit 1.5.1 From 15fbb134cf1ec01db02df1b3eeeb5b6781e6a613 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 17 Oct 2021 00:41:06 +0200 Subject: :bug: user settings --- api/src/routes/users/@me/settings.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/users/@me/settings.ts b/api/src/routes/users/@me/settings.ts index 4e014126..b22b72fb 100644 --- a/api/src/routes/users/@me/settings.ts +++ b/api/src/routes/users/@me/settings.ts @@ -10,8 +10,9 @@ router.patch("/", route({ body: "UserSettingsSchema" }), async (req: Request, re const body = req.body as UserSettings; if (body.locale === "en") body.locale = "en-US"; // fix discord client crash on unkown locale - // only users can update user settings - await User.update({ id: req.user_id, bot: false }, { settings: body }); + const user = await User.findOneOrFail({ id: req.user_id, bot: false }); + user.settings = { ...user.settings, ...body }; + await user.save(); res.sendStatus(204); }); -- cgit 1.5.1 From 841f1d6d2a83d0cdd19dbd92a77e9229acb17610 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 17 Oct 2021 00:41:21 +0200 Subject: :art: clean up instances on start --- api/src/util/Instance.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'api/src') diff --git a/api/src/util/Instance.ts b/api/src/util/Instance.ts index 7dcd126e..6bddfa98 100644 --- a/api/src/util/Instance.ts +++ b/api/src/util/Instance.ts @@ -1,4 +1,4 @@ -import { Config, Guild } from "@fosscord/util"; +import { Config, Guild, Session } from "@fosscord/util"; export async function initInstance() { // TODO: clean up database and delete tombstone data @@ -15,4 +15,7 @@ export async function initInstance() { await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } }); } } + + // TODO: do no clear sessions for instance cluster + await Session.delete({}); } -- cgit 1.5.1 From 41e49be9b390f1aa107eb5c03fed8ebac5df0c73 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 17 Oct 2021 01:53:29 +0200 Subject: :bug: fix typing --- api/src/routes/channels/#channel_id/typing.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'api/src') diff --git a/api/src/routes/channels/#channel_id/typing.ts b/api/src/routes/channels/#channel_id/typing.ts index 45ed76db..56652368 100644 --- a/api/src/routes/channels/#channel_id/typing.ts +++ b/api/src/routes/channels/#channel_id/typing.ts @@ -9,14 +9,13 @@ router.post("/", route({ permission: "SEND_MESSAGES" }), async (req: Request, re const user_id = req.user_id; const timestamp = Date.now(); const channel = await Channel.findOneOrFail({ id: channel_id }); - const member = await Member.findOneOrFail({ where: { id: user_id }, relations: ["roles", "user"] }); + const member = await Member.findOne({ where: { id: user_id, guild_id: channel.guild_id }, relations: ["roles", "user"] }); await emitEvent({ event: "TYPING_START", channel_id: channel_id, data: { - // this is the paylod - member: { ...member, roles: member.roles?.map((x) => x.id) }, + ...(member ? { member: { ...member, roles: member?.roles?.map((x) => x.id) } } : null), channel_id, timestamp, user_id, -- cgit 1.5.1