Switch post html/content to Bytes from Text
This commit is contained in:
parent
34a92a265f
commit
69c482a165
7 changed files with 114 additions and 67 deletions
|
@ -151,7 +151,7 @@ const Post = ({
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendRequest("/api/post", {
|
await sendRequest("/api/post", {
|
||||||
title,
|
title,
|
||||||
files: docs,
|
files: docs,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { withMethods } from "@lib/api-middleware/with-methods"
|
import { withMethods } from "@lib/api-middleware/with-methods"
|
||||||
|
|
||||||
import { authOptions } from "@lib/server/auth"
|
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 { NextApiRequest, NextApiResponse } from "next"
|
||||||
import { unstable_getServerSession } from "next-auth/next"
|
import { unstable_getServerSession } from "next-auth/next"
|
||||||
import { File } from "@lib/server/prisma"
|
import { File } from "@lib/server/prisma"
|
||||||
|
@ -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,52 +44,51 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
.digest("hex")
|
.digest("hex")
|
||||||
}
|
}
|
||||||
|
|
||||||
const post = await prisma.post.create({
|
const fileHtml = await Promise.all(
|
||||||
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(
|
|
||||||
files.map(async (file) => {
|
files.map(async (file) => {
|
||||||
const html = (await getHtmlFromFile(file)) as string
|
return await getHtmlFromFile({
|
||||||
|
content: file.content,
|
||||||
return prisma.file.create({
|
title: file.title
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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)
|
return res.json(post)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(500).json(error)
|
return res.status(500).json(error)
|
||||||
|
|
|
@ -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?
|
||||||
|
|
Loading…
Reference in a new issue