From 580958a18165971045a566678bd2c0c57e4a29d3 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 21 Nov 2023 14:56:58 -1000 Subject: [PATCH] Begin work on media attachments --- bun.lockb | Bin 334328 -> 334656 bytes database/entities/Attachment.ts | 47 ++++++++++++++ index.ts | 2 +- package.json | 1 + prisma/schema.prisma | 19 ++++++ server/api/api/v2/media/index.ts | 103 +++++++++++++++++++++++++++++++ types/lysand/Object.ts | 14 ++++- 7 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 database/entities/Attachment.ts create mode 100644 server/api/api/v2/media/index.ts diff --git a/bun.lockb b/bun.lockb index ec44b8dbc0c17ca2a0178fec3c07b8cb048feb25..a499d768115a4b89165a9088df34bf0b90a27e8d 100755 GIT binary patch delta 51198 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 aX8p5jCbH4FwrTv31N>xqQK+ulSN{R0*b}J$ delta 51504 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 Xbaqbx{^sc0(C87WUUaZan{EFBHx7Ad diff --git a/database/entities/Attachment.ts b/database/entities/Attachment.ts new file mode 100644 index 00000000..6d0ce1a2 --- /dev/null +++ b/database/entities/Attachment.ts @@ -0,0 +1,47 @@ +import { ConfigType } from "@config"; +import { Attachment } from "@prisma/client"; +import { APIAsyncAttachment } from "~types/entities/async_attachment"; +import { APIAttachment } from "~types/entities/attachment"; + +export const attachmentToAPI = ( + attachment: Attachment +): APIAsyncAttachment | APIAttachment => { + let type = "unknown"; + + if (attachment.mime_type.startsWith("image/")) { + type = "image"; + } else if (attachment.mime_type.startsWith("video/")) { + type = "video"; + } else if (attachment.mime_type.startsWith("audio/")) { + type = "audio"; + } + + return { + id: attachment.id, + type: type as any, + url: attachment.url, + remote_url: attachment.remote_url, + preview_url: attachment.thumbnail_url, + text_url: null, + meta: { + width: attachment.width || undefined, + height: attachment.height || undefined, + fps: attachment.fps || undefined, + size: attachment.size?.toString() || undefined, + duration: attachment.duration || undefined, + length: attachment.size?.toString() || undefined, + // Idk whether size or length is the right value + }, + description: attachment.description, + blurhash: attachment.blurhash, + }; +}; + +export const getUrl = (hash: string, config: ConfigType) => { + if (config.media.backend === "local") { + return `${config.http.base_url}/media/${hash}`; + } else if (config.media.backend === "s3") { + return `${config.s3.public_url}/${hash}`; + } + return ""; +}; diff --git a/index.ts b/index.ts index cc8bcbb3..30ee83d2 100644 --- a/index.ts +++ b/index.ts @@ -160,7 +160,7 @@ const logRequest = async (req: Request) => { }; // Remove previous console.log -console.clear(); +// console.clear(); console.log( `${chalk.green(`✓`)} ${chalk.bold( diff --git a/package.json b/package.json index 5b03fbf6..3ad29519 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.429.0", "@prisma/client": "^5.6.0", + "blurhash": "^2.0.5", "chalk": "^5.3.0", "html-to-text": "^9.0.5", "ip-matching": "^2.1.2", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index dcf058ff..bd29f93f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -121,6 +121,7 @@ model Status { replies Status[] @relation("StatusToStatusReply") quotes Status[] @relation("StatusToStatusQuote") pinnedBy User[] @relation("UserPinnedNotes") + attachments Attachment[] } model Token { @@ -136,6 +137,24 @@ model Token { applicationId String? @db.Uuid } +model Attachment { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + url String + remote_url String? + thumbnail_url String? + mime_type String + description String? + blurhash String? + sha256 String? + fps Int? + duration Int? + width Int? + height Int? + size Int? + status Status? @relation(fields: [statusId], references: [id], onDelete: Cascade) + statusId String? @db.Uuid +} + model User { id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid uri String @unique diff --git a/server/api/api/v2/media/index.ts b/server/api/api/v2/media/index.ts new file mode 100644 index 00000000..6cc7ccda --- /dev/null +++ b/server/api/api/v2/media/index.ts @@ -0,0 +1,103 @@ +import { applyConfig } from "@api"; +import { errorResponse, jsonResponse } from "@response"; +import { client } from "~database/datasource"; +import { encode } from "blurhash"; +import { getFromRequest } from "~database/entities/User"; +import { APIRouteMeta } from "~types/api"; +import sharp from "sharp"; +import { uploadFile } from "~classes/media"; +import { getConfig } from "@config"; +import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; + +export const meta: APIRouteMeta = applyConfig({ + allowedMethods: ["POST"], + ratelimits: { + max: 10, + duration: 60, + }, + route: "/api/v2/media", + auth: { + required: true, + }, +}); + +/** + * Fetch a user + */ +export default async (req: Request): Promise => { + const { user } = await getFromRequest(req); + + if (!user) { + return errorResponse("Unauthorized", 401); + } + + const form = await req.formData(); + + const file = form.get("file") as unknown as File | undefined; + const thumbnail = form.get("thumbnail"); + const description = form.get("description"); + + // Floating point numbers from -1.0 to 1.0, comma delimited + // const focus = form.get("focus"); + + if (!file) { + return errorResponse("No file provided", 400); + } + + const sha256 = new Bun.SHA256(); + + const isImage = file.type.startsWith("image/"); + + const metadata = isImage + ? await sharp(await file.arrayBuffer()).metadata() + : null; + + const blurhash = isImage + ? encode( + new Uint8ClampedArray(await file.arrayBuffer()), + metadata?.width ?? 0, + metadata?.height ?? 0, + 4, + 4 + ) + : null; + + let url = ""; + + const config = getConfig(); + + if (isImage) { + const hash = await uploadFile(file, config); + + url = hash ? getUrl(hash, config) : ""; + } + + let thumbnailUrl = ""; + + if (thumbnail) { + const hash = await uploadFile(thumbnail as unknown as File, config); + + thumbnailUrl = hash ? getUrl(hash, config) : ""; + } + + const newAttachment = await client.attachment.create({ + data: { + url, + thumbnail_url: thumbnailUrl, + sha256: sha256.update(await file.arrayBuffer()).digest("hex"), + mime_type: file.type, + description: (description as string | undefined) ?? "", + size: file.size, + blurhash: blurhash ?? undefined, + width: metadata?.width ?? undefined, + height: metadata?.height ?? undefined, + }, + }); + + // Add job to process videos and other media + + return jsonResponse({ + ...attachmentToAPI(newAttachment), + url: undefined, + }); +}; diff --git a/types/lysand/Object.ts b/types/lysand/Object.ts index 697329e2..4efe731b 100644 --- a/types/lysand/Object.ts +++ b/types/lysand/Object.ts @@ -161,5 +161,17 @@ export interface ContentFormat { content: string; content_type: string; description?: string; - size?: string; + size?: number; + hash?: { + md5?: string; + sha1?: string; + sha256?: string; + sha512?: string; + [key: string]: string | undefined; + }; + blurhash?: string; + fps?: number; + width?: number; + height?: number; + duration?: number; }