diff --git a/app/[id]/page.tsx b/app/[id]/page.tsx
index 6298ec4..948cea3 100644
--- a/app/[id]/page.tsx
+++ b/app/[id]/page.tsx
@@ -1,114 +1,40 @@
'use client';
-import {
- CalendarDays,
- Copy,
- Download,
- Eye,
- FileText,
- Plus,
-} from 'lucide-react';
import { useRouter } from 'next/navigation';
-import { useEffect, useRef, useState } from 'react';
-
import Prism from 'prismjs';
+import { use, useEffect, useState } from 'react';
+
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-python';
import 'prismjs/components/prism-typescript';
-import 'prismjs/themes/prism-tomorrow.css';
-
import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
+import 'prismjs/themes/prism-tomorrow.css';
-/** ---------- Loader animé (spinner + skeletons) ---------- */
-function LoadingPaste() {
- return (
-
- {/* Zone code (skeleton lignes) */}
-
-
-
-
-
Chargement du paste…
-
-
- {Array.from({ length: 28 }).map((_, i) => (
-
- ))}
-
-
-
+import AuthSection from "@/components/paste-form/AuthSection";
+import CodeViewer from '@/components/paste/CodeViewer';
+import LoadingPaste from '@/components/paste/LoadingPaste';
+import PasswordForm from '@/components/paste/PasswordForm';
+import PasteSidebar from '@/components/paste/PasteSidebar';
- {/* Sidebar (skeleton cartes + boutons) */}
-
-
+export default function PastePage({
+ params,
+}: {
+ params: Promise<{ id: string }>;
+}) {
+ const { id } = use(params);
+ const router = useRouter();
-
-
-
-
-
- {Array.from({ length: 4 }).map((_, i) => (
-
- ))}
-
-
-
-
-
- );
-}
-/** -------------------------------------------------------- */
-
-export default function PastePage({ params }: { params: { id: string } }) {
const [paste, setPaste] = useState(null);
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const [language, setLanguage] = useState('language-javascript');
- const [copied, setCopied] = useState(false);
-
- const codeRef = useRef(null);
- const router = useRouter();
const fetchPaste = async (pwd?: string) => {
setLoading(true);
- const res = await fetch(`/api/paste/${params.id}`, {
+ const res = await fetch(`/api/paste/${id}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: pwd || '' }),
@@ -144,7 +70,7 @@ export default function PastePage({ params }: { params: { id: string } }) {
useEffect(() => {
fetchPaste();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [id]);
useEffect(() => {
if (paste?.content) {
@@ -152,179 +78,18 @@ export default function PastePage({ params }: { params: { id: string } }) {
}
}, [paste]);
- // 👉 Remplace l'ancien message "Loading..." par le loader animé
if (loading) return ;
if (error || (paste?.protected && !paste?.content)) {
- return (
-
- );
+ return fetchPaste(pwd)} />;
}
return (
-
-
-
- {paste.content}
-
-
-
-
-
-
-
-
- AltBin
-
-
router.push('/')}
- className="
- cursor-pointer inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs font-semibold
- bg-green-600 text-white shadow-sm
- hover:bg-green-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-green-400/60
- "
- >
-
- CREATE
-
-
- {paste.title && (
-
- {paste.title}
-
- )}
-
-
-
-
-
-
-
Views
-
{paste.views}
-
-
-
-
-
-
Created
-
- {new Date(paste.createdAt).toLocaleDateString()}
-
-
-
-
-
-
-
-
-
{
- await navigator.clipboard.writeText(paste.content);
- setCopied(true);
- setTimeout(() => setCopied(false), 1200);
- }}
- className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
- title="Copy"
- >
-
-
-
-
window.open(`/api/${params.id}?raw=1`, '_blank')}
- className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
- title="Raw"
- >
-
-
-
-
{
- const blob = new Blob([paste.content], { type: 'text/plain' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = `${params.id}.txt`;
- a.click();
- URL.revokeObjectURL(url);
- }}
- className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
- title="Download"
- >
-
-
-
-
router.push('/')}
- className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
- title="New paste"
- >
-
-
-
-
-
- Copied!
-
-
+
+
+
);
+
}
\ No newline at end of file
diff --git a/app/api/paste/[id]/route.ts b/app/api/paste/[id]/route.ts
index 2c46997..23bf75f 100644
--- a/app/api/paste/[id]/route.ts
+++ b/app/api/paste/[id]/route.ts
@@ -5,12 +5,51 @@ import { NextRequest, NextResponse } from 'next/server';
export const dynamic = 'force-dynamic';
export const revalidate = 0;
+// --- GET pour raw ou JSON ---
+export async function GET(
+ request: NextRequest,
+ context: { params: Promise<{ id: string }> }
+) {
+ const { id: pasteId } = await context.params; // ✅ await obligatoire
+
+ if (!pasteId) {
+ return NextResponse.json({ error: 'Invalid ID' }, { status: 400 });
+ }
+
+ const paste = await prisma.paste.findUnique({ where: { id: pasteId } });
+ if (!paste) {
+ return NextResponse.json({ error: 'Not found' }, { status: 404 });
+ }
+
+ // Si paste protégé, on bloque le GET
+ if (paste.password) {
+ return NextResponse.json({ error: 'This paste is protected' }, { status: 403 });
+ }
+
+ const searchParams = request.nextUrl.searchParams;
+ if (searchParams.get('raw') === '1') {
+ return new NextResponse(paste.content, {
+ status: 200,
+ headers: { 'Content-Type': 'text/plain; charset=utf-8' },
+ });
+ }
+
+ return NextResponse.json({
+ id: paste.id,
+ title: paste.title,
+ content: paste.content,
+ createdAt: paste.createdAt,
+ views: paste.views,
+ protected: !!paste.password,
+ });
+}
+
+// --- POST pour récupérer en validant le password ---
export async function POST(
request: NextRequest,
- context: { params: { id: string } }
+ context: { params: Promise<{ id: string }> }
) {
- const { params } = context;
- const pasteId = params.id;
+ const { id: pasteId } = await context.params; // ✅ ici aussi
const { password = '' } = await request.json();
@@ -65,4 +104,4 @@ export async function POST(
return NextResponse.json(payload, {
headers: { 'Cache-Control': 'no-store' },
});
-}
+}
\ No newline at end of file
diff --git a/app/raw/[id]/route.ts b/app/raw/[id]/route.ts
new file mode 100644
index 0000000..07e696a
--- /dev/null
+++ b/app/raw/[id]/route.ts
@@ -0,0 +1,25 @@
+import { prisma } from "@/lib/prisma";
+import { NextResponse } from "next/server";
+
+export async function GET(
+ request: Request,
+ { params }: { params: { id: string } }
+) {
+ const { id } = params;
+
+ if (!id) {
+ return new NextResponse("Invalid ID", { status: 400 });
+ }
+
+ const paste = await prisma.paste.findUnique({ where: { id } });
+ if (!paste) {
+ return new NextResponse("Paste not found", { status: 404 });
+ }
+
+ return new NextResponse(paste.content, {
+ headers: {
+ "Content-Type": "text/plain; charset=utf-8",
+ "Cache-Control": "no-store",
+ },
+ });
+}
\ No newline at end of file
diff --git a/app/raw/page.tsx b/app/raw/page.tsx
new file mode 100644
index 0000000..75eab21
--- /dev/null
+++ b/app/raw/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from "next/navigation";
+
+export default function RawIndexPage() {
+ redirect("/");
+}
\ No newline at end of file
diff --git a/components/paste/CodeViewer.tsx b/components/paste/CodeViewer.tsx
new file mode 100644
index 0000000..561e07a
--- /dev/null
+++ b/components/paste/CodeViewer.tsx
@@ -0,0 +1,38 @@
+'use client';
+
+import Prism from 'prismjs';
+import 'prismjs/components/prism-bash';
+import 'prismjs/components/prism-javascript';
+import 'prismjs/components/prism-json';
+import 'prismjs/components/prism-python';
+import 'prismjs/components/prism-typescript';
+import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
+import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
+import 'prismjs/themes/prism-tomorrow.css';
+import { useEffect, useRef } from 'react';
+
+export default function CodeViewer({
+ paste,
+ language,
+}: {
+ paste: any;
+ language: string;
+}) {
+ const codeRef = useRef(null);
+
+ useEffect(() => {
+ if (paste?.content) {
+ Prism.highlightAll();
+ }
+ }, [paste, language]);
+
+ return (
+
+
+
+ {paste.content}
+
+
+
+ );
+}
diff --git a/components/paste/LoadingPaste.tsx b/components/paste/LoadingPaste.tsx
new file mode 100644
index 0000000..8c6ca94
--- /dev/null
+++ b/components/paste/LoadingPaste.tsx
@@ -0,0 +1,62 @@
+export default function LoadingPaste() {
+ return (
+
+
+
+
+
+ {Array.from({ length: 28 }).map((_, i) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ {[...Array(2)].map((_, i) => (
+
+ ))}
+
+
+
+
+
+ {[...Array(4)].map((_, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/paste/PasswordForm.tsx b/components/paste/PasswordForm.tsx
new file mode 100644
index 0000000..13c1bde
--- /dev/null
+++ b/components/paste/PasswordForm.tsx
@@ -0,0 +1,39 @@
+export default function PasswordForm({
+ onSubmit,
+ error,
+}: {
+ onSubmit: (pwd: string) => void;
+ error?: string;
+}) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/components/paste/PasteSidebar.tsx b/components/paste/PasteSidebar.tsx
new file mode 100644
index 0000000..214f933
--- /dev/null
+++ b/components/paste/PasteSidebar.tsx
@@ -0,0 +1,107 @@
+'use client';
+
+import { CalendarDays, Copy, Download, Eye, FileText, Plus } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+
+export default function PasteSidebar({ paste, id }: { paste: any; id: string }) {
+ const [copied, setCopied] = useState(false);
+ const router = useRouter();
+
+ return (
+
+
+
+
+ AltBin
+
+
router.push("/")}
+ className="cursor-pointer inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs font-semibold bg-green-600 text-white shadow-sm hover:bg-green-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-green-400/60"
+ >
+ CREATE
+
+
+ {paste.title && (
+
+ {paste.title}
+
+ )}
+
+
+ {/* Infos */}
+
+
+
+
+
Views
+
{paste.views}
+
+
+
+
+
+
Created
+
+ {new Date(paste.createdAt).toLocaleDateString()}
+
+
+
+
+
+
+
+ {/* Actions */}
+
+
{
+ await navigator.clipboard.writeText(paste.content);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1200);
+ }}
+ title="Copy"
+ className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
+ >
+
+
+
window.open(`/raw/${id}`, "_blank")}
+ title="Raw"
+ className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
+ >
+
+
+
{
+ const blob = new Blob([paste.content], { type: "text/plain" });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = `${id}.txt`;
+ a.click();
+ URL.revokeObjectURL(url);
+ }}
+ title="Download"
+ className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
+ >
+
+
+
router.push("/")}
+ title="New paste"
+ className="cursor-pointer inline-flex items-center justify-center rounded-lg p-2 bg-[#151b2e] ring-1 ring-white/10 hover:bg-[#1e253d] hover:ring-blue-400/30"
+ >
+
+
+
+
+
+ Copied!
+
+
+ );
+}
\ No newline at end of file
diff --git a/prisma/altbin.db b/prisma/altbin.db
index ae4f326..cbb331b 100644
Binary files a/prisma/altbin.db and b/prisma/altbin.db differ