mirror of
https://github.com/rikkaneko/paste.git
synced 2025-06-06 16:45:41 +00:00
Add README.md
Update API doc Fix incorrect authentication header key in delete Signed-off-by: Joe Ma <rikkaneko23@gmail.com>
This commit is contained in:
parent
3553a5a513
commit
0ea569d47d
2 changed files with 106 additions and 21 deletions
97
README.md
Normal file
97
README.md
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
# Paste
|
||||||
|
This is a pastebin-like, simple file sharing application targeted to run on Cloudflare Worker.
|
||||||
|
[pb.nekoul.com](http://pb.nekoul.com) is the current deployment of this project.
|
||||||
|
The maximum upload file size is limited to **10 MB** and the paste will be kept for **28 days** only by default.
|
||||||
|
*All data may be deleted or expired without any notification and guarantee.*
|
||||||
|
Please **DO NOT** abuse this service.
|
||||||
|
|
||||||
|
## Supported features
|
||||||
|
- [x] Upload paste
|
||||||
|
- [x] Download paste
|
||||||
|
- [x] Delete paste
|
||||||
|
- [ ] Update existing paste
|
||||||
|
- [x] Password protection (support [HTTP Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) and `x-pass` header)
|
||||||
|
- [x] Limit access times
|
||||||
|
- [x] View paste in browsers (only for text and media file)
|
||||||
|
- [ ] Expiring paste (*not support directly, see [this section](#expiring-paste)*)
|
||||||
|
- [ ] Render paste code with syntax highlighting
|
||||||
|
|
||||||
|
## Service architecture
|
||||||
|
This project is designed to use a S3-compatible object storage (via [aws4fetch](https://github.com/mhart/aws4fetch)) as the backend storage
|
||||||
|
and [Cloudflare Worker KV](https://developers.cloudflare.com/workers/runtime-apis/kv) as index.
|
||||||
|
All requests are handled by [Cloudflare Worker](https://developers.cloudflare.com/workers) with the entry point `fetch()`.
|
||||||
|
It is worth noting that Cloudflare Worker is run *before* the cache. Therefore, all requests will not be cached.
|
||||||
|
[Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache/) is used instead to interact with Cloudflare cache.
|
||||||
|
|
||||||
|
## Environment variable
|
||||||
|
|Name|Description|
|
||||||
|
|-|-|
|
||||||
|
|`SERVICE_URL`|The URL of the service|
|
||||||
|
|`PASTE_INDEX_HTML_URL`|The URL of the service frontpage HTML|
|
||||||
|
|`UUID_LENGTH`|The length of the UUID of the paste (Default: 4)|
|
||||||
|
|`PASTE_INDEX`|The Unique ID of Cloudflare KV namespace|
|
||||||
|
|`AWS_ACCESS_KEY_ID`|The AWS access key|
|
||||||
|
|`AWS_SECRET_ACCESS_KEY`|The AWS secret key|
|
||||||
|
|`ENDPOINT`|The gateway endpoint to the S3 service|
|
||||||
|
|
||||||
|
(Compatible to any S3-compatible object storage)
|
||||||
|
**`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `ENDPOINT` should be kept secret, i.e., [put into the encrypted store](https://developers.cloudflare.com/workers/platform/environment-variables/#adding-secrets-via-wrangler).**
|
||||||
|
|
||||||
|
## API Specification
|
||||||
|
### GET /
|
||||||
|
Fetch the Web frontpage HTML for uploading text/file (used for browsers)
|
||||||
|
|
||||||
|
### GET /api
|
||||||
|
Fetch API specification
|
||||||
|
|
||||||
|
### GET /\<uuid\>
|
||||||
|
Fetch the paste by uuid. *If the password is set, this request requires additional `x-pass` header or to use [HTTP Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication).*
|
||||||
|
|
||||||
|
### POST /
|
||||||
|
Create new paste. Currently, only `multipart/form-data` and raw request are supported.
|
||||||
|
|
||||||
|
#### For `multipart/form-data` request,
|
||||||
|
|Form Key|Description|
|
||||||
|
|-|-|
|
||||||
|
|`u`|Upload content|
|
||||||
|
|`pass`|Paste's password|
|
||||||
|
|`read-limit`|The maximum access count|
|
||||||
|
|
||||||
|
#### For raw request,
|
||||||
|
|Header Key|Description|
|
||||||
|
|-|-|
|
||||||
|
|`content-type`|The media type (MIME) of the data and encoding|
|
||||||
|
|`x-title`|File's title|
|
||||||
|
|`x-pass`|Paste's password|
|
||||||
|
|`x-read-limit`|The maximum access count|
|
||||||
|
|
||||||
|
The request body contains the upload content.
|
||||||
|
|
||||||
|
### GET /\<uuid\>/\<option\> (Not implemented)
|
||||||
|
Fetch the paste (code) in rendered HTML with syntax highlighting
|
||||||
|
Currently, only the following options is supported for `option`
|
||||||
|
|Option|Meaning|
|
||||||
|
|-|-|
|
||||||
|
|`settings`|Fetch the paste information|
|
||||||
|
|`download`|Download paste as attachment|
|
||||||
|
|`raw`|Display paste as plain text|
|
||||||
|
|
||||||
|
*The authentication requirement is as same as `GET /\<uuid\>`. *
|
||||||
|
|
||||||
|
### DELETE /\<uuid\>
|
||||||
|
Delete paste by uuid. *If the password is set, this request requires additional `x-pass` header*
|
||||||
|
|
||||||
|
### POST /\<uuid\>/settings (Not implemented)
|
||||||
|
Update paste setting. *If the password is set, this request requires additional `x-pass` header*
|
||||||
|
|
||||||
|
## Expiring paste
|
||||||
|
S3 object lifecycle rules and Cloudflare KV's expiring key can be used to implemented expiring paste.
|
||||||
|
Reference for Amazon S3 can be found in [here](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)
|
||||||
|
, and Blackblaze B2 in [here](https://www.backblaze.com/b2/docs/lifecycle_rules.html).
|
||||||
|
|
||||||
|
## Remark
|
||||||
|
You are welcome to use my project and depoly your own service.
|
||||||
|
Due to the fact that the `SERVICE_URL` is hard-coded into the `paste.html`,
|
||||||
|
you may simply use `Ctrl`+`R` to replace `pb.nekoul.com` with your own service URL.
|
||||||
|
|
||||||
|
### Of course, contribute and report issues are also welcome! \:D
|
30
src/index.ts
30
src/index.ts
|
@ -43,13 +43,15 @@ GET /api Fetch API specification
|
||||||
# Authentication support HTTP Basic access authentication (RFC 7617) or the x-pass header
|
# Authentication support HTTP Basic access authentication (RFC 7617) or the x-pass header
|
||||||
GET /<uuid> Fetch the paste by uuid [x]
|
GET /<uuid> Fetch the paste by uuid [x]
|
||||||
|
|
||||||
GET /<uuid>/<lang> Fetch the paste (code) in rendered HTML with syntax highlighting [ ]
|
# Currently, only the following options is supported for <option>,
|
||||||
GET /<uuid>/settings Fetch the paste information [x]
|
# "settings": Fetch the paste information
|
||||||
GET /<uuid>/download Download the paste [x]
|
# "download": Download paste as attachment
|
||||||
|
# "raw": Display paste as plain text
|
||||||
|
GET /<uuid>/<option> Fetch the paste (code) in rendered HTML with syntax highlighting [ ]
|
||||||
|
|
||||||
# Only support multipart/form-data and raw request
|
# Only support multipart/form-data and raw request
|
||||||
# For form-data, u=<upload-data>, both title and content-type is deduced from the u
|
# For form-data, u=<upload-data>, both title and content-type is deduced from the u
|
||||||
# The following key is supported for both HTTP form request and headers
|
# The following key is supported for both HTTP form request and headers, prefix "x-" for header keys
|
||||||
# x-title: File title, i.e.,
|
# x-title: File title, i.e.,
|
||||||
# content-type: The media type (MIME) of the data and encoding, i.e., text/plain; charset=UTF-8;
|
# content-type: The media type (MIME) of the data and encoding, i.e., text/plain; charset=UTF-8;
|
||||||
# x-pass: Paste password
|
# x-pass: Paste password
|
||||||
|
@ -62,9 +64,6 @@ POST /<uuid>/settings Update paste setting, i.e., passcode and valid time [ ]
|
||||||
# For paste with password protected, all API call related to the pastes requires additional x-pass header
|
# For paste with password protected, all API call related to the pastes requires additional x-pass header
|
||||||
|
|
||||||
* uuid: [A-z0-9]{${UUID_LENGTH}}
|
* uuid: [A-z0-9]{${UUID_LENGTH}}
|
||||||
# Currently support two options
|
|
||||||
# "download": Download paste as attachment
|
|
||||||
# "raw": Display paste as plain text
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
* Password protection [x]
|
* Password protection [x]
|
||||||
|
@ -76,7 +75,7 @@ Limitation
|
||||||
* Max. 10MB file size upload
|
* Max. 10MB file size upload
|
||||||
* Paste will be kept for 180 days only
|
* Paste will be kept for 180 days only
|
||||||
|
|
||||||
Last update on 2 June.
|
Last update on 7 June.
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const gen_id = customAlphabet(
|
const gen_id = customAlphabet(
|
||||||
|
@ -92,16 +91,6 @@ export default {
|
||||||
const {pathname} = new URL(url);
|
const {pathname} = new URL(url);
|
||||||
const path = pathname.replace(/\/+$/, "") || "/";
|
const path = pathname.replace(/\/+$/, "") || "/";
|
||||||
let cache = caches.default;
|
let cache = caches.default;
|
||||||
// Bypass script will also bypass (1) password authentication and (2) auto expire on access count
|
|
||||||
// Bypass script to get cached response faster
|
|
||||||
// if (method == "GET") {
|
|
||||||
// let cached = await cache.match(url);
|
|
||||||
// if (cached !== undefined) {
|
|
||||||
// let {readable, writable} = new TransformStream();
|
|
||||||
// cached.body!.pipeTo(writable);
|
|
||||||
// return new Response(readable, cached);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const s3 = new AwsClient({
|
const s3 = new AwsClient({
|
||||||
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
accessKeyId: env.AWS_ACCESS_KEY_ID,
|
||||||
|
@ -188,7 +177,6 @@ export default {
|
||||||
}
|
}
|
||||||
mime_type = headers.get("content-type") || mime_type;
|
mime_type = headers.get("content-type") || mime_type;
|
||||||
password = headers.get("x-pass") || undefined;
|
password = headers.get("x-pass") || undefined;
|
||||||
// Handle read-limit:read_count_remain
|
|
||||||
const count = headers.get("x-read-limit") || undefined;
|
const count = headers.get("x-read-limit") || undefined;
|
||||||
if (count !== undefined && !isNaN(+count)) {
|
if (count !== undefined && !isNaN(+count)) {
|
||||||
read_limit = Number(count) || undefined;
|
read_limit = Number(count) || undefined;
|
||||||
|
@ -396,8 +384,8 @@ export default {
|
||||||
|
|
||||||
// Check password if needed
|
// Check password if needed
|
||||||
if (descriptor.password !== undefined) {
|
if (descriptor.password !== undefined) {
|
||||||
if (headers.has("pass")) {
|
if (headers.has("x-pass")) {
|
||||||
const pass = headers.get("pass");
|
const pass = headers.get("x-pass");
|
||||||
if (descriptor.password !== sha256(pass!).slice(0, 16)) {
|
if (descriptor.password !== sha256(pass!).slice(0, 16)) {
|
||||||
return new Response("Incorrect password.\n", {
|
return new Response("Incorrect password.\n", {
|
||||||
status: 403
|
status: 403
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue