add deletion timer, other small fixes

AuthorKonata <konata@posteo.jp>
Date
Commitde611b1e07e4de06f908e857bb4d34c55d46afdc
Parent311eb46
6 files changed, 145 insertions(+), 15 deletions(-)
▾Massets/default.css
@@ -73,3 +73,16 @@ input[type=reset] {
max-width: 25rem;
align-self: center;
}
dialog {
background-color: #120c0e;
color: #d9d8dc;
border-color: #eb99a1;
form {
display: flex;
flex-direction: column;
align-items: center;
}
}
▾Mbun.lock
@@ -5,8 +5,10 @@
"name": "zigbin",
"dependencies": {
"@chneau/elysia-compression": "^1.0.11",
"@elysiajs/cron": "^1.2.0",
"@elysiajs/html": "^1.2.0",
"@elysiajs/static": "^1.2.0",
"elysia": "latest",
"elysia": "^1.2.0",
"file-type": "^20.0.1",
"highlight.js": "^11.11.1",
"pkcs7-padding": "^0.1.1",
@@ -22,8 +24,16 @@
"packages": {
"@chneau/elysia-compression": ["@chneau/elysia-compression@1.0.11", "", { "dependencies": { "elysia": "^1.1.9" } }, "sha512-J4wfz5Qs68p/DNvkg5DXxo8sxJKSibzaUBP4W56uvqgs9NtHG0LC6tnyv7sAPEjNKzh21l6kinMoXbtVDp/KSQ=="],
"@elysiajs/cron": ["@elysiajs/cron@1.2.0", "", { "dependencies": { "croner": "^6.0.3" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-Xg2bUCZNEp2zRzsYeNQ6BUP3uslF5BEPnuLHLnsytiAEqzFTHzS7813XbmHs8xNZKuUAkAnJNTVnQb1Jt5zTwg=="],
"@elysiajs/html": ["@elysiajs/html@1.2.0", "", { "dependencies": { "@kitajs/html": "^4.1.0", "@kitajs/ts-html-plugin": "^4.0.1" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-j/exB9TLn2/KOMWRfPcJ7MNOsmr85uHRjC+kKPLyb1lRtNDWzgab4deUQrxtIv4gt4PDSKmywNnhyD+GrdnH3w=="],
"@elysiajs/static": ["@elysiajs/static@1.2.0", "", { "dependencies": { "node-cache": "^5.1.2" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-oLpAi8c+maPpA0XhhK3BELaIjIG+nXg/K9p8cFfW4q5ayRD59a3MOMOOGgpiXZkHJzLPWcouhhyyLAYtaANW4g=="],
"@kitajs/html": ["@kitajs/html@4.2.7", "", { "dependencies": { "csstype": "^3.1.3" } }, "sha512-HMAHy0GLH7i7wY1o0W1TeugNhtWefUkhb9l6zx6v2fIkR1cPlUtQQdkTt/jkBA7723yO7uBERcXFPZpWT7HWPQ=="],
"@kitajs/ts-html-plugin": ["@kitajs/ts-html-plugin@4.1.1", "", { "dependencies": { "chalk": "^4.1.2", "tslib": "^2.8.1", "yargs": "^17.7.2" }, "peerDependencies": { "@kitajs/html": "^4.2.5", "typescript": "^5.6.2" }, "bin": { "ts-html-plugin": "dist/cli.js", "xss-scan": "dist/cli.js" } }, "sha512-wmjyV8hmJmDOnUM/ZyPkc0UBYgUYmf32/93rkW8wr8h+HiHVMU0tEKFnmRdBjTcy9jwoC9Bnt2NuzS9l67lq5g=="],
"@sinclair/typebox": ["@sinclair/typebox@0.34.15", "", {}, "sha512-xeIzl3h1Znn9w/LTITqpiwag0gXjA+ldi2ZkXIBxGEppGCW211Tza+eL6D4pKqs10bj5z2umBWk5WL6spQ2OCQ=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.6", "", { "dependencies": { "debug": "^4.3.7", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-SdR/i05U7Xhnsq36iyIq/ZiGGw4PKzw4ww3bOq80Pjj4wyXpqyTcgrgdDdGlcatnlvzNJx8CQw3hp6QZvkUwhA=="],
@@ -38,24 +48,50 @@
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
"croner": ["croner@6.0.7", "", {}, "sha512-k3Xx3Rcclfr60Yx4TmvsF3Yscuiql8LSvYLaphTsaq5Hk8La4Z/udmUANMOTKpgGGroI2F6/XOr9cU9OFkYluQ=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"elysia": ["elysia@1.2.10", "", { "dependencies": { "@sinclair/typebox": "^0.34.13", "cookie": "^1.0.2", "memoirist": "^0.2.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-QcNl2FjhHFRpKaqy1NoMpyCjJ7OcKBnHwLUkqGu09QwIV84PFb82ILvYJG4GS1RbGv76OA50luaqBLrM3SLZ2w=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"file-type": ["file-type@20.0.1", "", { "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-R80W+NZ+s1M8PsC8L7xn+J0N72vEyxstMH+4vwIyt5B7ojfG80h/nKu2Qw/NYJTIYyGUeaPNLnzfJd78SWnG3A=="],
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"memoirist": ["memoirist@0.2.0", "", {}, "sha512-DA1V11OWsKmYjgYHfT1luus0FtTjUbILfI9s5M+ckK29tBLON6GDhH5GwxDz7E1ou4Bdzm9vhbeCaRAWxwG+0g=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
@@ -70,10 +106,20 @@
"pkcs7-padding": ["pkcs7-padding@0.1.1", "", {}, "sha512-tM/sUOL5FdH7x6gSLHTNDFi1bAut/EXGFp/Ih8uRkVd2IdXwb5QWnxBgQXn6buadj1pLi3CYKCnrxb+XoTz+Ww=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strtok3": ["strtok3@10.2.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^6.1.0" } }, "sha512-S884oIGzokq3LkL/6jXw5c5oJXRiGt4jB42cuWdaooJdxFMSn99snaShh3cQmJx3jALV2eoyW3rV/TJxwOaBPA=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"token-types": ["token-types@6.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
"uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
@@ -81,5 +127,13 @@
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
"utf-8-validate": ["utf-8-validate@6.0.5", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA=="],
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
}
}
▾Mpackage.json
@@ -8,8 +8,9 @@
},
"dependencies": {
"@chneau/elysia-compression": "^1.0.11",
"@elysiajs/static": "^1.2.0",
"@elysiajs/cron": "^1.2.0",
"@elysiajs/html": "^1.2.0",
"@elysiajs/static": "^1.2.0",
"elysia": "^1.2.0",
"file-type": "^20.0.1",
"highlight.js": "^11.11.1",
▾Msrc/client-index.ts
@@ -29,6 +29,7 @@ async function encrypt(content: Uint8Array, password: string): Promise<ArrayBuff
}
async function uploadFile() {
(document.getElementById("upload-dialog") as HTMLDialogElement | null)?.showModal()
const passwordInput = document.getElementById("password") as HTMLInputElement | null
const fileInput = document.getElementById("file") as HTMLInputElement | null
const filenameInput = document.getElementById("filename") as HTMLInputElement | null
@@ -65,12 +66,13 @@ async function uploadFile() {
form.submit()
}
(document.getElementById("uploadForm") as HTMLFormElement | null)?.reset()
const jsguards = document.getElementsByClassName("jsguard")
for (let i = 0; i < jsguards.length; i++) {
jsguards[i].remove()
}
(document.getElementById("uploadForm") as HTMLFormElement | null)?.reset();
window.uploadFile = uploadFile
//disable bfcache, otherwise dialog will stay open when navigating back
window.addEventListener('unload', function () { });
window.addEventListener('beforeunload', function () { });
▾Msrc/components.tsx
@@ -6,7 +6,7 @@ import hljs from 'highlight.js'
import isValidUTF8 from 'utf-8-validate'
import { humanFileSize } from './shared'
const filetypes = ["none", "blob"].concat(hljs.listLanguages().sort())
export const filetypes = ["none", "blob"].concat(hljs.listLanguages().sort())
function Template(props: PropsWithChildren<{ css: string }>) {
return (
@@ -38,6 +38,9 @@ export function NotFound() {
export function Index(hostname: string, withJs: boolean) {
let body = (
<>
<dialog id="upload-dialog">
File is being uploaded, please wait
</dialog>
{withJs ?
<div style="text-align: center">JS version with client-side encryption, for the version without js an with server-side encryption, go <a href="/">here</a>.</div>
: <div style="text-align: center">JS-less version with server-side encryption, for the version with client-side encryption, go to <a href="/js">/js</a>.</div>
@@ -58,6 +61,9 @@ export function Index(hostname: string, withJs: boolean) {
))
}
</select>
<label for="delete-in">Delete in:</label>
<small>Optional. If set, file will be deleted after the given number of minutes.</small>
<input type="number" id="delete-in" name="delete_in_minutes" min="0" placeholder="0" />
<label for="password">Password:</label>
<small>Optional. If set, file will be encrypted and requires the given password to open.</small>
<input type="password" id="password" name="password" placeholder="Password" />
@@ -77,6 +83,8 @@ export function Index(hostname: string, withJs: boolean) {
# optional{"\n"}
-F filename="file name" \{"\n"}
# optional{"\n"}
-F delete_in_minutes="60" \{"\n"}
# optional{"\n"}
-F password="mypassword" \{"\n"}
# optional{"\n"}
-F encrypted="on"</pre>
@@ -137,7 +145,24 @@ export function WrongPassword() {
)
}
export async function ShowFile(filename: string, uuid: string, content: Uint8Array | null, filetype: string) {
function humanReadableTime(minutes: number) {
const years = Math.floor(minutes / 525600);
minutes %= 525600;
const days = Math.floor(minutes / 1440);
minutes %= 1440;
const hours = Math.floor(minutes / 60);
minutes %= 60;
let result = "";
if (years > 0) result += `${years} year${years > 1 ? "s" : ""} `
if (days > 0) result += `${days} day${days > 1 ? "s" : ""} `
if (hours > 0) result += `${hours} hour${hours > 1 ? "s" : ""} `
if (minutes > 0) result += `${minutes} minute${minutes > 1 ? "s" : ""} `
if (result === "") result = "0 minutes"
return result.trim();
}
export async function ShowFile(filename: string, uuid: string, content: Uint8Array | null, filetype: string, delete_at: number | null) {
//if content is null, this is for the js frontend
let preview;
if (!content) {
@@ -183,6 +208,7 @@ export async function ShowFile(filename: string, uuid: string, content: Uint8Arr
<hr />
<p id="filesize">size: {content ? humanFileSize(content.byteLength) : "unknown"}</p>
<p>declared type: {filetype}</p>
{delete_at ? <p>delete at: {new Date(delete_at * 1000).toISOString()} (in {humanReadableTime(Math.floor((delete_at - (Date.now() / 1000)) / 60))})</p> : ""}
<form id="download-form" action={`/raw/${uuid}`} method="get">
<button>Download</button>
</form>
▾Msrc/index.ts
@@ -4,13 +4,16 @@ import { randomUUIDv7 } from "bun"
import staticPlugin from "@elysiajs/static"
import { html } from '@elysiajs/html'
import crypto from 'crypto'
import { DecryptFile, Index, NotFound, SetCookie, ShowFile, WrongPassword } from "./components"
import { DecryptFile, filetypes, Index, NotFound, SetCookie, ShowFile, WrongPassword } from "./components"
import cron from "@elysiajs/cron"
//TODO: add deletion timer
const db = new Database("./db.sqlite")
db.exec("PRAGMA foreign_keys = ON")
db.exec("PRAGMA journal_mode = WAL2")
db.exec("CREATE TABLE IF NOT EXISTS files (uuid TEXT PRIMARY KEY, filename TEXT NOT NULL, content BLOB NOT NULL, filetype TEXT NOT NULL, encrypted INTEGER NOT NULL) STRICT")
db.exec("CREATE TABLE IF NOT EXISTS files (uuid TEXT PRIMARY KEY, filename TEXT NOT NULL, content BLOB NOT NULL, filetype TEXT NOT NULL, encrypted INTEGER NOT NULL, delete_at INTEGER) STRICT")
db.exec("PRAGMA optimize")
@@ -32,6 +35,15 @@ function decrypt(encrypted_content: Uint8Array, password: string): Buffer {
return content
}
function stringArrayToEnum<T extends string>(
arr: readonly T[]
): { [K in T]: K } {
return arr.reduce((acc, key) => {
acc[key] = key;
return acc;
}, Object.create(null));
}
const app = new Elysia({
serve: {
maxRequestBodySize: 1024 * 1024 * 1024 // 1GB
@@ -39,6 +51,13 @@ const app = new Elysia({
})
.use(staticPlugin({ assets: "./assets", prefix: "/" }))
.use(html())
.use(cron({
name: "delete",
pattern: "*/5 * * * * *",
run() {
db.exec("DELETE FROM files WHERE delete_at < strftime('%s', 'now')")
}
}))
.get("/", ({ server }) => Index(server?.url.toString()!, false))
.get("/js", ({ server }) => Index(server?.url.toString()!, true))
.post("/upload", async ({ set, body, query }) => {
@@ -46,13 +65,18 @@ const app = new Elysia({
const uuid = randomUUIDv7()
let content = Buffer.from(await body.file.bytes())
let encrypted = false;
let delete_at: number | null = null
if (body.delete_in_minutes) {
delete_at = Math.floor(Date.now() / 1000) + Number(body.delete_in_minutes) * 60
}
if (body.encrypted === "on") {
encrypted = true
} else if (body.password) {
content = encrypt(content, body.password)
encrypted = true
}
db.exec("INSERT INTO files (uuid, filename, content, filetype, encrypted) VALUES (?, ?, ?, ?, ?)", [uuid, body.filename || body.file.name, content, body.filetype, encrypted])
db.exec("INSERT INTO files (uuid, filename, content, filetype, encrypted, delete_at) VALUES (?, ?, ?, ?, ?, ?)",
[uuid, body.filename || body.file.name, content, body.filetype, encrypted, delete_at])
set.status = StatusMap["See Other"]
if (query.withJs) {
set.headers["location"] = `/show-js/${uuid}`
@@ -62,12 +86,21 @@ const app = new Elysia({
return `Created with id: ${uuid}`
}, {
body: t.Object({ file: t.File(), filename: t.Optional(t.String()), filetype: t.String()/*todo:verifiy via t.Enum*/, password: t.Optional(t.String()), encrypted: t.Optional(t.String()) }),
body: t.Object({
file: t.File(),
filename: t.Optional(t.String()),
//filetype: t.String()/*todo:verifiy via t.Enum*/,
filetype: t.Enum(stringArrayToEnum(filetypes)),
password: t.Optional(t.String()),
encrypted: t.Optional(t.String()),
delete_in_minutes: t.String({ format: "regex", pattern: "(^$|^[0-9]+$)" })
}),
query: t.Object({ withJs: t.Optional(t.Boolean()) })
})
.get("/show/:uuid", async ({ set, params, cookie }) => {
const result = db.prepare("SELECT filename, content, filetype, encrypted FROM files WHERE uuid = ?").get(params.uuid) as { filename: string, content: Uint8Array, filetype: string, encrypted: boolean } || null
const result = db.prepare("SELECT filename, content, filetype, encrypted, delete_at FROM files WHERE uuid = ?").get(params.uuid) as
{ filename: string, content: Uint8Array, filetype: string, encrypted: boolean, delete_at: number | null } || null
if (!result) {
set.status = StatusMap["Not Found"]
return NotFound()
@@ -89,17 +122,18 @@ const app = new Elysia({
}
}
}
return ShowFile(result.filename, params.uuid, result.content, result.filetype)
return ShowFile(result.filename, params.uuid, result.content, result.filetype, result.delete_at)
}, { params: t.Object({ uuid: t.String() }) })
.get("/show-js/:uuid", async ({ set, params, cookie }) => {
const result = db.prepare("SELECT filename, content, filetype, encrypted FROM files WHERE uuid = ?").get(params.uuid) as { filename: string, content: Uint8Array, filetype: string, encrypted: boolean } || null
const result = db.prepare("SELECT filename, content, filetype, encrypted, delete_at FROM files WHERE uuid = ?").get(params.uuid) as
{ filename: string, content: Uint8Array, filetype: string, encrypted: boolean, delete_at: number | null } || null
if (!result) {
set.status = StatusMap["Not Found"]
return NotFound()
}
return ShowFile(result.filename, params.uuid, null, result.filetype)
return ShowFile(result.filename, params.uuid, null, result.filetype, result.delete_at)
}, { params: t.Object({ uuid: t.String() }) })
.post("/set-cookie/:uuid", ({ set, body, params }) => {