Improve configuration documentation

This commit is contained in:
Aode (lion) 2021-10-28 00:17:37 -05:00
parent f9f4fc63d6
commit f596edc363
4 changed files with 209 additions and 62 deletions

View file

@ -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:

View file

@ -1,65 +1,116 @@
## 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
#
# Not specifying filters implies all filters are permitted
filters = [
'identity',
'thumbnail',
'resize',
'crop',
'blur',
'identity',
'thumbnail',
'resize',
'crop',
'blur',
]
## 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: url for exporting otlp traces
## Optional: shared secret for internal endpoints
# environment variable: PICTRS_API_KEY
# default: empty
#
# Not specifying opentelemetry-url means no traces will be exported
opentelemetry-url = 'http://localhost:4317/'
# 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/'
## 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'

View file

@ -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,

View file

@ -220,10 +220,12 @@ 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() {
return InternalFuture::Internal {
future: self.1.call(req),
};
if let (Ok(header), Some(api_key)) = (value.to_str(), &self.0) {
if header == api_key {
return InternalFuture::Internal {
future: self.1.call(req),
};
}
}
}