+
+
diff --git a/paste.iml b/paste.iml
new file mode 100644
index 0000000..80cc739
--- /dev/null
+++ b/paste.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index f356f85..71073c7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,8 +1,28 @@
+/*
+ * This file is part of paste.
+ * Copyright (c) 2022 Joe Ma
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
import {AwsClient} from "aws4fetch";
import { customAlphabet } from 'nanoid'
// Constants
-const SERVICE_URL = "https://paste.nekoul.com"
+const SERVICE_URL = "paste.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;
@@ -11,30 +31,37 @@ export interface Env {
ENDPOINT: string
}
-const API_DOCS =
- `Paste service https://paste.nekoul.com
+const API_SPEC_TEXT =
+ `Paste service https://${SERVICE_URL}
-[API Draft]
-GET / Fetch the HTML for uploading text/file [ ]
-GET / Fetch the paste by uuid [x]
-GET // Fetch the paste (code) in rendered HTML with syntax highlighting [ ]
-GET //settings Fetch the paste information [x]
-GET /status Fetch service information [x]
-PUT / Create new paste [x]
-POST / Update the paste by uuid [x]
-DELETE / Delete paste by uuid [x]
-POST //settings Update paste setting, i.e., passcode and valid time [ ]
+[API Specification]
+GET / Fetch the Web frontpage for uploading text/file [x]
+GET /api Fetch API specification
+GET / Fetch the paste by uuid [x]
+GET // Fetch the paste (code) in rendered HTML with syntax highlighting [ ]
+GET //settings Fetch the paste information [x]
+GET //download Download the paste [x]
+POST / Create new paste [x] # Only support multipart/form-data and raw data
+DELETE / Delete paste by uuid [x]
+POST //settings Update paste setting, i.e., passcode and valid time [ ]
+
+* uuid: [A-z0-9]{${UUID_LENGTH}}
+* option: Render language
+
+Features
+* Password protection [ ]
+* Expiring paste [ ]
[ ] indicated not implemented
Limitation
* Max. 10MB file size upload (Max. 100MB body size for free Cloudflare plan)
-Last update on 30 May.
+Last update on 2 June.
`;
const gen_id = customAlphabet(
- "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 8);
+ "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", UUID_LENGTH);
export default {
async fetch(
@@ -50,24 +77,60 @@ export default {
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
});
- // if (hostname !== SERVICE_URL) {
- // // Invalid case
- // return new Response(null, { status: 403 })
- // }
+ // Special path
+ if (path === "/api" && method == "GET") {
+ return new Response(API_SPEC_TEXT);
+ }
if (path === "/") {
switch (method) {
// Fetch the HTML for uploading text/file
case "GET":
- return new Response(API_DOCS);
+ return await fetch(PASTE_INDEX_HTML_URL);
// Create new paste
- case "PUT":
+ case "POST":
let uuid = gen_id();
- let buffer = await request.arrayBuffer();
+ let buffer: ArrayBuffer;
+ let title: string | undefined;
+ // Handle content-type
+ const content_type = headers.get("content-type") || "";
+ // Content-Type: multipart/form-data
+ if (content_type.includes("form")) {
+ let formdata = await request.formData();
+ let data = formdata.get("upload-content");
+ if (data === null) {
+ return new Response("Invalid request.\n", {
+ status: 422
+ })
+ }
+ // File
+ if (data instanceof File) {
+ title = data.name ?? undefined;
+ buffer = await data.arrayBuffer();
+ // Text
+ } else {
+ buffer = new TextEncoder().encode(data)
+ }
+
+ // Raw body
+ } else {
+ title = headers.get("title") ?? undefined;
+ buffer = await request.arrayBuffer();
+ }
+
// Check request.body size <= 10MB
if (buffer.byteLength > 10485760) {
- return new Response("File size must be under 10MB.\n");
+ 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
+ });
}
let res = await s3.fetch(`${env.ENDPOINT}/${uuid}`, {
@@ -78,10 +141,8 @@ export default {
if (res.ok) {
// Upload success
let descriptor: PasteIndexEntry = {
- title: headers.get("title") || undefined,
- last_modified: Date.now(),
- password: undefined,
- editable: undefined // Default: true
+ title: title ?? undefined,
+ last_modified: Date.now()
};
let counter = await env.PASTE_INDEX.get("__count__") || "0";
@@ -96,7 +157,7 @@ export default {
}
- } else if (path.length >= 9) {
+ } else if (path.length >= UUID_LENGTH + 1) {
// RegExpr to match //