diff --git a/pages/paste/[id].js b/pages/paste/[id].js new file mode 100644 index 0000000..ca97051 --- /dev/null +++ b/pages/paste/[id].js @@ -0,0 +1,194 @@ +import db from '../../lib/db'; +import Editor from "@monaco-editor/react"; +import ReactMarkdown from 'react-markdown'; +import rehypeHighlight from 'rehype-highlight'; +import 'highlight.js/styles/github-dark.css'; +import { useState, useEffect } from 'react'; +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import { SITE_CONFIG } from '../../config'; + +export async function getServerSideProps({ params }) { + const paste = db.prepare('SELECT id, content, language, filename, created_at, expires_at, expiry_type, view_count, allow_discussions FROM pastes WHERE id = ?').get(params.id); + + if (!paste) return { notFound: true }; + + if (paste.expires_at && new Date(paste.expires_at) < new Date()) { + db.prepare('DELETE FROM pastes WHERE id = ?').run(params.id); + return { notFound: true }; + } + + let showBurnWarning = false; + if (paste.expiry_type === 'burn') { + if (paste.view_count === 0) { + db.prepare('UPDATE pastes SET view_count = 1 WHERE id = ?').run(params.id); + } else { + showBurnWarning = true; + db.prepare('DELETE FROM pastes WHERE id = ?').run(params.id); + } + } + + const comments = db.prepare('SELECT * FROM comments WHERE paste_id = ? ORDER BY created_at DESC').all(params.id); + + return { + props: { + paste: JSON.parse(JSON.stringify(paste)), + initialComments: JSON.parse(JSON.stringify(comments)), + showBurnWarning + } + }; +} + +export default function ViewPaste({ paste, initialComments, showBurnWarning }) { + const [author, setAuthor] = useState(''); + const [comment, setComment] = useState(''); + const [comments, setComments] = useState(initialComments); + const [copied, setCopied] = useState(false); + const [mounted, setMounted] = useState(false); + const [canDelete, setCanDelete] = useState(false); + + const router = useRouter(); + + useEffect(() => { + setMounted(true); + const token = localStorage.getItem(`delete_token_${paste.id}`); + if (token) setCanDelete(true); + }, [paste.id]); + + const handleDelete = async () => { + if (!confirm("Delete this paste permanently?")) return; + const token = localStorage.getItem(`delete_token_${paste.id}`); + const res = await fetch(`/api/pastes?id=${paste.id}&token=${token}`, { method: 'DELETE' }); + + if (res.ok) { + localStorage.removeItem(`delete_token_${paste.id}`); + router.push('/'); + } + }; + + const copyToClipboard = () => { + const url = window.location.href; + navigator.clipboard.writeText(url); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + const addComment = async (e) => { + e.preventDefault(); + await fetch('/api/pastes', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ type: 'comment', pasteId: paste.id, author, commentContent: comment }) + }); + setComments([{ author, content: comment, created_at: new Date().toISOString() }, ...comments]); + setAuthor(''); + setComment(''); + }; + + if (!mounted) return
; + + return ( +
+ + {`${paste.filename} // ${SITE_CONFIG.title}`} + + +
+ {showBurnWarning && ( +
+ 🔥 +
+

Final Viewing

+

This paste has been deleted from the server.

+
+
+ )} + +
+
+

{paste.filename}

+

+ {paste.language} • {new Date(paste.created_at).toLocaleDateString()} +

+
+
+ {canDelete && ( + + )} + + Raw + + +
+
+ +
+ +
+ + {paste.allow_discussions === 1 && ( +
+

Discussion

+ + {!showBurnWarning && ( +
+
+ setAuthor(e.target.value)} + required + /> +