mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-12-22 19:31:35 +00:00
Improve configuration documentation
This commit is contained in:
parent
f9f4fc63d6
commit
f596edc363
4 changed files with 209 additions and 62 deletions
80
README.md
80
README.md
|
@ -9,10 +9,10 @@ _a simple image hosting service_
|
|||
## Usage
|
||||
### Running
|
||||
```
|
||||
pict-rs 0.3.0-alpha.39
|
||||
pict-rs 0.3.0-alpha.42
|
||||
|
||||
USAGE:
|
||||
pict-rs [FLAGS] [OPTIONS] --path <path>
|
||||
pict-rs [FLAGS] [OPTIONS] [SUBCOMMAND]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
|
@ -20,33 +20,65 @@ FLAGS:
|
|||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-a, --addr <addr>
|
||||
The address and port the server binds to. [env: PICTRS_ADDR=] [default: 0.0.0.0:8080]
|
||||
|
||||
--api-key <api-key>
|
||||
An optional string to be checked on requests to privileged endpoints [env: PICTRS_API_KEY=]
|
||||
|
||||
-a, --addr <addr> The address and port the server binds to.
|
||||
--api-key <api-key> An optional string to be checked on requests to privileged endpoints
|
||||
-c, --config-file <config-file> Path to the pict-rs configuration file
|
||||
-f, --filters <filters>...
|
||||
An optional list of filters to permit, supports 'identity', 'thumbnail', 'resize', 'crop', and 'blur' [env:
|
||||
PICTRS_ALLOWED_FILTERS=]
|
||||
An optional list of filters to permit, supports 'identity', 'thumbnail', 'resize', 'crop', and 'blur'
|
||||
|
||||
-i, --image-format <image-format>
|
||||
An optional image format to convert all uploaded files into, supports 'jpg', 'png', and 'webp' [env:
|
||||
PICTRS_FORMAT=]
|
||||
-m, --max-file-size <max-file-size>
|
||||
Specify the maximum allowed uploaded file size (in Megabytes) [env: PICTRS_MAX_FILE_SIZE=] [default: 40]
|
||||
|
||||
--max-image-height <max-image-height>
|
||||
Specify the maximum width in pixels allowed on an image [env: PICTRS_MAX_IMAGE_HEIGHT=] [default: 10000]
|
||||
|
||||
--max-image-width <max-image-width>
|
||||
Specify the maximum width in pixels allowed on an image [env: PICTRS_MAX_IMAGE_WIDTH=] [default: 10000]
|
||||
An optional image format to convert all uploaded files into, supports 'jpg', 'png', and 'webp'
|
||||
|
||||
-m, --max-file-size <max-file-size> Specify the maximum allowed uploaded file size (in Megabytes)
|
||||
--max-image-area <max-image-area> Specify the maximum area in pixels allowed in an image
|
||||
--max-image-height <max-image-height> Specify the maximum width in pixels allowed on an image
|
||||
--max-image-width <max-image-width> Specify the maximum width in pixels allowed on an image
|
||||
-o, --opentelemetry-url <opentelemetry-url>
|
||||
Enable OpenTelemetry Tracing exports to the given OpenTelemetry collector [env: PICTRS_OPENTELEMETRY_URL=]
|
||||
Enable OpenTelemetry Tracing exports to the given OpenTelemetry collector
|
||||
|
||||
-p, --path <path> The path to the data directory, e.g. data/ [env: PICTRS_PATH=]
|
||||
-p, --path <path> The path to the data directory, e.g. data/
|
||||
|
||||
SUBCOMMANDS:
|
||||
file-store
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
s3-store
|
||||
```
|
||||
|
||||
```
|
||||
pict-rs-file-store 0.3.0-alpha.42
|
||||
|
||||
USAGE:
|
||||
pict-rs file-store [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--path <path>
|
||||
```
|
||||
|
||||
```
|
||||
pict-rs-s3-store 0.3.0-alpha.42
|
||||
|
||||
USAGE:
|
||||
pict-rs s3-store [OPTIONS] --bucket-name <bucket-name> --region <region>
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--access-key <access-key>
|
||||
--bucket-name <bucket-name>
|
||||
--region <region>
|
||||
--secret-key <secret-key>
|
||||
--security-token <security-token>
|
||||
--session-token <session-token>
|
||||
```
|
||||
|
||||
See [`pict-rs.toml`](https://git.asonix.dog/asonix/pict-rs/src/branch/main/pict-rs.toml) for more configuration
|
||||
|
||||
#### Example:
|
||||
Running on all interfaces, port 8080, storing data in /opt/data
|
||||
```
|
||||
|
@ -60,6 +92,10 @@ Running locally, port 8080, storing data in data/, and only allowing the `thumbn
|
|||
```
|
||||
$ ./pict-rs -a 127.0.0.1:8080 -p data/ -w thumbnail identity
|
||||
```
|
||||
Running from a configuration file
|
||||
```
|
||||
$ ./pict-rs -c ./pict-rs.toml
|
||||
```
|
||||
|
||||
#### Docker
|
||||
Run the following commands:
|
||||
|
|
93
pict-rs.toml
93
pict-rs.toml
|
@ -1,19 +1,23 @@
|
|||
## Required: path to store pict-rs database
|
||||
# environment variable: PICTRS_PATH
|
||||
path = './data'
|
||||
|
||||
## Optional: pict-rs binding address
|
||||
# environment variable: PICTRS_ADDR
|
||||
# default: 0.0.0.0:8080
|
||||
addr = '0.0.0.0:8080'
|
||||
|
||||
## Optional: format to transcode all uploaded images
|
||||
# environment variable: PICTRS_IMAGE_FORMAT
|
||||
# valid options: 'jpeg', 'png', 'webp'
|
||||
# default: empty
|
||||
#
|
||||
# Not specifying image-format means images will be stored in their original format
|
||||
# Not specifying image_format means images will be stored in their original format
|
||||
# This does not affect gif or mp4 uploads
|
||||
image-format = 'jpeg'
|
||||
image_format = 'jpeg'
|
||||
|
||||
## Optional: permitted image processing filters
|
||||
# environment variable: PICTRS_FILTERS
|
||||
# valid options: 'identity', 'thumbnail', 'resize', 'crop', 'blur'
|
||||
# default: empty
|
||||
#
|
||||
|
@ -27,39 +31,86 @@ filters = [
|
|||
]
|
||||
|
||||
## Optional: image bounds
|
||||
# environment variable: PICTRS_MAX_FILE_SIZE
|
||||
# default: 40
|
||||
max-file-size = 40 # in Megabytes
|
||||
max_file_size = 40 # in Megabytes
|
||||
# environment variable: PICTRS_MAX_IMAGE_WIDTH
|
||||
# default: 10,000
|
||||
max-image-width = 10000 # in Pixels
|
||||
max_image_width = 10000 # in Pixels
|
||||
# environment variable: PICTRS_MAX_IMAGE_HEIGHT
|
||||
# default: 10,000
|
||||
max-image-height = 10000 # in Pixels
|
||||
max_image_height = 10000 # in Pixels
|
||||
# environment variable: PICTRS_MAX_IMAGE_AREA
|
||||
# default: 40,000
|
||||
max-image-area = 40000 # in Pixels
|
||||
max_image_area = 40000 # in Pixels
|
||||
|
||||
## Optional:
|
||||
## Optional: skip image validation on the import endpoint
|
||||
# environment variable: PICTRS_SKIP_VALIDATE_IMPORTS
|
||||
# default: false
|
||||
skip-validate-imports = false
|
||||
skip_validate_imports = false
|
||||
|
||||
## Optional: shared secret for internal endpoints
|
||||
# environment variable: PICTRS_API_KEY
|
||||
# default: empty
|
||||
#
|
||||
# Not specifying api_key disables internal endpoints
|
||||
api_key = 'API_KEY'
|
||||
|
||||
## Optional: url for exporting otlp traces
|
||||
# environment variable: PICTRS_OPENTELEMETRY_URL
|
||||
# default: empty
|
||||
#
|
||||
# Not specifying opentelemetry-url means no traces will be exported
|
||||
opentelemetry-url = 'http://localhost:4317/'
|
||||
# Not specifying opentelemetry_url means no traces will be exported
|
||||
opentelemetry_url = 'http://localhost:4317/'
|
||||
|
||||
## Optional: store definition
|
||||
# default: empty
|
||||
#
|
||||
# Not specifying a store will default to using a file-store in the same directory provided by the `path` field defined above
|
||||
# default store: file_store
|
||||
[store]
|
||||
type = 'file-store'
|
||||
path = './data'
|
||||
type = "file_store"
|
||||
|
||||
## Example file store
|
||||
# [store]
|
||||
#
|
||||
# # environment variable: PICTRS_STORE__TYPE
|
||||
# type = 'file_store'
|
||||
#
|
||||
# # Optional: file path
|
||||
# # environment variable: PICTRS_STORE__PATH
|
||||
# # default: empty
|
||||
# #
|
||||
# # Not specifying path means pict-rs' top-level `path` config is used
|
||||
# path = './data'
|
||||
|
||||
## Example s3 store
|
||||
# [store]
|
||||
# type = 's3-store'
|
||||
# bucket-name = 'rust-s3'
|
||||
#
|
||||
# # environment variable: PICTRS_STORE__TYPE
|
||||
# type = 's3_store'
|
||||
#
|
||||
# # Required: bucket name
|
||||
# # environment variable: PICTRS_STORE__BUCKET_NAME
|
||||
# bucket_name = 'rust_s3'
|
||||
#
|
||||
# # Required: bucket region
|
||||
# # environment variable: PICTRS_STORE__REGION
|
||||
# region = 'eu-central-1' # can also be endpoint of local s3 store
|
||||
# access-key = 'ACCESS_KEY'
|
||||
# secret-key = 'SECRET_KEY'
|
||||
# security-token = 'SECURITY_TOKEN'
|
||||
# session-token = 'SESSION-TOKEN'
|
||||
#
|
||||
# # Optional: bucket access key
|
||||
# # environment variable: PICTRS_STORE__ACCESS_KEY
|
||||
# # default: empty
|
||||
# access_key = 'ACCESS_KEY'
|
||||
#
|
||||
# # Optional: bucket secret key
|
||||
# # environment variable: PICTRS_STORE__SECRET_KEY
|
||||
# # default: empty
|
||||
# secret_key = 'SECRET_KEY'
|
||||
#
|
||||
# # Optional: bucket security token
|
||||
# # environment variable: PICTRS_STORE__SECURITY_TOKEN
|
||||
# # default: empty
|
||||
# security_token = 'SECURITY_TOKEN'
|
||||
#
|
||||
# # Optional: bucket session token
|
||||
# # environment variable: PICTRS_STORE__SESSION_TOKEN
|
||||
# # default: empty
|
||||
# session_token = 'SESSION_TOKEN'
|
||||
|
|
|
@ -13,20 +13,27 @@ pub(crate) struct Args {
|
|||
overrides: Overrides,
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, structopt::StructOpt)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) struct Overrides {
|
||||
#[structopt(
|
||||
short,
|
||||
long,
|
||||
help = "Whether to skip validating images uploaded via the internal import API"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
skip_validate_imports: bool,
|
||||
|
||||
#[structopt(short, long, help = "The address and port the server binds to.")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
addr: Option<SocketAddr>,
|
||||
|
||||
#[structopt(short, long, help = "The path to the data directory, e.g. data/")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PathBuf>,
|
||||
|
||||
#[structopt(
|
||||
|
@ -34,6 +41,7 @@ pub(crate) struct Overrides {
|
|||
long,
|
||||
help = "An optional image format to convert all uploaded files into, supports 'jpg', 'png', and 'webp'"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
image_format: Option<Format>,
|
||||
|
||||
#[structopt(
|
||||
|
@ -41,6 +49,7 @@ pub(crate) struct Overrides {
|
|||
long,
|
||||
help = "An optional list of filters to permit, supports 'identity', 'thumbnail', 'resize', 'crop', and 'blur'"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
filters: Option<Vec<String>>,
|
||||
|
||||
#[structopt(
|
||||
|
@ -48,21 +57,26 @@ pub(crate) struct Overrides {
|
|||
long,
|
||||
help = "Specify the maximum allowed uploaded file size (in Megabytes)"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_file_size: Option<usize>,
|
||||
|
||||
#[structopt(long, help = "Specify the maximum width in pixels allowed on an image")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_image_width: Option<usize>,
|
||||
|
||||
#[structopt(long, help = "Specify the maximum width in pixels allowed on an image")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_image_height: Option<usize>,
|
||||
|
||||
#[structopt(long, help = "Specify the maximum area in pixels allowed in an image")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_image_area: Option<usize>,
|
||||
|
||||
#[structopt(
|
||||
long,
|
||||
help = "An optional string to be checked on requests to privileged endpoints"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
api_key: Option<String>,
|
||||
|
||||
#[structopt(
|
||||
|
@ -70,33 +84,69 @@ pub(crate) struct Overrides {
|
|||
long,
|
||||
help = "Enable OpenTelemetry Tracing exports to the given OpenTelemetry collector"
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
opentelemetry_url: Option<Url>,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
store: Option<Store>,
|
||||
}
|
||||
|
||||
impl Overrides {
|
||||
fn is_default(&self) -> bool {
|
||||
!self.skip_validate_imports
|
||||
&& self.addr.is_none()
|
||||
&& self.path.is_none()
|
||||
&& self.image_format.is_none()
|
||||
&& self.filters.is_none()
|
||||
&& self.max_file_size.is_none()
|
||||
&& self.max_image_width.is_none()
|
||||
&& self.max_image_height.is_none()
|
||||
&& self.max_image_area.is_none()
|
||||
&& self.api_key.is_none()
|
||||
&& self.opentelemetry_url.is_none()
|
||||
&& self.store.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, structopt::StructOpt)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(tag = "type")]
|
||||
pub(crate) enum Store {
|
||||
FileStore {
|
||||
// defaults to {config.path}
|
||||
#[structopt(long)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PathBuf>,
|
||||
},
|
||||
#[cfg(feature = "object-storage")]
|
||||
S3Store {
|
||||
#[structopt(long)]
|
||||
bucket_name: String,
|
||||
|
||||
#[structopt(long)]
|
||||
region: crate::serde_str::Serde<s3::Region>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[structopt(long)]
|
||||
access_key: Option<String>,
|
||||
|
||||
#[structopt(long)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
secret_key: Option<String>,
|
||||
|
||||
#[structopt(long)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
security_token: Option<String>,
|
||||
|
||||
#[structopt(long)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
session_token: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) struct Config {
|
||||
skip_validate_imports: bool,
|
||||
addr: SocketAddr,
|
||||
|
@ -113,7 +163,7 @@ pub(crate) struct Config {
|
|||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) struct Defaults {
|
||||
skip_validate_imports: bool,
|
||||
addr: SocketAddr,
|
||||
|
@ -141,17 +191,25 @@ impl Defaults {
|
|||
impl Config {
|
||||
pub(crate) fn build() -> anyhow::Result<Self> {
|
||||
let args = Args::from_args();
|
||||
let mut base_config = config::Config::try_from(&Defaults::new())?;
|
||||
let mut base_config = config::Config::new();
|
||||
base_config.merge(config::Config::try_from(&Defaults::new())?)?;
|
||||
|
||||
if let Some(path) = args.config_file {
|
||||
base_config.merge(config::File::from(path))?;
|
||||
};
|
||||
|
||||
base_config.merge(config::Config::try_from(&args.overrides)?)?;
|
||||
if !args.overrides.is_default() {
|
||||
let merging = config::Config::try_from(&args.overrides)?;
|
||||
|
||||
base_config.merge(config::Environment::with_prefix("PICTRS"))?;
|
||||
base_config.merge(merging)?;
|
||||
}
|
||||
|
||||
Ok(base_config.try_into()?)
|
||||
base_config.merge(config::Environment::with_prefix("PICTRS").separator("__"))?;
|
||||
|
||||
let config: Self = base_config.try_into()?;
|
||||
println!("{:#?}", config);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self) -> &Store {
|
||||
|
@ -208,7 +266,7 @@ impl Config {
|
|||
pub(crate) struct FormatError(String);
|
||||
|
||||
#[derive(Copy, Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum Format {
|
||||
Jpeg,
|
||||
Png,
|
||||
|
|
|
@ -220,12 +220,14 @@ where
|
|||
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
if let Some(value) = req.headers().get("x-api-token") {
|
||||
if value.to_str().is_ok() && value.to_str().ok() == self.0.as_deref() {
|
||||
if let (Ok(header), Some(api_key)) = (value.to_str(), &self.0) {
|
||||
if header == api_key {
|
||||
return InternalFuture::Internal {
|
||||
future: self.1.call(req),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InternalFuture::Error {
|
||||
error: Some(ApiError),
|
||||
|
|
Loading…
Reference in a new issue