summary refs log tree commit diff
path: root/util/src
diff options
context:
space:
mode:
authorSamuel <34555296+Flam3rboy@users.noreply.github.com>2021-10-15 18:39:57 +0200
committerGitHub <noreply@github.com>2021-10-15 18:39:57 +0200
commit1bbf789a7eb54e19459931d3928db1e34472f6d2 (patch)
tree4ac117b32e6b6c8d2c380e552597e6caad70294d /util/src
parentMerge pull request #462 from hbjydev/unit-tests-expanded (diff)
parent:sparkles: sticker events (diff)
downloadserver-1bbf789a7eb54e19459931d3928db1e34472f6d2.tar.xz
Merge pull request #455 from fosscord/sticker
Stickers
Diffstat (limited to 'util/src')
-rw-r--r--util/src/entities/Message.ts2
-rw-r--r--util/src/entities/Migration.ts18
-rw-r--r--util/src/entities/ReadState.ts5
-rw-r--r--util/src/entities/Sticker.ts32
-rw-r--r--util/src/entities/StickerPack.ts31
-rw-r--r--util/src/entities/index.ts2
-rw-r--r--util/src/interfaces/Event.ts10
-rw-r--r--util/src/migrations/1633881705509-VanityInvite.ts2
-rw-r--r--util/src/migrations/1634308884591-Stickers.ts66
-rw-r--r--util/src/migrations/migrate_db_engine.js109
-rw-r--r--util/src/util/Database.ts20
-rw-r--r--util/src/util/cdn.ts4
12 files changed, 179 insertions, 122 deletions
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, {
diff --git a/util/src/entities/Migration.ts b/util/src/entities/Migration.ts
new file mode 100644
index 00000000..09df70fb
--- /dev/null
+++ b/util/src/entities/Migration.ts
@@ -0,0 +1,18 @@
+import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn } from "typeorm";
+import { BaseClassWithoutId } from ".";
+
+export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb")
+	? ObjectIdColumn
+	: PrimaryGeneratedColumn;
+
+@Entity("migrations")
+export class Migration extends BaseClassWithoutId {
+	@PrimaryIdAutoGenerated()
+	id: number;
+
+	@Column()
+	timestamp: number;
+
+	@Column()
+	name: string;
+}
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/Sticker.ts b/util/src/entities/Sticker.ts
index 036ff2d0..37bc6fbe 100644
--- a/util/src/entities/Sticker.ts
+++ b/util/src/entities/Sticker.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { User } from "./User";
 import { BaseClass } from "./BaseClass";
 import { Guild } from "./Guild";
 
@@ -8,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,
@@ -21,11 +23,22 @@ export class Sticker extends BaseClass {
 	@Column({ nullable: true })
 	description?: string;
 
-	@Column()
-	tags: string;
+	@Column({ nullable: true })
+	available?: boolean;
 
-	@Column()
-	pack_id: string;
+	@Column({ nullable: true })
+	tags?: string;
+
+	@Column({ nullable: true })
+	@RelationId((sticker: Sticker) => sticker.pack)
+	pack_id?: string;
+
+	@JoinColumn({ name: "pack_id" })
+	@ManyToOne(() => require("./StickerPack").StickerPack, {
+		onDelete: "CASCADE",
+		nullable: true,
+	})
+	pack: import("./StickerPack").StickerPack;
 
 	@Column({ nullable: true })
 	guild_id?: string;
@@ -36,6 +49,15 @@ export class Sticker extends BaseClass {
 	})
 	guild?: Guild;
 
+	@Column({ nullable: true })
+	user_id?: string;
+
+	@JoinColumn({ name: "user_id" })
+	@ManyToOne(() => User, {
+		onDelete: "CASCADE",
+	})
+	user?: User;
+
 	@Column({ type: "int" })
 	type: StickerType;
 
diff --git a/util/src/entities/StickerPack.ts b/util/src/entities/StickerPack.ts
new file mode 100644
index 00000000..ec8c69a2
--- /dev/null
+++ b/util/src/entities/StickerPack.ts
@@ -0,0 +1,31 @@
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
+import { Sticker } from ".";
+import { BaseClass } from "./BaseClass";
+
+@Entity("sticker_packs")
+export class StickerPack extends BaseClass {
+	@Column()
+	name: string;
+
+	@Column({ nullable: true })
+	description?: string;
+
+	@Column({ nullable: true })
+	banner_asset_id?: string;
+
+	@OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack, {
+		cascade: true,
+		orphanedRowAction: "delete",
+	})
+	stickers: Sticker[];
+
+	// sku_id: string
+
+	@Column({ nullable: true })
+	@RelationId((pack: StickerPack) => pack.cover_sticker)
+	cover_sticker_id?: string;
+
+	@ManyToOne(() => Sticker, { nullable: true })
+	@JoinColumn()
+	cover_sticker?: Sticker;
+}
diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts
index 7b1c9750..b52841c9 100644
--- a/util/src/entities/index.ts
+++ b/util/src/entities/index.ts
@@ -11,6 +11,7 @@ export * from "./Guild";
 export * from "./Invite";
 export * from "./Member";
 export * from "./Message";
+export * from "./Migration";
 export * from "./RateLimit";
 export * from "./ReadState";
 export * from "./Recipient";
