Switch post html/content to Bytes from Text

This commit is contained in:
Max Leiter 2022-12-17 17:15:21 -08:00
parent 34a92a265f
commit 69c482a165
7 changed files with 114 additions and 67 deletions

View file

@ -47,14 +47,13 @@ export async function createPostFromGist(
return { return {
title: file.filename, title: file.filename,
content: content, content: Buffer.from(content, "utf-8"),
sha: crypto sha: crypto
.createHash("sha256") .createHash("sha256")
.update(content) .update(content)
.digest("hex") .digest("hex")
.toString(), .toString(),
// TODO: shouldn't need this cast html: Buffer.from(html as string, "utf-8"),
html: html as string,
userId: userId userId: userId
} }
}) })

View file

@ -1,4 +1,3 @@
import type { File } from "@lib/server/prisma"
import markdown from "@wcj/markdown-to-html" import markdown from "@wcj/markdown-to-html"
/** /**
* returns rendered HTML from a Drift file * returns rendered HTML from a Drift file
@ -6,7 +5,10 @@ import markdown from "@wcj/markdown-to-html"
export async function getHtmlFromFile({ export async function getHtmlFromFile({
content, content,
title title
}: Pick<File, "content" | "title">) { }: {
content: string
title: string
}) {
const renderAsMarkdown = [ const renderAsMarkdown = [
"markdown", "markdown",
"md", "md",

View file

@ -36,12 +36,44 @@ if (config.enable_admin) {
if (process.env.NODE_ENV !== "production") global.prisma = prisma if (process.env.NODE_ENV !== "production") global.prisma = prisma
export type PostWithFiles = Post & { const postWithFiles = Prisma.validator<Prisma.PostArgs>()({
files: File[] include: {
files: true
}
})
const postWithAuthor = Prisma.validator<Prisma.PostArgs>()({
include: {
author: true
}
})
const postWithFilesAndAuthor = Prisma.validator<Prisma.PostArgs>()({
include: {
files: true,
author: true
}
})
export type ServerPostWithFiles = Prisma.PostGetPayload<typeof postWithFiles>
export type PostWithAuthor = Prisma.PostGetPayload<typeof postWithAuthor>
export type ServerPostWithFilesAndAuthor = Prisma.PostGetPayload<typeof postWithFilesAndAuthor>
export type PostWithFiles = Omit<ServerPostWithFiles, "files"> & {
files: Omit<ServerPostWithFiles["files"][number], "content" | "html"> & {
content: string
html: string
}[]
} }
export type PostWithFilesAndAuthor = PostWithFiles & { export type PostWithFilesAndAuthor = Omit<
author: User ServerPostWithFilesAndAuthor,
"files"
> & {
files: Omit<ServerPostWithFilesAndAuthor["files"][number], "content" | "html"> & {
content: string
html: string
}[]
} }
export const getFilesForPost = async (postId: string) => { export const getFilesForPost = async (postId: string) => {
@ -68,7 +100,7 @@ export async function getPostsByUser(userId: string): Promise<Post[]>
export async function getPostsByUser( export async function getPostsByUser(
userId: string, userId: string,
includeFiles: true includeFiles: true
): Promise<PostWithFiles[]> ): Promise<ServerPostWithFiles[]>
export async function getPostsByUser(userId: User["id"], withFiles?: boolean) { export async function getPostsByUser(userId: User["id"], withFiles?: boolean) {
const posts = await prisma.post.findMany({ const posts = await prisma.post.findMany({
where: { where: {
@ -148,13 +180,26 @@ export const getPostById = async (
postId: Post["id"], postId: Post["id"],
options?: GetPostByIdOptions options?: GetPostByIdOptions
): Promise<Post | PostWithFiles | PostWithFilesAndAuthor | null> => { ): Promise<Post | PostWithFiles | PostWithFilesAndAuthor | null> => {
const post = await prisma.post.findUnique({ let post = await prisma.post.findUnique({
where: { where: {
id: postId id: postId
}, },
...options ...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 return post
} }
@ -167,7 +212,7 @@ export const getAllPosts = async ({
withFiles?: boolean withFiles?: boolean
withAuthor?: boolean withAuthor?: boolean
} & Prisma.PostFindManyArgs = {}): Promise< } & Prisma.PostFindManyArgs = {}): Promise<
Post[] | PostWithFiles[] | PostWithFilesAndAuthor[] Post[] | ServerPostWithFiles[] | ServerPostWithFilesAndAuthor[]
> => { > => {
const posts = await prisma.post.findMany({ const posts = await prisma.post.findMany({
include: { include: {
@ -216,7 +261,7 @@ export const searchPosts = async (
userId?: User["id"] userId?: User["id"]
publicOnly?: boolean publicOnly?: boolean
} = {} } = {}
): Promise<PostWithFiles[]> => { ): Promise<ServerPostWithFiles[]> => {
const posts = await prisma.post.findMany({ const posts = await prisma.post.findMany({
where: { where: {
OR: [ OR: [
@ -231,9 +276,8 @@ export const searchPosts = async (
files: { files: {
some: { some: {
content: { content: {
search: query in: Buffer.from(query)
}, }
userId: userId
} }
}, },
visibility: publicOnly ? "public" : undefined visibility: publicOnly ? "public" : undefined
@ -245,5 +289,5 @@ export const searchPosts = async (
} }
}) })
return posts as PostWithFiles[] return posts as ServerPostWithFiles[]
} }

View file

@ -1,6 +1,6 @@
import { withMethods } from "@lib/api-middleware/with-methods" import { withMethods } from "@lib/api-middleware/with-methods"
import { parseQueryParam } from "@lib/server/parse-query-param" 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 type { NextApiRequest, NextApiResponse } from "next"
import { getSession } from "next-auth/react" import { getSession } from "next-auth/react"
import { prisma } from "lib/server/prisma" import { prisma } from "lib/server/prisma"
@ -16,7 +16,6 @@ export default withMethods(["GET", "PUT", "DELETE"], handler)
async function handleGet(req: NextApiRequest, res: NextApiResponse<any>) { async function handleGet(req: NextApiRequest, res: NextApiResponse<any>) {
const id = parseQueryParam(req.query.id) const id = parseQueryParam(req.query.id)
const files = req.query.files ? parseQueryParam(req.query.files) : true
if (!id) { if (!id) {
return res.status(400).json({ error: "Missing id" }) return res.status(400).json({ error: "Missing id" })
@ -44,7 +43,9 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse<any>) {
// the user can always go directly to their own post // the user can always go directly to their own post
if (session?.user.id === post.authorId) { if (session?.user.id === post.authorId) {
return res.json(post) return res.json({
...post,
})
} }
if (post.visibility === "protected") { if (post.visibility === "protected") {
@ -56,7 +57,9 @@ async function handleGet(req: NextApiRequest, res: NextApiResponse<any>) {
.toString() .toString()
if (hash === post.password) { if (hash === post.password) {
return res.json(post) return res.json({
...post,
})
} else { } else {
return { return {
isProtected: true, isProtected: true,

View file

@ -25,7 +25,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse<any>) {
return res.status(401).json({ error: "Unauthorized" }) return res.status(401).json({ error: "Unauthorized" })
} }
const files = req.body.files as File[] const files = req.body.files as Omit<File, 'content' | 'html'> & { content: string; html: string; }[]
const fileTitles = files.map((file) => file.title) const fileTitles = files.map((file) => file.title)
const missingTitles = fileTitles.filter((title) => title === "") const missingTitles = fileTitles.filter((title) => title === "")
if (missingTitles.length > 0) { if (missingTitles.length > 0) {
@ -44,7 +44,17 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse<any>) {
.digest("hex") .digest("hex")
} }
const post = await prisma.post.create({ const fileHtml = await Promise.all(
files.map(async (file) => {
return await getHtmlFromFile({
content: file.content,
title: file.title
})
})
)
const post = await prisma.post
.create({
data: { data: {
title: req.body.title, title: req.body.title,
description: req.body.description, description: req.body.description,
@ -53,42 +63,31 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse<any>) {
expiresAt: req.body.expiresAt, expiresAt: req.body.expiresAt,
parentId: req.body.parentId, parentId: req.body.parentId,
authorId: session.user.id, authorId: session.user.id,
files: {
// files: { create: files.map((file) => {
// connectOrCreate: postFiles.map((file) => ({ console.log(file)
// where: { return {
// sha: file.sha
// },
// create: file
// }))
// }
}
})
await Promise.all(
files.map(async (file) => {
const html = (await getHtmlFromFile(file)) as string
return prisma.file.create({
data: {
title: file.title, title: file.title,
content: file.content, content: Buffer.from(file.content, "utf-8"),
sha: crypto sha: crypto
.createHash("sha256") .createHash("sha256")
.update(file.content) .update(file.content)
.digest("hex") .digest("hex")
.toString(), .toString(),
html: html, html: Buffer.from(
userId: session.user.id, fileHtml[files.indexOf(file)] as string,
post: { "utf-8"
connect: { ),
id: post.id userId: session.user.id
} }
})
} }
} }
}) })
.catch((error) => {
console.log(error)
return res.status(500).json(error)
}) })
)
return res.json(post) return res.json(post)
} catch (error) { } catch (error) {

View file

@ -16,9 +16,9 @@ model SequelizeMeta {
model File { model File {
id String @id @default(cuid()) id String @id @default(cuid())
title String title String
content String content Bytes
sha String sha String
html String html Bytes
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
deletedAt DateTime? deletedAt DateTime?