Update README.md with response

Fix logic error in `large_upload/complete/uuid`

Signed-off-by: Joe Ma <rikkaneko23@gmail.com>
This commit is contained in:
Joe Ma 2025-08-01 21:34:28 +08:00
parent 2d7ce3f1c4
commit 0301a9d9f8
No known key found for this signature in database
GPG key ID: 7A0ECF5F5EDC587F
2 changed files with 87 additions and 27 deletions

112
README.md
View file

@ -1,8 +1,12 @@
# Paste # Paste
This is a pastebin-like, simple file sharing application targeted to run on Cloudflare Worker. This project is a fast, anonymous file and text sharing platform built on a serverless architecture with live instance available at [pb.nekoid.cc](https://pb.nekoid.cc), deployed as a Cloudflare Workers, written in Typescript. Originally designed to quickly share logs, configuration files, and even command output with another computer.
[pb.nekoid.cc](http://pb.nekoid.cc) 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. This service enables users to quickly and anonymously share files, texts, and URLs with QR Codes and shortened URLs.
All the upload files are stored in an object service, which can be any S3-compatible service, like AWS S3. It supports fast file upload
and fetch via RESTful API, web interface and cURL for console, and caching of frequently accessed files with Cloudflare CDN.
The maximum upload file size is limited to **250 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.* *All data may be deleted or expired without any notification and guarantee.*
Please **DO NOT** abuse this service. Please **DO NOT** abuse this service.
@ -30,7 +34,7 @@ It is worth noting that Cloudflare Worker is run *before* the cache. Therefore,
## Environment variable ## Environment variable
|Name|Description| |Variable name|Description|
|-|-| |-|-|
|`SERVICE_URL`|Service URL| |`SERVICE_URL`|Service URL|
|`PASTE_INDEX_HTML_URL`|Service frontpage HTML URL| |`PASTE_INDEX_HTML_URL`|Service frontpage HTML URL|
@ -44,10 +48,11 @@ It is worth noting that Cloudflare Worker is run *before* the cache. Therefore,
|`LARGE_ENDPOINT`|S3 endpoint to upload *large paste*| |`LARGE_ENDPOINT`|S3 endpoint to upload *large paste*|
|`LARGE_DOWNLOAD_ENDPOINT`|S3/CDN endpoint/ to retrieve *large paste*| |`LARGE_DOWNLOAD_ENDPOINT`|S3/CDN endpoint/ to retrieve *large paste*|
(Compatible to any S3-compatible object storage) `AWS_ACCESS_*` and `LARGE_AWS_ACCESS_*` is the access credentials to S3-compatible object storages.
**`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `ENDPOINT` should be kept secret, These environment variable can be set in `wrangler.toml` or using the following command:
i.e., [encrypted store](https://developers.cloudflare.com/workers/platform/environment-variables/#adding-secrets-via-wrangler) ```sh
.** $ pnpm wrangler secret put <KEY>
```
## Usage ## Usage
@ -56,13 +61,13 @@ i.e., [encrypted store](https://developers.cloudflare.com/workers/platform/envir
Upload a file (Raw body) with password enabled Upload a file (Raw body) with password enabled
```sh ```sh
$ curl -g -T ${FILE} -H "x-pass: exmaple1234" "https://pb.nekoid.cc" $ curl -g -X POST -T <file-path> -H "x-pass: exmaple1234" "https://pb.nekoid.cc"
``` ```
Upload a file (Formdata) with password enabled Upload a file (Formdata) with password enabled
```shell ```shell
$ curl -F u=@exmaple.txt -F "pass=example1234" "https://pb.nekoid.cc" $ curl -F u=@<file-path> -F "pass=example1234" "https://pb.nekoid.cc"
``` ```
Upload command ouput as paste Upload command ouput as paste
@ -80,13 +85,13 @@ $ echo "Hello, world!" | curl -F u=@- 'https://pb.nekoid.cc?qr=1'
Get paste Get paste
```shell ```shell
$ curl https://pb.nekoid.cc/uuid $ curl https://pb.nekoid.cc/<uuid>
``` ```
Delete paste Delete paste
```shell ```shell
$ curl -X DELETE https://pb.nekoid.cc/uuid $ curl -X DELETE https://pb.nekoid.cc/<uuid>
``` ```
### **Web** ### **Web**
@ -96,20 +101,20 @@ This HTML form currenly only support paste upload.
## API Specification ## API Specification
### GET / ### `GET /`
Fetch the Web frontpage HTML for uploading text/file (used for browsers) Fetch the Web frontpage HTML for uploading text/file (used for browsers)
### GET /api ### `GET /api`
Fetch API specification Fetch API specification
### GET /\<uuid\> ### `GET /<uuid>`
Fetch the paste by uuid. *If the password is set, this request requires additional `x-pass` header or to 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).* use [HTTP Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication).*
### POST / ### `POST /`
Create new paste. Currently, only `multipart/form-data` and raw request are supported. Create new paste. Currently, only `multipart/form-data` and raw request are supported.
Add `?qr=1` to enable QR code generation for paste link. Add `?qr=1` to enable QR code generation for paste link.
@ -149,7 +154,44 @@ The request body contains the upload content.
|`large_paste`|Large paste(>25MB)| |`large_paste`|Large paste(>25MB)|
|`link`|URL link to be redirected| |`link`|URL link to be redirected|
### GET /\<uuid\>/\<option\> #### Response
Upon a successful upload using `POST /` or a call to `GET /<uuid>/settings`, the endpoint will respond in the following format
In default mode, designed for text console:
```
uuid: MRFS
link: https://pb.nekoid.cc/MRFS
type: paste
title: satanichia.png
mime-type: image/png
size: 2420328 bytes (2.308 MiB)
password: false
access times: 5
max_access_n: -
created at 2025-08-01T06:59:44.336Z
expired at 2025-08-29T06:59:44.336Z
```
In JSON mode (`?json=1`)
```json
{
"uuid":"MRFS",
"link":"https://pb.nekoid.cc/MRFS",
"link_qr":"https://qrcode.nekoid.cc/?q=https%3A%2F%2Fpb.nekoid.cc%2FMRFS&type=svg",
"type":"paste",
"title":"satanichia.png",
"mime_type":"image/png",
"human_readable_size":"2.308 MiB",
"size":2420328,
"password":false,
"access_n":5,
"created":"2025-08-01T06:59:44.336Z",
"expired":"2025-08-29T06:59:44.336Z"
}
```
### `GET /<uuid>/<option>`
Fetch the paste (code) in rendered HTML with syntax highlighting Fetch the paste (code) in rendered HTML with syntax highlighting
Add `?qr=1` to enable QR code generation for paste link. Add `?qr=1` to enable QR code generation for paste link.
@ -164,15 +206,11 @@ Currently, only the following options is supported for `option`
*The authentication requirement is as same as `GET /<uuid>`.* *The authentication requirement is as same as `GET /<uuid>`.*
### DELETE /\<uuid\> ### `DELETE /<uuid>`
Delete paste by uuid. *If the password is set, this request requires additional `x-pass` header* Delete paste by uuid. *If the password is set, this request requires additional `x-pass` header*
### POST /\<uuid\>/settings (Not implemented) ### `POST /api/large_upload/create`
Update paste setting. *If the password is set, this request requires additional `x-pass` header*
### POST /api/large_upload/create
Generate the presigned URL for upload large paste to the given S3 endpoint `LARGE_ENDPOINT` using HTTP `PUT` request. Generate the presigned URL for upload large paste to the given S3 endpoint `LARGE_ENDPOINT` using HTTP `PUT` request.
@ -189,11 +227,30 @@ Generate the presigned URL for upload large paste to the given S3 endpoint `LARG
The `file-size` and `file-sha256sum` field is required. The `file-size` and `file-sha256sum` field is required.
### POST /api/large_upload/complete/\<uuid\> #### Response
```json
{
"uuid": "<new-uuid>",
"expiration": <expiration-time>,
"file_size": <expected-upload-file-size>,
"file_hash": "<expected-upload-file-hash>",
"signed_url": "<upload-path>",
"required_headers": {
"Content-Length": "<expected-upload-file-size>",
"X-Amz-Content-Sha256": "<expected-upload-file-hash>"
}
}
```
Then, you can upload the file to `signed_url` using `PUT` method with and only with the headers supplied in `required_headers`. You can find reference code to perform the transection in the [frontend code](https://github.com/rikkaneko/paste/blob/2d7ce3f1c435103b7f676b0b325c1e0036afaded/frontend/static/paste.js#L274).
Note that you can only upload the specific file matching the `file-size` and `file-sha256-hash` provided in `/create` request.
### `POST /api/large_upload/complete/<uuid>`
Finialize the paste created from `/api/large_upload/create`. Finialize the paste created from `/api/large_upload/create`.
### GET /api/large_upload/\<uuid\> ### `GET /api/large_upload/<uuid>`
Generate the presigned URL for upload large paste to the given S3 endpoint `LARGE_DOWNLOAD_ENDPOINT` using HTTP `GET` request. Generate the presigned URL for upload large paste to the given S3 endpoint `LARGE_DOWNLOAD_ENDPOINT` using HTTP `GET` request.
@ -203,10 +260,13 @@ S3 object lifecycle rules and Cloudflare KV's expiring key can be used to implem
Reference for Amazon S3 can be found in [here](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html) 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). , and Blackblaze B2 in [here](https://www.backblaze.com/b2/docs/lifecycle_rules.html).
## Paste API client
The Paste API client for the command line interface (CLI), as well as versions for Android and iOS, will be available soon. :D
## Remark ## Remark
You are welcome to use my project and depoly your own service. 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`, Due to the fact that the `SERVICE_URL` is hard-coded into the `paste.html`,
you may simply use `Ctrl`+`R` to replace `pb.nekoid.cc` with your own service URL. you may simply use `Ctrl`+`R` to replace `pb.nekoid.cc` with your own service URL.
### Of course, contribute and report issues are also welcome! \:D Of course, contribute and report issues are also welcome! \:D

View file

@ -207,7 +207,7 @@ router.post('/complete/:uuid', async (request, env, ctx) => {
} }
const descriptor: PasteIndexEntry = JSON.parse(val); const descriptor: PasteIndexEntry = JSON.parse(val);
if (descriptor.paste_type == PasteType.large_paste || !descriptor.upload_track?.pending_upload) { if (descriptor.paste_type !== PasteType.large_paste || !descriptor.upload_track?.pending_upload) {
return new Response('Invalid operation.\n', { return new Response('Invalid operation.\n', {
status: 442, status: 442,
}); });