fix middleware, migrate gist importing
This commit is contained in:
parent
bc2a4acd29
commit
8e7828d562
19 changed files with 98 additions and 86 deletions
|
@ -17,7 +17,7 @@ const Description = ({ onChange, description }: props) => {
|
||||||
label="Description"
|
label="Description"
|
||||||
maxLength={256}
|
maxLength={256}
|
||||||
width="100%"
|
width="100%"
|
||||||
placeholder="A short description of your post"
|
placeholder="An optional description of your post"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import NewPost from "../../components/new"
|
import NewPost from "../../components/new"
|
||||||
import { notFound } from "next/navigation"
|
import { notFound, redirect } from "next/navigation"
|
||||||
import { getPostById } from "@lib/server/prisma"
|
import { getPostById } from "@lib/server/prisma"
|
||||||
|
import { getSession } from "@lib/server/session"
|
||||||
|
|
||||||
const NewFromExisting = async ({
|
const NewFromExisting = async ({
|
||||||
params
|
params
|
||||||
|
@ -9,6 +10,11 @@ const NewFromExisting = async ({
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
|
const session = await getSession()
|
||||||
|
if (!session?.user) {
|
||||||
|
return redirect("/signin")
|
||||||
|
}
|
||||||
|
|
||||||
const { id } = params
|
const { id } = params
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
|
|
@ -37,10 +37,9 @@ const getPost = async (id: string) => {
|
||||||
return { post, isAuthor: isAuthorOrAdmin }
|
return { post, isAuthor: isAuthorOrAdmin }
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be authed to see unlisted/private
|
|
||||||
if (
|
if (
|
||||||
(post.visibility === "unlisted" || post.visibility === "private") &&
|
(post.visibility === "private") &&
|
||||||
!user
|
!isAuthorOrAdmin
|
||||||
) {
|
) {
|
||||||
return notFound()
|
return notFound()
|
||||||
}
|
}
|
||||||
|
|
11
client/app/admin/components/admin.tsx
Normal file
11
client/app/admin/components/admin.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { getAllPosts, getAllUsers } from "@lib/server/prisma"
|
||||||
|
import styles from "./admin.module.css"
|
||||||
|
import PostTable from "./post-table"
|
||||||
|
import UserTable from "./user-table"
|
||||||
|
|
||||||
|
const Admin = async () => {
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Admin
|
|
@ -2,15 +2,15 @@
|
||||||
import SettingsGroup from "@components/settings-group"
|
import SettingsGroup from "@components/settings-group"
|
||||||
import { Fieldset, useToasts } from "@geist-ui/core/dist"
|
import { Fieldset, useToasts } from "@geist-ui/core/dist"
|
||||||
import byteToMB from "@lib/byte-to-mb"
|
import byteToMB from "@lib/byte-to-mb"
|
||||||
import { Post } from "@lib/server/prisma";
|
import { PostWithFiles } from "@lib/server/prisma";
|
||||||
import Table from "rc-table"
|
import Table from "rc-table"
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useMemo } from "react"
|
||||||
import ActionDropdown from "./action-dropdown"
|
import ActionDropdown from "./action-dropdown"
|
||||||
|
|
||||||
const PostTable = ({
|
const PostTable = ({
|
||||||
posts,
|
posts,
|
||||||
}: {
|
}: {
|
||||||
posts: Post[]
|
posts: PostWithFiles[]
|
||||||
}) => {
|
}) => {
|
||||||
const tablePosts = useMemo(
|
const tablePosts = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import type { PostVisibility } from "@lib/types"
|
|
||||||
import Badge from "../badge"
|
import Badge from "../badge"
|
||||||
|
|
||||||
type CastPostVisibility = PostVisibility | string
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
visibility: CastPostVisibility
|
visibility: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const VisibilityBadge = ({ visibility }: Props) => {
|
const VisibilityBadge = ({ visibility }: Props) => {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Button, ButtonGroup, Loading, useToasts } from "@geist-ui/core/dist"
|
import { Button, ButtonGroup, Loading, useToasts } from "@geist-ui/core/dist"
|
||||||
import type { PostVisibility } from "@lib/types"
|
|
||||||
import PasswordModal from "@components/password-modal"
|
import PasswordModal from "@components/password-modal"
|
||||||
import { useCallback, useState } from "react"
|
import { useCallback, useState } from "react"
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ const VisibilityControl = ({ postId, visibility, setVisibility }: Props) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
async (visibility: PostVisibility, password?: string) => {
|
async (visibility: string, password?: string) => {
|
||||||
if (visibility === "protected" && !password) {
|
if (visibility === "protected" && !password) {
|
||||||
setPasswordModalVisible(true)
|
setPasswordModalVisible(true)
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,7 +10,6 @@ import Parent from "@geist-ui/icons/arrowUpCircle"
|
||||||
import styles from "./list-item.module.css"
|
import styles from "./list-item.module.css"
|
||||||
import Link from "@components/link"
|
import Link from "@components/link"
|
||||||
import type { PostWithFiles } from "@lib/server/prisma"
|
import type { PostWithFiles } from "@lib/server/prisma"
|
||||||
import type { PostVisibility } from "@lib/types"
|
|
||||||
import type { File } from "@lib/server/prisma"
|
import type { File } from "@lib/server/prisma"
|
||||||
import Tooltip from "@components/tooltip"
|
import Tooltip from "@components/tooltip"
|
||||||
import Badge from "@components/badges/badge"
|
import Badge from "@components/badges/badge"
|
||||||
|
@ -76,7 +75,7 @@ const ListItem = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.badges}>
|
<div className={styles.badges}>
|
||||||
<VisibilityBadge visibility={post.visibility as PostVisibility} />
|
<VisibilityBadge visibility={post.visibility} />
|
||||||
<Badge type="secondary">
|
<Badge type="secondary">
|
||||||
{post.files?.length === 1
|
{post.files?.length === 1
|
||||||
? "1 file"
|
? "1 file"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Note, Input, Textarea, Button, useToasts } from "@geist-ui/core/dist"
|
import { Note, Input, Textarea, Button, useToasts } from "@geist-ui/core/dist"
|
||||||
import { User } from "@lib/server/prisma"
|
import { User } from "next-auth"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
const Profile = ({ user }: { user: User }) => {
|
const Profile = ({ user }: { user: User }) => {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import fetch from "node-fetch"
|
|
||||||
import { Response } from "node-fetch"
|
|
||||||
import { Gist, GistFile } from "./types"
|
import { Gist, GistFile } from "./types"
|
||||||
|
|
||||||
async function fetchHelper(response: Response): Promise<Response> {
|
async function fetchHelper(response: Response): Promise<Response> {
|
68
client/lib/gist/transform.ts
Normal file
68
client/lib/gist/transform.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { Gist } from "./types"
|
||||||
|
import * as crypto from "crypto"
|
||||||
|
import type { Post } from "@lib/server/prisma"
|
||||||
|
import { getHtmlFromFile } from "@lib/server/get-html-from-drift-file"
|
||||||
|
import { prisma } from "@lib/server/prisma"
|
||||||
|
|
||||||
|
export type AdditionalPostInformation = Pick<
|
||||||
|
Post,
|
||||||
|
"visibility" | "password" | "expiresAt"
|
||||||
|
> & {
|
||||||
|
userId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPostFromGist(
|
||||||
|
{ userId, visibility, password, expiresAt }: AdditionalPostInformation,
|
||||||
|
gist: Gist
|
||||||
|
): Promise<Post> {
|
||||||
|
const files = Object.values(gist.files)
|
||||||
|
const [title, description] = gist.description.split("\n", 1)
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
throw new Error("The gist did not have any files")
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPost = await prisma.post.create({
|
||||||
|
data: {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
visibility,
|
||||||
|
password,
|
||||||
|
expiresAt,
|
||||||
|
createdAt: new Date(gist.created_at),
|
||||||
|
author: {
|
||||||
|
connect: {
|
||||||
|
id: userId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
createMany: {
|
||||||
|
data: await Promise.all(
|
||||||
|
files.map(async (file) => {
|
||||||
|
const content = await file.content()
|
||||||
|
const html = await getHtmlFromFile({
|
||||||
|
content,
|
||||||
|
title: file.filename
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: file.filename,
|
||||||
|
content: content,
|
||||||
|
sha: crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(content)
|
||||||
|
.digest("hex")
|
||||||
|
.toString(),
|
||||||
|
// TODO: shouldn't need this cast
|
||||||
|
html: html as string,
|
||||||
|
userId: userId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return newPost
|
||||||
|
}
|
|
@ -51,7 +51,7 @@ export const authOptions: NextAuthOptions = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: dbUser.id,
|
id: dbUser.id,
|
||||||
name: dbUser.username,
|
name: dbUser.displayName,
|
||||||
email: dbUser.email,
|
email: dbUser.email,
|
||||||
picture: dbUser.image,
|
picture: dbUser.image,
|
||||||
role: dbUser.role || "user",
|
role: dbUser.role || "user",
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default withAuth(
|
||||||
)
|
)
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
match: [
|
matcher: [
|
||||||
// "/signout",
|
// "/signout",
|
||||||
// "/",
|
// "/",
|
||||||
"/signin",
|
"/signin",
|
||||||
|
|
0
client/pages/api/gist.ts
Normal file
0
client/pages/api/gist.ts
Normal file
|
@ -1,65 +0,0 @@
|
||||||
import getHtmlFromFile from "@lib/get-html-from-drift-file"
|
|
||||||
import { Post } from "@lib/models/Post"
|
|
||||||
import { File } from "@lib/models/File"
|
|
||||||
import { Gist } from "./types"
|
|
||||||
import * as crypto from "crypto"
|
|
||||||
|
|
||||||
export type AdditionalPostInformation = Pick<
|
|
||||||
Post,
|
|
||||||
"visibility" | "password" | "expiresAt"
|
|
||||||
> & {
|
|
||||||
userId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createPostFromGist(
|
|
||||||
{ userId, visibility, password, expiresAt }: AdditionalPostInformation,
|
|
||||||
gist: Gist
|
|
||||||
): Promise<Post> {
|
|
||||||
const files = Object.values(gist.files)
|
|
||||||
const [title, description] = gist.description.split("\n", 1)
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
|
||||||
throw new Error("The gist did not have any files")
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPost = new Post({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
visibility,
|
|
||||||
password,
|
|
||||||
expiresAt,
|
|
||||||
createdAt: new Date(gist.created_at)
|
|
||||||
})
|
|
||||||
|
|
||||||
await newPost.save()
|
|
||||||
await newPost.$add("users", userId)
|
|
||||||
const newFiles = await Promise.all(
|
|
||||||
files.map(async (file) => {
|
|
||||||
const content = await file.content()
|
|
||||||
const html = getHtmlFromFile({ content, title: file.filename })
|
|
||||||
const newFile = new File({
|
|
||||||
title: file.filename,
|
|
||||||
content,
|
|
||||||
sha: crypto
|
|
||||||
.createHash("sha256")
|
|
||||||
.update(content)
|
|
||||||
.digest("hex")
|
|
||||||
.toString(),
|
|
||||||
html: html || "",
|
|
||||||
userId: userId,
|
|
||||||
postId: newPost.id
|
|
||||||
})
|
|
||||||
await newFile.save()
|
|
||||||
return newFile
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
newFiles.map(async (file) => {
|
|
||||||
await newPost.$add("files", file.id)
|
|
||||||
await newPost.save()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return newPost
|
|
||||||
}
|
|
Loading…
Reference in a new issue