client-show.ts
Raw
1import { fileTypeFromBuffer } from 'file-type'
2import hljs from 'highlight.js'
3import { humanFileSize } from './shared'
4
5async function decrypt(encryptedContent: ArrayBuffer, password: string): Promise<ArrayBuffer> {
6 const encryptedArray = new Uint8Array(encryptedContent)
7 const salt = encryptedArray.slice(0, 16)
8 const iv = encryptedArray.slice(16, 32)
9 const keyMaterial = await crypto.subtle.importKey(
10 "raw",
11 new TextEncoder().encode(password),
12 { name: "PBKDF2" },
13 false,
14 ["deriveBits", "deriveKey"]
15 )
16 const key = await crypto.subtle.deriveKey(
17 {
18 name: "PBKDF2",
19 salt: salt,
20 iterations: 100000,
21 hash: "SHA-512"
22 },
23 keyMaterial,
24 { name: "AES-CBC", length: 256 },
25 false,
26 ["decrypt"]
27 )
28 const decryptedContent = await crypto.subtle.decrypt(
29 { name: "AES-CBC", iv: iv },
30 key,
31 encryptedArray.slice(32)
32 )
33 return decryptedContent
34}
35
36function isValidUTF8(buf: ArrayBuffer) {
37 try {
38 new TextDecoder("utf-8", { fatal: true }).decode(buf)
39 return true
40 } catch (e) {
41 return false
42 }
43}
44
45async function showContent(content: ArrayBuffer, filetype: string) {
46 const filesize = document.getElementById("filesize") as HTMLDivElement | null
47 if (filesize) {
48 filesize.textContent = `size: ${humanFileSize(content.byteLength)}`
49 }
50
51 const mediaBox = document.getElementById("mediabox") as HTMLDivElement | null
52 if (!mediaBox) {
53 console.error("Missing mediaBox element.")
54 return
55 }
56 let preview: HTMLElement = document.createElement("div")
57 preview.textContent = "This file can't be previewed"
58 if (isValidUTF8(content) && filetype !== "blob") {
59 preview = document.createElement("pre")
60 if (filetype === "none") {
61 preview.textContent = new TextDecoder().decode(content)
62 } else {
63 preview.innerHTML = hljs.highlight(new TextDecoder("utf-8").decode(content), { language: filetype }).value
64 }
65 } else {
66 const filetype = await fileTypeFromBuffer(content)
67 if (filetype) {
68 const blob = new Blob([content], { type: filetype.mime })
69 const url = URL.createObjectURL(blob)
70 if (filetype.mime.startsWith("audio/")) {
71 preview = document.createElement("audio")
72 preview.setAttribute("controls", "")
73 preview.setAttribute("src", url)
74 } else if (filetype.mime.startsWith("video/")) {
75 preview = document.createElement("video")
76 preview.setAttribute("controls", "")
77 preview.setAttribute("src", url)
78 } else if (filetype.mime.startsWith("image/")) {
79 preview = document.createElement("img")
80 preview.setAttribute("src", url)
81 }
82 }
83 }
84 mediaBox.innerHTML = ""
85 mediaBox.appendChild(preview)
86}
87
88let content: ArrayBuffer
89let filetype: string
90let filename: string
91
92async function onPasswordSubmit() {
93 const passwordInput = document.getElementById("password") as HTMLInputElement | null
94 const passwordLabel = document.getElementById("password-label") as HTMLLabelElement | null
95 if (!passwordInput || !passwordLabel) {
96 console.error("Missing passwordInput or passwordLabel element.")
97 return
98 }
99 try {
100 content = await decrypt(content, passwordInput.value)
101 } catch (e) {
102 passwordLabel.textContent = "Incorrect password"
103 return
104 }
105 const dialog = document.getElementById("password-dialog") as HTMLDialogElement | null
106 dialog?.close()
107 const downloadForm = document.getElementById("download-form") as HTMLFormElement | null
108 downloadForm?.setAttribute("action", "")
109 downloadForm?.addEventListener("submit", (e) => {
110 e.preventDefault()
111 const blob = new Blob([content])
112 const url = URL.createObjectURL(blob)
113 const link = document.createElement("a")
114 link.download = filename
115 link.href = url
116 link.click();
117 })
118 showContent(content, filetype)
119}
120
121const uuid = window.location.pathname.split("/").filter((x) => x !== "").reverse()[0]
122
123fetch("/raw/" + uuid + "?ignore_password=true").then(async (response) => {
124 content = await response.arrayBuffer()
125 const mediaBox = document.getElementById("mediabox")
126 if(mediaBox) {
127 mediaBox.innerHTML = ""
128 }
129 filetype = response.headers.get("filetype") || "none"
130 filename = response.headers.get("filename") || ""
131 if (response.headers.get("encrypted") === "true") {
132 const dialog = document.getElementById("password-dialog") as HTMLDialogElement | null
133 dialog?.showModal()
134 } else {
135 showContent(content, filetype)
136 }
137});
138
139(window as any).onPasswordSubmit = onPasswordSubmit