mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
refactor(database): ♻️ Make emojis use a Media instead of just rawdogging the URI
This commit is contained in:
parent
c7aae24d42
commit
cf1104d762
|
|
@ -1,6 +1,7 @@
|
||||||
import { apiRoute, auth, emojiValidator, jsonOrForm } from "@/api";
|
import { apiRoute, auth, emojiValidator, jsonOrForm } from "@/api";
|
||||||
import { mimeLookup } from "@/content_types";
|
import { mimeLookup } from "@/content_types";
|
||||||
import { createRoute } from "@hono/zod-openapi";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
|
import type { ContentFormat } from "@versia/federation/types";
|
||||||
import { Emoji, Media, db } from "@versia/kit/db";
|
import { Emoji, Media, db } from "@versia/kit/db";
|
||||||
import { Emojis, RolePermissions } from "@versia/kit/tables";
|
import { Emojis, RolePermissions } from "@versia/kit/tables";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
@ -230,8 +231,6 @@ export default apiRoute((app) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaManager = new MediaManager(config);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
global: emojiGlobal,
|
global: emojiGlobal,
|
||||||
alt,
|
alt,
|
||||||
|
|
@ -248,11 +247,12 @@ export default apiRoute((app) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modifiedMedia = structuredClone(emoji.data.media);
|
||||||
const modified = structuredClone(emoji.data);
|
const modified = structuredClone(emoji.data);
|
||||||
|
|
||||||
if (element) {
|
if (element) {
|
||||||
// Check of emoji is an image
|
// Check of emoji is an image
|
||||||
let contentType =
|
const contentType =
|
||||||
element instanceof File
|
element instanceof File
|
||||||
? element.type
|
? element.type
|
||||||
: await mimeLookup(element);
|
: await mimeLookup(element);
|
||||||
|
|
@ -265,23 +265,33 @@ export default apiRoute((app) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = "";
|
let contentFormat: ContentFormat | undefined;
|
||||||
|
|
||||||
if (element instanceof File) {
|
if (element instanceof File) {
|
||||||
const uploaded = await mediaManager.addFile(element);
|
const mediaManager = new MediaManager(config);
|
||||||
|
|
||||||
url = Media.getUrl(uploaded.path);
|
const { uploadedFile, path } =
|
||||||
contentType = uploaded.uploadedFile.type;
|
await mediaManager.addFile(element);
|
||||||
|
|
||||||
|
contentFormat = await Media.fileToContentFormat(
|
||||||
|
uploadedFile,
|
||||||
|
Media.getUrl(path),
|
||||||
|
{ description: alt },
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
url = element;
|
contentFormat = {
|
||||||
|
[contentType]: {
|
||||||
|
content: element,
|
||||||
|
remote: true,
|
||||||
|
description: alt,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
modified.url = url;
|
modifiedMedia.content = contentFormat;
|
||||||
modified.contentType = contentType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modified.shortcode = shortcode ?? modified.shortcode;
|
modified.shortcode = shortcode ?? modified.shortcode;
|
||||||
modified.alt = alt ?? modified.alt;
|
|
||||||
modified.category = category ?? modified.category;
|
modified.category = category ?? modified.category;
|
||||||
|
|
||||||
if (emojiGlobal !== undefined) {
|
if (emojiGlobal !== undefined) {
|
||||||
|
|
@ -317,7 +327,7 @@ export default apiRoute((app) => {
|
||||||
|
|
||||||
const mediaManager = new MediaManager(config);
|
const mediaManager = new MediaManager(config);
|
||||||
|
|
||||||
await mediaManager.deleteFileByUrl(emoji.data.url);
|
await mediaManager.deleteFileByUrl(emoji.media.getUrl());
|
||||||
|
|
||||||
await db.delete(Emojis).where(eq(Emojis.id, id));
|
await db.delete(Emojis).where(eq(Emojis.id, id));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { apiRoute, auth, emojiValidator, jsonOrForm } from "@/api";
|
import { apiRoute, auth, emojiValidator, jsonOrForm } from "@/api";
|
||||||
import { mimeLookup } from "@/content_types";
|
import { mimeLookup } from "@/content_types";
|
||||||
import { createRoute } from "@hono/zod-openapi";
|
import { createRoute } from "@hono/zod-openapi";
|
||||||
|
import type { ContentFormat } from "@versia/federation/types";
|
||||||
import { Emoji, Media } from "@versia/kit/db";
|
import { Emoji, Media } from "@versia/kit/db";
|
||||||
import { Emojis, RolePermissions } from "@versia/kit/tables";
|
import { Emojis, RolePermissions } from "@versia/kit/tables";
|
||||||
import { and, eq, isNull, or } from "drizzle-orm";
|
import { and, eq, isNull, or } from "drizzle-orm";
|
||||||
|
|
@ -130,10 +131,8 @@ export default apiRoute((app) =>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = "";
|
|
||||||
|
|
||||||
// Check of emoji is an image
|
// Check of emoji is an image
|
||||||
let contentType =
|
const contentType =
|
||||||
element instanceof File ? element.type : await mimeLookup(element);
|
element instanceof File ? element.type : await mimeLookup(element);
|
||||||
|
|
||||||
if (!contentType.startsWith("image/")) {
|
if (!contentType.startsWith("image/")) {
|
||||||
|
|
@ -144,25 +143,38 @@ export default apiRoute((app) =>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let contentFormat: ContentFormat | undefined;
|
||||||
|
|
||||||
if (element instanceof File) {
|
if (element instanceof File) {
|
||||||
const mediaManager = new MediaManager(config);
|
const mediaManager = new MediaManager(config);
|
||||||
|
|
||||||
const uploaded = await mediaManager.addFile(element);
|
const { uploadedFile, path } = await mediaManager.addFile(element);
|
||||||
|
|
||||||
url = Media.getUrl(uploaded.path);
|
contentFormat = await Media.fileToContentFormat(
|
||||||
contentType = uploaded.uploadedFile.type;
|
uploadedFile,
|
||||||
|
Media.getUrl(path),
|
||||||
|
{ description: alt },
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
url = element;
|
contentFormat = {
|
||||||
|
[contentType]: {
|
||||||
|
content: element,
|
||||||
|
remote: true,
|
||||||
|
description: alt,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const media = await Media.insert({
|
||||||
|
content: contentFormat,
|
||||||
|
});
|
||||||
|
|
||||||
const emoji = await Emoji.insert({
|
const emoji = await Emoji.insert({
|
||||||
shortcode,
|
shortcode,
|
||||||
url,
|
mediaId: media.id,
|
||||||
visibleInPicker: true,
|
visibleInPicker: true,
|
||||||
ownerId: global ? null : user.id,
|
ownerId: global ? null : user.id,
|
||||||
category,
|
category,
|
||||||
contentType,
|
|
||||||
alt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return context.json(emoji.toApi(), 201);
|
return context.json(emoji.toApi(), 201);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||||
import type { Status as ApiStatus } from "@versia/client/types";
|
import type { Status as ApiStatus } from "@versia/client/types";
|
||||||
import { db } from "@versia/kit/db";
|
import { Media, db } from "@versia/kit/db";
|
||||||
import { Emojis } from "@versia/kit/tables";
|
import { Emojis } from "@versia/kit/tables";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { config } from "~/packages/config-manager/index.ts";
|
import { config } from "~/packages/config-manager/index.ts";
|
||||||
import { fakeRequest, getTestUsers } from "~/tests/utils";
|
import { fakeRequest, getTestUsers } from "~/tests/utils";
|
||||||
|
|
||||||
const { users, tokens, deleteUsers } = await getTestUsers(5);
|
const { users, tokens, deleteUsers } = await getTestUsers(5);
|
||||||
|
let media: Media;
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await deleteUsers();
|
await deleteUsers();
|
||||||
|
|
@ -14,10 +15,17 @@ afterAll(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
media = await Media.insert({
|
||||||
|
content: {
|
||||||
|
"image/png": {
|
||||||
|
content: "https://example.com/test.png",
|
||||||
|
remote: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
await db.insert(Emojis).values({
|
await db.insert(Emojis).values({
|
||||||
contentType: "image/png",
|
|
||||||
shortcode: "test",
|
shortcode: "test",
|
||||||
url: "https://example.com/test.png",
|
mediaId: media.id,
|
||||||
visibleInPicker: true,
|
visibleInPicker: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { emojiValidatorWithColons, emojiValidatorWithIdentifiers } from "@/api";
|
||||||
import { proxyUrl } from "@/response";
|
import { proxyUrl } from "@/response";
|
||||||
import type { Emoji as APIEmoji } from "@versia/client/types";
|
import type { Emoji as APIEmoji } from "@versia/client/types";
|
||||||
import type { CustomEmojiExtension } from "@versia/federation/types";
|
import type { CustomEmojiExtension } from "@versia/federation/types";
|
||||||
import { type Instance, db } from "@versia/kit/db";
|
import { type Instance, Media, db } from "@versia/kit/db";
|
||||||
import { Emojis, type Instances } from "@versia/kit/tables";
|
import { Emojis, type Instances, type Medias } from "@versia/kit/tables";
|
||||||
import {
|
import {
|
||||||
type InferInsertModel,
|
type InferInsertModel,
|
||||||
type InferSelectModel,
|
type InferSelectModel,
|
||||||
|
|
@ -17,11 +17,12 @@ import {
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { BaseInterface } from "./base.ts";
|
import { BaseInterface } from "./base.ts";
|
||||||
|
|
||||||
type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
|
type EmojiType = InferSelectModel<typeof Emojis> & {
|
||||||
|
media: InferSelectModel<typeof Medias>;
|
||||||
instance: InferSelectModel<typeof Instances> | null;
|
instance: InferSelectModel<typeof Instances> | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
export class Emoji extends BaseInterface<typeof Emojis, EmojiType> {
|
||||||
public static schema = z.object({
|
public static schema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
shortcode: z.string(),
|
shortcode: z.string(),
|
||||||
|
|
@ -32,7 +33,13 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||||
global: z.boolean(),
|
global: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
public static $type: EmojiWithInstance;
|
public static $type: EmojiType;
|
||||||
|
public media: Media;
|
||||||
|
|
||||||
|
public constructor(data: EmojiType) {
|
||||||
|
super(data);
|
||||||
|
this.media = new Media(data.media);
|
||||||
|
}
|
||||||
|
|
||||||
public async reload(): Promise<void> {
|
public async reload(): Promise<void> {
|
||||||
const reloaded = await Emoji.fromId(this.data.id);
|
const reloaded = await Emoji.fromId(this.data.id);
|
||||||
|
|
@ -65,6 +72,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||||
orderBy,
|
orderBy,
|
||||||
with: {
|
with: {
|
||||||
instance: true,
|
instance: true,
|
||||||
|
media: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -86,15 +94,13 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||||
orderBy,
|
orderBy,
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
with: { ...extra?.with, instance: true },
|
with: { ...extra?.with, instance: true, media: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
return found.map((s) => new Emoji(s));
|
return found.map((s) => new Emoji(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async update(
|
public async update(newEmoji: Partial<EmojiType>): Promise<EmojiType> {
|
||||||
newEmoji: Partial<EmojiWithInstance>,
|
|
||||||
): Promise<EmojiWithInstance> {
|
|
||||||
await db.update(Emojis).set(newEmoji).where(eq(Emojis.id, this.id));
|
await db.update(Emojis).set(newEmoji).where(eq(Emojis.id, this.id));
|
||||||
|
|
||||||
const updated = await Emoji.fromId(this.data.id);
|
const updated = await Emoji.fromId(this.data.id);
|
||||||
|
|
@ -107,7 +113,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||||
return updated.data;
|
return updated.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public save(): Promise<EmojiWithInstance> {
|
public save(): Promise<EmojiType> {
|
||||||
return this.update(this.data);
|
return this.update(this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,29 +188,25 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
shortcode: this.data.shortcode,
|
shortcode: this.data.shortcode,
|
||||||
static_url: proxyUrl(this.data.url) ?? "", // TODO: Add static version
|
static_url: proxyUrl(this.media.getUrl()) ?? "", // TODO: Add static version
|
||||||
url: proxyUrl(this.data.url) ?? "",
|
url: proxyUrl(this.media.getUrl()) ?? "",
|
||||||
visible_in_picker: this.data.visibleInPicker,
|
visible_in_picker: this.data.visibleInPicker,
|
||||||
category: this.data.category ?? undefined,
|
category: this.data.category ?? undefined,
|
||||||
global: this.data.ownerId === null,
|
global: this.data.ownerId === null,
|
||||||
description: this.data.alt ?? undefined,
|
description:
|
||||||
|
this.media.data.content[this.media.getPreferredMimeType()]
|
||||||
|
.description ?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public toVersia(): CustomEmojiExtension["emojis"][0] {
|
public toVersia(): CustomEmojiExtension["emojis"][0] {
|
||||||
return {
|
return {
|
||||||
name: `:${this.data.shortcode}:`,
|
name: `:${this.data.shortcode}:`,
|
||||||
url: {
|
url: this.media.toVersia(),
|
||||||
[this.data.contentType]: {
|
|
||||||
content: this.data.url,
|
|
||||||
description: this.data.alt || undefined,
|
|
||||||
remote: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromVersia(
|
public static async fromVersia(
|
||||||
emoji: CustomEmojiExtension["emojis"][0],
|
emoji: CustomEmojiExtension["emojis"][0],
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
): Promise<Emoji> {
|
): Promise<Emoji> {
|
||||||
|
|
@ -217,11 +219,11 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
|
||||||
throw new Error("Could not extract shortcode from emoji name");
|
throw new Error("Could not extract shortcode from emoji name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const media = await Media.fromVersia(emoji.url);
|
||||||
|
|
||||||
return Emoji.insert({
|
return Emoji.insert({
|
||||||
shortcode,
|
shortcode,
|
||||||
url: Object.entries(emoji.url)[0][1].content,
|
mediaId: media.id,
|
||||||
alt: Object.entries(emoji.url)[0][1].description || undefined,
|
|
||||||
contentType: Object.keys(emoji.url)[0],
|
|
||||||
visibleInPicker: true,
|
visibleInPicker: true,
|
||||||
instanceId: instance.id,
|
instanceId: instance.id,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
||||||
emoji: {
|
emoji: {
|
||||||
with: {
|
with: {
|
||||||
instance: true,
|
instance: true,
|
||||||
|
media: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
author: true,
|
author: true,
|
||||||
|
|
@ -98,6 +99,7 @@ export class Reaction extends BaseInterface<typeof Reactions, ReactionType> {
|
||||||
emoji: {
|
emoji: {
|
||||||
with: {
|
with: {
|
||||||
instance: true,
|
instance: true,
|
||||||
|
media: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
author: true,
|
author: true,
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ export const findManyNotes = async (
|
||||||
emoji: {
|
emoji: {
|
||||||
with: {
|
with: {
|
||||||
instance: true,
|
instance: true,
|
||||||
|
media: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -79,6 +80,7 @@ export const findManyNotes = async (
|
||||||
emoji: {
|
emoji: {
|
||||||
with: {
|
with: {
|
||||||
instance: true,
|
instance: true,
|
||||||
|
media: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ export const userRelations = {
|
||||||
emoji: {
|
emoji: {
|
||||||
with: {
|
with: {
|
||||||
instance: true,
|
instance: true,
|
||||||
|
media: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { parseUserAddress, userAddressValidator } from "@/api";
|
import { parseUserAddress, userAddressValidator } from "@/api";
|
||||||
import { Args, type Command, Flags, type Interfaces } from "@oclif/core";
|
import { Args, type Command, Flags, type Interfaces } from "@oclif/core";
|
||||||
import { type Emoji, Instance, User, db } from "@versia/kit/db";
|
import { Emoji, Instance, User } from "@versia/kit/db";
|
||||||
import { Emojis, Instances, Users } from "@versia/kit/tables";
|
import { Emojis, Instances, Users } from "@versia/kit/tables";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { and, eq, getTableColumns, like } from "drizzle-orm";
|
import { and, eq, inArray, like } from "drizzle-orm";
|
||||||
import { BaseCommand } from "./base.ts";
|
import { BaseCommand } from "./base.ts";
|
||||||
|
|
||||||
export type FlagsType<T extends typeof Command> = Interfaces.InferredFlags<
|
export type FlagsType<T extends typeof Command> = Interfaces.InferredFlags<
|
||||||
|
|
@ -203,14 +203,7 @@ export abstract class EmojiFinderCommand<
|
||||||
this.args = args as ArgsType<T>;
|
this.args = args as ArgsType<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findEmojis(): Promise<
|
public async findEmojis(): Promise<Emoji[]> {
|
||||||
Omit<
|
|
||||||
typeof Emoji.$type & {
|
|
||||||
instanceUrl: string | null;
|
|
||||||
},
|
|
||||||
"instance"
|
|
||||||
>[]
|
|
||||||
> {
|
|
||||||
// Check if there are asterisks in the identifier but no pattern flag, warn the user if so
|
// Check if there are asterisks in the identifier but no pattern flag, warn the user if so
|
||||||
if (this.args.identifier.includes("*") && !this.flags.pattern) {
|
if (this.args.identifier.includes("*") && !this.flags.pattern) {
|
||||||
this.log(
|
this.log(
|
||||||
|
|
@ -228,22 +221,26 @@ export abstract class EmojiFinderCommand<
|
||||||
? this.args.identifier.replace(/\*/g, "%")
|
? this.args.identifier.replace(/\*/g, "%")
|
||||||
: this.args.identifier;
|
: this.args.identifier;
|
||||||
|
|
||||||
return await db
|
const instanceIds =
|
||||||
.select({
|
this.flags.type === "instance"
|
||||||
...getTableColumns(Emojis),
|
? (
|
||||||
instanceUrl: Instances.baseUrl,
|
await Instance.manyFromSql(
|
||||||
})
|
operator(Instances.baseUrl, identifier),
|
||||||
.from(Emojis)
|
)
|
||||||
.leftJoin(Instances, eq(Emojis.instanceId, Instances.id))
|
).map((instance) => instance.id)
|
||||||
.where(
|
: undefined;
|
||||||
and(
|
|
||||||
this.flags.type === "shortcode"
|
return await Emoji.manyFromSql(
|
||||||
? operator(Emojis.shortcode, identifier)
|
and(
|
||||||
: undefined,
|
this.flags.type === "shortcode"
|
||||||
this.flags.type === "instance"
|
? operator(Emojis.shortcode, identifier)
|
||||||
? operator(Instances.baseUrl, identifier)
|
: undefined,
|
||||||
: undefined,
|
instanceIds && instanceIds.length > 0
|
||||||
),
|
? inArray(Emojis.instanceId, instanceIds)
|
||||||
);
|
: undefined,
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
this.flags.limit,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { Emojis } from "@versia/kit/tables";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { and, eq, isNull } from "drizzle-orm";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
import ora from "ora";
|
import ora from "ora";
|
||||||
import { MediaManager } from "~/classes/media/media-manager";
|
|
||||||
import { BaseCommand } from "~/cli/base";
|
import { BaseCommand } from "~/cli/base";
|
||||||
import { config } from "~/packages/config-manager";
|
import { config } from "~/packages/config-manager";
|
||||||
|
|
||||||
|
|
@ -97,35 +96,22 @@ export default class EmojiAdd extends BaseCommand<typeof EmojiAdd> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaManager = new MediaManager(config);
|
|
||||||
|
|
||||||
const spinner = ora("Uploading emoji").start();
|
const spinner = ora("Uploading emoji").start();
|
||||||
|
|
||||||
const uploaded = await mediaManager.addFile(file).catch((e: Error) => {
|
const media = await Media.fromFile(file);
|
||||||
spinner.fail();
|
|
||||||
this.log(`${chalk.red("✗")} Error: ${chalk.red(e.message)}`);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!uploaded) {
|
|
||||||
return this.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
spinner.succeed();
|
spinner.succeed();
|
||||||
|
|
||||||
await Emoji.insert({
|
await Emoji.insert({
|
||||||
shortcode: args.shortcode,
|
shortcode: args.shortcode,
|
||||||
url: Media.getUrl(uploaded.path),
|
mediaId: media.id,
|
||||||
visibleInPicker: true,
|
visibleInPicker: true,
|
||||||
contentType: uploaded.uploadedFile.type,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.log(
|
this.log(
|
||||||
`${chalk.green("✓")} Created emoji ${chalk.green(
|
`${chalk.green("✓")} Created emoji ${chalk.green(
|
||||||
args.shortcode,
|
args.shortcode,
|
||||||
)} with url ${chalk.blue(
|
)} with url ${chalk.blue(chalk.underline(media.getUrl()))}`,
|
||||||
chalk.underline(Media.getUrl(uploaded.path)),
|
|
||||||
)}`,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.exit(0);
|
this.exit(0);
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,10 @@ export default class EmojiDelete extends EmojiFinderCommand<
|
||||||
|
|
||||||
flags.print &&
|
flags.print &&
|
||||||
this.log(
|
this.log(
|
||||||
formatArray(emojis, [
|
formatArray(
|
||||||
"id",
|
emojis.map((e) => e.data),
|
||||||
"shortcode",
|
["id", "shortcode", "alt", "contentType", "instanceUrl"],
|
||||||
"alt",
|
),
|
||||||
"contentType",
|
|
||||||
"instanceUrl",
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (flags.confirm) {
|
if (flags.confirm) {
|
||||||
|
|
@ -80,13 +77,13 @@ export default class EmojiDelete extends EmojiFinderCommand<
|
||||||
const spinner = ora("Deleting emoji(s)").start();
|
const spinner = ora("Deleting emoji(s)").start();
|
||||||
|
|
||||||
for (const emoji of emojis) {
|
for (const emoji of emojis) {
|
||||||
spinner.text = `Deleting emoji ${chalk.gray(emoji.shortcode)} (${
|
spinner.text = `Deleting emoji ${chalk.gray(emoji.data.shortcode)} (${
|
||||||
emojis.findIndex((e) => e.id === emoji.id) + 1
|
emojis.findIndex((e) => e.id === emoji.id) + 1
|
||||||
}/${emojis.length})`;
|
}/${emojis.length})`;
|
||||||
|
|
||||||
const mediaManager = new MediaManager(config);
|
const mediaManager = new MediaManager(config);
|
||||||
|
|
||||||
await mediaManager.deleteFileByUrl(emoji.url);
|
await mediaManager.deleteFileByUrl(emoji.media.getUrl());
|
||||||
|
|
||||||
await db.delete(Emojis).where(eq(Emojis.id, emoji.id));
|
await db.delete(Emojis).where(eq(Emojis.id, emoji.id));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { and, inArray, isNull } from "drizzle-orm";
|
||||||
import { lookup } from "mime-types";
|
import { lookup } from "mime-types";
|
||||||
import ora from "ora";
|
import ora from "ora";
|
||||||
import { unzip } from "unzipit";
|
import { unzip } from "unzipit";
|
||||||
import { MediaManager } from "~/classes/media/media-manager";
|
|
||||||
import { BaseCommand } from "~/cli/base";
|
import { BaseCommand } from "~/cli/base";
|
||||||
import { config } from "~/packages/config-manager";
|
import { config } from "~/packages/config-manager";
|
||||||
|
|
||||||
|
|
@ -169,8 +168,6 @@ export default class EmojiImport extends BaseCommand<typeof EmojiImport> {
|
||||||
|
|
||||||
const importSpinner = ora("Importing emojis").start();
|
const importSpinner = ora("Importing emojis").start();
|
||||||
|
|
||||||
const mediaManager = new MediaManager(config);
|
|
||||||
|
|
||||||
const successfullyImported: MetaType["emojis"] = [];
|
const successfullyImported: MetaType["emojis"] = [];
|
||||||
|
|
||||||
for (const emoji of newEmojis) {
|
for (const emoji of newEmojis) {
|
||||||
|
|
@ -197,26 +194,12 @@ export default class EmojiImport extends BaseCommand<typeof EmojiImport> {
|
||||||
type: contentType,
|
type: contentType,
|
||||||
});
|
});
|
||||||
|
|
||||||
const uploaded = await mediaManager
|
const media = await Media.fromFile(newFile);
|
||||||
.addFile(newFile)
|
|
||||||
.catch((e: Error) => {
|
|
||||||
this.log(
|
|
||||||
`${chalk.red("✗")} Error uploading ${chalk.red(
|
|
||||||
emoji.emoji.name,
|
|
||||||
)}: ${chalk.red(e.message)}`,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!uploaded) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Emoji.insert({
|
await Emoji.insert({
|
||||||
shortcode: emoji.emoji.name,
|
shortcode: emoji.emoji.name,
|
||||||
url: Media.getUrl(uploaded.path),
|
mediaId: media.id,
|
||||||
visibleInPicker: true,
|
visibleInPicker: true,
|
||||||
contentType: uploaded.uploadedFile.type,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
successfullyImported.push(emoji);
|
successfullyImported.push(emoji);
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
import type { Config } from "drizzle-kit";
|
import type { Config } from "drizzle-kit";
|
||||||
|
import { config } from "./packages/config-manager/index.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drizzle can't properly resolve imports with top-level await, so uncomment
|
||||||
|
* this line when generating migrations.
|
||||||
|
*/
|
||||||
export default {
|
export default {
|
||||||
dialect: "postgresql",
|
dialect: "postgresql",
|
||||||
out: "./drizzle/migrations",
|
out: "./drizzle/migrations",
|
||||||
schema: "./drizzle/schema.ts",
|
schema: "./drizzle/schema.ts",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
host: "localhost",
|
/* host: "localhost",
|
||||||
port: 40000,
|
port: 40000,
|
||||||
user: "lysand",
|
user: "lysand",
|
||||||
password: "lysand",
|
password: "lysand",
|
||||||
database: "lysand",
|
database: "lysand", */
|
||||||
/* host: config.database.host,
|
host: config.database.host,
|
||||||
port: Number(config.database.port),
|
port: Number(config.database.port),
|
||||||
user: config.database.username,
|
user: config.database.username,
|
||||||
password: config.database.password,
|
password: config.database.password,
|
||||||
database: config.database.database, */
|
database: config.database.database,
|
||||||
},
|
},
|
||||||
// Print all statements
|
// Print all statements
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
|
|
||||||
5
drizzle/migrations/0043_mute_jigsaw.sql
Normal file
5
drizzle/migrations/0043_mute_jigsaw.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
ALTER TABLE "Emojis" ADD COLUMN "mediaId" uuid;--> statement-breakpoint
|
||||||
|
ALTER TABLE "Emojis" ADD CONSTRAINT "Emojis_mediaId_Medias_id_fk" FOREIGN KEY ("mediaId") REFERENCES "public"."Medias"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
|
||||||
|
ALTER TABLE "Emojis" DROP COLUMN "url";--> statement-breakpoint
|
||||||
|
ALTER TABLE "Emojis" DROP COLUMN "alt";--> statement-breakpoint
|
||||||
|
ALTER TABLE "Emojis" DROP COLUMN "content_type";
|
||||||
1
drizzle/migrations/0044_quiet_jasper_sitwell.sql
Normal file
1
drizzle/migrations/0044_quiet_jasper_sitwell.sql
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE "Emojis" ALTER COLUMN "mediaId" SET NOT NULL;
|
||||||
2331
drizzle/migrations/meta/0043_snapshot.json
Normal file
2331
drizzle/migrations/meta/0043_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
2331
drizzle/migrations/meta/0044_snapshot.json
Normal file
2331
drizzle/migrations/meta/0044_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -302,6 +302,20 @@
|
||||||
"when": 1737660317024,
|
"when": 1737660317024,
|
||||||
"tag": "0042_swift_the_phantom",
|
"tag": "0042_swift_the_phantom",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 43,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1738080562679,
|
||||||
|
"tag": "0043_mute_jigsaw",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 44,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1738082427051,
|
||||||
|
"tag": "0044_quiet_jasper_sitwell",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,13 @@ export const Challenges = pgTable("Challenges", {
|
||||||
export const Emojis = pgTable("Emojis", {
|
export const Emojis = pgTable("Emojis", {
|
||||||
id: id(),
|
id: id(),
|
||||||
shortcode: text("shortcode").notNull(),
|
shortcode: text("shortcode").notNull(),
|
||||||
url: text("url").notNull(),
|
mediaId: uuid("mediaId")
|
||||||
|
.references(() => Medias.id, {
|
||||||
|
onDelete: "cascade",
|
||||||
|
onUpdate: "cascade",
|
||||||
|
})
|
||||||
|
.notNull(),
|
||||||
visibleInPicker: boolean("visible_in_picker").notNull(),
|
visibleInPicker: boolean("visible_in_picker").notNull(),
|
||||||
alt: text("alt"),
|
|
||||||
contentType: text("content_type").notNull(),
|
|
||||||
instanceId: uuid("instanceId").references(() => Instances.id, {
|
instanceId: uuid("instanceId").references(() => Instances.id, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
onUpdate: "cascade",
|
onUpdate: "cascade",
|
||||||
|
|
@ -68,6 +71,10 @@ export const Emojis = pgTable("Emojis", {
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EmojisRelations = relations(Emojis, ({ one, many }) => ({
|
export const EmojisRelations = relations(Emojis, ({ one, many }) => ({
|
||||||
|
media: one(Medias, {
|
||||||
|
fields: [Emojis.mediaId],
|
||||||
|
references: [Medias.id],
|
||||||
|
}),
|
||||||
instance: one(Instances, {
|
instance: one(Instances, {
|
||||||
fields: [Emojis.instanceId],
|
fields: [Emojis.instanceId],
|
||||||
references: [Instances.id],
|
references: [Instances.id],
|
||||||
|
|
@ -357,6 +364,7 @@ export const Medias = pgTable("Medias", {
|
||||||
|
|
||||||
export const MediasRelations = relations(Medias, ({ many }) => ({
|
export const MediasRelations = relations(Medias, ({ many }) => ({
|
||||||
notes: many(Notes),
|
notes: many(Notes),
|
||||||
|
emojis: many(Emojis),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const Notifications = pgTable("Notifications", {
|
export const Notifications = pgTable("Notifications", {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue