11import { getPasteOrThrow } from "~/services/paste-service" ;
22import { escapeHtml , renderLayout } from "~/templates/layout" ;
33
4- export default eventHandler ( async ( event ) => {
5- const paste = await getPasteOrThrow ( event . context . params ?. id ) ;
4+ export default defineEventHandler ( async ( event ) => {
5+ const id = getRouterParam ( event , "id" ) ;
6+
7+ if ( ! id ) {
8+ throw createError ( {
9+ statusCode : 400 ,
10+ statusMessage : "Missing paste id" ,
11+ } ) ;
12+ }
13+
14+ const paste = await getPasteOrThrow ( id ) ;
615 const host = getRequestHost ( event , { xForwardedHost : true } ) ;
716
817 const metadataList = `
918 <ul class="list-group list-group-flush">
1019 <li class="list-group-item bg-transparent text-light border-secondary">
1120 <strong>ID:</strong> ${ paste . id }
1221 </li>
13- ${ paste . source ? `<li class="list-group-item bg-transparent text-light border-secondary"><strong>Source:</strong> ${ escapeHtml ( paste . source ) } </li>` : "" }
22+ ${
23+ paste . source
24+ ? `<li class="list-group-item bg-transparent text-light border-secondary"><strong>Source:</strong> ${ escapeHtml ( paste . source ) } </li>`
25+ : ""
26+ }
1427 <li class="list-group-item bg-transparent text-light border-secondary">
1528 <strong>Created:</strong> ${ formatDate ( paste . createdAt ) }
1629 </li>
@@ -24,34 +37,47 @@ export default eventHandler(async (event) => {
2437 <div class="mb-3">
2538 <a class="btn btn-link text-muted text-decoration-none" href="/">← Back to home</a>
2639 </div>
40+
2741 <div class="card shadow-sm p-4 mb-4">
2842 <div class="d-flex justify-content-between flex-wrap gap-2 align-items-start">
2943 <div>
3044 <h1 class="h4 mb-2">${ escapeHtml ( paste . title ) } </h1>
3145 <p class="text-muted mb-0">Share this link with support to review the logs.</p>
3246 </div>
47+
3348 <div class="d-flex gap-2">
3449 <a class="btn btn-outline-light btn-sm" href="/api/pastes/${ paste . id } /raw" target="_blank" rel="noopener">Raw</a>
35- <button class="btn btn-outline-info btn-sm" id="copy-link" data-url="${ escapeHtml ( `https://${ host } /p/${ paste . id } ` ) } ">Copy link</button>
50+ <button
51+ class="btn btn-outline-info btn-sm"
52+ id="copy-link"
53+ data-url="${ escapeHtml ( `https://${ host } /p/${ paste . id } ` ) } "
54+ >
55+ Copy link
56+ </button>
3657 </div>
3758 </div>
3859 </div>
60+
3961 <div class="row g-4">
4062 <div class="col-12 col-lg-4">
4163 <div class="card shadow-sm p-4">
4264 <h2 class="h6">Details</h2>
4365 ${ metadataList }
4466 </div>
4567 </div>
68+
4669 <div class="col-12 col-lg-8">
4770 <div class="card shadow-sm p-4">
4871 <div class="d-flex justify-content-between align-items-center mb-3">
4972 <h2 class="h6 mb-0">Log contents</h2>
5073 <button class="btn btn-sm btn-secondary" id="copy-log">Copy log</button>
5174 </div>
52- <div class="log-viewer" id="log" data-raw="${ escapeHtml (
53- paste . content ,
54- ) } ">
75+
76+ <div
77+ class="log-viewer"
78+ id="log"
79+ data-raw="${ escapeHtml ( paste . content ) } "
80+ >
5581 ${ renderLogLines ( paste . content ) }
5682 </div>
5783 </div>
@@ -60,37 +86,40 @@ export default eventHandler(async (event) => {
6086 ` ;
6187
6288 const inlineScript = String . raw `
63- (() => {
64- const copyLinkBtn = document.getElementById("copy-link");
65- const copyLogBtn = document.getElementById("copy-log");
66-
67- copyLinkBtn?.addEventListener("click", async () => {
68- const url = copyLinkBtn.dataset.url;
69- if (!url) return;
70- try {
71- await navigator.clipboard.writeText(url);
72- copyLinkBtn.textContent = "Copied!";
73- setTimeout(() => (copyLinkBtn.textContent = "Copy link"), 2000);
74- } catch {
75- copyLinkBtn.textContent = "Failed";
76- setTimeout(() => (copyLinkBtn.textContent = "Copy link"), 2000);
77- }
78- });
79-
80- copyLogBtn?.addEventListener("click", async () => {
81- const logEl = document.getElementById("log");
82- const rawLog = logEl?.getAttribute("data-raw") ?? logEl?.textContent ?? "";
83- try {
84- await navigator.clipboard.writeText(rawLog);
85- copyLogBtn.textContent = "Copied!";
86- setTimeout(() => (copyLogBtn.textContent = "Copy log"), 2000);
87- } catch {
88- copyLogBtn.textContent = "Failed";
89- setTimeout(() => (copyLogBtn.textContent = "Copy log"), 2000);
90- }
91- });
92- })();
93- ` ;
89+ (() => {
90+ const copyLinkBtn = document.getElementById("copy-link");
91+ const copyLogBtn = document.getElementById("copy-log");
92+
93+ copyLinkBtn?.addEventListener("click", async () => {
94+ const url = copyLinkBtn.dataset.url;
95+ if (!url) return;
96+
97+ try {
98+ await navigator.clipboard.writeText(url);
99+ copyLinkBtn.textContent = "Copied!";
100+ setTimeout(() => (copyLinkBtn.textContent = "Copy link"), 2000);
101+ } catch {
102+ copyLinkBtn.textContent = "Failed";
103+ setTimeout(() => (copyLinkBtn.textContent = "Copy link"), 2000);
104+ }
105+ });
106+
107+ copyLogBtn?.addEventListener("click", async () => {
108+ const logEl = document.getElementById("log");
109+ const rawLog =
110+ logEl?.getAttribute("data-raw") ?? logEl?.textContent ?? "";
111+
112+ try {
113+ await navigator.clipboard.writeText(rawLog);
114+ copyLogBtn.textContent = "Copied!";
115+ setTimeout(() => (copyLogBtn.textContent = "Copy log"), 2000);
116+ } catch {
117+ copyLogBtn.textContent = "Failed";
118+ setTimeout(() => (copyLogBtn.textContent = "Copy log"), 2000);
119+ }
120+ });
121+ })();
122+ ` ;
94123
95124 return renderLayout ( {
96125 title : `${ paste . title } · Xyra Paste` ,
@@ -109,19 +138,23 @@ const formatDate = (timestamp: number) =>
109138
110139const formatExpiry = ( expiresAt : number | null ) => {
111140 if ( ! expiresAt ) return "Never" ;
141+
112142 const diffMs = expiresAt - Date . now ( ) ;
113143 if ( diffMs <= 0 ) return "Expired" ;
144+
114145 return new Date ( expiresAt ) . toLocaleString ( ) ;
115146} ;
116147
117148const renderLogLines = ( content : string ) => {
118149 const normalized = content . replace ( / \r \n / g, "\n" ) . split ( "\n" ) ;
150+
119151 return normalized
120152 . map ( ( line , index ) => {
121153 const safeLine = escapeHtml ( line ) || " " ;
122- return `<div class="log-line"><span class="log-line-number">${
123- index + 1
124- } </span><span class="log-line-text">${ safeLine } </span></div>`;
154+ return `<div class="log-line">
155+ <span class="log-line-number">${ index + 1 } </span>
156+ <span class="log-line-text">${ safeLine } </span>
157+ </div>` ;
125158 } )
126159 . join ( "" ) ;
127160} ;
0 commit comments