Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 6eca480d6447d873e2e5eed9
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/Admin.tsx
// A paste's private admin page (/<id>/admin#k=<owner key>, plus &e=<key> when encrypted with a key in the link):
// edit or delete it. Reading here never counts as a view, so a burn-after-reading paste stays unread.
import { useEffect, useMemo, useRef, useState, type FormEvent } from "react";
import { navigate } from "./lib/router";
import { openPaste, updatePaste, deletePaste, type Paste } from "./lib/store";
import { open as unseal, seal, openWithPassword, sealWithPassword, type Sealed } from "./lib/crypto";
import { savePaste, savedPaste, forgetPaste } from "./lib/mine";
import { HOST, shareUrl, adminLink, expiryText, explain } from "./lib/format";
import { copy, share, canShare, toast } from "./ui";
import { AdminSkeleton } from "./Skeletons";
import { LANGUAGES, MAX_BYTES, checkContent, checkTitle, byteLength, formatBytes, isPasswordLocked } from "../functions/lib/paste";
type State =
| { kind: "opening" }
| { kind: "nokey" }
| { kind: "gone"; forgotten: boolean }
| { kind: "error"; message: string }
| { kind: "deleted" }
| { kind: "ready"; paste: Paste; open: Sealed | null; key: string; secret?: string };
export default function AdminPage({ id }: { id: string }) {
const [state, setState] = useState<State>({ kind: "opening" });
// Pasting the full admin link into a tab already on this page changes only the hash, so read the key again.
const [hash, setHash] = useState(location.hash);
useEffect(() => {
const onHash = () => setHash(location.hash);
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
useEffect(() => {
setState({ kind: "opening" });
const params = new URLSearchParams(hash.slice(1));
const saved = savedPaste(id);
const key = params.get("k") || saved?.key || "";
const secret = params.get("e") || saved?.secret || undefined;
if (!key) { setState({ kind: "nokey" }); return; }
let live = true;
(async () => {
const paste = await openPaste(id, key);
if (!live) return;
if (!paste) {
// This browser made it with this key, so it is gone (expired, deleted, or opened once).
const forgotten = saved?.key === key;
if (forgotten) forgetPaste(id);
return setState({ kind: "gone", forgotten });
}
const password = isPasswordLocked(paste);
let open: Sealed | null = paste.encrypted ? null : paste;
if (paste.encrypted && !password && secret) open = await unseal(paste.content, secret).catch(() => null);
if (!live) return;
savePaste({ id, key, title: open?.title ?? saved?.title ?? "", language: open?.language ?? saved?.language ?? "text", createdAt: paste.createdAt, ttl: paste.ttl, burn: paste.burn, secret: open ? secret : undefined, password: password || undefined });
setState({ kind: "ready", paste, open, key, secret: open ? secret : undefined });
})().catch((err) => { if (live) setState({ kind: "error", message: explain(err) }); });
return () => { live = false; };
}, [id, hash]);
useEffect(() => { document.title = "Manage paste | Paste"; }, []);
if (state.kind === "opening") return <AdminSkeleton />;
if (state.kind === "error") return (
<div className="wrap page-pad narrow message enter">
<h1 className="h1">This paste could not be opened.</h1>
<p className="lede">{state.message}</p>
<button className="btn" type="button" onClick={() => location.reload()}>Try again</button>
</div>
);
if (state.kind === "ready") return <AdminView id={id} paste={state.paste} initial={state.open} ownerKey={state.key} secret={state.secret} onDeleted={() => setState({ kind: "deleted" })} />;
const [title, text] =
state.kind === "nokey" ? ["This admin link is missing its key.", "Open the full admin link you saved when you made the paste. It ends with #k= and a long code."]
: state.kind === "deleted" ? ["The paste is deleted.", "Its link no longer works, for anyone."]
: state.forgotten ? ["This paste is gone.", "It expired, was deleted, or was opened once after being set to delete after the first view. It is off your list now."]
: ["This admin link does not work.", "The paste may be gone, or the admin link was cut short. Check that you copied all of it."];
return (
<div className="wrap page-pad narrow message enter">
<h1 className="h1">{title}</h1>
<p className="lede">{text}</p>
<a className="btn" href="/" onClick={(e) => { e.preventDefault(); navigate("/"); }}>Make a paste</a>
</div>
);
}
function AdminView({ id, paste, initial, ownerKey, secret, onDeleted }: {
id: string; paste: Paste; initial: Sealed | null; ownerKey: string; secret?: string; onDeleted: () => void;
}) {
const password = isPasswordLocked(paste);
const empty = { title: "", language: "text", content: "" };
const [saved, setSaved] = useState<Sealed>(initial ? { ...initial, language: initial.language || "text" } : empty);
const [draft, setDraft] = useState(saved);
// A password paste opens for editing once its password is typed here; it is never stored.
const [unlocked, setUnlocked] = useState<string | null>(null);
const locked = paste.encrypted && !initial && !unlocked;
const [busy, setBusy] = useState(false);
const [error, setError] = useState<string | null>(null);
const [confirming, setConfirming] = useState(false);
const link = shareUrl(id, secret);
const bytes = useMemo(() => byteLength(draft.content), [draft.content]);
const changed = draft.title !== saved.title || draft.language !== saved.language || draft.content !== saved.content;
// Leaving with unsaved edits asks first.
useEffect(() => {
if (!changed) return;
const warn = (e: BeforeUnloadEvent) => { e.preventDefault(); e.returnValue = ""; };
window.addEventListener("beforeunload", warn);
return () => window.removeEventListener("beforeunload", warn);
}, [changed]);
const save = async (e: FormEvent) => {
e.preventDefault();
const c = checkContent(draft.content);
if ("error" in c) return setError(c.error);
const t = checkTitle(draft.title);
if ("error" in t) return setError(t.error);
setBusy(true);
setError(null);
try {
const next = { title: t.title, language: draft.language, content: c.content };
const content = unlocked ? await sealWithPassword(next, unlocked) : secret ? await seal(next, secret) : null;
await updatePaste(id, content ? { content } : next);
setSaved(next);
setDraft(next);
savePaste({ id, key: ownerKey, title: next.title, language: next.language, createdAt: paste.createdAt, ttl: paste.ttl, burn: paste.burn, secret, password: password || undefined });
toast("Saved. The link shows the new version now.");
} catch (err) {
setError(explain(err));
} finally {
setBusy(false);
}
};
const remove = async () => {
setBusy(true);
try {
await deletePaste(id);
forgetPaste(id);
onDeleted();
} catch (err) {
setError(explain(err));
setBusy(false);
}
};
const view = `/${id}${secret ? `#${secret}` : ""}`;
return (
<div className="admin enter">
<header className="edit-head">
<div className="wrap edit-head-inner">
<div className="edit-head-text">
<p className="eyebrow">Manage your paste</p>
<h1 className="edit-title">{saved.title || (locked ? "Locked paste" : "Untitled paste")}</h1>
<p className="muted small edit-link">{HOST}/{id}</p>
</div>
<div className="row edit-head-actions">
<button className="btn" type="button" onClick={() => share(link, saved.title || "Paste", "Link copied")} disabled={paste.encrypted && !password && !secret}>
{canShare() ? "Share link" : "Copy link"}
</button>
{!paste.burn && !(paste.encrypted && !password && !secret) && (
<a className="btn btn-line" href={view} onClick={(e) => { e.preventDefault(); if (!changed || confirm("Leave without saving your changes?")) navigate(view); }}>Open</a>
)}
</div>
</div>
</header>
<div className="wrap edit-body">
{locked && password ? (
<UnlockForEdit paste={paste} onOpen={(open, pw) => { const next = { ...open, language: open.language || "text" }; setSaved(next); setDraft(next); setUnlocked(pw); }} />
) : (
<form className="card pad stack" onSubmit={save} noValidate aria-label="Edit the paste">
{locked && <p className="notice">This paste is encrypted, and this admin link does not carry its key. Open the admin link that ends with &e= and a long code to edit it. You can still delete it.</p>}
<div className="admin-fields">
<label className="field">
<span>Title</span>
<input className="input" value={draft.title} onChange={(e) => setDraft({ ...draft, title: e.target.value })} maxLength={200} placeholder="Untitled paste" disabled={locked} autoComplete="off" />
</label>
<label className="field">
<span>Language</span>
<select className="select" value={draft.language} onChange={(e) => setDraft({ ...draft, language: e.target.value })} disabled={locked}>
{LANGUAGES.map((l) => <option key={l.id} value={l.id}>{l.name}</option>)}
</select>
</label>
</div>
<label className="field">
<span>Text</span>
<textarea className="textarea admin-text" value={draft.content} onChange={(e) => setDraft({ ...draft, content: e.target.value })}
spellCheck={false} autoCapitalize="off" autoComplete="off" autoCorrect="off" disabled={locked} placeholder={locked ? "Encrypted" : ""} />
{!locked && <small className={bytes > MAX_BYTES ? "warn" : ""}>{formatBytes(bytes)} of 512 KB</small>}
</label>
{error && <p className="notice notice-error" role="alert">{error}</p>}
<div className="row">
<button className="btn" type="submit" disabled={locked || !changed || bytes > MAX_BYTES} aria-busy={busy || undefined}><span>Save changes</span></button>
{changed && <button className="btn btn-quiet" type="button" onClick={() => { setDraft(saved); setError(null); }}>Discard changes</button>}
</div>
</form>
)}
<aside className="card pad admin-side">
<h2 className="h3">Details</h2>
<dl className="details">
<div><dt>Made</dt><dd>{new Date(paste.createdAt * 1000).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })}</dd></div>
<div><dt>Expiry</dt><dd>{expiryText(paste)}{paste.burn ? ". Not opened yet" : ""}</dd></div>
<div><dt>Who can open it</dt><dd>{password ? "Anyone with the link and the password." : "Anyone with the link."} Nobody can list pastes.</dd></div>
{paste.encrypted && <div><dt>Encryption</dt><dd>{password ? "Locked with a password in the browser." : "Encrypted in the browser, with the key in the link."} The server holds only scrambled text.</dd></div>}
</dl>
<div className="danger-zone">
{confirming ? (
<div className="stack enter" style={{ "--gap": "10px" } as React.CSSProperties}>
<p>Delete this paste for everyone? This cannot be undone.</p>
<div className="row">
<button className="btn btn-sm btn-danger btn-line" type="button" onClick={remove} aria-busy={busy || undefined}><span>Delete for good</span></button>
<button className="btn btn-sm btn-quiet" type="button" onClick={() => setConfirming(false)}>Keep it</button>
</div>
</div>
) : (
<button className="btn btn-sm btn-line btn-danger" type="button" onClick={() => setConfirming(true)}>Delete paste</button>
)}
</div>
</aside>
<div className="notice edit-key">
<p><strong>This page's address is your private admin link.</strong> Bookmark it, or keep it somewhere safe. It works on any device, and anyone with it can change or delete the paste.</p>
<button className="btn btn-sm btn-line" type="button" onClick={() => copy(adminLink({ id, key: ownerKey, secret }), "Admin link copied")}>Copy admin link</button>
</div>
</div>
</div>
);
}
function UnlockForEdit({ paste, onOpen }: { paste: Paste; onOpen: (open: Sealed, password: string) => void }) {
const input = useRef<HTMLInputElement>(null);
const [busy, setBusy] = useState(false);
const [error, setError] = useState<string | null>(null);
const submit = async (e: FormEvent) => {
e.preventDefault();
const pw = input.current?.value ?? "";
if (!pw) return setError("Type the paste's password.");
setBusy(true);
setError(null);
try {
onOpen(await openWithPassword(paste.content, pw), pw);
} catch {
setError("That password does not open this paste.");
setBusy(false);
}
};
return (
<form className="card pad stack" onSubmit={submit} noValidate aria-label="Unlock to edit">
<h2 className="h3">Locked with a password</h2>
<p className="muted">Type the password to edit the text. You can delete the paste without it.</p>
<label className="field">
<span>Password</span>
<input ref={input} className="input" type="password" autoComplete="off" autoCapitalize="off" spellCheck={false} enterKeyHint="go"
aria-invalid={error ? true : undefined} aria-describedby={error ? "admin-unlock-error" : undefined} onChange={() => setError(null)} />
{error && <span className="field-error" id="admin-unlock-error" role="alert">{error}</span>}
</label>
<div className="row"><button className="btn" type="submit" aria-busy={busy || undefined}><span>Unlock to edit</span></button></div>
</form>
);
}
Change history
Quality pass: password lock, year expiry, line links, text/plain API
Bounded source sync
Bounded source sync
Bounded source sync