diff --git a/src/index.ts b/src/index.ts index 9ce1f09..0889a60 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,24 +16,24 @@ * along with this program. If not, see . */ -import {AwsClient} from "aws4fetch"; -import {customAlphabet} from "nanoid"; -import {sha256} from "js-sha256"; +import {AwsClient} from 'aws4fetch'; +import {customAlphabet} from 'nanoid'; +import {sha256} from 'js-sha256'; // Constants -const SERVICE_URL = "pb.nekoul.com" -const PASTE_INDEX_HTML_URL = "https://raw.githubusercontent.com/rikkaneko/paste/main/paste.html" -const UUID_LENGTH = 4 +const SERVICE_URL = 'pb.nekoul.com'; +const PASTE_INDEX_HTML_URL = 'https://raw.githubusercontent.com/rikkaneko/paste/main/paste.html'; +const UUID_LENGTH = 4; export interface Env { PASTE_INDEX: KVNamespace; AWS_ACCESS_KEY_ID: string; AWS_SECRET_ACCESS_KEY: string; - ENDPOINT: string + ENDPOINT: string; } const API_SPEC_TEXT = -`Paste service https://${SERVICE_URL} + `Paste service https://${SERVICE_URL} [API Specification] GET / Fetch the Web frontpage for uploading text/file [x] @@ -78,74 +78,74 @@ Last update on 7 June. `; const gen_id = customAlphabet( - "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", UUID_LENGTH); + '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', UUID_LENGTH); export default { async fetch( request: Request, env: Env, - ctx: ExecutionContext + ctx: ExecutionContext, ): Promise { const {url, method, headers} = request; const {pathname} = new URL(url); - const path = pathname.replace(/\/+$/, "") || "/"; + const path = pathname.replace(/\/+$/, '') || '/'; let cache = caches.default; const s3 = new AwsClient({ accessKeyId: env.AWS_ACCESS_KEY_ID, - secretAccessKey: env.AWS_SECRET_ACCESS_KEY + secretAccessKey: env.AWS_SECRET_ACCESS_KEY, }); // Special path - if (path === "/favicon.ico" && method == "GET") { + if (path === '/favicon.ico' && method == 'GET') { return new Response(null, { headers: { - "cache-control": "public, max-age=172800", + 'cache-control': 'public, max-age=172800', }, - status: 404 - }) + status: 404, + }); } - if (path === "/api" && method == "GET") { + if (path === '/api' && method == 'GET') { return new Response(API_SPEC_TEXT); } - if (path === "/") { + if (path === '/') { switch (method) { // Fetch the HTML for uploading text/file - case "GET": { + case 'GET': { return await fetch(PASTE_INDEX_HTML_URL, { cf: { - cacheEverything: true - } + cacheEverything: true, + }, }).then(value => { let res = new Response(value.body, value); // Add the correct content-type to response header - res.headers.set("content-type", "text/html; charset=UTF-8;"); + res.headers.set('content-type', 'text/html; charset=UTF-8;'); // Remove the default CSP header - res.headers.delete("content-security-policy"); + res.headers.delete('content-security-policy'); return res; - }) + }); } // Create new paste - case "POST": + case 'POST': const uuid = gen_id(); let buffer: ArrayBuffer; let title: string | undefined; // Handle content-type - const content_type = headers.get("content-type") || ""; + const content_type = headers.get('content-type') || ''; let mime_type: string | undefined; let password: string | undefined; let read_limit: number | undefined; // Content-Type: multipart/form-data - if (content_type.includes("form")) { + if (content_type.includes('form')) { const formdata = await request.formData(); - const data = formdata.get("u"); + const data = formdata.get('u'); if (data === null) { - return new Response("Invalid request.\n", { - status: 422 - }) + return new Response('Invalid request.\n', { + status: 422, + }); } // File if (data instanceof File) { @@ -155,29 +155,29 @@ export default { buffer = await data.arrayBuffer(); // Text } else { - buffer = new TextEncoder().encode(data) - mime_type = "text/plain; charset=UTF-8;" + buffer = new TextEncoder().encode(data); + mime_type = 'text/plain; charset=UTF-8;'; } // Set password - const pass = formdata.get("pass"); - if (typeof pass === "string") { + const pass = formdata.get('pass'); + if (typeof pass === 'string') { password = pass || undefined; } - const count = formdata.get("read-limit"); - if (typeof count === "string" && !isNaN(+count)) { + const count = formdata.get('read-limit'); + if (typeof count === 'string' && !isNaN(+count)) { read_limit = Number(count) || undefined; } // Raw body } else { - if (headers.has("x-title")) { - title = headers.get("x-title") || ""; + if (headers.has('x-title')) { + title = headers.get('x-title') || ''; } - mime_type = headers.get("content-type") || mime_type; - password = headers.get("x-pass") || undefined; - const count = headers.get("x-read-limit") || undefined; + mime_type = headers.get('content-type') || mime_type; + password = headers.get('x-pass') || undefined; + const count = headers.get('x-read-limit') || undefined; if (count !== undefined && !isNaN(+count)) { read_limit = Number(count) || undefined; } @@ -186,30 +186,30 @@ export default { // Check password rules if (password && !check_password_rules(password)) { - return new Response("Invalid password. " + - "Password must contain alphabets and digits only, and has a length of 4 or more.", { - status: 422 - }) + return new Response('Invalid password. ' + + 'Password must contain alphabets and digits only, and has a length of 4 or more.', { + status: 422, + }); } // Check request.body size <= 10MB const size = buffer.byteLength; if (size > 10485760) { - return new Response("Paste size must be under 10MB.\n", { - status: 422 + return new Response('Paste size must be under 10MB.\n', { + status: 422, }); } // Check request.body size not empty if (buffer.byteLength == 0) { - return new Response("Paste cannot be empty.\n", { - status: 422 + return new Response('Paste cannot be empty.\n', { + status: 422, }); } const res = await s3.fetch(`${env.ENDPOINT}/${uuid}`, { - method: "PUT", - body: buffer + method: 'PUT', + body: buffer, }); if (res.ok) { @@ -217,18 +217,18 @@ export default { const descriptor: PasteIndexEntry = { title: title ?? undefined, last_modified: Date.now(), - password: password? sha256(password).slice(0, 16): undefined, + password: password ? sha256(password).slice(0, 16) : undefined, size, read_count_remain: read_limit, - mime_type + mime_type, }; // Key will be expired after 28 day if unmodified ctx.waitUntil(env.PASTE_INDEX.put(uuid, JSON.stringify(descriptor), {expirationTtl: 100800})); return new Response(get_paste_info(uuid, descriptor)); } else { - return new Response("Unable to upload the paste.\n", { - status: 500 + return new Response('Unable to upload the paste.\n', { + status: 500, }); } @@ -236,38 +236,38 @@ export default { } else if (path.length >= UUID_LENGTH + 1) { // RegExpr to match //