gen prisma migration, fix up rendering html (somewhat) in RSC

This commit is contained in:
Max Leiter 2022-11-12 01:57:30 -08:00
parent 86b9172527
commit 096cf41eee
17 changed files with 218 additions and 137 deletions

View file

@ -1,5 +1,3 @@
import { TOKEN_COOKIE_NAME } from "@lib/constants"
import { getCookie } from "cookies-next"
import { memo, useEffect, useState } from "react"
import styles from "./preview.module.css"
@ -30,7 +28,7 @@ const MarkdownPreview = ({ height = 500, fileId, content, title }: Props) => {
content
})
const resp = await fetch(`/api/files/get-html?${urlQuery}`, {
const resp = await fetch(`/api/file/get-html?${urlQuery}`, {
method: "GET"
})

View file

@ -12,7 +12,7 @@ import FormattingIcons from "./formatting-icons"
import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor"
import { Button, Input, Spacer, Tabs, Textarea } from "@geist-ui/core/dist"
import Preview from "./preview"
import Preview from "../../../../components/preview"
// import Link from "next/link"
type Props = {

View file

@ -14,7 +14,7 @@ import {
Tooltip,
Tag
} from "@geist-ui/core/dist"
import HtmlPreview from "app/(posts)/new/components/edit-document-list/edit-document/preview"
import HtmlPreview from "app/(posts)/components/preview"
import FadeIn from "@components/fade-in"
// import Link from "next/link"

View file

@ -4,7 +4,7 @@ import { Spacer, Tabs, Card, Textarea, Text } from "@geist-ui/core/dist"
import Image from "next/image"
import styles from "./home.module.css"
// TODO:components/new-post/ move these styles
import markdownStyles from "app/(posts)/new/components/edit-document-list/edit-document/preview/preview.module.css";
import markdownStyles from "app/(posts)/components/preview/preview.module.css";
const Home = ({
introTitle,
introContent,

View file

@ -9,7 +9,7 @@ export default async function Mine() {
const userId = (await getCurrentUser())?.id
if (!userId) {
redirect(authOptions.pages?.signIn || "/new")
return redirect(authOptions.pages?.signIn || "/new")
}
const posts = await getPostsByUser(userId, true)

View file

View file

@ -1,8 +1,6 @@
import { marked } from "marked"
// import Highlight, { defaultProps, Language } from "prism-react-renderer"
import { renderToStaticMarkup } from "react-dom/server"
import Highlight, { defaultProps, Language } from "prism-react-renderer"
import Image from "next/image"
import Link from "next/link"
// // image sizes. DDoS Safe?
// const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
@ -14,7 +12,7 @@ import Link from "next/link"
// Lexer.rules.inline.breaks.link = imageSizeLink;
//@ts-ignore
// delete defaultProps.theme
delete defaultProps.theme
// import linkStyles from '../components/link/link.module.css'
const renderer = new marked.Renderer()
@ -33,20 +31,18 @@ const renderer = new marked.Renderer()
// )
// }
// renderer.link = (href, _, text) => {
// const isHrefLocal = href?.startsWith('/') || href?.startsWith('#')
// if (isHrefLocal) {
// return renderToStaticMarkup(
// <a href={href || ''}>
// {text}
// </a>
// )
// }
renderer.link = (href, _, text) => {
const isHrefLocal = href?.startsWith('/') || href?.startsWith('#')
if (isHrefLocal) {
return <a href={href || ''}>
{text}
</a>
}
// // dirty hack
// // if text contains elements, render as html
// return <a href={href || ""} target="_blank" rel="noopener noreferrer" dangerouslySetInnerHTML={{ __html: convertHtmlEntities(text) }} ></a>
// }
// dirty hack
// if text contains elements, render as html
return <a href={href || ""} target="_blank" rel="noopener noreferrer" dangerouslySetInnerHTML={{ __html: convertHtmlEntities(text) }} ></a>
}
// @ts-ignore
renderer.image = function (href, _, text) {
@ -71,21 +67,20 @@ renderer.listitem = (text, task, checked) => {
return <li>{text}</li>
}
//@ts-ignore
renderer.code = (code: string, language: string) => {
return (
<pre>
{/* {title && <code>{title} </code>} */}
{/* {language && title && <code style={{}}> {language} </code>} */}
<Code
language={language}
// title={title}
code={code}
// highlight={highlight}
/>
</pre>
)
}
// //@ts-ignore
// renderer.code = (code: string, language: string) => {
// return (<pre>
// {/* {title && <code>{title} </code>} */}
// {/* {language && title && <code style={{}}> {language} </code>} */}
// <Code
// language={language}
// // title={title}
// code={code}
// // highlight={highlight}
// />
// </pre>
// )
// }
marked.setOptions({
gfm: true,
@ -136,7 +131,7 @@ const Code = ({
// https://mdxjs.com/guides/syntax-harkedighlighting#all-together
return (
<>
{/* <Highlight
<Highlight
{...defaultProps}
code={code.trim()}
language={language as Language}
@ -177,7 +172,7 @@ const Code = ({
))}
</code>
)}
</Highlight> */}
</Highlight>
<>
<code {...props} dangerouslySetInnerHTML={{ __html: code }} />
</>

View file

@ -24,10 +24,6 @@ const nextConfig = {
},
async rewrites() {
return [
{
source: "/server-api/:path*",
destination: `${process.env.API_URL}/:path*`
},
{
source: "/file/raw/:id",
destination: `/api/raw/:id`

View file

@ -4,44 +4,44 @@ import { parseQueryParam } from "@lib/server/parse-query-param"
import prisma from "@lib/server/prisma"
import { NextApiRequest, NextApiResponse } from "next"
export default withMethods(["GET"], (
req: NextApiRequest,
res: NextApiResponse
) => {
const query = req.query
const fileId = parseQueryParam(query.fileId)
const content = parseQueryParam(query.content)
const title = parseQueryParam(query.title)
export default withMethods(
["GET"],
async (req: NextApiRequest, res: NextApiResponse) => {
const query = req.query
const fileId = parseQueryParam(query.fileId)
const content = parseQueryParam(query.content)
const title = parseQueryParam(query.title)
if (fileId && (content || title)) {
return res.status(400).json({ error: "Too many arguments" })
}
if (fileId && (content || title)) {
return res.status(400).json({ error: "Too many arguments" })
}
if (fileId) {
const file = await prisma.file.findUnique({
where: {
id: fileId
if (fileId) {
const file = await prisma.file.findUnique({
where: {
id: fileId
}
})
if (!file) {
return res.status(404).json({ error: "File not found" })
}
})
if (!file) {
return res.status(404).json({ error: "File not found" })
return res.json(file.html)
} else {
if (!content || !title) {
return res.status(400).json({ error: "Missing arguments" })
}
const renderedHTML = getHtmlFromFile({
title,
content
})
res.setHeader("Content-Type", "text/plain")
res.status(200).write(renderedHTML)
res.end()
return
}
return res.json(file.html)
} else {
if (!content || !title) {
return res.status(400).json({ error: "Missing arguments" })
}
const renderedHTML = getHtmlFromFile({
title,
content
})
res.setHeader("Content-Type", "text/plain")
res.status(200).write(renderedHTML)
res.end()
return
}
}
)

View file

@ -9,6 +9,8 @@ const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => {
}
})
console.log("file", file, "id", req.query.id)
if (!file) {
return res.status(404).end()
}

View file

@ -0,0 +1,33 @@
import { NextApiRequest, NextApiResponse } from "next"
import prisma from "lib/server/prisma"
import { parseQueryParam } from "@lib/server/parse-query-param"
const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => {
const { id, download } = req.query
const file = await prisma.file.findUnique({
where: {
id: parseQueryParam(id)
}
})
if (!file) {
return res.status(404).json({ error: "File not found" })
}
res.setHeader("Content-Type", "text/plain; charset=utf-8")
res.setHeader("Cache-Control", "s-maxage=86400")
const { title, content } = file
if (download) {
res.setHeader("Content-Disposition", `attachment; filename="${title}"`)
} else {
res.setHeader("Content-Disposition", `inline; filename="${title}"`)
}
res.status(200).write(content, "utf-8")
res.end()
}
export default getRawFile

View file

@ -1,14 +1,8 @@
// nextjs typescript api handler
import { withCurrentUser } from "@lib/api-middleware/with-current-user"
import { withMethods } from "@lib/api-middleware/with-methods"
import {
NextApiHandlerWithParsedBody,
withValidation
} from "@lib/api-middleware/with-validation"
import { authOptions } from "@lib/server/auth"
import { CreatePostSchema } from "@lib/validations/post"
import { Post } from "@prisma/client"
import prisma, { getPostById } from "@lib/server/prisma"
import { NextApiRequest, NextApiResponse } from "next"
import { unstable_getServerSession } from "next-auth/next"

View file

@ -1,34 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next"
const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => {
const { id, download } = req.query
const file = await fetch(`${process.env.API_URL}/files/raw/${id}`, {
headers: {
Accept: "text/plain",
"x-secret-key": process.env.SECRET_KEY || "",
Authorization: `Bearer ${req.cookies["drift-token"]}`
}
})
res.setHeader("Content-Type", "text/plain; charset=utf-8")
res.setHeader("Cache-Control", "s-maxage=86400")
if (file.ok) {
const json = await file.json()
const data = json
const { title, content } = data
// serve the file raw as plain text
if (download) {
res.setHeader("Content-Disposition", `attachment; filename="${title}"`)
} else {
res.setHeader("Content-Disposition", `inline; filename="${title}"`)
}
res.status(200).write(content, "utf-8")
res.end()
} else {
res.status(404).send("File not found")
}
}
export default getRawFile

View file

@ -0,0 +1,109 @@
-- CreateTable
CREATE TABLE "SequelizeMeta" (
"name" TEXT NOT NULL,
CONSTRAINT "SequelizeMeta_pkey" PRIMARY KEY ("name")
);
-- CreateTable
CREATE TABLE "files" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"sha" TEXT NOT NULL,
"html" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
"userId" TEXT NOT NULL,
"postId" TEXT NOT NULL,
CONSTRAINT "files_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "posts" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"visibility" TEXT NOT NULL,
"password" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
"expiresAt" TIMESTAMP(3),
"parentId" TEXT,
"description" TEXT,
"authorId" TEXT NOT NULL,
CONSTRAINT "posts_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "accounts" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"refresh_token" TEXT,
"access_token" TEXT,
"expires_at" INTEGER,
"token_type" TEXT,
"scope" TEXT,
"id_token" TEXT,
"session_state" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"refresh_token_expires_in" INTEGER,
CONSTRAINT "accounts_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Session" (
"id" TEXT NOT NULL,
"sessionToken" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL,
"name" TEXT,
"email" TEXT,
"emailVerified" TIMESTAMP(3),
"image" TEXT,
"username" TEXT,
"role" TEXT DEFAULT 'user',
"password" TEXT,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "verification_tokens" (
"identifier" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "accounts_provider_providerAccountId_key" ON "accounts"("provider", "providerAccountId");
-- CreateIndex
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
-- CreateIndex
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
-- CreateIndex
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
-- CreateIndex
CREATE UNIQUE INDEX "verification_tokens_token_key" ON "verification_tokens"("token");
-- CreateIndex
CREATE UNIQUE INDEX "verification_tokens_identifier_token_key" ON "verification_tokens"("identifier", "token");

View file

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View file

@ -9,21 +9,6 @@ datasource db {
referentialIntegrity = "prisma"
}
model AuthTokens {
id String @default(cuid())
token String
expiredReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
userId String
// TODO: verify this isn't necessary / is replaced by an implicit m-n relation
// users DriftUser[] @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([id, token])
// make id and token keys
@@unique([id, token])
}
model SequelizeMeta {
name String @id
@ -33,7 +18,7 @@ model File {
id String @id @default(cuid())
title String
content String
sha String @unique
sha String
html String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt