🐛 fix .delete -> add onDelete: "CASCADE"

This commit is contained in:
Flam3rboy 2021-09-20 20:22:56 +02:00
parent 0247df3e42
commit 62f9b35185
23 changed files with 150 additions and 94 deletions

View File

@ -13,15 +13,8 @@ router.post("/", route({}), async (req: Request, res: Response) => {
const guild = await Guild.findOneOrFail({ where: { id: guild_id }, select: ["owner_id"] });
if (guild.owner_id !== req.user_id) throw new HTTPError("You are not the owner of this guild", 401);
// do not put everything into promise all, because of "QueryFailedError: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed"
await Message.delete({ guild_id }); // messages must be deleted before channel
await Promise.all([
Role.delete({ guild_id }),
Channel.delete({ guild_id }),
Emoji.delete({ guild_id }),
Member.delete({ guild_id }),
Guild.delete({ id: guild_id }), // this will also delete all guild related data
emitEvent({
event: "GUILD_DELETE",
data: {
@ -31,9 +24,6 @@ router.post("/", route({}), async (req: Request, res: Response) => {
} as GuildDeleteEvent)
]);
await Invite.delete({ guild_id }); // invite must be deleted after channel
await Guild.delete({ id: guild_id }); // guild must be deleted after everything else
return res.sendStatus(204);
});

View File

@ -41,7 +41,9 @@ export class Application extends BaseClass {
verify_key: string;
@JoinColumn({ name: "team_id" })
@ManyToOne(() => Team)
@ManyToOne(() => Team, {
onDelete: "CASCADE",
})
team?: Team;
@JoinColumn({ name: "guild_id" })

View File

@ -31,7 +31,9 @@ export class Attachment extends BaseClass {
message_id: string;
@JoinColumn({ name: "message_id" })
@ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments)
@ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments, {
onDelete: "CASCADE",
})
message: import("./Message").Message;
@BeforeRemove()

View File

@ -10,7 +10,9 @@ export class Ban extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
@Column({ nullable: true })
@ -18,7 +20,9 @@ export class Ban extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column({ nullable: true })

View File

@ -6,6 +6,7 @@ import {
EntityMetadata,
FindConditions,
getConnection,
getManager,
PrimaryColumn,
RemoveOptions,
} from "typeorm";
@ -79,44 +80,41 @@ export class BaseClassWithoutId extends BaseEntity {
return repository.decrement(conditions, propertyPath, value);
}
static async delete<T>(criteria: FindConditions<T>, options?: RemoveOptions) {
if (!criteria) throw new Error("You need to specify delete criteria");
// static async delete<T>(criteria: FindConditions<T>, options?: RemoveOptions) {
// if (!criteria) throw new Error("You need to specify delete criteria");
const repository = this.getRepository();
const promises = repository.metadata.relations.map(async (x) => {
if (x.orphanedRowAction !== "delete") return;
if (typeof x.type === "string") return;
// 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");
// 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 getConnection()
.getRepository(x.inverseEntityMetadata.target)
.delete({ [foreignKey.columnNames[0]]: id });
}
});
await Promise.all(promises);
return super.delete(criteria, options);
}
// if (x.relationType === "many-to-many") {
// return getConnection()
// .createQueryBuilder()
// .relation(this, x.propertyName)
// .of(id)
// .remove({ [foreignKey.columnNames[0]]: id });
// } else if (
// x.relationType === "one-to-one" ||
// x.relationType === "many-to-one" ||
// x.relationType === "one-to-many"
// ) {
// return (x.inverseEntityMetadata.target as any).delete({ [foreignKey.columnNames[0]]: id });
// }
// });
// await Promise.all(promises);
// return super.delete(criteria, options);
// }
}
export class BaseClass extends BaseClassWithoutId {

View File

@ -45,7 +45,6 @@ export class Channel extends BaseClass {
@OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, {
cascade: true,
orphanedRowAction: "delete",
onDelete: "CASCADE",
})
recipients?: Recipient[];
@ -57,7 +56,9 @@ export class Channel extends BaseClass {
guild_id?: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column({ nullable: true })
@ -110,35 +111,30 @@ export class Channel extends BaseClass {
@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[];

View File

@ -11,7 +11,9 @@ export class ConnectedAccount extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
@Column({ select: false })

View File

@ -15,7 +15,9 @@ export class Emoji extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column()

View File

@ -84,7 +84,6 @@ export class Guild extends BaseClass {
@OneToMany(() => Ban, (ban: Ban) => ban.guild, {
cascade: true,
orphanedRowAction: "delete",
onDelete: "CASCADE",
})
bans: Ban[];
@ -147,7 +146,6 @@ export class Guild extends BaseClass {
@OneToMany(() => Channel, (channel: Channel) => channel.guild, {
cascade: true,
orphanedRowAction: "delete",
onDelete: "CASCADE",
})
channels: Channel[];

View File

@ -34,7 +34,9 @@ export class Invite extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column({ nullable: true })
@ -42,7 +44,9 @@ export class Invite extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel)
@ManyToOne(() => Channel, {
onDelete: "CASCADE",
})
channel: Channel;
@Column({ nullable: true })
@ -58,7 +62,9 @@ export class Invite extends BaseClass {
target_user_id: string;
@JoinColumn({ name: "target_user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62
@Column({ nullable: true })

View File

@ -39,7 +39,9 @@ export class Member extends BaseClassWithoutId {
id: string;
@JoinColumn({ name: "id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
@Column()
@ -47,7 +49,9 @@ export class Member extends BaseClassWithoutId {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column({ nullable: true })
@ -55,7 +59,6 @@ export class Member extends BaseClassWithoutId {
@JoinTable({
name: "member_roles",
joinColumn: { name: "index", referencedColumnName: "index" },
inverseJoinColumn: {
name: "role_id",

View File

@ -54,7 +54,9 @@ export class Message extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel)
@ManyToOne(() => Channel, {
onDelete: "CASCADE",
})
channel: Channel;
@Column({ nullable: true })
@ -62,7 +64,9 @@ export class Message extends BaseClass {
guild_id?: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild?: Guild;
@Column({ nullable: true })
@ -70,7 +74,9 @@ export class Message extends BaseClass {
author_id: string;
@JoinColumn({ name: "author_id", referencedColumnName: "id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
author?: User;
@Column({ nullable: true })
@ -132,7 +138,6 @@ export class Message extends BaseClass {
@OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
cascade: true,
orphanedRowAction: "delete",
onDelete: "CASCADE",
})
attachments?: Attachment[];

View File

@ -15,7 +15,9 @@ export class ReadState extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel)
@ManyToOne(() => Channel, {
onDelete: "CASCADE",
})
channel: Channel;
@Column({ nullable: true })
@ -23,7 +25,9 @@ export class ReadState extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
@Column({ nullable: true })

View File

@ -8,7 +8,9 @@ export class Recipient extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => require("./Channel").Channel)
@ManyToOne(() => require("./Channel").Channel, {
onDelete: "CASCADE",
})
channel: import("./Channel").Channel;
@Column()
@ -16,7 +18,9 @@ export class Recipient extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => require("./User").User)
@ManyToOne(() => require("./User").User, {
onDelete: "CASCADE",
})
user: import("./User").User;
@Column({ default: false })

View File

@ -17,7 +17,9 @@ export class Relationship extends BaseClass {
from_id: string;
@JoinColumn({ name: "from_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
from: User;
@Column({})
@ -25,7 +27,9 @@ export class Relationship extends BaseClass {
to_id: string;
@JoinColumn({ name: "to_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
to: User;
@Column({ nullable: true })

View File

@ -10,7 +10,9 @@ export class Role extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column()

View File

@ -11,7 +11,9 @@ export class Session extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
//TODO check, should be 32 char long hex string

View File

@ -31,7 +31,9 @@ export class Sticker extends BaseClass {
guild_id?: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild?: Guild;
@Column({ type: "simple-enum", enum: StickerType })

View File

@ -9,7 +9,9 @@ export class Team extends BaseClass {
icon?: string;
@JoinColumn({ name: "member_ids" })
@OneToMany(() => TeamMember, (member: TeamMember) => member.team)
@OneToMany(() => TeamMember, (member: TeamMember) => member.team, {
orphanedRowAction: "delete",
})
members: TeamMember[];
@Column()

View File

@ -20,7 +20,9 @@ export class TeamMember extends BaseClass {
team_id: string;
@JoinColumn({ name: "team_id" })
@ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members)
@ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members, {
onDelete: "CASCADE",
})
team: import("./Team").Team;
@Column({ nullable: true })
@ -28,6 +30,8 @@ export class TeamMember extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
}

View File

@ -127,11 +127,17 @@ export class User extends BaseClass {
public_flags: number;
@JoinColumn({ name: "relationship_ids" })
@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from)
@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, {
cascade: true,
orphanedRowAction: "delete",
})
relationships: Relationship[];
@JoinColumn({ name: "connected_account_ids" })
@OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user)
@OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, {
cascade: true,
orphanedRowAction: "delete",
})
connected_accounts: ConnectedAccount[];
@Column({ type: "simple-json", select: false })

View File

@ -13,7 +13,9 @@ export class VoiceState extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild?: Guild;
@Column({ nullable: true })
@ -21,7 +23,9 @@ export class VoiceState extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel)
@ManyToOne(() => Channel, {
onDelete: "CASCADE",
})
channel: Channel;
@Column({ nullable: true })
@ -29,11 +33,15 @@ export class VoiceState extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
// @JoinColumn([{ name: "user_id", referencedColumnName: "id" },{ name: "guild_id", referencedColumnName: "guild_id" }])
// @ManyToOne(() => Member)
// @ManyToOne(() => Member, {
// onDelete: "CASCADE",
// })
//TODO find a way to make it work without breaking Guild.voice_states
member: Member;

View File

@ -32,7 +32,9 @@ export class Webhook extends BaseClass {
guild_id: string;
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
guild: Guild;
@Column({ nullable: true })
@ -40,7 +42,9 @@ export class Webhook extends BaseClass {
channel_id: string;
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel)
@ManyToOne(() => Channel, {
onDelete: "CASCADE",
})
channel: Channel;
@Column({ nullable: true })
@ -48,7 +52,9 @@ export class Webhook extends BaseClass {
application_id: string;
@JoinColumn({ name: "application_id" })
@ManyToOne(() => Application)
@ManyToOne(() => Application, {
onDelete: "CASCADE",
})
application: Application;
@Column({ nullable: true })
@ -56,7 +62,9 @@ export class Webhook extends BaseClass {
user_id: string;
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User)
@ManyToOne(() => User, {
onDelete: "CASCADE",
})
user: User;
@Column({ nullable: true })
@ -64,6 +72,8 @@ export class Webhook extends BaseClass {
source_guild_id: string;
@JoinColumn({ name: "source_guild_id" })
@ManyToOne(() => Guild)
@ManyToOne(() => Guild, {
onDelete: "CASCADE",
})
source_guild: Guild;
}