mirror of
https://github.com/rikkaneko/paste.git
synced 2025-06-06 16:45:41 +00:00
Add Content-Type header to response
Support auto-detection of MIME types based on filename extension Signed-off-by: Joe Ma <rikkaneko23@gmail.com>
This commit is contained in:
parent
c9e3376d21
commit
1ec2a9f7f0
4 changed files with 47 additions and 31 deletions
|
@ -3,11 +3,13 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aws4fetch": "^1.0.13",
|
"aws4fetch": "^1.0.13",
|
||||||
"nanoid": "^3.3.4"
|
"nanoid": "^3.3.4",
|
||||||
|
"mime-types": "^2.1.35"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^3.11.0",
|
"@cloudflare/workers-types": "^3.11.0",
|
||||||
"typescript": "^4.7.2",
|
"typescript": "^4.7.2",
|
||||||
"wrangler": "^2.0.7"
|
"wrangler": "^2.0.7",
|
||||||
|
"@types/mime-types": "^2.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<h4>Upload file</h4>
|
<h4>Upload file</h4>
|
||||||
<form action="https://paste.nekoul.com" method="POST" enctype=multipart/form-data>
|
<form action="https://paste.nekoul.com" method="POST" enctype=multipart/form-data>
|
||||||
<div>
|
<div>
|
||||||
<input id="upload_file" type="file" name="upload-content">
|
<input id="upload_file" type="file" name="u">
|
||||||
<div><input type="submit" value="Send"> (<span id="file_size">0 byte</span>)</div>
|
<div><input type="submit" value="Send"> (<span id="file_size">0 byte</span>)</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
<form action="https://paste.nekoul.com" method="POST" enctype=multipart/form-data>
|
<form action="https://paste.nekoul.com" method="POST" enctype=multipart/form-data>
|
||||||
<div>
|
<div>
|
||||||
<textarea id="text_input" style="width: 30%; max-width: 100%; " rows ="5" cols="50"
|
<textarea id="text_input" style="width: 30%; max-width: 100%; " rows ="5" cols="50"
|
||||||
name="upload-content" placeholder="Paste your text here..."></textarea>
|
name="u" placeholder="Paste your text here..."></textarea>
|
||||||
<div><input type="submit" value="Send"></div>
|
<div><input type="submit" value="Send"></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
67
src/index.ts
67
src/index.ts
|
@ -17,7 +17,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AwsClient} from "aws4fetch";
|
import {AwsClient} from "aws4fetch";
|
||||||
import { customAlphabet } from 'nanoid'
|
import {customAlphabet} from "nanoid";
|
||||||
|
import {contentType} from "mime-types"
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const SERVICE_URL = "paste.nekoul.com"
|
const SERVICE_URL = "paste.nekoul.com"
|
||||||
|
@ -93,7 +94,7 @@ export default {
|
||||||
}).then(value => {
|
}).then(value => {
|
||||||
let res = new Response(value.body, value);
|
let res = new Response(value.body, value);
|
||||||
// Add the correct content-type to response header
|
// 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
|
// Remove the default CSP header
|
||||||
res.headers.delete("content-security-policy");
|
res.headers.delete("content-security-policy");
|
||||||
return res;
|
return res;
|
||||||
|
@ -102,15 +103,16 @@ export default {
|
||||||
|
|
||||||
// Create new paste
|
// Create new paste
|
||||||
case "POST":
|
case "POST":
|
||||||
let uuid = gen_id();
|
const uuid = gen_id();
|
||||||
let buffer: ArrayBuffer;
|
let buffer: ArrayBuffer;
|
||||||
let title: string | undefined;
|
let title: string | undefined;
|
||||||
// Handle content-type
|
// Handle content-type
|
||||||
const content_type = headers.get("content-type") || "";
|
const content_type = headers.get("content-type") || "";
|
||||||
|
let mime: string | undefined;
|
||||||
// Content-Type: multipart/form-data
|
// Content-Type: multipart/form-data
|
||||||
if (content_type.includes("form")) {
|
if (content_type.includes("form")) {
|
||||||
let formdata = await request.formData();
|
const formdata = await request.formData();
|
||||||
let data = formdata.get("upload-content");
|
const data = formdata.get("u");
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
return new Response("Invalid request.\n", {
|
return new Response("Invalid request.\n", {
|
||||||
status: 422
|
status: 422
|
||||||
|
@ -120,14 +122,20 @@ export default {
|
||||||
if (data instanceof File) {
|
if (data instanceof File) {
|
||||||
title = data.name ?? undefined;
|
title = data.name ?? undefined;
|
||||||
buffer = await data.arrayBuffer();
|
buffer = await data.arrayBuffer();
|
||||||
|
mime = data.type;
|
||||||
// Text
|
// Text
|
||||||
} else {
|
} else {
|
||||||
buffer = new TextEncoder().encode(data)
|
buffer = new TextEncoder().encode(data)
|
||||||
|
mime = "text/plain; charset=UTF-8;"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw body
|
// Raw body
|
||||||
} else {
|
} else {
|
||||||
title = headers.get("title") ?? undefined;
|
if (headers.has("title")) {
|
||||||
|
title = headers.get("title")!;
|
||||||
|
mime = contentType(title) || undefined;
|
||||||
|
}
|
||||||
|
mime = headers.get("content-type") ?? mime;
|
||||||
buffer = await request.arrayBuffer();
|
buffer = await request.arrayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,19 +153,20 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await s3.fetch(`${env.ENDPOINT}/${uuid}`, {
|
const res = await s3.fetch(`${env.ENDPOINT}/${uuid}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: buffer
|
body: buffer
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
// Upload success
|
// Upload success
|
||||||
let descriptor: PasteIndexEntry = {
|
const descriptor: PasteIndexEntry = {
|
||||||
title: title ?? undefined,
|
title: title ?? undefined,
|
||||||
|
mime_type: mime,
|
||||||
last_modified: Date.now()
|
last_modified: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
let counter = await env.PASTE_INDEX.get("__count__") || "0";
|
const counter = await env.PASTE_INDEX.get("__count__") || "0";
|
||||||
await env.PASTE_INDEX.put(uuid, JSON.stringify(descriptor));
|
await env.PASTE_INDEX.put(uuid, JSON.stringify(descriptor));
|
||||||
await env.PASTE_INDEX.put("__count__", (Number(counter) + 1).toString());
|
await env.PASTE_INDEX.put("__count__", (Number(counter) + 1).toString());
|
||||||
return new Response(get_paste_info(uuid, descriptor));
|
return new Response(get_paste_info(uuid, descriptor));
|
||||||
|
@ -185,13 +194,13 @@ export default {
|
||||||
status: 442
|
status: 442
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let val = await env.PASTE_INDEX.get(uuid);
|
const val = await env.PASTE_INDEX.get(uuid);
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
return new Response("Paste not found.\n", {
|
return new Response("Paste not found.\n", {
|
||||||
status: 404
|
status: 404
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let descriptor: PasteIndexEntry = JSON.parse(val);
|
const descriptor: PasteIndexEntry = JSON.parse(val);
|
||||||
|
|
||||||
// Handling /<uuid>/settings
|
// Handling /<uuid>/settings
|
||||||
if (option === "settings") {
|
if (option === "settings") {
|
||||||
|
@ -223,12 +232,6 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
res = new Response(origin.body, origin);
|
res = new Response(origin.body, origin);
|
||||||
// Remove x-amz-* headers
|
|
||||||
for (let [key, value] of res.headers.entries()) {
|
|
||||||
if (key.startsWith("x-amz")) {
|
|
||||||
res.headers.delete(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
// UUID exists in index but not found in remote object storage service
|
// UUID exists in index but not found in remote object storage service
|
||||||
|
@ -237,10 +240,18 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.headers.append("Cache-Control", "max-age=3600");
|
// Remove x-amz-* headers
|
||||||
|
for (let [key, value] of res.headers.entries()) {
|
||||||
|
if (key.startsWith("x-amz")) {
|
||||||
|
res.headers.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.headers.set("cache-control", "public, max-age=18000");
|
||||||
|
res.headers.set("content-type", descriptor.mime_type ?? "application/octet-stream");
|
||||||
|
|
||||||
if (option === "download") {
|
if (option === "download") {
|
||||||
res.headers.append("Content-Disposition",
|
res.headers.set("content-disposition",
|
||||||
`attachment; filename="${encodeURIComponent(descriptor.title ?? uuid)}"`);
|
`attachment; filename="${encodeURIComponent(descriptor.title ?? uuid)}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +278,7 @@ export default {
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
await env.PASTE_INDEX.delete(uuid);
|
await env.PASTE_INDEX.delete(uuid);
|
||||||
let counter = await env.PASTE_INDEX.get("__count__") || "1";
|
const counter = await env.PASTE_INDEX.get("__count__") || "1";
|
||||||
await env.PASTE_INDEX.put("__count__", (Number(counter) - 1).toString());
|
await env.PASTE_INDEX.put("__count__", (Number(counter) - 1).toString());
|
||||||
|
|
||||||
// Invalidate CF cache
|
// Invalidate CF cache
|
||||||
|
@ -291,18 +302,20 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
function get_paste_info(uuid: string, descriptor: PasteIndexEntry): string {
|
function get_paste_info(uuid: string, descriptor: PasteIndexEntry): string {
|
||||||
let date = new Date(descriptor.last_modified)
|
const date = new Date(descriptor.last_modified)
|
||||||
return `https://${SERVICE_URL}/${uuid}
|
return `https://${SERVICE_URL}/${uuid}
|
||||||
ID: ${uuid}
|
id: ${uuid}
|
||||||
Title: ${descriptor.title || "<empty>"}
|
title: ${descriptor.title || "<empty>"}
|
||||||
Password: ${(!!descriptor.password)}
|
mime-type: ${descriptor.mime_type ?? "application/octet-stream"}
|
||||||
Editable: ${descriptor.editable? descriptor.editable: true}
|
password: ${(!!descriptor.password)}
|
||||||
Last modified at ${date.toISOString()}
|
editable: ${descriptor.editable? descriptor.editable: true}
|
||||||
|
last modified at ${date.toISOString()}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PasteIndexEntry {
|
interface PasteIndexEntry {
|
||||||
title?: string
|
title?: string,
|
||||||
|
mime_type?: string,
|
||||||
last_modified: number,
|
last_modified: number,
|
||||||
password?: string
|
password?: string
|
||||||
editable?: boolean // Default: True
|
editable?: boolean // Default: True
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
name = "paste"
|
name = "paste"
|
||||||
main = "src/index.ts"
|
main = "src/index.ts"
|
||||||
compatibility_date = "2022-05-30"
|
compatibility_date = "2022-05-30"
|
||||||
|
node_compat = true
|
||||||
kv_namespaces = [
|
kv_namespaces = [
|
||||||
{ binding = "PASTE_INDEX", id = "a578863da0564cd7beadd9ce4a2d53e8", preview_id = "66d9440e13124099a5e508fe1ff0a489" }
|
{ binding = "PASTE_INDEX", id = "a578863da0564cd7beadd9ce4a2d53e8", preview_id = "66d9440e13124099a5e508fe1ff0a489" }
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue