202 lines
10 KiB
Markdown
202 lines
10 KiB
Markdown
# Pastable
|
|
|
|
Pastable is a free and open-source Plabble initiative for sharing text, code, short links, and one-time secrets.
|
|
|
|
## Run locally
|
|
|
|
With Rust and Cargo installed:
|
|
|
|
```sh
|
|
cargo run
|
|
```
|
|
|
|
The server listens on `0.0.0.0:3000` and is available locally at `http://localhost:3000`.
|
|
Set `PORT` to listen on another port.
|
|
|
|
On startup, the server creates the SQLite database and initializes its tables before accepting requests.
|
|
The default database is `pastable.db` in the working directory. Set `DATABASE_URL` to use a different location:
|
|
|
|
```sh
|
|
DATABASE_URL=sqlite://data.db cargo run
|
|
```
|
|
|
|
Route handlers can access the shared database pool with the `State(pool): State<SqlitePool>` extractor.
|
|
|
|
## Browser app
|
|
|
|
Open `/gui` for the browser workspace. Create text pastes, one-time secrets, and short links; choose custom IDs, languages, expiry, and view limits. After saving, a dialog prominently displays the returned link for copying or opening. Opening a share navigates directly to its page or redirect.
|
|
|
|
The language dropdown is populated from irosashi's supported grammars. The home page renders this README as HTML and shares the browser app's logo, navigation, and styling.
|
|
|
|
Sign in with HTTP Basic credentials to browse, filter, edit, and delete your own shares. A new username creates an account. Credentials stay in page memory and are cleared on sign-out or reload. Creating and managing shares uses the existing API without page reloads.
|
|
|
|
Opening a paste or following a short link counts as a view. Listing records and loading them into the editor do not. Updates preserve omitted options and view counters.
|
|
|
|
## Features
|
|
|
|
- Share text and code, with optional syntax highlighting.
|
|
- Set an expiration time or a maximum number of views.
|
|
- Share a secret that can only be viewed once.
|
|
- Create short links.
|
|
- Use HTTP Basic authentication to associate pastes with a user, update them, and list them.
|
|
|
|
## API
|
|
|
|
All paths are relative to the server URL. `{id}` is a path parameter containing a paste or short-link ID.
|
|
|
|
### Create a paste
|
|
|
|
| Method | Path | Behavior |
|
|
| --- | --- | --- |
|
|
| `POST` | `/` | Create a paste with a generated ID. |
|
|
| `POST` | `/{id}` | Use the requested ID if available; otherwise generate a random ID. |
|
|
| `POST` | `/once` | Create a paste with `max_views` fixed to `1`. |
|
|
| `POST` | `/once/{id}` | Request a custom ID and set `max_views` to `1`. Generate a random ID if the requested ID is taken. |
|
|
|
|
**Request body:** raw text, or an `application/x-www-form-urlencoded` form. Use a form to supply optional parameters.
|
|
|
|
| Form parameter | Type | Required | Default | Description |
|
|
| --- | --- | --- | --- | --- |
|
|
| `content` | String | Yes | — | Text or code to store. For raw text requests, the entire body supplies this value. |
|
|
| `language` | String | No | Auto detect | Language for syntax highlighting. When omitted, irosashi detects from the first line (for example a shebang), then from a requested custom ID such as `main.rs`. Without a match, the paste stays plain text. Use `text` to force plain text. |
|
|
| `expires` | Integer | No | No expiration | Lifetime in seconds from the request; `0` expires immediately. Must be nonnegative. |
|
|
| `max_views` | Integer | No | Unlimited views | Positive maximum number of views before self-destruction. The `/once` routes force this to `1`. |
|
|
|
|
**Authentication:** optional HTTP Basic authentication. If supplied, the paste is associated with that user. An account is created if the user does not exist.
|
|
|
|
**Returns:** the assigned ID as plain text. This is an ID, not a full URL. Always use the returned ID, since a requested custom ID may already be taken.
|
|
|
|
Generated IDs contain exactly 8 alphanumeric characters, generated in Rust with `rand`. Confusing characters (`0`, `1`, `I`, `L`, `O`, `i`, `l`, `o`) are excluded. IDs are shared by pastes and short links. Reserved route names (`gui`, `list`, `once`, `to`, and `static`) and IDs containing `/` are unavailable as custom IDs; these also receive a generated ID.
|
|
|
|
Create a paste from raw text:
|
|
|
|
```sh
|
|
curl --request POST 'http://localhost:3000/' \
|
|
--header 'Content-Type: text/plain' \
|
|
--data-binary 'Hello, world!'
|
|
```
|
|
|
|
Create a paste with optional parameters:
|
|
|
|
```sh
|
|
curl 'http://localhost:3000/' \
|
|
--data-urlencode 'content=println!("Hello, world!");' \
|
|
--data-urlencode 'language=rust' \
|
|
--data-urlencode 'max_views=5'
|
|
```
|
|
|
|
Request a custom ID for a one-time secret:
|
|
|
|
```sh
|
|
curl --request POST 'http://localhost:3000/once/my-secret' \
|
|
--header 'Content-Type: text/plain' \
|
|
--data-binary 'A secret to share once'
|
|
```
|
|
|
|
### Read a paste
|
|
|
|
`GET /{id}` retrieves a paste by its ID.
|
|
|
|
| Parameter | Location | Required | Description |
|
|
| --- | --- | --- | --- |
|
|
| `id` | Path | Yes | The paste ID returned when it was created. |
|
|
| `Accept` | Header | No | Use `text/html` for syntax-highlighted HTML or `text/plain` for plain text. |
|
|
|
|
**Returns:** the paste as HTML or plain text, based on the `Accept` header. The default is plain text. HTML uses the stored language for syntax highlighting, falling back to plain text for missing or unknown languages.
|
|
|
|
HTML paste pages show the view counter, including the current read, and the maximum when set. HTML and plain-text responses include `X-Paste-Views` and, when limited, `X-Paste-Max-Views` response headers, so displaying the counter does not require another read.
|
|
|
|
The HTML page includes a **Copy raw code** button that preserves the original content and whitespace without another request or view. If clipboard access is unavailable, the raw text is selected for manual copying.
|
|
|
|
Each successful read increments `views`. The final permitted read returns the content and deletes the paste. Missing, expired, and exhausted pastes return `404`. Expired and exhausted records are also cleaned up in the background every minute.
|
|
|
|
```sh
|
|
curl 'http://localhost:3000/my-paste' \
|
|
--header 'Accept: text/plain'
|
|
```
|
|
|
|
### Create a short link
|
|
|
|
| Method | Path | Behavior |
|
|
| --- | --- | --- |
|
|
| `POST` | `/to` | Create a short link with a generated ID. |
|
|
| `POST` | `/to/{id}` | Use the requested ID if available; otherwise generate a random ID. |
|
|
|
|
**Request body:** the destination URL as raw text, or an `application/x-www-form-urlencoded` form with the following parameters:
|
|
|
|
| Form parameter | Type | Required | Description |
|
|
| --- | --- | --- | --- |
|
|
| `href` | String | Yes | Absolute HTTP or HTTPS destination URL. For raw text requests, the entire body supplies this value. |
|
|
| `expires` | Integer | No | Lifetime in seconds from the request; `0` expires immediately. Must be nonnegative. Defaults to no expiration. |
|
|
| `max_views` | Integer | No | Positive maximum number of redirects before self-destruction. Defaults to unlimited redirects. |
|
|
|
|
**Returns:** the assigned ID as plain text. Use it in `/to/{id}` to follow the link.
|
|
|
|
```sh
|
|
curl 'http://localhost:3000/to' \
|
|
--data-urlencode 'href=https://example.com' \
|
|
--data-urlencode 'expires=3600' \
|
|
--data-urlencode 'max_views=5'
|
|
```
|
|
|
|
### Follow a short link
|
|
|
|
`GET /to/{id}` looks up a short link by its ID.
|
|
|
|
**Returns:** a `307 Temporary Redirect` to the destination URL, or `404` if the link is missing, expired, or exhausted. Each successful redirect increments `views`. The final permitted redirect deletes the short link immediately. Expiration and view limits use the same logic as text pastes.
|
|
|
|
### Update a paste
|
|
|
|
`PUT /{id}` updates a paste by its ID.
|
|
|
|
**Authentication:** HTTP Basic authentication of the creator is required.
|
|
|
|
The same request body as [the POST request](#create-a-paste). `content` is required; omitted `language`, `expires`, and `max_views` retain their current values. A supplied `expires` starts a new lifetime from the update request. The view counter is preserved, and `updated_at` is set to the current time.
|
|
|
|
**Returns:** `204 No Content` on success, `401` without authentication, `403` for another user's paste or an anonymous paste, and `404` for a missing, expired, or exhausted paste.
|
|
|
|
### Update a short link
|
|
|
|
`PUT /to/{id}` updates a short link by its ID.
|
|
|
|
**Authentication:** HTTP Basic authentication of the creator is required.
|
|
|
|
Use the same request body as [creating a short link](#create-a-short-link): the new destination as raw text, or a form with `href` and optional `expires` and `max_views`. Omitted options retain their current values. The view counter is preserved, and `updated_at` is set to the current time.
|
|
|
|
**Returns:** `204 No Content` on success, with the same authentication and missing-record errors as the paste update endpoint. Invalid destination URLs return `400`.
|
|
|
|
```sh
|
|
curl --request PUT --user 'alice:example-password' 'http://localhost:3000/to/my-link' \
|
|
--data-urlencode 'href=https://example.org' \
|
|
--data-urlencode 'expires=3600' \
|
|
--data-urlencode 'max_views=10'
|
|
```
|
|
|
|
### Delete a paste
|
|
|
|
`DELETE /{id}` deletes a paste or short link by its ID.
|
|
|
|
**Authentication:** HTTP Basic authentication of the creator is required.
|
|
|
|
**Returns:** `204 No Content` on success, with the same authentication and missing-paste errors as the update endpoint.
|
|
|
|
### List pastes
|
|
|
|
`GET /list` returns the authenticated user's pastes and short links.
|
|
|
|
**Authentication:** HTTP Basic authentication is required.
|
|
|
|
**Returns:** a plain-text list of that user's active records, ordered by creation time and ID. Each entry contains the ID on one line, followed by the content, with a blank line between entries. An empty list returns an empty body. Listing does not increment view counters. Missing authentication returns `401`.
|
|
|
|
With `Accept: text/html`, the response is an HTML table containing IDs, types, content, languages, view counts and limits, creation and update times, and expiration times. Dates are displayed in the browser's local timezone.
|
|
|
|
With `Accept: application/json`, the response is an array of full `Paste` records, including `id`, `paste_type` (`TextV1` or `UrlV1`), `content`, `language`, `expiration`, `max_views`, `views`, `created_at`, `updated_at`, and `owner`. Timestamps are Unix seconds; unset optional values are `null`.
|
|
|
|
```sh
|
|
curl --user 'alice:example-password' 'http://localhost:3000/list'
|
|
```
|
|
|
|
### Home page
|
|
|
|
`GET /` returns the home page.
|