summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--api/src/routes/channels/#channel_id/messages/#message_id/index.ts3
-rw-r--r--api/src/util/Attachments.ts12
-rw-r--r--api/src/util/index.ts1
-rw-r--r--util/src/entities/Attachment.ts9
-rw-r--r--util/src/entities/Channel.ts130
-rw-r--r--util/src/entities/Guild.ts54
-rw-r--r--util/src/entities/Message.ts11
7 files changed, 156 insertions, 64 deletions
diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts

index b5220fab..7f7de264 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts
@@ -3,7 +3,6 @@ import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; import { handleMessage, postHandleMessage } from "@fosscord/api"; import { MessageCreateSchema } from "../index"; -import { deleteMessageAttachments } from "@fosscord/api/util/Attachments"; const router = Router(); // TODO: message content/embed string length limit @@ -34,7 +33,6 @@ router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGE }); await Promise.all([ - await deleteMessageAttachments(message_id, new_message.attachments), //This delete all the attachments not in the array new_message!.save(), await emitEvent({ event: "MESSAGE_UPDATE", @@ -60,7 +58,6 @@ router.delete("/", route({}), async (req: Request, res: Response) => { permission.hasThrow("MANAGE_MESSAGES"); } - await deleteMessageAttachments(message_id); await Message.delete({ id: message_id }); await emitEvent({ diff --git a/api/src/util/Attachments.ts b/api/src/util/Attachments.ts deleted file mode 100644
index addda97f..00000000 --- a/api/src/util/Attachments.ts +++ /dev/null
@@ -1,12 +0,0 @@ -import { Attachment } from "@fosscord/util"; -import { deleteFile } from "@fosscord/api"; -import { URL } from "url"; - -export async function deleteMessageAttachments(messageId: string, keep?: Attachment[]) { - let attachments = await Attachment.find({ message_id: messageId }); - if (keep) - attachments = attachments.filter(x => !keep.map(k => k.id).includes(x.id)); - await Promise.all(attachments.map(a => a.remove())); - - attachments.forEach(a => deleteFile((new URL(a.url)).pathname)); //We don't need to await since this is done on the cdn -} diff --git a/api/src/util/index.ts b/api/src/util/index.ts
index 4b1e8e77..3e47ce4e 100644 --- a/api/src/util/index.ts +++ b/api/src/util/index.ts
@@ -1,5 +1,4 @@ export * from "./Base64"; -export * from "./cdn"; export * from "./FieldError"; export * from "./ipAddress"; export * from "./Message"; diff --git a/util/src/entities/Attachment.ts b/util/src/entities/Attachment.ts
index ca893400..82c5ecf5 100644 --- a/util/src/entities/Attachment.ts +++ b/util/src/entities/Attachment.ts
@@ -1,4 +1,6 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { URL } from "url"; +import { deleteFile } from "../util/cdn"; import { BaseClass } from "./BaseClass"; @Entity("attachments") @@ -31,4 +33,9 @@ export class Attachment extends BaseClass { @JoinColumn({ name: "message_id" }) @ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments) message: import("./Message").Message; + + @BeforeRemove() + onDelete() { + return deleteFile(new URL(this.url).pathname); + } } diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts
index ea632778..b1f75f33 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts
@@ -1,4 +1,14 @@ -import { Column, Entity, JoinColumn, 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 { PublicUserProjection, User } from "./User"; @@ -6,8 +16,12 @@ import { HTTPError } from "lambert-server"; import { containsAll, emitEvent, getPermission, Snowflake, trimSpecial } from "../util"; import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; import { Recipient } from "./Recipient"; -import { DmChannelDTO } from "../dtos"; import { Message } from "./Message"; +import { ReadState } from "./ReadState"; +import { Invite } from "./Invite"; +import { VoiceState } from "./VoiceState"; +import { Webhook } from "./Webhook"; +import { DmChannelDTO } from "../dtos"; export enum ChannelType { GUILD_TEXT = 0, // a text channel within a server @@ -32,13 +46,17 @@ export class Channel extends BaseClass { @Column({ nullable: true }) name?: string; - @Column({ type: 'text', nullable: true }) + @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 }) @@ -99,6 +117,42 @@ 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<Channel>, user_id: string = "0", @@ -151,10 +205,10 @@ export class Channel extends BaseClass { new Channel(channel).save(), !opts?.skipEventEmit ? emitEvent({ - event: "CHANNEL_CREATE", - data: channel, - guild_id: channel.guild_id, - } as ChannelCreateEvent) + event: "CHANNEL_CREATE", + data: channel, + guild_id: channel.guild_id, + } as ChannelCreateEvent) : Promise.resolve(), ]); @@ -174,17 +228,20 @@ export class Channel extends BaseClass { let channel = null; - const channelRecipients = [...recipients, creator_user_id] + const channelRecipients = [...recipients, creator_user_id]; - const userRecipients = await Recipient.find({ where: { user_id: creator_user_id }, relations: ["channel", "channel.recipients"] }) + const userRecipients = await Recipient.find({ + where: { user_id: creator_user_id }, + relations: ["channel", "channel.recipients"], + }); for (let ur of userRecipients) { - let re = ur.channel.recipients!.map(r => r.user_id) + let re = ur.channel.recipients!.map((r) => r.user_id); if (re.length === channelRecipients.length) { if (containsAll(re, channelRecipients)) { if (channel == null) { - channel = ur.channel - await ur.assign({ closed: false }).save() + channel = ur.channel; + await ur.assign({ closed: false }).save(); } } } @@ -196,80 +253,83 @@ export class Channel extends BaseClass { channel = await new Channel({ name, type, - owner_id: (type === ChannelType.DM ? undefined : creator_user_id), + owner_id: type === ChannelType.DM ? undefined : creator_user_id, created_at: new Date(), last_message_id: null, - recipients: channelRecipients.map((x) => new Recipient({ user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })), + recipients: channelRecipients.map( + (x) => + new Recipient({ user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) }) + ), }).save(); } - - const channel_dto = await DmChannelDTO.from(channel) + const channel_dto = await DmChannelDTO.from(channel); if (type === ChannelType.GROUP_DM) { - for (let recipient of channel.recipients!) { await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto.excludedRecipients([recipient.user_id]), - user_id: recipient.user_id - }) + user_id: recipient.user_id, + }); } } else { await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id }); } - return channel_dto.excludedRecipients([creator_user_id]) + return channel_dto.excludedRecipients([creator_user_id]); } static async removeRecipientFromChannel(channel: Channel, user_id: string) { - await Recipient.delete({ channel_id: channel.id, user_id: user_id }) - channel.recipients = channel.recipients?.filter(r => r.user_id !== user_id) + await Recipient.delete({ channel_id: channel.id, user_id: user_id }); + channel.recipients = channel.recipients?.filter((r) => r.user_id !== user_id); if (channel.recipients?.length === 0) { await Channel.deleteChannel(channel); await emitEvent({ event: "CHANNEL_DELETE", data: await DmChannelDTO.from(channel, [user_id]), - user_id: user_id + user_id: user_id, }); - return + return; } await emitEvent({ event: "CHANNEL_DELETE", data: await DmChannelDTO.from(channel, [user_id]), - user_id: user_id + user_id: user_id, }); //If the owner leave we make the first recipient in the list the new owner if (channel.owner_id === user_id) { - channel.owner_id = channel.recipients!.find(r => r.user_id !== user_id)!.user_id //Is there a criteria to choose the new owner? + channel.owner_id = channel.recipients!.find((r) => r.user_id !== user_id)!.user_id; //Is there a criteria to choose the new owner? await emitEvent({ event: "CHANNEL_UPDATE", data: await DmChannelDTO.from(channel, [user_id]), - channel_id: channel.id + channel_id: channel.id, }); } - await channel.save() + await channel.save(); await emitEvent({ - event: "CHANNEL_RECIPIENT_REMOVE", data: { + event: "CHANNEL_RECIPIENT_REMOVE", + data: { channel_id: channel.id, - user: await User.findOneOrFail({ where: { id: user_id }, select: PublicUserProjection }) - }, channel_id: channel.id + user: await User.findOneOrFail({ where: { id: user_id }, select: PublicUserProjection }), + }, + channel_id: channel.id, } as ChannelRecipientRemoveEvent); } static async deleteChannel(channel: Channel) { - await Message.delete({ channel_id: channel.id }) //TODO we should also delete the attachments from the cdn but to do that we need to move cdn.ts in util + await Message.delete({ channel_id: channel.id }); //TODO we should also delete the attachments from the cdn but to do that we need to move cdn.ts in util //TODO before deleting the channel we should check and delete other relations - await Channel.delete({ id: channel.id }) + await Channel.delete({ id: channel.id }); } isDm() { - return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM + return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM; } } 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" })