diff --git a/src/app/(posts)/new/components/new.tsx b/src/app/(posts)/new/components/new.tsx index 3c092321..c795702d 100644 --- a/src/app/(posts)/new/components/new.tsx +++ b/src/app/(posts)/new/components/new.tsx @@ -151,7 +151,7 @@ const Post = ({ setSubmitting(false) return } - + await sendRequest("/api/post", { title, files: docs, diff --git a/src/lib/gist/transform.ts b/src/lib/gist/transform.ts index aba9add3..56211368 100644 --- a/src/lib/gist/transform.ts +++ b/src/lib/gist/transform.ts @@ -47,14 +47,13 @@ export async function createPostFromGist( return { title: file.filename, - content: content, + content: Buffer.from(content, "utf-8"), sha: crypto .createHash("sha256") .update(content) .digest("hex") .toString(), - // TODO: shouldn't need this cast - html: html as string, + html: Buffer.from(html as string, "utf-8"), userId: userId } }) diff --git a/src/lib/server/get-html-from-drift-file.ts b/src/lib/server/get-html-from-drift-file.ts index f3558ae3..63b9d9a6 100644 --- a/src/lib/server/get-html-from-drift-file.ts +++ b/src/lib/server/get-html-from-drift-file.ts @@ -1,4 +1,3 @@ -import type { File } from "@lib/server/prisma" import markdown from "@wcj/markdown-to-html" /** * returns rendered HTML from a Drift file @@ -6,7 +5,10 @@ import markdown from "@wcj/markdown-to-html" export async function getHtmlFromFile({ content, title -}: Pick) { +}: { + content: string + title: string +}) { const renderAsMarkdown = [ "markdown", "md", diff --git a/src/lib/server/prisma.ts b/src/lib/server/prisma.ts index beecc9a0..d05a856c 100644 --- a/src/lib/server/prisma.ts +++ b/src/lib/server/prisma.ts @@ -36,12 +36,44 @@ if (config.enable_admin) { if (process.env.NODE_ENV !== "production") global.prisma = prisma -export type PostWithFiles = Post & { - files: File[] +const postWithFiles = Prisma.validator()({ + include: { + files: true + } +}) + +const postWithAuthor = Prisma.validator()({ + include: { + author: true + } +}) + +const postWithFilesAndAuthor = Prisma.validator()({ + include: { + files: true, + author: true + } +}) + +export type ServerPostWithFiles = Prisma.PostGetPayload +export type PostWithAuthor = Prisma.PostGetPayload +export type ServerPostWithFilesAndAuthor = Prisma.PostGetPayload + +export type PostWithFiles = Omit & { + files: Omit & { + content: string + html: string + }[] } -export type PostWithFilesAndAuthor = PostWithFiles & { - author: User +export type PostWithFilesAndAuthor = Omit< + ServerPostWithFilesAndAuthor, + "files" +> & { + files: Omit & { + content: string + html: string + }[] } export const getFilesForPost = async (postId: string) => { @@ -68,7 +100,7 @@ export async function getPostsByUser(userId: string): Promise export async function getPostsByUser( userId: string, includeFiles: true -): Promise +): Promise export async function getPostsByUser(userId: User["id"], withFiles?: boolean) { const posts = await prisma.post.findMany({ where: { @@ -148,13 +180,26 @@ export const getPostById = async ( postId: Post["id"], options?: GetPostByIdOptions ): Promise => { - const post = await prisma.post.findUnique({ + let post = await prisma.post.findUnique({ where: { id: postId }, ...options }) + if (post) { + if ("files" in post) { + // @ts-expect-error TODO: fix types so files can exist + console.log(post.files) + // @ts-expect-error TODO: fix types so files can exist + post.files = post.files.map((file) => ({ + ...file, + content: file.content ? file.content.toString() : undefined, + html: file.html ? file.html.toString() : undefined + })) + } + } + return post } @@ -167,7 +212,7 @@ export const getAllPosts = async ({ withFiles?: boolean withAuthor?: boolean } & Prisma.PostFindManyArgs = {}): Promise< - Post[] | PostWithFiles[] | PostWithFilesAndAuthor[] + Post[] | ServerPostWithFiles[] | ServerPostWithFilesAndAuthor[] > => { const posts = await prisma.post.findMany({ include: { @@ -216,7 +261,7 @@ export const searchPosts = async ( userId?: User["id"] publicOnly?: boolean } = {} -): Promise => { +): Promise => { const posts = await prisma.post.findMany({ where: { OR: [ @@ -231,9 +276,8 @@ export const searchPosts = async ( files: { some: { content: { - search: query - }, - userId: userId + in: Buffer.from(query) + } } }, visibility: publicOnly ? "public" : undefined @@ -245,5 +289,5 @@ export const searchPosts = async ( } }) - return posts as PostWithFiles[] + return posts as ServerPostWithFiles[] } diff --git a/src/pages/api/post/[id].ts b/src/pages/api/post/[id].ts index 45c305c3..72a49049 100644 --- a/src/pages/api/post/[id].ts +++ b/src/pages/api/post/[id].ts @@ -1,6 +1,6 @@ import { withMethods } from "@lib/api-middleware/with-methods" import { parseQueryParam } from "@lib/server/parse-query-param" -import { getPostById } from "@lib/server/prisma" +import { getPostById, PostWithFiles } from "@lib/server/prisma" import type { NextApiRequest, NextApiResponse } from "next" import { getSession } from "next-auth/react" import { prisma } from "lib/server/prisma" @@ -16,7 +16,6 @@ export default withMethods(["GET", "PUT", "DELETE"], handler) async function handleGet(req: NextApiRequest, res: NextApiResponse) { const id = parseQueryParam(req.query.id) - const files = req.query.files ? parseQueryParam(req.query.files) : true if (!id) { return res.status(400).json({ error: "Missing id" }) @@ -44,7 +43,9 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse) { // the user can always go directly to their own post if (session?.user.id === post.authorId) { - return res.json(post) + return res.json({ + ...post, + }) } if (post.visibility === "protected") { @@ -56,7 +57,9 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse) { .toString() if (hash === post.password) { - return res.json(post) + return res.json({ + ...post, + }) } else { return { isProtected: true, diff --git a/src/pages/api/post/index.ts b/src/pages/api/post/index.ts index c75b6d52..799eab1b 100644 --- a/src/pages/api/post/index.ts +++ b/src/pages/api/post/index.ts @@ -3,7 +3,7 @@ import { withMethods } from "@lib/api-middleware/with-methods" import { authOptions } from "@lib/server/auth" -import {prisma, getPostById } from "@lib/server/prisma" +import { prisma, getPostById } from "@lib/server/prisma" import { NextApiRequest, NextApiResponse } from "next" import { unstable_getServerSession } from "next-auth/next" import { File } from "@lib/server/prisma" @@ -25,7 +25,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { return res.status(401).json({ error: "Unauthorized" }) } - const files = req.body.files as File[] + const files = req.body.files as Omit & { content: string; html: string; }[] const fileTitles = files.map((file) => file.title) const missingTitles = fileTitles.filter((title) => title === "") if (missingTitles.length > 0) { @@ -44,52 +44,51 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { .digest("hex") } - const post = await prisma.post.create({ - data: { - title: req.body.title, - description: req.body.description, - visibility: req.body.visibility, - password: hashedPassword, - expiresAt: req.body.expiresAt, - parentId: req.body.parentId, - authorId: session.user.id, - - // files: { - // connectOrCreate: postFiles.map((file) => ({ - // where: { - // sha: file.sha - // }, - // create: file - // })) - // } - } - }) - - await Promise.all( + const fileHtml = await Promise.all( files.map(async (file) => { - const html = (await getHtmlFromFile(file)) as string - - return prisma.file.create({ - data: { - title: file.title, - content: file.content, - sha: crypto - .createHash("sha256") - .update(file.content) - .digest("hex") - .toString(), - html: html, - userId: session.user.id, - post: { - connect: { - id: post.id - } - } - } + return await getHtmlFromFile({ + content: file.content, + title: file.title }) }) ) + const post = await prisma.post + .create({ + data: { + title: req.body.title, + description: req.body.description, + visibility: req.body.visibility, + password: hashedPassword, + expiresAt: req.body.expiresAt, + parentId: req.body.parentId, + authorId: session.user.id, + files: { + create: files.map((file) => { + console.log(file) + return { + title: file.title, + content: Buffer.from(file.content, "utf-8"), + sha: crypto + .createHash("sha256") + .update(file.content) + .digest("hex") + .toString(), + html: Buffer.from( + fileHtml[files.indexOf(file)] as string, + "utf-8" + ), + userId: session.user.id + } + }) + } + } + }) + .catch((error) => { + console.log(error) + return res.status(500).json(error) + }) + return res.json(post) } catch (error) { return res.status(500).json(error) diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma index 16a684bd..a6095637 100644 --- a/src/prisma/schema.prisma +++ b/src/prisma/schema.prisma @@ -16,9 +16,9 @@ model SequelizeMeta { model File { id String @id @default(cuid()) title String - content String + content Bytes sha String - html String + html Bytes createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime?