Fixes found through integration test crate
This commit is contained in:
parent
b53e06ef55
commit
f17a214865
12 changed files with 815 additions and 63 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@
|
||||||
szurubooru-integration-test/szurubooru/data/*
|
szurubooru-integration-test/szurubooru/data/*
|
||||||
szurubooru-integration-test/szurubooru/pgsql/*
|
szurubooru-integration-test/szurubooru/pgsql/*
|
||||||
szurubooru-integration-test/szurubooru/server/*
|
szurubooru-integration-test/szurubooru/server/*
|
||||||
|
**/.DS_Store
|
||||||
|
|
|
||||||
149
Cargo.lock
generated
149
Cargo.lock
generated
|
|
@ -687,6 +687,15 @@ version = "0.4.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata 0.1.10",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
|
|
@ -771,6 +780,16 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
|
|
@ -839,6 +858,12 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
|
@ -924,6 +949,15 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "progress"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b820305721858696053a7fd0215cfeeee16ecaaf96b7a209945428e02f1c44"
|
||||||
|
dependencies = [
|
||||||
|
"terminal_size",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.36"
|
||||||
|
|
@ -980,8 +1014,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata",
|
"regex-automata 0.4.7",
|
||||||
"regex-syntax",
|
"regex-syntax 0.8.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax 0.6.29",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -992,9 +1035,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax",
|
"regex-syntax 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
|
@ -1226,6 +1275,15 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
|
|
@ -1346,7 +1404,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "szurubooru-client"
|
name = "szurubooru-client"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -1372,7 +1430,11 @@ dependencies = [
|
||||||
name = "szurubooru-integration-test"
|
name = "szurubooru-integration-test"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"progress",
|
||||||
"szurubooru-client",
|
"szurubooru-client",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1388,6 +1450,16 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "terminal_size"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.63"
|
||||||
|
|
@ -1408,6 +1480,16 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
|
@ -1438,6 +1520,7 @@ dependencies = [
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
|
"tracing",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1542,6 +1625,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1609,6 +1722,12 @@ version = "2.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
@ -1725,6 +1844,28 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "szurubooru-client"
|
name = "szurubooru-client"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Scott Lyons <scottalyons@gmail.com>"]
|
authors = ["Scott Lyons <scottalyons@gmail.com>"]
|
||||||
description = "A wrapper around the Szurubooru API, including type-safe Query and Sort tokens"
|
description = "A wrapper around the Szurubooru API, including type-safe Query and Sort tokens"
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
use crate::{errors::*, models::*, tokens::*};
|
use crate::{errors::*, models::*, tokens::*};
|
||||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||||
|
use reqwest::header::CONTENT_TYPE;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, ACCEPT, AUTHORIZATION},
|
header::{HeaderMap, ACCEPT, AUTHORIZATION},
|
||||||
multipart::{Form, Part},
|
multipart::{Form, Part},
|
||||||
Client, ClientBuilder, Method, RequestBuilder, Response,
|
Client, ClientBuilder, Method, RequestBuilder, Response,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -88,6 +90,12 @@ impl SzurubooruClient {
|
||||||
SzurubooruClient::new(host, auth, allow_insecure)
|
SzurubooruClient::new(host, auth, allow_insecure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new client with anonymous credentials
|
||||||
|
pub fn new_anonymous(host: &str, allow_insecure: bool) -> SzurubooruResult<Self> {
|
||||||
|
let auth = SzurubooruAuth::None;
|
||||||
|
SzurubooruClient::new(host, auth, allow_insecure)
|
||||||
|
}
|
||||||
|
|
||||||
fn new(host: &str, auth: SzurubooruAuth, allow_insecure: bool) -> SzurubooruResult<Self> {
|
fn new(host: &str, auth: SzurubooruAuth, allow_insecure: bool) -> SzurubooruResult<Self> {
|
||||||
let host = if host.ends_with("/") {
|
let host = if host.ends_with("/") {
|
||||||
&host[0..host.len() - 1]
|
&host[0..host.len() - 1]
|
||||||
|
|
@ -103,6 +111,7 @@ impl SzurubooruClient {
|
||||||
let mut header_map = HeaderMap::new();
|
let mut header_map = HeaderMap::new();
|
||||||
//header_map.append(AUTHORIZATION, token_header_value.parse().unwrap());
|
//header_map.append(AUTHORIZATION, token_header_value.parse().unwrap());
|
||||||
header_map.append(ACCEPT, "application/json".parse().unwrap());
|
header_map.append(ACCEPT, "application/json".parse().unwrap());
|
||||||
|
header_map.append(CONTENT_TYPE, "application/json".parse().unwrap());
|
||||||
|
|
||||||
let client = ClientBuilder::new()
|
let client = ClientBuilder::new()
|
||||||
.danger_accept_invalid_certs(allow_insecure)
|
.danger_accept_invalid_certs(allow_insecure)
|
||||||
|
|
@ -288,7 +297,7 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
T: AsRef<str> + Display,
|
T: AsRef<str> + Display,
|
||||||
{
|
{
|
||||||
let mut req_url = self.client.base_url.clone();
|
let mut req_url = self.client.base_url.clone();
|
||||||
req_url.set_path(path.as_ref());
|
req_url.set_path(&format!("/api{}", path.as_ref()));
|
||||||
|
|
||||||
if let Some(query_vec) = query {
|
if let Some(query_vec) = query {
|
||||||
let mut qpm = req_url.query_pairs_mut();
|
let mut qpm = req_url.query_pairs_mut();
|
||||||
|
|
@ -315,7 +324,7 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
// This doesn't detect the required `mut` for some reason
|
// This doesn't detect the required `mut` for some reason
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut req = self.client.client.request(method, req_url);
|
let mut req = self.client.client.request(method, req_url);
|
||||||
match &self.client.auth {
|
let req = match &self.client.auth {
|
||||||
SzurubooruAuth::TokenAuth(t) => {
|
SzurubooruAuth::TokenAuth(t) => {
|
||||||
let mut header_map = HeaderMap::new();
|
let mut header_map = HeaderMap::new();
|
||||||
header_map.append(AUTHORIZATION, t.parse().unwrap());
|
header_map.append(AUTHORIZATION, t.parse().unwrap());
|
||||||
|
|
@ -324,7 +333,8 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
}
|
}
|
||||||
SzurubooruAuth::BasicAuth(u, p) => req.basic_auth(u, Some(p)),
|
SzurubooruAuth::BasicAuth(u, p) => req.basic_auth(u, Some(p)),
|
||||||
SzurubooruAuth::None => req,
|
SzurubooruAuth::None => req,
|
||||||
}
|
};
|
||||||
|
req
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self), fields(base_url=self.client.base_url.to_string()))]
|
#[tracing::instrument(skip(self), fields(base_url=self.client.base_url.to_string()))]
|
||||||
|
|
@ -351,6 +361,20 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
self.handle_request(request).await
|
self.handle_request(request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_response(&self, response: Response) -> SzurubooruResult<Response> {
|
||||||
|
if response.status().is_client_error() || response.status().is_server_error() {
|
||||||
|
let resp_json = response
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.map_err(SzurubooruClientError::RequestError)?;
|
||||||
|
let server_error = serde_json::from_str::<SzurubooruServerError>(&resp_json)
|
||||||
|
.map_err(|e| SzurubooruClientError::ResponseParsingError(e, resp_json))?;
|
||||||
|
Err(SzurubooruClientError::SzurubooruServerError(server_error))
|
||||||
|
} else {
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_request<T: DeserializeOwned>(
|
async fn handle_request<T: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
request: RequestBuilder,
|
request: RequestBuilder,
|
||||||
|
|
@ -361,10 +385,11 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
|
|
||||||
let response = self.client.client.execute(request).await;
|
let response = self.client.client.execute(request).await;
|
||||||
|
|
||||||
let response = response
|
let response = self
|
||||||
.map_err(SzurubooruClientError::RequestError)?
|
.handle_response(response.map_err(SzurubooruClientError::RequestError)?)
|
||||||
.error_for_status()
|
.await?;
|
||||||
.map_err(SzurubooruClientError::RequestError)?;
|
//.error_for_status()
|
||||||
|
//.map_err(SzurubooruClientError::RequestError)?;
|
||||||
|
|
||||||
let response_text = response
|
let response_text = response
|
||||||
.text()
|
.text()
|
||||||
|
|
@ -400,13 +425,13 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
pub async fn update_tag_category<T>(
|
pub async fn update_tag_category<T>(
|
||||||
&self,
|
&self,
|
||||||
name: T,
|
name: T,
|
||||||
resource: &TagCategoryResource,
|
update_tag_cat: &CreateUpdateTagCategory,
|
||||||
) -> SzurubooruResult<TagCategoryResource>
|
) -> SzurubooruResult<TagCategoryResource>
|
||||||
where
|
where
|
||||||
T: AsRef<str> + Display,
|
T: AsRef<str> + Display,
|
||||||
{
|
{
|
||||||
let path = format!("/tag-category/{name}");
|
let path = format!("/tag-category/{name}");
|
||||||
self.do_request(Method::PUT, &path, None, Some(resource))
|
self.do_request(Method::PUT, &path, None, Some(update_tag_cat))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,8 +452,9 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
{
|
{
|
||||||
let path = format!("/tag-category/{name}");
|
let path = format!("/tag-category/{name}");
|
||||||
let version_obj = ResourceVersion { version };
|
let version_obj = ResourceVersion { version };
|
||||||
self.do_request(Method::DELETE, &path, None, Some(&version_obj))
|
self.do_request::<Value, _, _>(Method::DELETE, &path, None, Some(&version_obj))
|
||||||
.await
|
.await
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets given tag category as default. All new tags created manually or automatically will
|
/// Sets given tag category as default. All new tags created manually or automatically will
|
||||||
|
|
@ -447,9 +473,9 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
/// all possible query tokens, or use (QueryToken)[tokens::QueryToken] for a custom token
|
/// all possible query tokens, or use (QueryToken)[tokens::QueryToken] for a custom token
|
||||||
pub async fn list_tags(
|
pub async fn list_tags(
|
||||||
&self,
|
&self,
|
||||||
query: &Vec<QueryToken>,
|
query: Option<&Vec<QueryToken>>,
|
||||||
) -> SzurubooruResult<PagedSearchResult<TagResource>> {
|
) -> SzurubooruResult<PagedSearchResult<TagResource>> {
|
||||||
self.do_request(Method::GET, "/tags", Some(query), None::<&String>)
|
self.do_request(Method::GET, "/tags", query, None::<&String>)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -502,8 +528,9 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
{
|
{
|
||||||
let path = format!("/tag/{name}");
|
let path = format!("/tag/{name}");
|
||||||
let version_obj = ResourceVersion { version };
|
let version_obj = ResourceVersion { version };
|
||||||
self.do_request(Method::DELETE, &path, None, Some(&version_obj))
|
self.do_request::<Value, _, _>(Method::DELETE, &path, None, Some(&version_obj))
|
||||||
.await
|
.await
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes source tag and merges all of its usages, suggestions and implications to the
|
/// Removes source tag and merges all of its usages, suggestions and implications to the
|
||||||
|
|
@ -697,11 +724,13 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
.build()
|
.build()
|
||||||
.map_err(SzurubooruClientError::RequestBuilderError)?;
|
.map_err(SzurubooruClientError::RequestBuilderError)?;
|
||||||
|
|
||||||
self.client
|
let resp_res = self
|
||||||
|
.client
|
||||||
.client
|
.client
|
||||||
.execute(request)
|
.execute(request)
|
||||||
.await
|
.await
|
||||||
.map_err(SzurubooruClientError::RequestError)
|
.map_err(|e| SzurubooruClientError::RequestError(e))?;
|
||||||
|
self.handle_response(resp_res).await
|
||||||
}
|
}
|
||||||
|
|
||||||
///Downloads the given post ID's image as a stream of bytes
|
///Downloads the given post ID's image as a stream of bytes
|
||||||
|
|
@ -718,10 +747,11 @@ impl<'a> SzurubooruRequest<'a> {
|
||||||
///Downloads the given post ID's image as a (Bytes)[bytes::Bytes] struct
|
///Downloads the given post ID's image as a (Bytes)[bytes::Bytes] struct
|
||||||
pub async fn get_post_content_bytes(&self, post_id: u32) -> SzurubooruResult<bytes::Bytes> {
|
pub async fn get_post_content_bytes(&self, post_id: u32) -> SzurubooruResult<bytes::Bytes> {
|
||||||
let content_response = self.get_post_content(post_id).await?;
|
let content_response = self.get_post_content(post_id).await?;
|
||||||
|
|
||||||
content_response
|
content_response
|
||||||
.bytes()
|
.bytes()
|
||||||
.await
|
.await
|
||||||
.map_err(SzurubooruClientError::RequestError)
|
.map_err(|e| SzurubooruClientError::RequestError(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves posts that look like the input image
|
/// Retrieves posts that look like the input image
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
//! See [here](https://github.com/rr-/szurubooru/blob/master/doc/API.md#field-selecting) for
|
//! See [here](https://github.com/rr-/szurubooru/blob/master/doc/API.md#field-selecting) for
|
||||||
//! more information.
|
//! more information.
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::{DateTime, Utc};
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use strum_macros::AsRefStr;
|
use strum_macros::AsRefStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
@ -98,9 +99,9 @@ pub struct TagResource {
|
||||||
/// the user by the web client on usage
|
/// the user by the web client on usage
|
||||||
pub suggestions: Option<Vec<MicroTagResource>>,
|
pub suggestions: Option<Vec<MicroTagResource>>,
|
||||||
/// time the tag was created
|
/// time the tag was created
|
||||||
pub creation_time: Option<NaiveDateTime>,
|
pub creation_time: Option<DateTime<Utc>>,
|
||||||
/// time the tag was edited
|
/// time the tag was edited
|
||||||
pub last_edit_time: Option<NaiveDateTime>,
|
pub last_edit_time: Option<DateTime<Utc>>,
|
||||||
/// the number of posts the tag was used in
|
/// the number of posts the tag was used in
|
||||||
pub usages: Option<u32>,
|
pub usages: Option<u32>,
|
||||||
/// the tag description (instructions how to use, history etc.) The client should render
|
/// the tag description (instructions how to use, history etc.) The client should render
|
||||||
|
|
@ -128,21 +129,27 @@ pub struct TagResource {
|
||||||
#[builder(setter(strip_option))]
|
#[builder(setter(strip_option))]
|
||||||
pub struct CreateUpdateTag {
|
pub struct CreateUpdateTag {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
/// resource version. See [versioning](ResourceVersion)
|
/// resource version. See [versioning](ResourceVersion)
|
||||||
pub version: Option<u32>,
|
pub version: Option<u32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
/// Tag names and aliases, must match `tag_name_regex` from the server's configuration
|
/// Tag names and aliases, must match `tag_name_regex` from the server's configuration
|
||||||
pub names: Option<Vec<String>>,
|
pub names: Option<Vec<String>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
/// Category that this tag belongs to. Must already exist
|
/// Category that this tag belongs to. Must already exist
|
||||||
pub category: Option<String>,
|
pub category: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
/// The tag description in Markdown format
|
/// The tag description in Markdown format
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
/// Tags that should be implied when this tag is used
|
/// Tags that should be implied when this tag is used
|
||||||
pub implications: Option<Vec<String>>,
|
pub implications: Option<Vec<String>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
/// Tags that should be suggested when this tag is used
|
/// Tags that should be suggested when this tag is used
|
||||||
pub suggestions: Option<Vec<String>>,
|
pub suggestions: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
@ -160,21 +167,31 @@ pub struct TagCategoryResource {
|
||||||
/// How many tags is the given category used with
|
/// How many tags is the given category used with
|
||||||
pub usages: Option<u32>,
|
pub usages: Option<u32>,
|
||||||
/// The order in which tags with this category are displayed, ascending
|
/// The order in which tags with this category are displayed, ascending
|
||||||
pub order: Option<String>,
|
pub order: Option<u32>,
|
||||||
/// Whether the tag category is the default one
|
/// Whether the tag category is the default one
|
||||||
pub default: Option<bool>,
|
pub default: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, Builder)]
|
||||||
#[builder(setter(into))]
|
#[builder(setter(strip_option))]
|
||||||
/// Used for creating or updating a Tag Category
|
/// Used for creating or updating a Tag Category
|
||||||
pub struct CreateUpdateTagCategory {
|
pub struct CreateUpdateTagCategory {
|
||||||
|
/// Resource version. See [versioning](ResourceVersion)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
|
pub version: Option<u32>,
|
||||||
/// The name of the category to create
|
/// The name of the category to create
|
||||||
pub name: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
|
pub name: Option<String>,
|
||||||
/// The display color to use for the category
|
/// The display color to use for the category
|
||||||
pub color: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
|
pub color: Option<String>,
|
||||||
/// The order in which tags with this category are displayed, ascending
|
/// The order in which tags with this category are displayed, ascending
|
||||||
pub order: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
|
pub order: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
|
||||||
|
|
@ -261,9 +278,9 @@ pub struct PostResource {
|
||||||
/// The post identifier
|
/// The post identifier
|
||||||
pub id: Option<u32>,
|
pub id: Option<u32>,
|
||||||
/// Time the post was created
|
/// Time the post was created
|
||||||
pub creation_time: Option<NaiveDateTime>,
|
pub creation_time: Option<DateTime<Utc>>,
|
||||||
/// Time the post was edited
|
/// Time the post was edited
|
||||||
pub last_edit_time: Option<NaiveDateTime>,
|
pub last_edit_time: Option<DateTime<Utc>>,
|
||||||
/// Whether the post is safe for work
|
/// Whether the post is safe for work
|
||||||
pub safety: Option<PostSafety>,
|
pub safety: Option<PostSafety>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
|
@ -311,7 +328,7 @@ pub struct PostResource {
|
||||||
/// How many posts are related to this post
|
/// How many posts are related to this post
|
||||||
pub relation_count: Option<u32>,
|
pub relation_count: Option<u32>,
|
||||||
/// The last time the post was featured
|
/// The last time the post was featured
|
||||||
pub last_feature_time: Option<NaiveDateTime>,
|
pub last_feature_time: Option<DateTime<Utc>>,
|
||||||
/// List of users who have favorited this post
|
/// List of users who have favorited this post
|
||||||
pub favorited_by: Option<Vec<MicroUserResource>>,
|
pub favorited_by: Option<Vec<MicroUserResource>>,
|
||||||
/// Whether the post uses custom thumbnail
|
/// Whether the post uses custom thumbnail
|
||||||
|
|
@ -326,7 +343,7 @@ pub struct PostResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
|
||||||
#[builder(setter(into, strip_option))]
|
#[builder(setter(strip_option))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
/// A `struct` used to create or update a post. For updating purposes
|
/// A `struct` used to create or update a post. For updating purposes
|
||||||
/// the [version](CreateUpdatePost::version) field is required
|
/// the [version](CreateUpdatePost::version) field is required
|
||||||
|
|
@ -447,10 +464,10 @@ pub struct UserResource {
|
||||||
pub rank: Option<UserRank>,
|
pub rank: Option<UserRank>,
|
||||||
#[serde(rename = "last-login-time")]
|
#[serde(rename = "last-login-time")]
|
||||||
/// The last login time
|
/// The last login time
|
||||||
pub last_login_time: Option<NaiveDateTime>,
|
pub last_login_time: Option<DateTime<Utc>>,
|
||||||
#[serde(rename = "creation-time")]
|
#[serde(rename = "creation-time")]
|
||||||
/// The user registration time
|
/// The user registration time
|
||||||
pub creation_time: Option<NaiveDateTime>,
|
pub creation_time: Option<DateTime<Utc>>,
|
||||||
/// How to render the user avatar
|
/// How to render the user avatar
|
||||||
pub avatar_style: Option<UserAvatarStyle>,
|
pub avatar_style: Option<UserAvatarStyle>,
|
||||||
/// The URL to the avatar
|
/// The URL to the avatar
|
||||||
|
|
@ -474,7 +491,7 @@ pub struct UserResource {
|
||||||
pub favorite_post_count: Option<SzuruEither<u32, bool>>,
|
pub favorite_post_count: Option<SzuruEither<u32, bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, Builder)]
|
||||||
#[builder(setter(into, strip_option))]
|
#[builder(setter(into, strip_option))]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
/// `struct` used to create or update a user resource. The version field is only used when
|
/// `struct` used to create or update a user resource. The version field is only used when
|
||||||
|
|
@ -482,15 +499,20 @@ pub struct UserResource {
|
||||||
pub struct CreateUpdateUser {
|
pub struct CreateUpdateUser {
|
||||||
/// Resource version. See [versioning](ResourceVersion)
|
/// Resource version. See [versioning](ResourceVersion)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[builder(default)]
|
||||||
pub version: Option<u32>,
|
pub version: Option<u32>,
|
||||||
/// The username
|
/// The username
|
||||||
|
#[builder(default)]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
/// The user's password
|
/// The user's password
|
||||||
|
#[builder(default)]
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
/// The user's desired rank, if not given will default to `default_rank` in the server's
|
/// The user's desired rank, if not given will default to `default_rank` in the server's
|
||||||
/// configuration
|
/// configuration
|
||||||
|
#[builder(default)]
|
||||||
pub rank: Option<UserRank>,
|
pub rank: Option<UserRank>,
|
||||||
/// The user avatar style, Gravatar or Manual
|
/// The user avatar style, Gravatar or Manual
|
||||||
|
#[builder(default)]
|
||||||
pub avatar_style: Option<UserAvatarStyle>,
|
pub avatar_style: Option<UserAvatarStyle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -517,15 +539,15 @@ pub struct UserAuthTokenResource {
|
||||||
/// Whether the token is still valid for authentication
|
/// Whether the token is still valid for authentication
|
||||||
pub enabled: Option<bool>,
|
pub enabled: Option<bool>,
|
||||||
/// Time when the token expires
|
/// Time when the token expires
|
||||||
pub expiration_time: Option<NaiveDateTime>,
|
pub expiration_time: Option<DateTime<Utc>>,
|
||||||
/// Resource version. See [versioning](ResourceVersion)
|
/// Resource version. See [versioning](ResourceVersion)
|
||||||
pub version: Option<u32>,
|
pub version: Option<u32>,
|
||||||
/// time the user token was created
|
/// time the user token was created
|
||||||
pub creation_time: Option<NaiveDateTime>,
|
pub creation_time: Option<DateTime<Utc>>,
|
||||||
/// time the user token was edited
|
/// time the user token was edited
|
||||||
pub last_edit_time: Option<NaiveDateTime>,
|
pub last_edit_time: Option<DateTime<Utc>>,
|
||||||
/// the last time this token was used
|
/// the last time this token was used
|
||||||
pub last_usage_time: Option<NaiveDateTime>,
|
pub last_usage_time: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Default)]
|
||||||
|
|
@ -545,7 +567,7 @@ pub struct CreateUpdateUserAuthToken {
|
||||||
pub note: Option<String>,
|
pub note: Option<String>,
|
||||||
/// Time when the token expires
|
/// Time when the token expires
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub expiration_time: Option<NaiveDateTime>,
|
pub expiration_time: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
@ -578,8 +600,14 @@ pub struct GlobalInfoConfig {
|
||||||
pub tag_category_name_regex: String,
|
pub tag_category_name_regex: String,
|
||||||
/// Default user rank upon signup
|
/// Default user rank upon signup
|
||||||
pub default_user_rank: String,
|
pub default_user_rank: String,
|
||||||
|
/// Whether safety is enabled
|
||||||
|
pub enable_safety: bool,
|
||||||
|
/// Contact email for this server
|
||||||
|
pub contact_email: Option<String>,
|
||||||
|
/// Is sending email enabled for this server
|
||||||
|
pub can_send_mails: bool,
|
||||||
/// Available privileges enabled for this server
|
/// Available privileges enabled for this server
|
||||||
pub privileges: Vec<String>,
|
pub privileges: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
@ -593,11 +621,11 @@ pub struct GlobalInfo {
|
||||||
/// The current featured post
|
/// The current featured post
|
||||||
pub featured_post: Option<u32>,
|
pub featured_post: Option<u32>,
|
||||||
/// The time the current featured post was featured
|
/// The time the current featured post was featured
|
||||||
pub featuring_time: Option<NaiveDateTime>,
|
pub featuring_time: Option<DateTime<Utc>>,
|
||||||
/// The user who uploaded the featured post
|
/// The user who uploaded the featured post
|
||||||
pub featuring_user: Option<u32>,
|
pub featuring_user: Option<u32>,
|
||||||
/// The current server time
|
/// The current server time
|
||||||
pub server_time: NaiveDateTime,
|
pub server_time: DateTime<Utc>,
|
||||||
/// The configuration for this server
|
/// The configuration for this server
|
||||||
pub config: GlobalInfoConfig,
|
pub config: GlobalInfoConfig,
|
||||||
}
|
}
|
||||||
|
|
@ -660,9 +688,9 @@ pub struct PoolResource {
|
||||||
/// An ordered list of posts. Posts are ordered by insertion by default
|
/// An ordered list of posts. Posts are ordered by insertion by default
|
||||||
pub posts: Option<Vec<MicroPostResource>>,
|
pub posts: Option<Vec<MicroPostResource>>,
|
||||||
/// Time the pool was created
|
/// Time the pool was created
|
||||||
pub creation_time: Option<NaiveDateTime>,
|
pub creation_time: Option<DateTime<Utc>>,
|
||||||
/// Time the pool was edited
|
/// Time the pool was edited
|
||||||
pub last_edit_time: Option<NaiveDateTime>,
|
pub last_edit_time: Option<DateTime<Utc>>,
|
||||||
/// The total number of posts the pool has
|
/// The total number of posts the pool has
|
||||||
pub post_count: Option<u32>,
|
pub post_count: Option<u32>,
|
||||||
/// The pool description (instructions how to use, history etc). The client should render
|
/// The pool description (instructions how to use, history etc). The client should render
|
||||||
|
|
@ -758,9 +786,9 @@ pub struct CommentResource {
|
||||||
/// The text of the comment
|
/// The text of the comment
|
||||||
pub text: Option<String>,
|
pub text: Option<String>,
|
||||||
/// When was the comment posted
|
/// When was the comment posted
|
||||||
pub creation_time: Option<NaiveDateTime>,
|
pub creation_time: Option<DateTime<Utc>>,
|
||||||
/// When was the last time this comment was edited
|
/// When was the last time this comment was edited
|
||||||
pub last_edit_time: Option<NaiveDateTime>,
|
pub last_edit_time: Option<DateTime<Utc>>,
|
||||||
/// The sum of the -1/0/+1 scores by other users
|
/// The sum of the -1/0/+1 scores by other users
|
||||||
pub score: Option<i32>,
|
pub score: Option<i32>,
|
||||||
/// The user's own score for this comment
|
/// The user's own score for this comment
|
||||||
|
|
@ -890,7 +918,7 @@ pub struct SnapshotResource {
|
||||||
/// The data associated with this resource change
|
/// The data associated with this resource change
|
||||||
pub data: Option<SnapshotData>,
|
pub data: Option<SnapshotData>,
|
||||||
/// When this resource change occurred
|
/// When this resource change occurred
|
||||||
pub time: Option<NaiveDateTime>,
|
pub time: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
@ -923,3 +951,78 @@ pub struct AroundPostResult {
|
||||||
/// The next post, if it exists
|
/// The next post, if it exists
|
||||||
next: Option<u32>,
|
next: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::models::{GlobalInfo, GlobalInfoConfig, TagCategoryResource};
|
||||||
|
use chrono::Datelike;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_global_info() {
|
||||||
|
let cfg_str = r#"{
|
||||||
|
"name": "integrationland",
|
||||||
|
"userNameRegex": "^[a-zA-Z0-9_-]{1,32}$",
|
||||||
|
"passwordRegex": "^.{5,}$",
|
||||||
|
"tagNameRegex": "^\\S+$",
|
||||||
|
"tagCategoryNameRegex": "^[^\\s%+#/]+$",
|
||||||
|
"defaultUserRank": "regular",
|
||||||
|
"enableSafety": true,
|
||||||
|
"contactEmail": null,
|
||||||
|
"canSendMails": false,
|
||||||
|
"privileges": {
|
||||||
|
"users:create:self": "anonymous",
|
||||||
|
"users:create:any": "administrator",
|
||||||
|
"comments:edit:own": "regular",
|
||||||
|
"comments:list": "regular",
|
||||||
|
"comments:view": "regular",
|
||||||
|
"comments:score": "regular",
|
||||||
|
"snapshots:list": "power",
|
||||||
|
"uploads:create": "regular",
|
||||||
|
"uploads:useDownloader": "power"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let global_config =
|
||||||
|
serde_json::from_str::<GlobalInfoConfig>(cfg_str).expect("Unable to parse cfg_str");
|
||||||
|
assert_eq!(global_config.can_send_mails, false);
|
||||||
|
let info_str = r#"{"postCount": 0,
|
||||||
|
"diskUsage": 0,
|
||||||
|
"serverTime": "2024-08-09T21:41:24.123623Z",
|
||||||
|
"config": {
|
||||||
|
"name": "integrationland",
|
||||||
|
"userNameRegex": "^[a-zA-Z0-9_-]{1,32}$",
|
||||||
|
"passwordRegex": "^.{5,}$",
|
||||||
|
"tagNameRegex": "^\\S+$",
|
||||||
|
"tagCategoryNameRegex": "^[^\\s%+#/]+$",
|
||||||
|
"defaultUserRank": "regular",
|
||||||
|
"enableSafety": true,
|
||||||
|
"contactEmail": null,
|
||||||
|
"canSendMails": false,
|
||||||
|
"privileges": {
|
||||||
|
"users:create:self": "anonymous"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"featuredPost": null,
|
||||||
|
"featuringUser": null,
|
||||||
|
"featuringTime": null
|
||||||
|
}"#;
|
||||||
|
let global_info =
|
||||||
|
serde_json::from_str::<GlobalInfo>(info_str).expect("Unable to parse info_str");
|
||||||
|
assert_eq!(global_info.server_time.year(), 2024);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_tag_category_resource() {
|
||||||
|
let input_str = r#" {
|
||||||
|
"name": "default",
|
||||||
|
"version": 1,
|
||||||
|
"color": "default",
|
||||||
|
"usages": 0,
|
||||||
|
"default": true,
|
||||||
|
"order": 1
|
||||||
|
}"#;
|
||||||
|
let tag_cat = serde_json::from_str::<TagCategoryResource>(input_str)
|
||||||
|
.expect("Unable to parse tag category string");
|
||||||
|
assert_eq!(tag_cat.name, Some("default".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
progress = "0.2.0"
|
||||||
szurubooru-client = { version = "0.1.0", path = "../szurubooru-client" }
|
szurubooru-client = { version = "0.1.0", path = "../szurubooru-client" }
|
||||||
|
tokio = { version = "1.39.2", features = ["full", "test-util", "tracing"] }
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,15 @@ services:
|
||||||
## or FQDNs/IP addresses if these services are running outside of Docker
|
## or FQDNs/IP addresses if these services are running outside of Docker
|
||||||
POSTGRES_HOST: sql
|
POSTGRES_HOST: sql
|
||||||
## Credentials for database:
|
## Credentials for database:
|
||||||
POSTGRES_USER:
|
POSTGRES_USER: pguser
|
||||||
POSTGRES_PASSWORD:
|
POSTGRES_PASSWORD: pgpassword
|
||||||
## Commented Values are Default:
|
## Commented Values are Default:
|
||||||
#POSTGRES_DB: defaults to same as POSTGRES_USER
|
#POSTGRES_DB: defaults to same as POSTGRES_USER
|
||||||
#POSTGRES_PORT: 5432
|
#POSTGRES_PORT: 5432
|
||||||
#LOG_SQL: 0 (1 for verbose SQL logs)
|
#LOG_SQL: 0 (1 for verbose SQL logs)
|
||||||
THREADS:
|
THREADS:
|
||||||
volumes:
|
volumes:
|
||||||
- "${MOUNT_DATA}:/data"
|
- "sz-data:/data"
|
||||||
- "${SERVER_MOUNT}/config.yaml:/opt/app/config.yaml"
|
- "${SERVER_MOUNT}/config.yaml:/opt/app/config.yaml"
|
||||||
|
|
||||||
client:
|
client:
|
||||||
|
|
@ -34,7 +34,7 @@ services:
|
||||||
BACKEND_HOST: server
|
BACKEND_HOST: server
|
||||||
BASE_URL: ${BASE_URL}
|
BASE_URL: ${BASE_URL}
|
||||||
volumes:
|
volumes:
|
||||||
- "${MOUNT_DATA}:/data:ro"
|
- "sz-data:/data:ro"
|
||||||
ports:
|
ports:
|
||||||
- "${PORT}:80"
|
- "${PORT}:80"
|
||||||
|
|
||||||
|
|
@ -42,7 +42,10 @@ services:
|
||||||
image: postgres:11-alpine
|
image: postgres:11-alpine
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER:
|
POSTGRES_USER: pguser
|
||||||
POSTGRES_PASSWORD:
|
POSTGRES_PASSWORD: pgpassword
|
||||||
volumes:
|
#volumes:
|
||||||
- "${MOUNT_SQL}:/var/lib/postgresql/data"
|
# - "${MOUNT_SQL}/sqldata:/var/lib/postgresql/data"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
sz-data:
|
||||||
|
|
|
||||||
BIN
szurubooru-integration-test/folly.jpg
Normal file
BIN
szurubooru-integration-test/folly.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
286
szurubooru-integration-test/src/main.rs
Normal file
286
szurubooru-integration-test/src/main.rs
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::process::Stdio;
|
||||||
|
use std::time::Duration;
|
||||||
|
use szurubooru_client::models::*;
|
||||||
|
use szurubooru_client::*;
|
||||||
|
use tokio::process::Command;
|
||||||
|
use tracing::level_filters::LevelFilter;
|
||||||
|
use tracing::{error, info, instrument};
|
||||||
|
use tracing_subscriber::filter::EnvFilter;
|
||||||
|
use tracing_subscriber::fmt::format::FmtSpan;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
#[tracing::instrument]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let ev = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::INFO.into())
|
||||||
|
.from_env_lossy();
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.compact()
|
||||||
|
//.with_span_events(FmtSpan::ACTIVE)
|
||||||
|
.with_target(false)
|
||||||
|
.with_level(false)
|
||||||
|
.with_env_filter(ev)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
//info!("Starting Szurubooru instance...");
|
||||||
|
let anon_client = start_instance().await;
|
||||||
|
|
||||||
|
let create_user = CreateUpdateUserBuilder::default()
|
||||||
|
.name("integration_user")
|
||||||
|
.password("integration_password")
|
||||||
|
.rank(UserRank::Administrator)
|
||||||
|
.avatar_style(UserAvatarStyle::Gravatar)
|
||||||
|
.build()
|
||||||
|
.expect("Unable to create CreateUser object");
|
||||||
|
let _user = anon_client
|
||||||
|
.request()
|
||||||
|
.create_user(&create_user)
|
||||||
|
.await
|
||||||
|
.expect("Error creating user");
|
||||||
|
|
||||||
|
let auth_client = SzurubooruClient::new_with_basic_auth(
|
||||||
|
"http://localhost:9801",
|
||||||
|
"integration_user",
|
||||||
|
"integration_password",
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
test_tag_categories(&auth_client).await;
|
||||||
|
test_tags(&auth_client).await;
|
||||||
|
|
||||||
|
Command::new("sh")
|
||||||
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("./stop_szurubooru.sh")
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.expect("Failed to stop szurubooru");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
async fn start_instance() -> SzurubooruClient {
|
||||||
|
info!("Starting Szurubooru instance...");
|
||||||
|
let anon_client = SzurubooruClient::new_anonymous("http://localhost:9801", true)
|
||||||
|
.expect("Can't create anonymous client");
|
||||||
|
|
||||||
|
Command::new("sh")
|
||||||
|
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
||||||
|
.arg("-c")
|
||||||
|
.arg("./start_szurubooru.sh")
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.expect("Failed to start szurubooru");
|
||||||
|
|
||||||
|
let mut connected = false;
|
||||||
|
let mut error = None;
|
||||||
|
|
||||||
|
for _ in 0..5 {
|
||||||
|
let info = anon_client.request().get_global_info().await;
|
||||||
|
if info.is_ok() {
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
} else if let Err(e) = info {
|
||||||
|
error = Some(e);
|
||||||
|
}
|
||||||
|
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
if !connected {
|
||||||
|
panic!(
|
||||||
|
"{}",
|
||||||
|
format!("Unable to connect to instance. Last error: {error:?}")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
anon_client
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(client), level = "INFO")]
|
||||||
|
async fn test_tag_categories(client: &SzurubooruClient) {
|
||||||
|
info!("Testing tag categories");
|
||||||
|
let tag_cats = client
|
||||||
|
.request()
|
||||||
|
.list_tag_categories()
|
||||||
|
.await
|
||||||
|
.expect("Could not list tag categories");
|
||||||
|
assert_eq!(tag_cats.results.len(), 1);
|
||||||
|
|
||||||
|
info!("Creating tag category");
|
||||||
|
let create_tag_cat = CreateUpdateTagCategoryBuilder::default()
|
||||||
|
.name("my_tag_cat".to_string())
|
||||||
|
.color("purple".to_string())
|
||||||
|
.order(1)
|
||||||
|
.build()
|
||||||
|
.expect("Unable to build create_tag_cat object");
|
||||||
|
let result_tag_cat = client
|
||||||
|
.request()
|
||||||
|
.create_tag_category(&create_tag_cat)
|
||||||
|
.await
|
||||||
|
.expect("Unable to create tag category");
|
||||||
|
assert_eq!(result_tag_cat.name, Some("my_tag_cat".to_string()));
|
||||||
|
let tag_cats = client
|
||||||
|
.request()
|
||||||
|
.list_tag_categories()
|
||||||
|
.await
|
||||||
|
.expect("Could not list tag categories again");
|
||||||
|
assert_eq!(tag_cats.results.len(), 2);
|
||||||
|
|
||||||
|
info!("GETting tag category");
|
||||||
|
let tag_res = client
|
||||||
|
.request()
|
||||||
|
.get_tag_category("my_tag_cat")
|
||||||
|
.await
|
||||||
|
.expect("Could not fetch tag category");
|
||||||
|
assert_eq!(result_tag_cat.color, tag_res.color);
|
||||||
|
|
||||||
|
info!("Updating tag category");
|
||||||
|
let update_tag_cat = CreateUpdateTagCategoryBuilder::default()
|
||||||
|
.version(tag_res.version)
|
||||||
|
.color("red".to_string())
|
||||||
|
.build()
|
||||||
|
.expect("Could not create update for tag category");
|
||||||
|
let update_res = client
|
||||||
|
.request()
|
||||||
|
.update_tag_category("my_tag_cat", &update_tag_cat)
|
||||||
|
.await
|
||||||
|
.expect("Unable to update tag category");
|
||||||
|
assert_eq!(update_res.color, Some("red".to_string()));
|
||||||
|
|
||||||
|
info!("Deleting tag category");
|
||||||
|
client
|
||||||
|
.request()
|
||||||
|
.delete_tag_category("my_tag_cat", update_res.version)
|
||||||
|
.await
|
||||||
|
.expect("Could not delete tag category");
|
||||||
|
let tag_cats = client
|
||||||
|
.request()
|
||||||
|
.list_tag_categories()
|
||||||
|
.await
|
||||||
|
.expect("Could not list tag categories again");
|
||||||
|
assert_eq!(tag_cats.results.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(client))]
|
||||||
|
async fn test_tags(client: &SzurubooruClient) {
|
||||||
|
info!("Testing tag functions");
|
||||||
|
|
||||||
|
info!("Listing tags");
|
||||||
|
let tag_list = client
|
||||||
|
.request()
|
||||||
|
.list_tags(None)
|
||||||
|
.await
|
||||||
|
.expect("Could not list tags");
|
||||||
|
assert_eq!(tag_list.total, 0);
|
||||||
|
|
||||||
|
info!("Creating tag");
|
||||||
|
let cutag = CreateUpdateTagBuilder::default()
|
||||||
|
.names(vec!["foo".to_string(), "foo2".to_string()])
|
||||||
|
.category("default".to_string())
|
||||||
|
.description("The foo tag".to_string())
|
||||||
|
.build()
|
||||||
|
.expect("Could not build the CreateUpdateTag");
|
||||||
|
let tag_res = client
|
||||||
|
.request()
|
||||||
|
.create_tag(&cutag)
|
||||||
|
.await
|
||||||
|
.expect("Could not create tag");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tag_res.names,
|
||||||
|
Some(vec!["foo".to_string(), "foo2".to_string()])
|
||||||
|
);
|
||||||
|
assert!(tag_res.description.is_some());
|
||||||
|
|
||||||
|
let tag_list = client
|
||||||
|
.request()
|
||||||
|
.list_tags(None)
|
||||||
|
.await
|
||||||
|
.expect("Could not list tags");
|
||||||
|
assert_eq!(tag_list.total, 1);
|
||||||
|
|
||||||
|
info!("Testing field selection");
|
||||||
|
let tag_list = client
|
||||||
|
.with_fields(vec!["version", "names", "category"])
|
||||||
|
.list_tags(None)
|
||||||
|
.await
|
||||||
|
.expect("Could not list tags");
|
||||||
|
assert_eq!(tag_list.total, 1);
|
||||||
|
assert!(tag_list.results.first().unwrap().description.is_none());
|
||||||
|
|
||||||
|
info!("Updating tag");
|
||||||
|
let utag = CreateUpdateTagBuilder::default()
|
||||||
|
.version(tag_res.version)
|
||||||
|
.description("The foo2 tag".to_string())
|
||||||
|
.build()
|
||||||
|
.expect("Could not build the CreateUpdateTag");
|
||||||
|
let tag_res2 = client
|
||||||
|
.request()
|
||||||
|
.update_tag("foo", &utag)
|
||||||
|
.await
|
||||||
|
.expect("Could not update tag");
|
||||||
|
assert_ne!(tag_res.description, tag_res2.description);
|
||||||
|
|
||||||
|
info!("Getting tag");
|
||||||
|
let tag_res3 = client
|
||||||
|
.request()
|
||||||
|
.get_tag("foo")
|
||||||
|
.await
|
||||||
|
.expect("Could not fetch tag");
|
||||||
|
assert_eq!(tag_res2.description, tag_res3.description);
|
||||||
|
|
||||||
|
info!("Creating a second tag");
|
||||||
|
let cutag = CreateUpdateTagBuilder::default()
|
||||||
|
.names(vec!["bar".to_string()])
|
||||||
|
.category("default".to_string())
|
||||||
|
.description("The foo tag".to_string())
|
||||||
|
.build()
|
||||||
|
.expect("Could not build the CreateUpdateTag");
|
||||||
|
let bar_tag = client
|
||||||
|
.request()
|
||||||
|
.create_tag(&cutag)
|
||||||
|
.await
|
||||||
|
.expect("Unable to create second tag");
|
||||||
|
let tag_list = client
|
||||||
|
.request()
|
||||||
|
.list_tags(None)
|
||||||
|
.await
|
||||||
|
.expect("Could not list tags");
|
||||||
|
assert_eq!(tag_list.total, 2);
|
||||||
|
|
||||||
|
info!("Merging tags");
|
||||||
|
let merge_tag = MergeTagsBuilder::default()
|
||||||
|
.remove_version(bar_tag.version)
|
||||||
|
.remove(bar_tag.names.as_ref().unwrap().first().unwrap())
|
||||||
|
.merge_to_version(tag_res3.version)
|
||||||
|
.merge_to(tag_res3.names.as_ref().unwrap().first().unwrap())
|
||||||
|
.build()
|
||||||
|
.expect("Could not create merge tags object");
|
||||||
|
let merged_tag = client
|
||||||
|
.request()
|
||||||
|
.merge_tag(&merge_tag)
|
||||||
|
.await
|
||||||
|
.expect("Could not merge tags");
|
||||||
|
assert_eq!(tag_res3.names, merged_tag.names);
|
||||||
|
let tag_list = client
|
||||||
|
.request()
|
||||||
|
.list_tags(None)
|
||||||
|
.await
|
||||||
|
.expect("Could not list tags");
|
||||||
|
assert_eq!(tag_list.total, 1);
|
||||||
|
|
||||||
|
info!("Deleting tag");
|
||||||
|
client
|
||||||
|
.request()
|
||||||
|
.delete_tag("foo", merged_tag.version)
|
||||||
|
.await
|
||||||
|
.expect("Could not delete tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: test Tag siblings once we've tested a post
|
||||||
12
szurubooru-integration-test/start_szurubooru.sh
Normal file → Executable file
12
szurubooru-integration-test/start_szurubooru.sh
Normal file → Executable file
|
|
@ -1,9 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
export MOUNT_DATA=szurubooru/data
|
#export MOUNT_DATA=szurubooru/data
|
||||||
export SERVER_MOUNT=szurubooru/server
|
export SERVER_MOUNT=./szurubooru/server
|
||||||
export MOUNT_SQL=szurubooru/pgsql
|
#export MOUNT_SQL=szurubooru/pgsql
|
||||||
export BASE_URL=http://localhost:5000
|
export BASE_URL=http://localhost:9801
|
||||||
export PORT=5000
|
export PORT=9801
|
||||||
|
|
||||||
|
docker compose down || true
|
||||||
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
|
||||||
3
szurubooru-integration-test/stop_szurubooru.sh
Executable file
3
szurubooru-integration-test/stop_szurubooru.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
docker compose down
|
||||||
179
szurubooru-integration-test/szurubooru/server/config.yaml
Normal file
179
szurubooru-integration-test/szurubooru/server/config.yaml
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
# rather than editing this file, it is strongly suggested to create config.yaml
|
||||||
|
# and override only what you need.
|
||||||
|
|
||||||
|
# shown in the website title and on the front page
|
||||||
|
name: integrationland
|
||||||
|
# full url to the homepage of this szurubooru site, with no trailing slash
|
||||||
|
domain: https://localhost:9801
|
||||||
|
# used to salt the users' password hashes and generate filenames for static content
|
||||||
|
secret: awdopk1mdqsapdoawd21!!!#asw
|
||||||
|
|
||||||
|
# Delete thumbnails and source files on post delete
|
||||||
|
# Original functionality is no, to mitigate the impacts of admins going
|
||||||
|
# on unchecked post purges.
|
||||||
|
delete_source_files: no
|
||||||
|
|
||||||
|
thumbnails:
|
||||||
|
avatar_width: 300
|
||||||
|
avatar_height: 300
|
||||||
|
post_width: 300
|
||||||
|
post_height: 300
|
||||||
|
|
||||||
|
# settings used to download files from the web on behalf of the api users
|
||||||
|
user_agent:
|
||||||
|
max_dl_filesize: 25.0E+6 # maximum filesize limit in bytes
|
||||||
|
|
||||||
|
# automatically convert animated GIF uploads to video formats
|
||||||
|
convert:
|
||||||
|
gif:
|
||||||
|
to_webm: false
|
||||||
|
to_mp4: false
|
||||||
|
|
||||||
|
# allow posts to be uploaded even if some image processing errors occur
|
||||||
|
allow_broken_uploads: false
|
||||||
|
|
||||||
|
# used to send password reset e-mails
|
||||||
|
smtp:
|
||||||
|
host: # example: localhost
|
||||||
|
port: # example: 25
|
||||||
|
user: # example: bot
|
||||||
|
pass: # example: groovy123
|
||||||
|
from: # example: noreply@example.com
|
||||||
|
# if host is left empty the password reset feature will be disabled,
|
||||||
|
# in which case it is recommended to fill contactEmail so that users
|
||||||
|
# know who to contact when they want to reset their password
|
||||||
|
|
||||||
|
contact_email: # example: bob@example.com. Meant for manual password reset procedures
|
||||||
|
|
||||||
|
enable_safety: yes
|
||||||
|
|
||||||
|
tag_name_regex: ^\S+$
|
||||||
|
tag_category_name_regex: ^[^\s%+#/]+$
|
||||||
|
|
||||||
|
pool_name_regex: ^\S+$
|
||||||
|
pool_category_name_regex: ^[^\s%+#/]+$
|
||||||
|
|
||||||
|
# don't make these more restrictive unless you want to annoy people; if you do
|
||||||
|
# customize them, make sure to update the instructions in the registration form
|
||||||
|
# template as well.
|
||||||
|
password_regex: '^.{5,}$'
|
||||||
|
user_name_regex: '^[a-zA-Z0-9_-]{1,32}$'
|
||||||
|
|
||||||
|
# webhooks to call when events occur (such as post/tag/user/etc. changes)
|
||||||
|
# the listed urls will be called with a HTTP POST request with a payload
|
||||||
|
# containing a snapshot resource as JSON. See doc/API.md for details
|
||||||
|
webhooks:
|
||||||
|
# - https://api.example.com/webhooks/
|
||||||
|
|
||||||
|
default_rank: regular
|
||||||
|
|
||||||
|
privileges:
|
||||||
|
'users:create:self': anonymous # Registration permission
|
||||||
|
'users:create:any': administrator
|
||||||
|
'users:list': regular
|
||||||
|
'users:view': regular
|
||||||
|
'users:edit:any:name': moderator
|
||||||
|
'users:edit:any:pass': moderator
|
||||||
|
'users:edit:any:email': moderator
|
||||||
|
'users:edit:any:avatar': moderator
|
||||||
|
'users:edit:any:rank': moderator
|
||||||
|
'users:edit:self:name': regular
|
||||||
|
'users:edit:self:pass': regular
|
||||||
|
'users:edit:self:email': regular
|
||||||
|
'users:edit:self:avatar': regular
|
||||||
|
'users:edit:self:rank': moderator # one can't promote themselves or anyone to upper rank than their own.
|
||||||
|
'users:delete:any': administrator
|
||||||
|
'users:delete:self': regular
|
||||||
|
|
||||||
|
'user_tokens:list:any': administrator
|
||||||
|
'user_tokens:list:self': regular
|
||||||
|
'user_tokens:create:any': administrator
|
||||||
|
'user_tokens:create:self': regular
|
||||||
|
'user_tokens:edit:any': administrator
|
||||||
|
'user_tokens:edit:self': regular
|
||||||
|
'user_tokens:delete:any': administrator
|
||||||
|
'user_tokens:delete:self': regular
|
||||||
|
|
||||||
|
'posts:create:anonymous': regular
|
||||||
|
'posts:create:identified': regular
|
||||||
|
'posts:list': anonymous
|
||||||
|
'posts:reverse_search': regular
|
||||||
|
'posts:view': anonymous
|
||||||
|
'posts:view:featured': anonymous
|
||||||
|
'posts:edit:content': power
|
||||||
|
'posts:edit:flags': regular
|
||||||
|
'posts:edit:notes': regular
|
||||||
|
'posts:edit:relations': regular
|
||||||
|
'posts:edit:safety': power
|
||||||
|
'posts:edit:source': regular
|
||||||
|
'posts:edit:tags': regular
|
||||||
|
'posts:edit:thumbnail': power
|
||||||
|
'posts:feature': moderator
|
||||||
|
'posts:delete': moderator
|
||||||
|
'posts:score': regular
|
||||||
|
'posts:merge': moderator
|
||||||
|
'posts:favorite': regular
|
||||||
|
'posts:bulk-edit:tags': power
|
||||||
|
'posts:bulk-edit:safety': power
|
||||||
|
'posts:bulk-edit:delete': power
|
||||||
|
|
||||||
|
'tags:create': regular
|
||||||
|
'tags:edit:names': power
|
||||||
|
'tags:edit:category': power
|
||||||
|
'tags:edit:description': power
|
||||||
|
'tags:edit:implications': power
|
||||||
|
'tags:edit:suggestions': power
|
||||||
|
'tags:list': regular
|
||||||
|
'tags:view': anonymous
|
||||||
|
'tags:merge': moderator
|
||||||
|
'tags:delete': moderator
|
||||||
|
|
||||||
|
'tag_categories:create': moderator
|
||||||
|
'tag_categories:edit:name': moderator
|
||||||
|
'tag_categories:edit:color': moderator
|
||||||
|
'tag_categories:edit:order': moderator
|
||||||
|
'tag_categories:list': anonymous
|
||||||
|
'tag_categories:view': anonymous
|
||||||
|
'tag_categories:delete': moderator
|
||||||
|
'tag_categories:set_default': moderator
|
||||||
|
|
||||||
|
'pools:create': regular
|
||||||
|
'pools:edit:names': power
|
||||||
|
'pools:edit:category': power
|
||||||
|
'pools:edit:description': power
|
||||||
|
'pools:edit:posts': power
|
||||||
|
'pools:list': regular
|
||||||
|
'pools:view': anonymous
|
||||||
|
'pools:merge': moderator
|
||||||
|
'pools:delete': moderator
|
||||||
|
|
||||||
|
'pool_categories:create': moderator
|
||||||
|
'pool_categories:edit:name': moderator
|
||||||
|
'pool_categories:edit:color': moderator
|
||||||
|
'pool_categories:list': anonymous
|
||||||
|
'pool_categories:view': anonymous
|
||||||
|
'pool_categories:delete': moderator
|
||||||
|
'pool_categories:set_default': moderator
|
||||||
|
|
||||||
|
'comments:create': regular
|
||||||
|
'comments:delete:any': moderator
|
||||||
|
'comments:delete:own': regular
|
||||||
|
'comments:edit:any': moderator
|
||||||
|
'comments:edit:own': regular
|
||||||
|
'comments:list': regular
|
||||||
|
'comments:view': regular
|
||||||
|
'comments:score': regular
|
||||||
|
|
||||||
|
'snapshots:list': power
|
||||||
|
|
||||||
|
'uploads:create': regular
|
||||||
|
'uploads:use_downloader': power
|
||||||
|
|
||||||
|
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
|
||||||
|
#debug: 0 # generate server logs?
|
||||||
|
#show_sql: 0 # show sql in server logs?
|
||||||
|
#data_url: /data/
|
||||||
|
#data_dir: /var/www/data
|
||||||
|
## usage: schema://user:password@host:port/database_name
|
||||||
|
## example: postgres://szuru:dog@localhost:5432/szuru_test
|
||||||
|
#database:
|
||||||
Loading…
Add table
Add a link
Reference in a new issue