diff --git a/src/app/blog/[[...tag]]/PostSummary.tsx b/src/app/blog/[[...tag]]/PostSummary.tsx index 6adba70..0510b82 100644 --- a/src/app/blog/[[...tag]]/PostSummary.tsx +++ b/src/app/blog/[[...tag]]/PostSummary.tsx @@ -3,8 +3,15 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { Summary } from "../types"; +import { publishArticle, unpublishArticle } from "../action"; -export default function PostSummary({ metadata }: { metadata: Summary }) { +export default function PostSummary({ + metadata, + loggedIn, +}: { + metadata: Summary; + loggedIn: boolean; +}) { const [elementColor, setElementColor] = useState("#999"); const router = useRouter(); @@ -77,8 +84,61 @@ export default function PostSummary({ metadata }: { metadata: Summary }) { gap: 4, }} > - Published: - {metadata.publishedDate.toLocaleDateString()} + {metadata.is_draft && ( + <> + draft - +
{ + e.currentTarget.style.color = "#eee"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.color = "#999"; + }} + onClick={async () => { + await publishArticle(metadata.slug); + router.refresh(); + }} + > + move to published +
+ + )} + {!metadata.is_draft && ( + <> +
+
+ published: + {metadata.publishedDate.toLocaleDateString()} +
+ {loggedIn && ( +
{ + e.currentTarget.style.color = "#eee"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.color = "#999"; + }} + onClick={async () => { + await unpublishArticle(metadata.slug); + router.refresh(); + }} + > + move to drafts +
+ )} +
+ + )}
-) { - const { page } = await searchParams; - const result = page ? parseInt(page) : 1; - if (isNaN(result) || result < 1) { - notFound(); - } - return result; -} +import { isLoggedIn } from "@/components/auth"; export default async function Blog({ params, @@ -38,7 +30,8 @@ export default async function Blog({ if (pageNumber > numberOfPages) { notFound(); } - const tags = await getTags(); + const loggedIn = await isLoggedIn(); + const tags = await getTags(loggedIn); return ( <> @@ -53,9 +46,23 @@ export default async function Blog({ ...occasionally
- {metadata.length > 0 && - metadata.map((m) => )} + {metadata + .filter((m) => loggedIn || !m.is_draft) + .map((m) => ( + + ))} ); } + +async function getPageNumber( + searchParams: Promise<{ page: string | undefined }> +) { + const { page } = await searchParams; + const result = page ? parseInt(page) : 1; + if (isNaN(result) || result < 1) { + notFound(); + } + return result; +} diff --git a/src/app/blog/action.ts b/src/app/blog/action.ts index 8cd1043..0f7b7e8 100644 --- a/src/app/blog/action.ts +++ b/src/app/blog/action.ts @@ -4,11 +4,17 @@ import { notFound } from "next/navigation"; import { Post, Summary } from "./types"; import { PrismaClient } from "@prisma/client"; -export async function getTags(): Promise { +export async function getTags(loggedIn: boolean): Promise { const prisma = new PrismaClient(); - const tags = (await prisma.tag.findMany({ select: { name: true } })).map( - (tag) => tag.name - ); + const filter = loggedIn + ? undefined + : { posts: { some: { is_draft: false } } }; + const tags = ( + await prisma.tag.findMany({ + select: { name: true }, + where: filter, + }) + ).map((tag) => tag.name); return tags; } @@ -51,7 +57,7 @@ export async function getSummaries( ...filter, omit: { contentMarkdown: true, contentRendered: true }, include: { tags: { select: { name: true } } }, - orderBy: { publishedDate: "desc" }, + orderBy: { publishedDate: "asc" }, skip: PAGE_SIZE * (pageNumber - 1), take: PAGE_SIZE, }) @@ -60,3 +66,16 @@ export async function getSummaries( }); return { metadata: posts, numberOfPages: numberOfPages }; } + +export async function publishArticle(slug: string) { + const prisma = new PrismaClient(); + await prisma.post.update({ + where: { slug }, + data: { is_draft: false, publishedDate: new Date() }, + }); +} + +export async function unpublishArticle(slug: string) { + const prisma = new PrismaClient(); + await prisma.post.update({ where: { slug }, data: { is_draft: true } }); +} diff --git a/src/app/blog/login/action.ts b/src/app/blog/login/action.ts index 8cd0693..a37e752 100644 --- a/src/app/blog/login/action.ts +++ b/src/app/blog/login/action.ts @@ -2,7 +2,7 @@ import { PrismaClient } from "@prisma/client"; import argon2 from "argon2"; -import { setSession } from "../write/auth"; +import { setSession } from "@/components/auth"; import { redirect, RedirectType } from "next/navigation"; export async function handleLogin(data: FormData) { diff --git a/src/app/blog/login/page.tsx b/src/app/blog/login/page.tsx index 26feaab..c182512 100644 --- a/src/app/blog/login/page.tsx +++ b/src/app/blog/login/page.tsx @@ -1,7 +1,7 @@ "use server"; import { redirect } from "next/navigation"; -import { isLoggedIn } from "../write/auth"; +import { isLoggedIn } from "@/components/auth"; import FormComponent from "./Form"; export default async function Login() { diff --git a/src/app/blog/post/[slug]/PostDisplay.tsx b/src/app/blog/post/[slug]/PostDisplay.tsx index ca1bbf0..6724506 100644 --- a/src/app/blog/post/[slug]/PostDisplay.tsx +++ b/src/app/blog/post/[slug]/PostDisplay.tsx @@ -3,8 +3,15 @@ import { useRouter } from "next/navigation"; import * as Types from "../../types"; import "highlight.js/styles/github-dark.css"; +import { publishArticle, unpublishArticle } from "../../action"; -export default function PostDisplay({ post }: { post: Types.Post }) { +export default function PostDisplay({ + post, + loggedIn, +}: { + post: Types.Post; + loggedIn: boolean; +}) { const router = useRouter(); return ( <> @@ -60,8 +67,61 @@ export default function PostDisplay({ post }: { post: Types.Post }) { gap: 4, }} > - Published: - {post.publishedDate.toLocaleDateString()} + {post.is_draft && ( + <> + draft - +
{ + e.currentTarget.style.color = "#eee"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.color = "#999"; + }} + onClick={async () => { + await publishArticle(post.slug); + router.refresh(); + }} + > + move to published +
+ + )} + {!post.is_draft && ( + <> +
+
+ published: + {post.publishedDate.toLocaleDateString()} +
+ {loggedIn && ( +
{ + e.currentTarget.style.color = "#eee"; + }} + onMouseLeave={(e) => { + e.currentTarget.style.color = "#999"; + }} + onClick={async () => { + await unpublishArticle(post.slug); + router.refresh(); + }} + > + move to drafts +
+ )} +
+ + )}
- + ); } diff --git a/src/app/blog/write/[[...slug]]/page.tsx b/src/app/blog/write/[[...slug]]/page.tsx index 9158614..68a2294 100644 --- a/src/app/blog/write/[[...slug]]/page.tsx +++ b/src/app/blog/write/[[...slug]]/page.tsx @@ -2,7 +2,7 @@ import { notFound, redirect } from "next/navigation"; import { getPost } from "../../action"; import { Post } from "../../types"; import Write from "../Write"; -import { isLoggedIn } from "../auth"; +import { isLoggedIn } from "@/components/auth"; export default async function WritePage({ params, diff --git a/src/app/contact/ContactComponent.tsx b/src/app/contact/ContactComponent.tsx index 6328f14..f8dd4aa 100644 --- a/src/app/contact/ContactComponent.tsx +++ b/src/app/contact/ContactComponent.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/no-unescaped-entities */ - "use client"; import SubmitContact from "@/components/contact"; diff --git a/src/app/links/page.tsx b/src/app/links/page.tsx index 9c4e547..62994ad 100644 --- a/src/app/links/page.tsx +++ b/src/app/links/page.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/no-unescaped-entities */ - "use client"; import { SiGitea } from "@icons-pack/react-simple-icons"; diff --git a/src/app/blog/write/auth.ts b/src/components/auth.ts similarity index 91% rename from src/app/blog/write/auth.ts rename to src/components/auth.ts index 6b70c9b..3d110de 100644 --- a/src/app/blog/write/auth.ts +++ b/src/components/auth.ts @@ -28,17 +28,16 @@ export async function decrypt( } } -export async function isLoggedIn() { +export async function isLoggedIn(): Promise { const cookieStore = (await cookies()).get("session")?.value; const session = await decrypt(cookieStore); if (session != null && session.admin) { - setSession(); return true; } return false; } -export async function setSession() { +export async function setSession(): Promise { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); (await cookies()).set("session", await encrypt({ admin: true }), { httpOnly: true,