Files
backerup-website/frontend/app/files/[token]/page.tsx
T
Anders Böttcher 50566f2a22 Initial Commit
2026-05-11 23:39:16 +02:00

122 lines
4.5 KiB
TypeScript

import Link from "next/link";
import { getFile } from "@/lib/store";
import { notFound } from "next/navigation";
import CopyLinkButton from "./CopyLinkButton";
function formatBytes(n: number) {
if (n < 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
return `${(n / 1024 / 1024).toFixed(2)} MB`;
}
export default async function FileDetailPage({
params,
}: {
params: Promise<{ token: string }>;
}) {
const { token } = await params;
const record = await getFile(token);
if (!record) notFound();
const isBackerup = record.type === "backerup";
const downloadUrl = `/api/files/${token}/download`;
return (
<div className="mx-auto max-w-2xl px-6 py-16">
<Link href="/browse" className="text-xs text-slate-500 hover:text-slate-300 transition-colors">
Back to browse
</Link>
<div className="mt-6">
<div className="flex items-center gap-3 flex-wrap">
{isBackerup ? (
<span className="rounded-full bg-cyan-500/15 px-3 py-1 text-xs font-semibold text-cyan-400">
.backerup
</span>
) : (
<span className="rounded-full bg-violet-500/15 px-3 py-1 text-xs font-semibold text-violet-400">
.json config
</span>
)}
<span className="font-mono text-sm text-slate-500">{record.originalName}</span>
</div>
{record.group && (
<span className="mt-3 inline-block rounded-full bg-slate-700/60 px-3 py-0.5 text-xs font-medium text-slate-400">
{record.group}
</span>
)}
<h1 className="mt-4 text-3xl font-bold text-white">
{record.displayName ?? record.containerName ?? record.originalName}
</h1>
{record.image && (
<p className="mt-1 text-slate-400">{record.image}</p>
)}
{record.description && (
<p className="mt-4 text-sm leading-relaxed text-slate-300">{record.description}</p>
)}
</div>
{/* Metadata grid */}
<div className="mt-8 rounded-2xl border border-slate-800 bg-slate-900/60 p-6 space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">File size</span>
<span className="text-sm font-mono text-slate-300">{formatBytes(record.size)}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Uploaded</span>
<span className="text-sm text-slate-300">
{new Date(record.uploadedAt).toLocaleString()}
</span>
</div>
{record.group && (
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Group</span>
<span className="text-sm font-mono text-slate-300">{record.group}</span>
</div>
)}
{record.containerName && (
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Container name</span>
<span className="text-sm font-mono text-slate-300">{record.containerName}</span>
</div>
)}
{record.image && (
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Image</span>
<span className="text-sm font-mono text-slate-300">{record.image}</span>
</div>
)}
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Share token</span>
<span className="text-sm font-mono text-slate-400">{token}</span>
</div>
</div>
{/* Actions */}
<div className="mt-6 space-y-3">
<a
href={downloadUrl}
className="flex w-full items-center justify-center gap-2 rounded-xl bg-cyan-500 py-3 text-sm font-bold text-slate-900 hover:bg-cyan-400 transition-colors"
download={record.originalName}
>
Download {record.originalName}
</a>
{/* Copy link */}
<CopyLinkButton token={token} />
</div>
{isBackerup && (
<div className="mt-8 rounded-xl border border-amber-500/20 bg-amber-500/5 p-4 text-xs text-amber-300">
<strong>Note:</strong> This is a full .backerup archive. It may contain volume data
in addition to the container config. Import it into Backerup to restore
or clone the container.
</div>
)}
</div>
);
}