pict-rs/README.md

497 lines
20 KiB
Markdown
Raw Normal View History

2020-06-07 02:01:04 +00:00
# pict-rs
_a simple image hosting service_
2021-02-11 01:38:42 +00:00
## Links
- Find the code on [gitea](https://git.asonix.dog/asonix/pict-rs)
- Join the discussion on [matrix](https://matrix.to/#/#pictrs:matrix.asonix.dog?via=matrix.asonix.dog)
- Hit me up on [mastodon](https://masto.asonix.dog/@asonix)
2020-06-07 02:01:04 +00:00
## Usage
### Running
```
2022-09-28 23:23:41 +00:00
$ pict-rs -h
2022-04-03 18:31:52 +00:00
A simple image hosting service
2020-06-07 02:01:04 +00:00
2022-09-28 23:23:41 +00:00
Usage: pict-rs [OPTIONS] <COMMAND>
Commands:
run Runs the pict-rs web server
filesystem Migrate from the provided filesystem storage
object-storage Migrate from the provided object storage
help Print this message or the help of the given subcommand(s)
Options:
-c, --config-file <CONFIG_FILE>
Path to the pict-rs configuration file
--old-db-path <OLD_DB_PATH>
Path to the old pict-rs sled database
--log-format <LOG_FORMAT>
Format of logs printed to stdout [possible values: compact, json, normal, pretty]
--log-targets <LOG_TARGETS>
Log levels to print to stdout, respects RUST_LOG formatting
--console-address <CONSOLE_ADDRESS>
Address and port to expose tokio-console metrics
--console-buffer-capacity <CONSOLE_BUFFER_CAPACITY>
Capacity of the console-subscriber Event Buffer
--opentelemetry-url <OPENTELEMETRY_URL>
URL to send OpenTelemetry metrics
--opentelemetry-service-name <OPENTELEMETRY_SERVICE_NAME>
Service Name to use for OpenTelemetry
--opentelemetry-targets <OPENTELEMETRY_TARGETS>
Log levels to use for OpenTelemetry, respects RUST_LOG formatting
--save-to <SAVE_TO>
File to save the current configuration for reproducible runs
-h, --help
Print help information
-V, --version
Print version information
2021-10-28 05:17:37 +00:00
```
2021-10-19 04:44:56 +00:00
2021-10-28 05:17:37 +00:00
```
2022-09-28 23:23:41 +00:00
$ pict-rs run -h
2022-04-03 18:31:52 +00:00
Runs the pict-rs web server
2021-10-28 05:17:37 +00:00
2022-09-28 23:23:41 +00:00
Usage: pict-rs run [OPTIONS] [COMMAND]
Commands:
filesystem Run pict-rs with filesystem storage
object-storage Run pict-rs with object storage
help Print this message or the help of the given subcommand(s)
Options:
-a, --address <ADDRESS>
The address and port to bind the pict-rs web server
--api-key <API_KEY>
The API KEY required to access restricted routes
--worker-id <WORKER_ID>
ID of this pict-rs node. Doesn't do much yet
--media-preprocess-steps <MEDIA_PREPROCESS_STEPS>
Optional pre-processing steps for uploaded media
--media-skip-validate-imports <MEDIA_SKIP_VALIDATE_IMPORTS>
Whether to validate media on the "import" endpoint [possible values: true, false]
--media-max-width <MEDIA_MAX_WIDTH>
The maximum width, in pixels, for uploaded media
--media-max-height <MEDIA_MAX_HEIGHT>
The maximum height, in pixels, for uploaded media
--media-max-area <MEDIA_MAX_AREA>
The maximum area, in pixels, for uploaded media
--media-max-file-size <MEDIA_MAX_FILE_SIZE>
The maximum size, in megabytes, for uploaded media
--media-max-frame-count <MEDIA_MAX_FRAME_COUNT>
The maximum number of frames allowed for uploaded GIF and MP4s
--media-enable-silent-video <MEDIA_ENABLE_SILENT_VIDEO>
Whether to enable GIF and silent video uploads [possible values: true, false]
2022-09-28 23:23:41 +00:00
--media-enable-full-video <MEDIA_ENABLE_FULL_VIDEO>
Whether to enable full video uploads [possible values: true, false]
--media-video-codec <MEDIA_VIDEO_CODEC>
Enforce a specific video codec for uploaded videos [possible values: h264, h265, av1, vp8, vp9]
--media-audio-codec <MEDIA_AUDIO_CODEC>
Enforce a specific audio codec for uploaded videos [possible values: aac, opus, vorbis]
2022-09-28 23:23:41 +00:00
--media-filters <MEDIA_FILTERS>
Which media filters should be enabled on the `process` endpoint
--media-format <MEDIA_FORMAT>
Enforce uploaded media is transcoded to the provided format [possible values: jpeg, webp, png]
-h, --help
Print help information (use `--help` for more detail)
2022-04-03 18:31:52 +00:00
```
Try running `help` commands for more runtime configuration options
```
$ pict-rs run filesystem -h
$ pict-rs run object-storage -h
$ pict-rs run filesystem sled -h
$ pict-rs run object-storage sled -h
2021-10-28 05:17:37 +00:00
```
2022-04-03 18:31:52 +00:00
See [`pict-rs.toml`](https://git.asonix.dog/asonix/pict-rs/src/branch/main/pict-rs.toml) for more
configuration
2021-10-28 05:17:37 +00:00
2020-06-07 02:01:04 +00:00
#### Example:
2022-04-03 23:23:36 +00:00
Run with the default configuration
```
$ ./pict-rs run
```
2020-06-07 02:01:04 +00:00
Running on all interfaces, port 8080, storing data in /opt/data
```
2022-04-03 23:23:36 +00:00
$ ./pict-rs run -a 0.0.0.0:8080 filesystem -p /opt/data/files sled -p /opt/data/sled-repo
2020-06-07 02:01:04 +00:00
```
Running locally, port 9000, storing data in data/, and converting all uploads to PNG
```
2022-04-03 23:23:36 +00:00
$ ./pict-rs run -a 127.0.0.1:9000 --media-format png filesystem -p data/files sled -p data/sled-repo
2020-06-07 02:01:04 +00:00
```
2020-06-07 19:12:19 +00:00
Running locally, port 8080, storing data in data/, and only allowing the `thumbnail` and `identity` filters
```
2022-04-03 23:23:36 +00:00
$ ./pict-rs run -a 127.0.0.1:8080 --media-filters thumbnail --media-filters identity filesystem -p data/files sled -p data/sled-repo
2020-06-07 19:12:19 +00:00
```
2021-10-28 05:17:37 +00:00
Running from a configuration file
```
$ ./pict-rs -c ./pict-rs.toml run
```
2022-04-03 23:23:36 +00:00
Migrating to object storage from filesystem storage
```
2022-04-03 23:23:36 +00:00
$ ./pict-rs filesystem -p data/sled-repo object-storage -a ACCESS_KEY -b BUCKET_NAME -r REGION -s SECRET_KEY
2021-10-28 05:17:37 +00:00
```
2022-04-03 23:23:36 +00:00
Dumping configuration overrides to a toml file
2021-11-01 15:46:14 +00:00
```
2022-04-03 23:23:36 +00:00
$ ./pict-rs --save-to pict-rs.toml run object-storage -a ACCESS_KEY -b pict-rs -r us-east-1 -s SECRET_KEY sled -p data/sled-repo
2021-11-01 15:46:14 +00:00
```
2020-06-07 02:01:04 +00:00
#### Docker
Run the following commands:
```
# Create a folder for the files (anywhere works)
$ mkdir ./pict-rs
$ cd ./pict-rs
$ mkdir -p volumes/pictrs
$ sudo chown -R 991:991 volumes/pictrs
2021-09-08 21:21:47 +00:00
$ wget https://git.asonix.dog/asonix/pict-rs/raw/branch/main/docker/prod/docker-compose.yml
$ sudo docker-compose up -d
```
###### Note
- pict-rs makes use of the system's temporary folder. This is generally `/tmp` on linux
2021-09-01 00:06:09 +00:00
- pict-rs makes use of an imagemagick security policy at
`/usr/lib/ImageMagick-$VERSION/config-Q16HDRI/policy.xml`
#### Docker Development
2021-09-01 00:06:09 +00:00
###### With Arch
```
2022-04-03 23:26:32 +00:00
$ cargo build
2021-09-01 00:06:09 +00:00
$ sudo docker run --rm -it -p 8080:8080 -v "$(pwd):/mnt" archlinux:latest
# pacman -Syu imagemagick ffmepg perl-image-exiftool
2022-04-03 23:23:36 +00:00
# cp /mnt/docker/prod/root/usr/lib/ImageMagick-7.1.0/config-Q16HDRI/policy.xml /usr/lib/ImageMagick-7.1.0/config-Q16HDRI/
2022-04-03 23:26:32 +00:00
# PATH=$PATH:/usr/bin/vendor_perl RUST_LOG=debug /mnt/target/debug/pict-rs run
2021-09-01 00:06:09 +00:00
```
###### With Alpine
```
2022-04-03 23:26:32 +00:00
$ cross build --target=x86_64-unknown-linux-musl
$ sudo docker run --rm -it -p 8080:8080 -v "$(pwd):/mnt alpine:3.15
2021-09-01 00:06:09 +00:00
# apk add imagemagick ffmpeg exiftool
2022-04-03 23:23:36 +00:00
# cp /mnt/docker/prod/root/usr/lib/ImageMagick-7.1.0/config-Q16HDRI/policy.xml /usr/lib/ImageMagick-7.1.0/config-Q16HDRI/
# RUST_LOG=debug /mnt/target/x86_64-unknown-linux-musl/debug/pict-rs RUN
```
2020-06-07 02:01:04 +00:00
### API
2020-07-11 22:27:49 +00:00
pict-rs offers the following endpoints:
2020-06-07 02:01:04 +00:00
- `POST /image` for uploading an image. Uploaded content must be valid multipart/form-data with an
image array located within the `images[]` key
This endpoint returns the following JSON structure on success with a 201 Created status
```json
{
"files": [
{
"delete_token": "JFvFhqJA98",
2022-04-03 23:23:36 +00:00
"file": "lkWZDRvugm.jpg",
"details": {
"width": 800,
"height": 800,
"content_type": "image/jpeg",
"created_at": "2022-04-08T18:33:42.957791698Z"
2022-04-03 23:23:36 +00:00
}
2020-06-07 02:01:04 +00:00
},
{
"delete_token": "kAYy9nk2WK",
2022-04-03 23:23:36 +00:00
"file": "8qFS0QooAn.jpg",
"details": {
"width": 400,
"height": 400,
"content_type": "image/jpeg",
"created_at": "2022-04-08T18:33:42.957791698Z"
2022-04-03 23:23:36 +00:00
}
2020-06-07 02:01:04 +00:00
},
{
"delete_token": "OxRpM3sf0Y",
2022-04-03 23:23:36 +00:00
"file": "1hJaYfGE01.jpg",
"details": {
"width": 400,
"height": 400,
"content_type": "image/jpeg",
"created_at": "2022-04-08T18:33:42.957791698Z"
2022-04-03 23:23:36 +00:00
}
2020-06-07 02:01:04 +00:00
}
],
"msg": "ok"
}
```
2022-04-03 23:23:36 +00:00
- `POST /image/backgrounded` Upload an image, like the `/image` endpoint, but don't wait to validate and process it.
This endpoint returns the following JSON structure on success with a 202 Accepted status
```json
{
"uploads": [
{
"upload_id": "c61422e1-9294-4f1f-977f-c696b7939467",
},
{
"upload_id": "62cc707f-725c-44b6-908f-2bd8946c3c29"
}
],
"msg": "ok"
}
```
2023-02-25 17:34:48 +00:00
- `GET /image/download?url={url}&backgrounded=(true|false)` Download an image
from a remote server, returning the same JSON payload as the `POST /image` endpoint by default.
if `backgrounded` is set to `true`, then the ingest processing will be queued for later and the
response json will be the same as the `POST /image/backgrounded` endpoint.
- `GET /image/backgrounded/claim?upload_id={uuid}` Wait for a backgrounded upload to complete, claiming it's result
2022-04-03 23:23:36 +00:00
Possible results:
- 200 Ok (validation and ingest complete):
```json
{
"files": [
{
"delete_token": "OxRpM3sf0Y",
2022-04-08 18:03:00 +00:00
"file": "1hJaYfGE01.jpg",
"details": {
"width": 400,
"height": 400,
"content_type": "image/jpeg",
"created_at": "2022-04-08T18:33:42.957791698Z"
2022-04-08 18:03:00 +00:00
}
2022-04-03 23:23:36 +00:00
}
],
"msg": "ok"
}
```
- 422 Unprocessable Entity (validation or otherwise failure):
```json
{
"msg": "Error message about what went wrong with upload"
}
```
- 204 No Content (Upload validation and ingest is not complete, and waiting timed out)
In this case, trying again is fine
2020-06-24 16:58:46 +00:00
- `GET /image/original/{file}` for getting a full-resolution image. `file` here is the `file` key from the
2020-06-07 02:01:04 +00:00
`/image` endpoint's JSON
- `GET /image/details/original/{file}` for getting the details of a full-resolution image.
The returned JSON is structured like so:
```json
{
"width": 800,
"height": 537,
"content_type": "image/webp",
"created_at": "2022-04-08T18:33:42.957791698Z"
}
```
2020-06-24 16:58:46 +00:00
- `GET /image/process.{ext}?src={file}&...` get a file with transformations applied.
2020-06-07 17:55:42 +00:00
existing transformations include
2020-06-24 16:58:46 +00:00
- `identity=true`: apply no changes
- `blur={float}`: apply a gaussian blur to the file
- `thumbnail={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}`
square using raw pixel sampling
- `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square
using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases
- `resize={filter}.(a){int}`: produce a thumbnail of the image fitting inside an `{int}` by
`{int}` square, or when `(a)` is present, produce a thumbnail whose area is smaller than
`{int}`. `{filter}` is optional, and indicates what filter to use when resizing the image.
Available filters are `Lanczos`, `Lanczos2`, `LanczosSharp`, `Lanczos2Sharp`, `Mitchell`,
and `RobidouxSharp`.
Examples:
- `resize=300`: Produce an image fitting inside a 300x300 px square
- `reizie=.a10000`: Produce an image whose area is at most 10000 px
- `resize=Mitchell.200`: Produce an image fitting inside a 200x200 px square using the
Mitchell filter
- `resize=RobidouxSharp.a40000`: Produce an image whose area is at most 40000 px using the
RobidouxSharp filter
2020-12-17 00:20:06 +00:00
- `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}`
aspect ratio. The resulting crop will be centered on the image. Either the width or height
of the image will remain full-size, depending on the image's aspect ratio and the requested
aspect ratio. For example, a 1600x900 image cropped with a 1x1 aspect ratio will become 900x900. A
1600x1100 image cropped with a 16x9 aspect ratio will become 1600x900.
2020-06-25 20:39:45 +00:00
2020-06-24 16:58:46 +00:00
Supported `ext` file extensions include `png`, `jpg`, and `webp`
2020-06-25 21:06:33 +00:00
2020-06-07 17:57:36 +00:00
An example of usage could be
```
2020-06-24 16:58:46 +00:00
GET /image/process.jpg?src=asdf.png&thumbnail=256&blur=3.0
2020-06-07 17:57:36 +00:00
```
2020-06-24 16:58:46 +00:00
which would create a 256x256px JPEG thumbnail and blur it
2022-04-03 23:23:36 +00:00
- `GET /image/process_backgrounded.{ext}?src={file}&...` queue transformations to be applied to a given file. This accepts the same arguments as the `process.{ext}` endpoint, but does not wait for the processing to complete.
- `GET /image/details/process.{ext}?src={file}&...` for getting the details of a processed image.
The returned JSON is the same format as listed for the full-resolution details endpoint.
2020-07-11 21:28:49 +00:00
- `DELETE /image/delete/{delete_token}/{file}` or `GET /image/delete/{delete_token}/{file}` to
delete a file, where `delete_token` and `file` are from the `/image` endpoint's JSON
The following endpoints are protected by an API key via the `X-Api-Token` header, and are disabled
2022-04-07 02:40:49 +00:00
unless the `--api-key` option is passed to the binary or the PICTRS__SERVER__API_KEY environment variable is
set.
A secure API key can be generated by any password generator.
2022-04-04 00:26:42 +00:00
- `POST /internal/import` for uploading an image while preserving the filename as the first alias.
The upload format and response format are the same as the `POST /image` endpoint.
- `POST /internal/purge?alias={alias}` Purge a file by it's alias. This removes all aliases and
files associated with the query.
2020-07-11 22:26:41 +00:00
This endpoint returns the following JSON
```json
{
"msg": "ok",
"aliases": ["asdf.png"]
}
```
- `GET /internal/aliases?alias={alias}` Get the aliases for a file by it's alias
2020-07-11 22:26:41 +00:00
This endpiont returns the same JSON as the purge endpoint
- `DELETE /internal/variants` Queue a cleanup for generated variants of uploaded images.
If any of the cleaned variants are fetched again, they will be re-generated.
- `GET /internal/identifier` Get the image identifier (file path or object path) for a given alias
On success, the returned json should look like this:
```json
{
"msg": "ok",
"identifier": "/path/to/object"
}
```
2020-07-11 21:28:49 +00:00
2021-09-11 20:31:00 +00:00
Additionally, all endpoints support setting deadlines, after which the request will cease
processing. To enable deadlines for your requests, you can set the `X-Request-Deadline` header to an
i128 value representing the number of nanoseconds since the UNIX Epoch. A simple way to calculate
this value is to use the `time` crate's `OffsetDateTime::unix_timestamp_nanos` method. For example,
```rust
// set deadline of 1ms
let deadline = time::OffsetDateTime::now_utc() + time::Duration::new(0, 1_000);
let request = client
.get("http://pict-rs:8080/image/details/original/asdfghjkla.png")
.insert_header(("X-Request-Deadline", deadline.unix_timestamp_nanos().to_string())))
.send()
.await;
```
2020-06-07 02:01:04 +00:00
## 0.3 to 0.4 Migration Guide
pict-rs will automatically migrate from the 0.3 db format to the 0.4 db format on the first launch
of 0.4. If you are running the provided docker container without any custom configuration, there are
no additional steps.
If you have any custom configuration for file paths, or you are running outside of docker, then
there is some extra configuration that needs to be done.
If your previous `PICTRS__PATH` variable or `path` config was set, it needs to be translated to the
new configuration format.
`PICTRS_PATH` has split into three separate config options:
- `PICTRS__OLD_DB__PATH`: This should be set to the same value that `PICTRS__PATH` was. It is used
during the migration from 0.3 to 0.4
- `PICTRS__REPO__PATH`: This is the location of the 0.4 database. It should be set to a subdirectory
of the previous `PICTRS__PATH` directory. I would recommend `/previous/path/sled-repo`
- `PICTRS__STORE__PATH`: This is the location of the files. It should be the `files` subdirectory of
the previous PICTRS__PATH directory.
if you configured via the configuration file, these would be
```toml
[old_db]
path = "/previous/path"
[repo]
path = "/previous/path/sled-repo"
[store]
path = "/previous/path/files"
```
If the migration doesn't work due to a configuration error, the new sled-repo directory can be
deleted and a new migration will be automatically triggered on the next launch.
## Filesystem to Object Storage migration
After migrating from 0.3 to 0.4, it is possible to migrate to object storage. This can be useful if
hosting in a cloud environment, since object storage is generally far cheaper than block storage.
The command will look something like this:
```bash
$ pict-rs \
filesystem \
-p /path/to/files \
object-storage \
-e https://object-storage-endpoint \
-b bucket-name \
-r region \
-a access-key \
-s secret-key \
sled \
-p /path/to/sled-repo
```
If you are running the docker container with default paths, it can be simplified to the following:
```bash
$ pict-rs \
filesystem \
object-storage \
-e https://object-storage-endpoint \
-b bucket-name \
-r region \
-a access-key \
-s secret-key
```
_This command must be run while pict-rs is offline._
After you've completed the migration, update your pict-rs configuration to use object storage. If
you configure using environment variables, make sure the following are set:
- `PICTRS__STORE__TYPE=object_storage`
- `PICTRS__STORE__ENDPOINT=https://object-storage-endpoint`
- `PICTRS__STORE__BUCKET_NAME=bucket-name`
- `PICTRS__STORE__REGION=region`
2023-06-13 03:48:43 +00:00
- `PICTRS__STORE__USE_PATH_STYLE=false` (set to true if your object storage requires path style access)
- `PICTRS__STORE__ACCESS_KEY=access-key`
- `PICTRS__STORE__SECRET_KEY=secret-key`
If you use the configuration file, this would be
```toml
[store]
type = "object_storage"
endpoint = "https://object-storage-endpoint"
bucket_name = "bucket-name"
region = "region"
2023-06-13 03:44:14 +00:00
use_path_style = false # Set to true if your object storage requires path style access
access_key = "access-key"
secret_key = "secret-key"
```
2020-06-07 02:01:04 +00:00
## Contributing
Feel free to open issues for anything you find an issue with. Please note that any contributed code will be licensed under the AGPLv3.
## FAQ
### Question: Is pict-rs stateless
Answer: No. pict-rs relies on an embedded key-value store called `sled` to store metadata about
uploaded media. This database maintains a set of files on the local disk and cannot be configured to
use a network.
### Question: Can I use a different database with pict-rs
Answer: No. Currently pict-rs only supports the embedded key-value store called `sled`. In the
future, I would like to support `Postgres` and `BonsaiDB`, but I am currently not offering a
timeline on support. If you care about this and are a rust developer, I would accept changes.
### Question: How can I submit changes
Answer: If you would like to contribute to pict-rs, you can push your code to a public git host of
your choice and let me know you did so via matrix or email. I can pull and merge your changes into
this repository from there.
Alternatively, you are welcome to email me a patch that I can apply.
I will not be creating additional accounts on my forgejo server, sorry not sorry.
### Question: I want to configure it with yaml instead of toml
Answer: That's not a question, but you can configure pict-rs with json, hjson, yaml, ini, or toml.
Writing configs in other formats is left as an exercise to the reader.
### Question: How do I donate to pict-rs
Answer: You don't. I get paid by having a job where I do other stuff. Don't give me money that I
don't need.
2020-06-07 02:01:04 +00:00
## License
2022-03-22 20:10:48 +00:00
Copyright © 2022 Riley Trautman
2020-06-07 02:01:04 +00:00
pict-rs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
pict-rs 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. This file is part of pict-rs.
You should have received a copy of the GNU General Public License along with pict-rs. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).