From 705206ec1b020bbae2b92de1e98e8a7609a6c850 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 19 Sep 2021 18:47:38 +0200 Subject: [PATCH] :art: add orphanedRowAction and cascade onDelete to entities --- util/package-lock.json | Bin 446397 -> 459190 bytes util/package.json | 2 + util/src/entities/Channel.ts | 71 +++++++++++++++++++++++++++++++---- util/src/entities/Guild.ts | 54 +++++++++++++++++++++----- util/src/entities/Message.ts | 11 ++++-- util/src/util/cdn.ts | 54 ++++++++++++++++++++++++++ util/src/util/index.ts | 1 + 7 files changed, 173 insertions(+), 20 deletions(-) create mode 100644 util/src/util/cdn.ts diff --git a/util/package-lock.json b/util/package-lock.json index 4b0f1f1517e9ee45bfdacc0a32ed79d16784a74c..e5a62d0f36a1fa06c3e0b137b0fd5e4f3349a0d8 100644 GIT binary patch delta 5089 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 UTdsz2A;@5N&2aB+s#^m80lVCg3;+NC delta 219 zcmV<103`pmgB-n|8?fgAv;P630h1782(!rp^oz6Em5vCrZ=Z1lvm>Q@1G6uzZ6C8> z%v(IS>j?qm9k=2x0Y@N*TTlVFTTlYTF1Im(0<*!B+R`7FAdv>2mwYe-4!5900~sur z>9hk4w>!560|A$Cv;!Bno^k`w)VD@W1S1HyWL5;`1Gjuz1W*H)7mx)Gw?JY9X&Sd+ zeFWGvw=1&*ni-b{r~((4TLc6VmmnYkHJ3jx0eH72)C6c7w}bEm_WPGHfdbRFAMXV# Vp_kM{1HQMvkp{jbw=TB^h)U^@SL^@) diff --git a/util/package.json b/util/package.json index 751484af..2fa93f9b 100644 --- a/util/package.json +++ b/util/package.json @@ -31,6 +31,7 @@ "@types/amqplib": "^0.8.1", "@types/jsonwebtoken": "^8.5.0", "@types/mongoose-autopopulate": "^0.10.1", + "@types/multer": "^1.4.7", "@types/node": "^14.17.9", "@types/node-fetch": "^2.5.12", "jest": "^27.0.6" @@ -44,6 +45,7 @@ "jsonwebtoken": "^8.5.1", "lambert-server": "^1.2.10", "missing-native-js-functions": "^1.2.15", + "multer": "^1.4.3", "node-fetch": "^2.6.1", "patch-package": "^6.4.7", "pg": "^8.7.1", diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index fc954f63..0196fb3e 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -1,12 +1,26 @@ -import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; +import { + Column, + Entity, + FindConditions, + JoinColumn, + ManyToOne, + ObjectID, + OneToMany, + RelationId, + RemoveOptions, +} from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; -import { Message } from "./Message"; import { User } from "./User"; import { HTTPError } from "lambert-server"; import { emitEvent, getPermission, Snowflake } from "../util"; import { ChannelCreateEvent } from "../interfaces"; import { Recipient } from "./Recipient"; +import { Message } from "./Message"; +import { ReadState } from "./ReadState"; +import { Invite } from "./Invite"; +import { VoiceState } from "./VoiceState"; +import { Webhook } from "./Webhook"; export enum ChannelType { GUILD_TEXT = 0, // a text channel within a server @@ -31,20 +45,22 @@ export class Channel extends BaseClass { @Column({ nullable: true }) name?: string; + @Column({ type: "text", nullable: true }) + icon?: string | null; + @Column({ type: "simple-enum", enum: ChannelType }) type: ChannelType; - @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, { cascade: true }) + @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) recipients?: Recipient[]; @Column({ nullable: true }) - @RelationId((channel: Channel) => channel.last_message) last_message_id: string; - @JoinColumn({ name: "last_message_id" }) - @ManyToOne(() => Message) - last_message?: Message; - @Column({ nullable: true }) @RelationId((channel: Channel) => channel.guild) guild_id?: string; @@ -100,6 +116,41 @@ export class Channel extends BaseClass { @Column({ nullable: true }) topic?: string; + @OneToMany(() => Invite, (invite: Invite) => invite.channel, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) + invites?: Invite[]; + + @OneToMany(() => Message, (message: Message) => message.channel, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) + messages?: Message[]; + + @OneToMany(() => VoiceState, (voice_state: VoiceState) => voice_state.channel, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) + voice_states?: VoiceState[]; + + @OneToMany(() => ReadState, (read_state: ReadState) => read_state.channel, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) + read_states?: ReadState[]; + + @OneToMany(() => Webhook, (webhook: Webhook) => webhook.channel, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) + webhooks?: Webhook[]; + // TODO: DM channel static async createChannel( channel: Partial, @@ -162,6 +213,10 @@ export class Channel extends BaseClass { return channel; } + + isDm() { + return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM; + } } export interface ChannelPermissionOverwrite { diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 7b5d2908..d46d2161 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -81,7 +81,11 @@ export class Guild extends BaseClass { // application?: string; @JoinColumn({ name: "ban_ids" }) - @OneToMany(() => Ban, (ban: Ban) => ban.guild) + @OneToMany(() => Ban, (ban: Ban) => ban.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) bans: Ban[]; @Column({ nullable: true }) @@ -124,15 +128,27 @@ export class Guild extends BaseClass { @Column({ nullable: true }) presence_count?: number; // users online - @OneToMany(() => Member, (member: Member) => member.guild) + @OneToMany(() => Member, (member: Member) => member.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) members: Member[]; @JoinColumn({ name: "role_ids" }) - @OneToMany(() => Role, (role: Role) => role.guild) + @OneToMany(() => Role, (role: Role) => role.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) roles: Role[]; @JoinColumn({ name: "channel_ids" }) - @OneToMany(() => Channel, (channel: Channel) => channel.guild) + @OneToMany(() => Channel, (channel: Channel) => channel.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) channels: Channel[]; @Column({ nullable: true }) @@ -144,23 +160,43 @@ export class Guild extends BaseClass { template: Template; @JoinColumn({ name: "emoji_ids" }) - @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild) + @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) emojis: Emoji[]; @JoinColumn({ name: "sticker_ids" }) - @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild) + @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) stickers: Sticker[]; @JoinColumn({ name: "invite_ids" }) - @OneToMany(() => Invite, (invite: Invite) => invite.guild) + @OneToMany(() => Invite, (invite: Invite) => invite.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) invites: Invite[]; @JoinColumn({ name: "voice_state_ids" }) - @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild) + @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) voice_states: VoiceState[]; @JoinColumn({ name: "webhook_ids" }) - @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild) + @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) webhooks: Webhook[]; @Column({ nullable: true }) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 506db71a..0712f545 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -8,12 +8,14 @@ import { Column, CreateDateColumn, Entity, + FindConditions, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId, + RemoveOptions, UpdateDateColumn, } from "typeorm"; import { BaseClass } from "./BaseClass"; @@ -112,7 +114,7 @@ export class Message extends BaseClass { mention_everyone?: boolean; @JoinTable({ name: "message_user_mentions" }) - @ManyToMany(() => User) + @ManyToMany(() => User, { orphanedRowAction: "delete", onDelete: "CASCADE", cascade: true }) mentions: User[]; @JoinTable({ name: "message_role_mentions" }) @@ -127,8 +129,11 @@ export class Message extends BaseClass { @ManyToMany(() => Sticker) sticker_items?: Sticker[]; - @JoinColumn({ name: "attachment_ids" }) - @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, { cascade: true }) + @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, { + cascade: true, + orphanedRowAction: "delete", + onDelete: "CASCADE", + }) attachments?: Attachment[]; @Column({ type: "simple-json" }) diff --git a/util/src/util/cdn.ts b/util/src/util/cdn.ts new file mode 100644 index 00000000..754d6244 --- /dev/null +++ b/util/src/util/cdn.ts @@ -0,0 +1,54 @@ +import FormData from "form-data"; +import { HTTPError } from "lambert-server"; +import fetch from "node-fetch"; +import { Config } from "./Config"; +import multer from "multer"; + +export async function uploadFile(path: string, file: Express.Multer.File) { + const form = new FormData(); + form.append("file", file.buffer, { + contentType: file.mimetype, + filename: file.originalname, + }); + + const response = await fetch(`${Config.get().cdn.endpoint || "http://localhost:3003"}${path}`, { + headers: { + signature: Config.get().security.requestSignature, + ...form.getHeaders(), + }, + method: "POST", + body: form, + }); + const result = await response.json(); + + if (response.status !== 200) throw result; + return result; +} + +export async function handleFile(path: string, body?: string): Promise { + if (!body || !body.startsWith("data:")) return body; + try { + const mimetype = body.split(":")[1].split(";")[0]; + const buffer = Buffer.from(body.split(",")[1], "base64"); + + // @ts-ignore + const { id } = await uploadFile(path, { buffer, mimetype, originalname: "banner" }); + return id; + } catch (error) { + console.error(error); + throw new HTTPError("Invalid " + path); + } +} + +export async function deleteFile(path: string) { + const response = await fetch(`${Config.get().cdn.endpoint || "http://localhost:3003"}${path}`, { + headers: { + signature: Config.get().security.requestSignature, + }, + method: "DELETE", + }); + const result = await response.json(); + + if (response.status !== 200) throw result; + return result; +} diff --git a/util/src/util/index.ts b/util/src/util/index.ts index 4e92f017..1ae96da9 100644 --- a/util/src/util/index.ts +++ b/util/src/util/index.ts @@ -1,6 +1,7 @@ export * from "./ApiError"; export * from "./BitField"; export * from "./checkToken"; +export * from "./cdn"; export * from "./Config"; export * from "./Constants"; export * from "./Database";