@@ -18,6 +19,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/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"
diff --git a/util/src/migrations/1633881705509-VanityInvite.ts b/util/src/migrations/1633881705509-VanityInvite.ts
index af9b98ae..45485310 100644
--- a/util/src/migrations/1633881705509-VanityInvite.ts
+++ b/util/src/migrations/1633881705509-VanityInvite.ts
@@ -1,6 +1,8 @@
 import { MigrationInterface, QueryRunner } from "typeorm";
 
 export class VanityInvite1633881705509 implements MigrationInterface {
+	name = "VanityInvite1633881705509";
+
 	public async up(queryRunner: QueryRunner): Promise<void> {
 		try {
 			await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN vanity_url_code`);
diff --git a/util/src/migrations/1634308884591-Stickers.ts b/util/src/migrations/1634308884591-Stickers.ts
new file mode 100644
index 00000000..fbc4649f
--- /dev/null
+++ b/util/src/migrations/1634308884591-Stickers.ts
@@ -0,0 +1,66 @@
+import { MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey } from "typeorm";
+
+export class Stickers1634308884591 implements MigrationInterface {
+	name = "Stickers1634308884591";
+
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		await queryRunner.dropForeignKey("read_states", "FK_6f255d873cfbfd7a93849b7ff74");
+		await queryRunner.changeColumn(
+			"stickers",
+			"tags",
+			new TableColumn({ name: "tags", type: "varchar", isNullable: true })
+		);
+		await queryRunner.changeColumn(
+			"stickers",
+			"pack_id",
+			new TableColumn({ name: "pack_id", type: "varchar", isNullable: true })
+		);
+		await queryRunner.changeColumn("stickers", "type", new TableColumn({ name: "type", type: "integer" }));
+		await queryRunner.changeColumn(
+			"stickers",
+			"format_type",
+			new TableColumn({ name: "format_type", type: "integer" })
+		);
+		await queryRunner.changeColumn(
+			"stickers",
+			"available",
+			new TableColumn({ name: "available", type: "boolean", isNullable: true })
+		);
+		await queryRunner.changeColumn(
+			"stickers",
+			"user_id",
+			new TableColumn({ name: "user_id", type: "boolean", isNullable: true })
+		);
+		await queryRunner.createForeignKey(
+			"stickers",
+			new TableForeignKey({
+				name: "FK_8f4ee73f2bb2325ff980502e158",
+				columnNames: ["user_id"],
+				referencedColumnNames: ["id"],
+				referencedTableName: "users",
+				onDelete: "CASCADE",
+			})
+		);
+		await queryRunner.createTable(
+			new Table({
+				name: "sticker_packs",
+				columns: [
+					new TableColumn({ name: "id", type: "varchar", isPrimary: true }),
+					new TableColumn({ name: "name", type: "varchar" }),
+					new TableColumn({ name: "description", type: "varchar", isNullable: true }),
+					new TableColumn({ name: "banner_asset_id", type: "varchar", isNullable: true }),
+					new TableColumn({ name: "cover_sticker_id", type: "varchar", isNullable: true }),
+				],
+				foreignKeys: [
+					new TableForeignKey({
+						columnNames: ["cover_sticker_id"],
+						referencedColumnNames: ["id"],
+						referencedTableName: "stickers",
+					}),
+				],
+			})
+		);
+	}
+
+	public async down(queryRunner: QueryRunner): Promise<void> {}
+}
diff --git a/util/src/migrations/migrate_db_engine.js b/util/src/migrations/migrate_db_engine.js
deleted file mode 100644
index 79e9d86f..00000000
--- a/util/src/migrations/migrate_db_engine.js
+++ /dev/null
@@ -1,109 +0,0 @@
-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,
-	ConfigEntity,
-	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 keys
-	const entities = [
-		ConfigEntity,
-		User,
-		Guild,
-		Channel,
-		Invite,
-		Role,
-		Ban,
-		Application,
-		Emoji,
-		ConnectedAccount,
-		Member,
-		ReadState,
-		Recipient,
-		Relationship,
-		Sticker,
-		Team,
-		TeamMember,
-		Template,
-		VoiceState,
-		Webhook,
-		Message,
-		Attachment,
-	];
-
-	const oldDB = await initDatabase();
-
-	const type = process.env.TO.includes("://") ? process.env.TO.split(":")[0]?.replace("+srv", "") : "sqlite";
-	const isSqlite = type.includes("sqlite");
-
-	// @ts-ignore
-	const newDB = await createConnection({
-		type,
-		url: isSqlite ? undefined : process.env.TO,
-		database: isSqlite ? process.env.TO : undefined,
-		entities,
-		name: "new",
-		synchronize: true,
-	});
-	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/util/Database.ts b/util/src/util/Database.ts
index 8bce3a6f..6124ffab 100644
--- a/util/src/util/Database.ts
+++ b/util/src/util/Database.ts
@@ -2,6 +2,7 @@ import path from "path";
 import "reflect-metadata";
 import { Connection, createConnection } from "typeorm";
 import * as Models from "../entities";
+import { Migration } from "../entities/Migration";
 import { yellow, green } from "nanocolors";
 
 // UUID extension option is only supported with postgres
@@ -33,10 +34,27 @@ export function initDatabase(): Promise<Connection> {
 		bigNumberStrings: false,
 		supportBigNumbers: true,
 		name: "default",
+		migrations: [path.join(__dirname, "..", "migrations", "*.js")],
 	});
 
-	promise.then((connection) => {
+	promise.then(async (connection: Connection) => {
 		dbConnection = connection;
+
+		// run migrations, and if it is a new fresh database, set it to the last migration
+		if (connection.migrations.length) {
+			if (!(await Migration.findOne({}))) {
+				let i = 0;
+
+				await Migration.insert(
+					connection.migrations.map((x) => ({
+						id: i++,
+						name: x.name,
+						timestamp: Date.now(),
+					}))
+				);
+			}
+		}
+		await connection.runMigrations();
 		console.log(`[Database] ${green("connected")}`);
 	});
 
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,