Compare commits
No commits in common. "0fcbc6bbe97e6dde941e9bc691ff9704416ea66b" and "b2f327d48fc95c8f54e9ac504f80458af2198fb4" have entirely different histories.
0fcbc6bbe9
...
b2f327d48f
@ -2,5 +2,5 @@
|
|||||||
recorder-playground = "run -p recorder --example playground -- --environment development"
|
recorder-playground = "run -p recorder --example playground -- --environment development"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
#rustflags = ["-Zthreads=8", "-Zshare-generics=y"]
|
rustflags = ["-Zthreads=8", "-Zshare-generics=y"]
|
||||||
rustflags = ["-Zthreads=8"]
|
# rustflags = ["-Zthreads=8"]
|
||||||
|
435
Cargo.lock
generated
435
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
37
Cargo.toml
37
Cargo.toml
@ -2,7 +2,6 @@
|
|||||||
members = [
|
members = [
|
||||||
"packages/testing-torrents",
|
"packages/testing-torrents",
|
||||||
"packages/util",
|
"packages/util",
|
||||||
"packages/util-derive",
|
|
||||||
"packages/fetch",
|
"packages/fetch",
|
||||||
"packages/downloader",
|
"packages/downloader",
|
||||||
"apps/recorder",
|
"apps/recorder",
|
||||||
@ -10,19 +9,6 @@ members = [
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
testing-torrents = { path = "./packages/testing-torrents" }
|
|
||||||
util = { path = "./packages/util" }
|
|
||||||
util-derive = { path = "./packages/util-derive" }
|
|
||||||
fetch = { path = "./packages/fetch" }
|
|
||||||
downloader = { path = "./packages/downloader" }
|
|
||||||
|
|
||||||
reqwest = { version = "0.12", features = [
|
|
||||||
"charset",
|
|
||||||
"http2",
|
|
||||||
"json",
|
|
||||||
"macos-system-configuration",
|
|
||||||
"cookies",
|
|
||||||
] }
|
|
||||||
moka = "0.12"
|
moka = "0.12"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
quirks_path = "0.1"
|
quirks_path = "0.1"
|
||||||
@ -31,12 +17,7 @@ testcontainers = { version = "0.24" }
|
|||||||
testcontainers-modules = { version = "0.12" }
|
testcontainers-modules = { version = "0.12" }
|
||||||
testcontainers-ext = { version = "0.1.0", features = ["tracing"] }
|
testcontainers-ext = { version = "0.1.0", features = ["tracing"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
tokio = { version = "1.45.1", features = [
|
tokio = { version = "1", features = ["macros", "fs", "rt-multi-thread"] }
|
||||||
"macros",
|
|
||||||
"fs",
|
|
||||||
"rt-multi-thread",
|
|
||||||
"signal",
|
|
||||||
] }
|
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
@ -49,10 +30,22 @@ serde_with = "3"
|
|||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
axum = { version = "0.8.3", features = ["macros"] }
|
axum = { version = "0.8.3", features = ["macros"] }
|
||||||
|
reqwest = { version = "0.12", default-features = false, features = [
|
||||||
|
"charset",
|
||||||
|
"http2",
|
||||||
|
"json",
|
||||||
|
"macos-system-configuration",
|
||||||
|
"rustls-tls",
|
||||||
|
"cookies",
|
||||||
|
] }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
||||||
axum-extra = "0.10"
|
axum-extra = "0.10"
|
||||||
mockito = { version = "1.6.1" }
|
|
||||||
convert_case = "0.8"
|
testing-torrents = { path = "./packages/testing-torrents" }
|
||||||
|
util = { path = "./packages/util" }
|
||||||
|
fetch = { path = "./packages/fetch" }
|
||||||
|
downloader = { path = "./packages/downloader" }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
jwt-authorizer = { git = "https://github.com/blablacio/jwt-authorizer.git", rev = "e956774" }
|
||||||
seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "10ba248" }
|
seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "10ba248" }
|
||||||
|
@ -25,11 +25,6 @@ testcontainers = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
downloader = { workspace = true }
|
|
||||||
util = { workspace = true }
|
|
||||||
util-derive = { workspace = true }
|
|
||||||
fetch = { workspace = true }
|
|
||||||
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
@ -54,18 +49,18 @@ serde_with = { workspace = true }
|
|||||||
moka = { workspace = true }
|
moka = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
mockito = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
sea-orm = { version = "1.1", features = [
|
sea-orm = { version = "1.1", features = [
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"sqlx-postgres",
|
"sqlx-postgres",
|
||||||
"runtime-tokio",
|
"runtime-tokio-rustls",
|
||||||
"macros",
|
"macros",
|
||||||
"debug-print",
|
"debug-print",
|
||||||
] }
|
] }
|
||||||
figment = { version = "0.10", features = ["toml", "json", "env", "yaml"] }
|
figment = { version = "0.10", features = ["toml", "json", "env", "yaml"] }
|
||||||
uuid = { version = "1.6.0", features = ["v4"] }
|
uuid = { version = "1.6.0", features = ["v4"] }
|
||||||
sea-orm-migration = { version = "1.1", features = ["runtime-tokio"] }
|
sea-orm-migration = { version = "1.1", features = ["runtime-tokio-rustls"] }
|
||||||
rss = "2"
|
rss = "2"
|
||||||
fancy-regex = "0.14"
|
fancy-regex = "0.14"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
@ -76,6 +71,7 @@ zune-image = "0.4.15"
|
|||||||
once_cell = "1.20.2"
|
once_cell = "1.20.2"
|
||||||
scraper = "0.23"
|
scraper = "0.23"
|
||||||
|
|
||||||
|
jwt-authorizer = "0.15.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
async-graphql = { version = "7", features = ["dynamic-schema"] }
|
async-graphql = { version = "7", features = ["dynamic-schema"] }
|
||||||
async-graphql-axum = "7"
|
async-graphql-axum = "7"
|
||||||
@ -101,8 +97,8 @@ tower-http = { version = "0.6", features = [
|
|||||||
"compression-full",
|
"compression-full",
|
||||||
] }
|
] }
|
||||||
tera = "1.20.0"
|
tera = "1.20.0"
|
||||||
openidconnect = { version = "4" }
|
openidconnect = { version = "4", features = ["rustls-tls"] }
|
||||||
dotenvy = "0.15.7"
|
dotenv = "0.15.0"
|
||||||
http = "1.2.0"
|
http = "1.2.0"
|
||||||
async-stream = "0.3.6"
|
async-stream = "0.3.6"
|
||||||
serde_variant = "0.1.3"
|
serde_variant = "0.1.3"
|
||||||
@ -110,19 +106,22 @@ tracing-appender = "0.2.3"
|
|||||||
clap = "4.5.31"
|
clap = "4.5.31"
|
||||||
ipnetwork = "0.21.1"
|
ipnetwork = "0.21.1"
|
||||||
typed-builder = "0.21.0"
|
typed-builder = "0.21.0"
|
||||||
|
serde_yaml = "0.9.34"
|
||||||
apalis = { version = "0.7", features = ["limit", "tracing", "catch-panic"] }
|
apalis = { version = "0.7", features = ["limit", "tracing", "catch-panic"] }
|
||||||
apalis-sql = { version = "0.7", features = ["postgres"] }
|
apalis-sql = { version = "0.7", features = ["postgres"] }
|
||||||
cocoon = { version = "0.4.3", features = ["getrandom", "thiserror"] }
|
cocoon = { version = "0.4.3", features = ["getrandom", "thiserror"] }
|
||||||
rand = "0.9.1"
|
rand = "0.9.1"
|
||||||
rust_decimal = "1.37.1"
|
rust_decimal = "1.37.1"
|
||||||
reqwest_cookie_store = "0.8.0"
|
reqwest_cookie_store = "0.8.0"
|
||||||
nanoid = "0.4.0"
|
mockito = { version = "1.6.1", optional = true }
|
||||||
jwtk = "0.4.0"
|
|
||||||
|
|
||||||
|
downloader = { workspace = true }
|
||||||
|
util = { workspace = true }
|
||||||
|
fetch = { workspace = true }
|
||||||
|
nanoid = "0.4.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "3"
|
serial_test = "3"
|
||||||
insta = { version = "1", features = ["redactions", "toml", "filters"] }
|
insta = { version = "1", features = ["redactions", "yaml", "filters"] }
|
||||||
rstest = "0.25"
|
rstest = "0.25"
|
||||||
ctor = "0.4.0"
|
ctor = "0.4.0"
|
||||||
mockito = { workspace = true }
|
|
||||||
|
@ -110,12 +110,12 @@ impl AppConfig {
|
|||||||
for f in try_filenames.iter() {
|
for f in try_filenames.iter() {
|
||||||
let p = try_dotenv_file_or_dir_path.join(f);
|
let p = try_dotenv_file_or_dir_path.join(f);
|
||||||
if p.exists() && p.is_file() {
|
if p.exists() && p.is_file() {
|
||||||
dotenvy::from_path(p)?;
|
dotenv::from_path(p)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if try_dotenv_file_or_dir_path.is_file() {
|
} else if try_dotenv_file_or_dir_path.is_file() {
|
||||||
dotenvy::from_path(try_dotenv_file_or_dir_path)?;
|
dotenv::from_path(try_dotenv_file_or_dir_path)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use jwt_authorizer::OneOrArray;
|
||||||
|
|
||||||
use jwtk::OneOrMany;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::serde_as;
|
use serde_with::{NoneAsEmptyString, serde_as};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct BasicAuthConfig {
|
pub struct BasicAuthConfig {
|
||||||
@ -24,9 +22,13 @@ pub struct OidcAuthConfig {
|
|||||||
#[serde(rename = "oidc_client_secret")]
|
#[serde(rename = "oidc_client_secret")]
|
||||||
pub client_secret: String,
|
pub client_secret: String,
|
||||||
#[serde(rename = "oidc_extra_scopes")]
|
#[serde(rename = "oidc_extra_scopes")]
|
||||||
pub extra_scopes: Option<OneOrMany<String>>,
|
pub extra_scopes: Option<OneOrArray<String>>,
|
||||||
#[serde(rename = "oidc_extra_claims")]
|
#[serde_as(as = "NoneAsEmptyString")]
|
||||||
pub extra_claims: Option<HashMap<String, Option<String>>>,
|
#[serde(rename = "oidc_extra_claim_key")]
|
||||||
|
pub extra_claim_key: Option<String>,
|
||||||
|
#[serde(rename = "oidc_extra_claim_value")]
|
||||||
|
#[serde_as(as = "NoneAsEmptyString")]
|
||||||
|
pub extra_claim_value: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -27,6 +27,10 @@ pub enum AuthError {
|
|||||||
FindAuthRecordError,
|
FindAuthRecordError,
|
||||||
#[snafu(display("Invalid credentials"))]
|
#[snafu(display("Invalid credentials"))]
|
||||||
BasicInvalidCredentials,
|
BasicInvalidCredentials,
|
||||||
|
#[snafu(transparent)]
|
||||||
|
OidcInitError {
|
||||||
|
source: jwt_authorizer::error::InitError,
|
||||||
|
},
|
||||||
#[snafu(display("Invalid oidc provider meta client error: {source}"))]
|
#[snafu(display("Invalid oidc provider meta client error: {source}"))]
|
||||||
OidcProviderHttpClientError { source: HttpClientError },
|
OidcProviderHttpClientError { source: HttpClientError },
|
||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
@ -62,10 +66,8 @@ pub enum AuthError {
|
|||||||
OidcSignatureVerificationError { source: SignatureVerificationError },
|
OidcSignatureVerificationError { source: SignatureVerificationError },
|
||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
OidcSigningError { source: SigningError },
|
OidcSigningError { source: SigningError },
|
||||||
#[snafu(display("Missing Bearer token"))]
|
|
||||||
OidcMissingBearerToken,
|
|
||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
OidcJwtkError { source: jwtk::Error },
|
OidcJwtAuthError { source: jwt_authorizer::AuthError },
|
||||||
#[snafu(display("Extra scopes {expected} do not match found scopes {found}"))]
|
#[snafu(display("Extra scopes {expected} do not match found scopes {found}"))]
|
||||||
OidcExtraScopesMatchError { expected: String, found: String },
|
OidcExtraScopesMatchError { expected: String, found: String },
|
||||||
#[snafu(display("Extra claim {key} does not match expected value {expected}, found {found}"))]
|
#[snafu(display("Extra claim {key} does not match expected value {expected}, found {found}"))]
|
||||||
|
@ -12,9 +12,8 @@ use axum::{
|
|||||||
http::{HeaderValue, request::Parts},
|
http::{HeaderValue, request::Parts},
|
||||||
};
|
};
|
||||||
use fetch::{HttpClient, client::HttpClientError};
|
use fetch::{HttpClient, client::HttpClientError};
|
||||||
use http::header::AUTHORIZATION;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use jwtk::jwk::RemoteJwksVerifier;
|
use jwt_authorizer::{NumericDate, OneOrArray, authorizer::Authorizer};
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use openidconnect::{
|
use openidconnect::{
|
||||||
AccessTokenHash, AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce,
|
AccessTokenHash, AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce,
|
||||||
@ -78,6 +77,21 @@ impl<'c> openidconnect::AsyncHttpClient<'c> for OidcHttpClient {
|
|||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
pub struct OidcAuthClaims {
|
pub struct OidcAuthClaims {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub iss: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub sub: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub aud: Option<OneOrArray<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub exp: Option<NumericDate>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nbf: Option<NumericDate>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub iat: Option<NumericDate>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub jti: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub scope: Option<String>,
|
pub scope: Option<String>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub custom: HashMap<String, Value>,
|
pub custom: HashMap<String, Value>,
|
||||||
@ -87,6 +101,40 @@ impl OidcAuthClaims {
|
|||||||
pub fn scopes(&self) -> std::str::Split<'_, char> {
|
pub fn scopes(&self) -> std::str::Split<'_, char> {
|
||||||
self.scope.as_deref().unwrap_or_default().split(',')
|
self.scope.as_deref().unwrap_or_default().split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_claim(&self, key: &str) -> Option<String> {
|
||||||
|
match key {
|
||||||
|
"iss" => self.iss.clone(),
|
||||||
|
"sub" => self.sub.clone(),
|
||||||
|
"aud" => self.aud.as_ref().map(|s| s.iter().join(",")),
|
||||||
|
"exp" => self.exp.clone().map(|s| s.0.to_string()),
|
||||||
|
"nbf" => self.nbf.clone().map(|s| s.0.to_string()),
|
||||||
|
"iat" => self.iat.clone().map(|s| s.0.to_string()),
|
||||||
|
"jti" => self.jti.clone(),
|
||||||
|
"scope" => self.scope.clone(),
|
||||||
|
key => self.custom.get(key).map(|s| s.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_claim(&self, key: &str) -> bool {
|
||||||
|
match key {
|
||||||
|
"iss" => self.iss.is_some(),
|
||||||
|
"sub" => self.sub.is_some(),
|
||||||
|
"aud" => self.aud.is_some(),
|
||||||
|
"exp" => self.exp.is_some(),
|
||||||
|
"nbf" => self.nbf.is_some(),
|
||||||
|
"iat" => self.iat.is_some(),
|
||||||
|
"jti" => self.jti.is_some(),
|
||||||
|
"scope" => self.scope.is_some(),
|
||||||
|
key => self.custom.contains_key(key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_audience(&self, aud: &str) -> bool {
|
||||||
|
self.aud
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|arr| arr.iter().any(|s| s == aud))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
@ -116,7 +164,7 @@ pub struct OidcAuthCallbackPayload {
|
|||||||
|
|
||||||
pub struct OidcAuthService {
|
pub struct OidcAuthService {
|
||||||
pub config: OidcAuthConfig,
|
pub config: OidcAuthConfig,
|
||||||
pub jwk_verifier: RemoteJwksVerifier,
|
pub api_authorizer: Authorizer<OidcAuthClaims>,
|
||||||
pub oidc_provider_client: Arc<HttpClient>,
|
pub oidc_provider_client: Arc<HttpClient>,
|
||||||
pub oidc_request_cache: Cache<String, OidcAuthRequest>,
|
pub oidc_request_cache: Cache<String, OidcAuthRequest>,
|
||||||
}
|
}
|
||||||
@ -269,68 +317,47 @@ impl AuthServiceTrait for OidcAuthService {
|
|||||||
request: &mut Parts,
|
request: &mut Parts,
|
||||||
) -> Result<AuthUserInfo, AuthError> {
|
) -> Result<AuthUserInfo, AuthError> {
|
||||||
let config = &self.config;
|
let config = &self.config;
|
||||||
let token = request
|
let token = self
|
||||||
.headers
|
.api_authorizer
|
||||||
.get(AUTHORIZATION)
|
.extract_token(&request.headers)
|
||||||
.and_then(|authorization| {
|
.ok_or(jwt_authorizer::AuthError::MissingToken())?;
|
||||||
authorization
|
|
||||||
.to_str()
|
|
||||||
.ok()
|
|
||||||
.and_then(|s| s.strip_prefix("Bearer "))
|
|
||||||
})
|
|
||||||
.ok_or(AuthError::OidcMissingBearerToken)?;
|
|
||||||
|
|
||||||
let token_data = self.jwk_verifier.verify::<OidcAuthClaims>(token).await?;
|
let token_data = self.api_authorizer.check_auth(&token).await?;
|
||||||
let claims = token_data.claims();
|
let claims = token_data.claims;
|
||||||
let sub = if let Some(sub) = claims.sub.as_deref() {
|
let sub = if let Some(sub) = claims.sub.as_deref() {
|
||||||
sub
|
sub
|
||||||
} else {
|
} else {
|
||||||
return Err(AuthError::OidcSubMissingError);
|
return Err(AuthError::OidcSubMissingError);
|
||||||
};
|
};
|
||||||
if !claims.aud.iter().any(|aud| aud == &config.audience) {
|
if !claims.contains_audience(&config.audience) {
|
||||||
return Err(AuthError::OidcAudMissingError {
|
return Err(AuthError::OidcAudMissingError {
|
||||||
aud: config.audience.clone(),
|
aud: config.audience.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let extra_claims = &claims.extra;
|
|
||||||
if let Some(expected_scopes) = config.extra_scopes.as_ref() {
|
if let Some(expected_scopes) = config.extra_scopes.as_ref() {
|
||||||
let found_scopes = extra_claims.scopes().collect::<HashSet<_>>();
|
let found_scopes = claims.scopes().collect::<HashSet<_>>();
|
||||||
if !expected_scopes
|
if !expected_scopes
|
||||||
.iter()
|
.iter()
|
||||||
.all(|es| found_scopes.contains(es as &str))
|
.all(|es| found_scopes.contains(es as &str))
|
||||||
{
|
{
|
||||||
return Err(AuthError::OidcExtraScopesMatchError {
|
return Err(AuthError::OidcExtraScopesMatchError {
|
||||||
expected: expected_scopes.iter().join(","),
|
expected: expected_scopes.iter().join(","),
|
||||||
found: extra_claims
|
found: claims.scope.unwrap_or_default(),
|
||||||
.scope
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(expected_extra_claims) = config.extra_claims.as_ref() {
|
if let Some(key) = config.extra_claim_key.as_ref() {
|
||||||
for (expected_key, expected_value) in expected_extra_claims.iter() {
|
if !claims.has_claim(key) {
|
||||||
match (extra_claims.custom.get(expected_key), expected_value) {
|
return Err(AuthError::OidcExtraClaimMissingError { claim: key.clone() });
|
||||||
(found_value, Some(expected_value)) => {
|
}
|
||||||
if let Some(Value::String(found_value)) = found_value
|
if let Some(value) = config.extra_claim_value.as_ref()
|
||||||
&& expected_value == found_value
|
&& claims.get_claim(key).is_none_or(|v| &v != value)
|
||||||
{
|
{
|
||||||
} else {
|
return Err(AuthError::OidcExtraClaimMatchError {
|
||||||
return Err(AuthError::OidcExtraClaimMatchError {
|
expected: value.clone(),
|
||||||
expected: expected_value.clone(),
|
found: claims.get_claim(key).unwrap_or_default().to_string(),
|
||||||
found: found_value.map(|v| v.to_string()).unwrap_or_default(),
|
key: key.clone(),
|
||||||
key: expected_key.clone(),
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, None) => {
|
|
||||||
return Err(AuthError::OidcExtraClaimMissingError {
|
|
||||||
claim: expected_key.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let subscriber_auth = match crate::models::auth::Model::find_by_pid(ctx, sub).await {
|
let subscriber_auth = match crate::models::auth::Model::find_by_pid(ctx, sub).await {
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use axum::http::request::Parts;
|
use axum::{
|
||||||
|
extract::FromRequestParts,
|
||||||
|
http::request::Parts,
|
||||||
|
response::{IntoResponse as _, Response},
|
||||||
|
};
|
||||||
use fetch::{
|
use fetch::{
|
||||||
HttpClient, HttpClientConfig,
|
HttpClient, HttpClientConfig,
|
||||||
client::{HttpClientCacheBackendConfig, HttpClientCachePresetConfig},
|
client::{HttpClientCacheBackendConfig, HttpClientCachePresetConfig},
|
||||||
};
|
};
|
||||||
use http::header::HeaderValue;
|
use http::header::HeaderValue;
|
||||||
use jwtk::jwk::RemoteJwksVerifier;
|
use jwt_authorizer::{JwtAuthorizer, Validation};
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use openidconnect::{IssuerUrl, core::CoreProviderMetadata};
|
|
||||||
use snafu::prelude::*;
|
use snafu::prelude::*;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AuthConfig,
|
AuthConfig,
|
||||||
basic::BasicAuthService,
|
basic::BasicAuthService,
|
||||||
errors::{AuthError, OidcProviderHttpClientSnafu, OidcProviderUrlSnafu},
|
errors::{AuthError, OidcProviderHttpClientSnafu},
|
||||||
oidc::{OidcAuthService, OidcHttpClient},
|
oidc::{OidcAuthClaims, OidcAuthService},
|
||||||
};
|
};
|
||||||
use crate::{app::AppContextTrait, models::auth::AuthType};
|
use crate::{app::AppContextTrait, models::auth::AuthType};
|
||||||
|
|
||||||
@ -26,6 +29,22 @@ pub struct AuthUserInfo {
|
|||||||
pub auth_type: AuthType,
|
pub auth_type: AuthType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromRequestParts<Arc<dyn AppContextTrait>> for AuthUserInfo {
|
||||||
|
type Rejection = Response;
|
||||||
|
|
||||||
|
async fn from_request_parts(
|
||||||
|
parts: &mut Parts,
|
||||||
|
state: &Arc<dyn AppContextTrait>,
|
||||||
|
) -> Result<Self, Self::Rejection> {
|
||||||
|
let auth_service = state.auth();
|
||||||
|
|
||||||
|
auth_service
|
||||||
|
.extract_user_info(state.as_ref(), parts)
|
||||||
|
.await
|
||||||
|
.map_err(|err| err.into_response())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AuthServiceTrait {
|
pub trait AuthServiceTrait {
|
||||||
async fn extract_user_info(
|
async fn extract_user_info(
|
||||||
@ -47,33 +66,27 @@ impl AuthService {
|
|||||||
let result = match config {
|
let result = match config {
|
||||||
AuthConfig::Basic(config) => AuthService::Basic(Box::new(BasicAuthService { config })),
|
AuthConfig::Basic(config) => AuthService::Basic(Box::new(BasicAuthService { config })),
|
||||||
AuthConfig::Oidc(config) => {
|
AuthConfig::Oidc(config) => {
|
||||||
let oidc_provider_client = Arc::new(
|
let validation = Validation::new()
|
||||||
HttpClient::from_config(HttpClientConfig {
|
.iss(&[&config.issuer])
|
||||||
exponential_backoff_max_retries: Some(3),
|
.aud(&[&config.audience]);
|
||||||
cache_backend: Some(HttpClientCacheBackendConfig::Moka { cache_size: 1 }),
|
|
||||||
cache_preset: Some(HttpClientCachePresetConfig::RFC7234),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.context(OidcProviderHttpClientSnafu)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
let provider_metadata = {
|
let oidc_provider_client = HttpClient::from_config(HttpClientConfig {
|
||||||
let client = OidcHttpClient(oidc_provider_client.clone());
|
exponential_backoff_max_retries: Some(3),
|
||||||
let issuer_url =
|
cache_backend: Some(HttpClientCacheBackendConfig::Moka { cache_size: 1 }),
|
||||||
IssuerUrl::new(config.issuer.clone()).context(OidcProviderUrlSnafu)?;
|
cache_preset: Some(HttpClientCachePresetConfig::RFC7234),
|
||||||
CoreProviderMetadata::discover_async(issuer_url, &client).await
|
..Default::default()
|
||||||
}?;
|
})
|
||||||
|
.context(OidcProviderHttpClientSnafu)?;
|
||||||
|
|
||||||
let jwk_verifier = RemoteJwksVerifier::new(
|
let api_authorizer = JwtAuthorizer::<OidcAuthClaims>::from_oidc(&config.issuer)
|
||||||
provider_metadata.jwks_uri().to_string().clone(),
|
.validation(validation)
|
||||||
None,
|
.build()
|
||||||
Duration::from_secs(300),
|
.await?;
|
||||||
);
|
|
||||||
|
|
||||||
AuthService::Oidc(Box::new(OidcAuthService {
|
AuthService::Oidc(Box::new(OidcAuthService {
|
||||||
config,
|
config,
|
||||||
jwk_verifier,
|
api_authorizer,
|
||||||
oidc_provider_client,
|
oidc_provider_client: Arc::new(oidc_provider_client),
|
||||||
oidc_request_cache: Cache::builder()
|
oidc_request_cache: Cache::builder()
|
||||||
.time_to_live(Duration::from_mins(5))
|
.time_to_live(Duration::from_mins(5))
|
||||||
.name("oidc_request_cache")
|
.name("oidc_request_cache")
|
||||||
@ -87,7 +100,6 @@ impl AuthService {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AuthServiceTrait for AuthService {
|
impl AuthServiceTrait for AuthService {
|
||||||
#[tracing::instrument(skip(self, ctx, request))]
|
|
||||||
async fn extract_user_info(
|
async fn extract_user_info(
|
||||||
&self,
|
&self,
|
||||||
ctx: &dyn AppContextTrait,
|
ctx: &dyn AppContextTrait,
|
||||||
|
@ -47,7 +47,7 @@ pub enum RecorderError {
|
|||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
RSSError { source: rss::Error },
|
RSSError { source: rss::Error },
|
||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
DotEnvError { source: dotenvy::Error },
|
DotEnvError { source: dotenv::Error },
|
||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
TeraError { source: tera::Error },
|
TeraError { source: tera::Error },
|
||||||
#[snafu(transparent)]
|
#[snafu(transparent)]
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,10 @@
|
|||||||
mod json;
|
mod json;
|
||||||
mod subscriber;
|
mod subscriber;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use async_graphql::dynamic::TypeRef;
|
use async_graphql::dynamic::TypeRef;
|
||||||
pub use json::{
|
pub use json::{JSONB_FILTER_INFO, jsonb_filter_condition_function};
|
||||||
JSONB_FILTER_NAME, jsonb_filter_condition_function,
|
|
||||||
register_jsonb_input_filter_to_dynamic_schema,
|
|
||||||
};
|
|
||||||
use maplit::btreeset;
|
use maplit::btreeset;
|
||||||
use seaography::{FilterInfo, FilterOperation as SeaographqlFilterOperation};
|
use seaography::{FilterInfo, FilterOperation as SeaographqlFilterOperation};
|
||||||
pub use subscriber::{SUBSCRIBER_ID_FILTER_INFO, subscriber_id_condition_function};
|
pub use subscriber::{SUBSCRIBER_ID_FILTER_INFO, subscriber_id_condition_function};
|
||||||
@ -16,4 +15,9 @@ pub fn init_custom_filter_info() {
|
|||||||
base_type: TypeRef::INT.into(),
|
base_type: TypeRef::INT.into(),
|
||||||
supported_operations: btreeset! { SeaographqlFilterOperation::Equals },
|
supported_operations: btreeset! { SeaographqlFilterOperation::Equals },
|
||||||
});
|
});
|
||||||
|
JSONB_FILTER_INFO.get_or_init(|| FilterInfo {
|
||||||
|
type_name: String::from("JsonbFilterInput"),
|
||||||
|
base_type: TypeRef::Named(Cow::Borrowed("serde_json::Value")).to_string(),
|
||||||
|
supported_operations: btreeset! { SeaographqlFilterOperation::Equals },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,14 @@ use once_cell::sync::OnceCell;
|
|||||||
use sea_orm::{DatabaseConnection, EntityTrait, Iterable};
|
use sea_orm::{DatabaseConnection, EntityTrait, Iterable};
|
||||||
use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper};
|
use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper};
|
||||||
|
|
||||||
use crate::graphql::{
|
use crate::graphql::infra::{
|
||||||
infra::{
|
filter::{
|
||||||
filter::{
|
JSONB_FILTER_INFO, SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info,
|
||||||
JSONB_FILTER_NAME, SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info,
|
subscriber_id_condition_function,
|
||||||
register_jsonb_input_filter_to_dynamic_schema, subscriber_id_condition_function,
|
|
||||||
},
|
|
||||||
guard::{guard_entity_with_subscriber_id, guard_field_with_subscriber_id},
|
|
||||||
transformer::{filter_condition_transformer, mutation_input_object_transformer},
|
|
||||||
util::{get_entity_column_key, get_entity_key},
|
|
||||||
},
|
},
|
||||||
views::register_subscriptions_to_schema,
|
guard::{guard_entity_with_subscriber_id, guard_field_with_subscriber_id},
|
||||||
|
transformer::{filter_condition_transformer, mutation_input_object_transformer},
|
||||||
|
util::{get_entity_column_key, get_entity_key},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static CONTEXT: OnceCell<BuilderContext> = OnceCell::new();
|
pub static CONTEXT: OnceCell<BuilderContext> = OnceCell::new();
|
||||||
@ -38,7 +35,9 @@ where
|
|||||||
let entity_column_key = get_entity_column_key::<T>(context, column);
|
let entity_column_key = get_entity_column_key::<T>(context, column);
|
||||||
context.filter_types.overwrites.insert(
|
context.filter_types.overwrites.insert(
|
||||||
entity_column_key.clone(),
|
entity_column_key.clone(),
|
||||||
Some(FilterType::Custom(JSONB_FILTER_NAME.to_string())),
|
Some(FilterType::Custom(
|
||||||
|
JSONB_FILTER_INFO.get().unwrap().type_name.clone(),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,12 +94,12 @@ pub fn schema(
|
|||||||
let context = CONTEXT.get_or_init(|| {
|
let context = CONTEXT.get_or_init(|| {
|
||||||
let mut context = BuilderContext::default();
|
let mut context = BuilderContext::default();
|
||||||
|
|
||||||
context.pagination_input.type_name = "PaginationInput".to_string();
|
context.pagination_input.type_name = "SeaographyPaginationInput".to_string();
|
||||||
context.pagination_info_object.type_name = "PaginationInfo".to_string();
|
context.pagination_info_object.type_name = "SeaographyPaginationInfo".to_string();
|
||||||
context.cursor_input.type_name = "CursorInput".to_string();
|
context.cursor_input.type_name = "SeaographyCursorInput".to_string();
|
||||||
context.offset_input.type_name = "OffsetInput".to_string();
|
context.offset_input.type_name = "SeaographyOffsetInput".to_string();
|
||||||
context.page_input.type_name = "PageInput".to_string();
|
context.page_input.type_name = "SeaographyPageInput".to_string();
|
||||||
context.page_info_object.type_name = "PageInfo".to_string();
|
context.page_info_object.type_name = "SeaographyPageInfo".to_string();
|
||||||
|
|
||||||
restrict_subscriber_for_entity::<bangumi::Entity>(
|
restrict_subscriber_for_entity::<bangumi::Entity>(
|
||||||
&mut context,
|
&mut context,
|
||||||
@ -161,7 +160,6 @@ pub fn schema(
|
|||||||
builder.schema = builder.schema.register(
|
builder.schema = builder.schema.register(
|
||||||
filter_types_map_helper.generate_filter_input(SUBSCRIBER_ID_FILTER_INFO.get().unwrap()),
|
filter_types_map_helper.generate_filter_input(SUBSCRIBER_ID_FILTER_INFO.get().unwrap()),
|
||||||
);
|
);
|
||||||
builder.schema = register_jsonb_input_filter_to_dynamic_schema(builder.schema);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -195,10 +193,6 @@ pub fn schema(
|
|||||||
builder.register_enumeration::<downloads::DownloadMime>();
|
builder.register_enumeration::<downloads::DownloadMime>();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
builder = register_subscriptions_to_schema(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
let schema = builder.schema_builder();
|
let schema = builder.schema_builder();
|
||||||
|
|
||||||
let schema = if let Some(depth) = depth {
|
let schema = if let Some(depth) = depth {
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
mod subscription;
|
mod subscription;
|
||||||
|
mod task;
|
||||||
pub use subscription::register_subscriptions_to_schema;
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_graphql::dynamic::{
|
use async_graphql::{Context, InputObject, Object, Result as GraphQLResult, SimpleObject};
|
||||||
Field, FieldFuture, FieldValue, InputObject, InputValue, Object, TypeRef,
|
|
||||||
};
|
|
||||||
use seaography::Builder as SeaographyBuilder;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use util_derive::DynamicGraphql;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::AppContextTrait,
|
app::AppContextTrait,
|
||||||
@ -14,213 +9,116 @@ use crate::{
|
|||||||
task::SubscriberTaskPayload,
|
task::SubscriberTaskPayload,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(DynamicGraphql, Serialize, Deserialize, Clone, Debug)]
|
pub struct SubscriptionMutation;
|
||||||
|
|
||||||
|
#[derive(InputObject)]
|
||||||
struct SyncOneSubscriptionFilterInput {
|
struct SyncOneSubscriptionFilterInput {
|
||||||
pub subscription_id: i32,
|
pub subscription_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncOneSubscriptionFilterInput {
|
#[derive(SimpleObject)]
|
||||||
fn input_type_name() -> &'static str {
|
struct SyncOneSubscriptionTaskOutput {
|
||||||
"SyncOneSubscriptionFilterInput"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn arg_name() -> &'static str {
|
|
||||||
"filter"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_input_object() -> InputObject {
|
|
||||||
InputObject::new(Self::input_type_name())
|
|
||||||
.description("The input of the subscriptionSyncOne series of mutations")
|
|
||||||
.field(InputValue::new(
|
|
||||||
SyncOneSubscriptionFilterInputFieldEnum::SubscriptionId.as_str(),
|
|
||||||
TypeRef::named_nn(TypeRef::INT),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(DynamicGraphql, Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct SyncOneSubscriptionInfo {
|
|
||||||
pub task_id: String,
|
pub task_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncOneSubscriptionInfo {
|
#[Object]
|
||||||
fn object_type_name() -> &'static str {
|
impl SubscriptionMutation {
|
||||||
"SyncOneSubscriptionInfo"
|
async fn sync_one_subscription_feeds_incremental(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
input: SyncOneSubscriptionFilterInput,
|
||||||
|
) -> GraphQLResult<SyncOneSubscriptionTaskOutput> {
|
||||||
|
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
||||||
|
|
||||||
|
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
||||||
|
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
||||||
|
|
||||||
|
let subscription_model = subscriptions::Model::find_by_id_and_subscriber_id(
|
||||||
|
app_ctx.as_ref(),
|
||||||
|
input.subscription_id,
|
||||||
|
subscriber_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let subscription = subscriptions::Subscription::try_from_model(&subscription_model)?;
|
||||||
|
|
||||||
|
let task_service = app_ctx.task();
|
||||||
|
|
||||||
|
let task_id = task_service
|
||||||
|
.add_subscriber_task(
|
||||||
|
auth_user_info.subscriber_auth.subscriber_id,
|
||||||
|
SubscriberTaskPayload::SyncOneSubscriptionFeedsIncremental(subscription.into()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(SyncOneSubscriptionTaskOutput {
|
||||||
|
task_id: task_id.to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_output_object() -> Object {
|
async fn sync_one_subscription_feeds_full(
|
||||||
Object::new(Self::object_type_name())
|
&self,
|
||||||
.description("The output of the subscriptionSyncOne series of mutations")
|
ctx: &Context<'_>,
|
||||||
.field(Field::new(
|
input: SyncOneSubscriptionFilterInput,
|
||||||
SyncOneSubscriptionInfoFieldEnum::TaskId,
|
) -> GraphQLResult<SyncOneSubscriptionTaskOutput> {
|
||||||
TypeRef::named_nn(TypeRef::STRING),
|
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
||||||
move |ctx| {
|
|
||||||
FieldFuture::new(async move {
|
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
||||||
let subscription_info = ctx.parent_value.try_downcast_ref::<Self>()?;
|
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
||||||
Ok(Some(async_graphql::Value::from(
|
|
||||||
subscription_info.task_id.as_str(),
|
let subscription_model = subscriptions::Model::find_by_id_and_subscriber_id(
|
||||||
)))
|
app_ctx.as_ref(),
|
||||||
})
|
input.subscription_id,
|
||||||
},
|
subscriber_id,
|
||||||
))
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let subscription = subscriptions::Subscription::try_from_model(&subscription_model)?;
|
||||||
|
|
||||||
|
let task_service = app_ctx.task();
|
||||||
|
|
||||||
|
let task_id = task_service
|
||||||
|
.add_subscriber_task(
|
||||||
|
auth_user_info.subscriber_auth.subscriber_id,
|
||||||
|
SubscriberTaskPayload::SyncOneSubscriptionFeedsFull(subscription.into()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(SyncOneSubscriptionTaskOutput {
|
||||||
|
task_id: task_id.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sync_one_subscription_sources(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
input: SyncOneSubscriptionFilterInput,
|
||||||
|
) -> GraphQLResult<SyncOneSubscriptionTaskOutput> {
|
||||||
|
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
||||||
|
|
||||||
|
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
||||||
|
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
||||||
|
|
||||||
|
let subscription_model = subscriptions::Model::find_by_id_and_subscriber_id(
|
||||||
|
app_ctx.as_ref(),
|
||||||
|
input.subscription_id,
|
||||||
|
subscriber_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let subscription = subscriptions::Subscription::try_from_model(&subscription_model)?;
|
||||||
|
|
||||||
|
let task_service = app_ctx.task();
|
||||||
|
|
||||||
|
let task_id = task_service
|
||||||
|
.add_subscriber_task(
|
||||||
|
auth_user_info.subscriber_auth.subscriber_id,
|
||||||
|
SubscriberTaskPayload::SyncOneSubscriptionSources(subscription.into()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(SyncOneSubscriptionTaskOutput {
|
||||||
|
task_id: task_id.to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_subscriptions_to_schema(mut builder: SeaographyBuilder) -> SeaographyBuilder {
|
|
||||||
builder.schema = builder
|
|
||||||
.schema
|
|
||||||
.register(SyncOneSubscriptionFilterInput::generate_input_object());
|
|
||||||
builder.schema = builder
|
|
||||||
.schema
|
|
||||||
.register(SyncOneSubscriptionInfo::generate_output_object());
|
|
||||||
|
|
||||||
builder.queries.push(
|
|
||||||
Field::new(
|
|
||||||
"subscriptionSyncOneFeedsIncremental",
|
|
||||||
TypeRef::named_nn(SyncOneSubscriptionInfo::object_type_name()),
|
|
||||||
move |ctx| {
|
|
||||||
FieldFuture::new(async move {
|
|
||||||
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
|
||||||
|
|
||||||
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
|
||||||
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
|
||||||
|
|
||||||
let filter_input: SyncOneSubscriptionFilterInput = ctx
|
|
||||||
.args
|
|
||||||
.get(SyncOneSubscriptionFilterInput::arg_name())
|
|
||||||
.unwrap()
|
|
||||||
.deserialize()?;
|
|
||||||
|
|
||||||
let subscription_model = subscriptions::Model::find_by_id_and_subscriber_id(
|
|
||||||
app_ctx.as_ref(),
|
|
||||||
filter_input.subscription_id,
|
|
||||||
subscriber_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let subscription =
|
|
||||||
subscriptions::Subscription::try_from_model(&subscription_model)?;
|
|
||||||
|
|
||||||
let task_service = app_ctx.task();
|
|
||||||
|
|
||||||
let task_id = task_service
|
|
||||||
.add_subscriber_task(
|
|
||||||
auth_user_info.subscriber_auth.subscriber_id,
|
|
||||||
SubscriberTaskPayload::SyncOneSubscriptionFeedsIncremental(
|
|
||||||
subscription.into(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Some(FieldValue::owned_any(SyncOneSubscriptionInfo {
|
|
||||||
task_id: task_id.to_string(),
|
|
||||||
})))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.argument(InputValue::new(
|
|
||||||
SyncOneSubscriptionFilterInput::arg_name(),
|
|
||||||
TypeRef::named_nn(SyncOneSubscriptionFilterInput::input_type_name()),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.queries.push(
|
|
||||||
Field::new(
|
|
||||||
"subscriptionSyncOneFeedsFull",
|
|
||||||
TypeRef::named_nn(SyncOneSubscriptionInfo::object_type_name()),
|
|
||||||
move |ctx| {
|
|
||||||
FieldFuture::new(async move {
|
|
||||||
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
|
||||||
|
|
||||||
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
|
||||||
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
|
||||||
|
|
||||||
let filter_input: SyncOneSubscriptionFilterInput = ctx
|
|
||||||
.args
|
|
||||||
.get(SyncOneSubscriptionFilterInput::arg_name())
|
|
||||||
.unwrap()
|
|
||||||
.deserialize()?;
|
|
||||||
|
|
||||||
let subscription_model = subscriptions::Model::find_by_id_and_subscriber_id(
|
|
||||||
app_ctx.as_ref(),
|
|
||||||
filter_input.subscription_id,
|
|
||||||
subscriber_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let subscription =
|
|
||||||
subscriptions::Subscription::try_from_model(&subscription_model)?;
|
|
||||||
|
|
||||||
let task_service = app_ctx.task();
|
|
||||||
|
|
||||||
let task_id = task_service
|
|
||||||
.add_subscriber_task(
|
|
||||||
auth_user_info.subscriber_auth.subscriber_id,
|
|
||||||
SubscriberTaskPayload::SyncOneSubscriptionFeedsFull(
|
|
||||||
subscription.into(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Some(FieldValue::owned_any(SyncOneSubscriptionInfo {
|
|
||||||
task_id: task_id.to_string(),
|
|
||||||
})))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.argument(InputValue::new(
|
|
||||||
SyncOneSubscriptionFilterInput::arg_name(),
|
|
||||||
TypeRef::named_nn(SyncOneSubscriptionFilterInput::input_type_name()),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.mutations.push(
|
|
||||||
Field::new(
|
|
||||||
"subscriptionSyncOneSources",
|
|
||||||
TypeRef::named_nn(SyncOneSubscriptionInfo::object_type_name()),
|
|
||||||
move |ctx| {
|
|
||||||
FieldFuture::new(async move {
|
|
||||||
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
|
||||||
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
|
||||||
|
|
||||||
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
|
||||||
|
|
||||||
let filter_input: SyncOneSubscriptionFilterInput = ctx
|
|
||||||
.args
|
|
||||||
.get(SyncOneSubscriptionFilterInput::arg_name())
|
|
||||||
.unwrap()
|
|
||||||
.deserialize()?;
|
|
||||||
|
|
||||||
let subscription_model = subscriptions::Model::find_by_id_and_subscriber_id(
|
|
||||||
app_ctx.as_ref(),
|
|
||||||
filter_input.subscription_id,
|
|
||||||
subscriber_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let subscription =
|
|
||||||
subscriptions::Subscription::try_from_model(&subscription_model)?;
|
|
||||||
|
|
||||||
let task_service = app_ctx.task();
|
|
||||||
|
|
||||||
let task_id = task_service
|
|
||||||
.add_subscriber_task(
|
|
||||||
auth_user_info.subscriber_auth.subscriber_id,
|
|
||||||
SubscriberTaskPayload::SyncOneSubscriptionSources(subscription.into()),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Some(FieldValue::owned_any(SyncOneSubscriptionInfo {
|
|
||||||
task_id: task_id.to_string(),
|
|
||||||
})))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.argument(InputValue::new(
|
|
||||||
SyncOneSubscriptionFilterInput::arg_name(),
|
|
||||||
TypeRef::named_nn(SyncOneSubscriptionFilterInput::input_type_name()),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
builder
|
|
||||||
}
|
|
||||||
|
27
apps/recorder/src/graphql/views/task.rs
Normal file
27
apps/recorder/src/graphql/views/task.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_graphql::{Context, InputObject, Object, Result as GraphQLResult};
|
||||||
|
|
||||||
|
use crate::{app::AppContextTrait, auth::AuthUserInfo};
|
||||||
|
|
||||||
|
struct TaskQuery;
|
||||||
|
|
||||||
|
#[derive(InputObject)]
|
||||||
|
struct SubscriberTasksFilterInput {
|
||||||
|
pub subscription_id: Option<i32>,
|
||||||
|
pub task_id: Option<String>,
|
||||||
|
pub task_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl TaskQuery {
|
||||||
|
async fn subscriber_tasks(&self, ctx: &Context<'_>) -> GraphQLResult<Vec<String>> {
|
||||||
|
let auth_user_info = ctx.data::<AuthUserInfo>()?;
|
||||||
|
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
||||||
|
let subscriber_id = auth_user_info.subscriber_auth.subscriber_id;
|
||||||
|
|
||||||
|
let task_service = app_ctx.task();
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,10 @@ impl MigrationTrait for Migration {
|
|||||||
let db = manager.get_connection();
|
let db = manager.get_connection();
|
||||||
|
|
||||||
db.execute_unprepared(&format!(
|
db.execute_unprepared(&format!(
|
||||||
r#"CREATE OR REPLACE VIEW subscriber_task AS
|
r#"CREATE VIEW IF NOT EXISTS subscriber_task AS
|
||||||
SELECT
|
SELECT
|
||||||
job,
|
job,
|
||||||
job_type,
|
task_type,
|
||||||
status,
|
status,
|
||||||
(job->'subscriber_id')::integer AS subscriber_id,
|
(job->'subscriber_id')::integer AS subscriber_id,
|
||||||
(job->'task_type')::text AS task_type,
|
(job->'task_type')::text AS task_type,
|
||||||
@ -29,7 +29,7 @@ SELECT
|
|||||||
done_at,
|
done_at,
|
||||||
priority
|
priority
|
||||||
FROM apalis.jobs
|
FROM apalis.jobs
|
||||||
WHERE job_type = '{SUBSCRIBER_TASK_APALIS_NAME}'
|
WHERE job_type = {SUBSCRIBER_TASK_APALIS_NAME}
|
||||||
AND jsonb_path_exists(job, '$.subscriber_id ? (@.type() == "number")')
|
AND jsonb_path_exists(job, '$.subscriber_id ? (@.type() == "number")')
|
||||||
AND jsonb_path_exists(job, '$.task_type ? (@.type() == "string")')"#,
|
AND jsonb_path_exists(job, '$.task_type ? (@.type() == "string")')"#,
|
||||||
))
|
))
|
||||||
@ -38,7 +38,7 @@ AND jsonb_path_exists(job, '$.task_type ? (@.type() == "string")')"#,
|
|||||||
db.execute_unprepared(&format!(
|
db.execute_unprepared(&format!(
|
||||||
r#"CREATE INDEX IF NOT EXISTS idx_apalis_jobs_subscriber_id
|
r#"CREATE INDEX IF NOT EXISTS idx_apalis_jobs_subscriber_id
|
||||||
ON apalis.jobs ((job -> 'subscriber_id'))
|
ON apalis.jobs ((job -> 'subscriber_id'))
|
||||||
WHERE job_type = '{SUBSCRIBER_TASK_APALIS_NAME}'
|
WHERE job_type = {SUBSCRIBER_TASK_APALIS_NAME}
|
||||||
AND jsonb_path_exists(job, '$.subscriber_id ? (@.type() == "number")')
|
AND jsonb_path_exists(job, '$.subscriber_id ? (@.type() == "number")')
|
||||||
AND jsonb_path_exists(job, '$.task_type ? (@.type() == "string")')"#
|
AND jsonb_path_exists(job, '$.task_type ? (@.type() == "string")')"#
|
||||||
))
|
))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use apalis::prelude::State;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
|
|
||||||
|
@ -55,9 +55,7 @@ pub async fn build_testing_database_service(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "testcontainers"))]
|
#[cfg(not(feature = "testcontainers"))]
|
||||||
pub async fn build_testing_database_service(
|
pub async fn build_testing_database_service() -> RecorderResult<DatabaseService> {
|
||||||
config: TestingDatabaseServiceConfig,
|
|
||||||
) -> RecorderResult<DatabaseService> {
|
|
||||||
let db_service = DatabaseService::from_config(DatabaseConfig {
|
let db_service = DatabaseService::from_config(DatabaseConfig {
|
||||||
uri: String::from("postgres://konobangu:konobangu@127.0.0.1:5432/konobangu"),
|
uri: String::from("postgres://konobangu:konobangu@127.0.0.1:5432/konobangu"),
|
||||||
enable_logging: true,
|
enable_logging: true,
|
||||||
@ -66,7 +64,7 @@ pub async fn build_testing_database_service(
|
|||||||
connect_timeout: 5000,
|
connect_timeout: 5000,
|
||||||
idle_timeout: 10000,
|
idle_timeout: 10000,
|
||||||
acquire_timeout: None,
|
acquire_timeout: None,
|
||||||
auto_migrate: config.auto_migrate,
|
auto_migrate: true,
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -17,14 +17,14 @@ type Documents = {
|
|||||||
"\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetSubscriptionsDocument,
|
"\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetSubscriptionsDocument,
|
||||||
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument,
|
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument,
|
||||||
"\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n": typeof types.DeleteSubscriptionsDocument,
|
"\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n": typeof types.DeleteSubscriptionsDocument,
|
||||||
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument,
|
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument,
|
||||||
"\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n": typeof types.CreateSubscriptionDocument,
|
"\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n": typeof types.CreateSubscriptionDocument,
|
||||||
};
|
};
|
||||||
const documents: Documents = {
|
const documents: Documents = {
|
||||||
"\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetSubscriptionsDocument,
|
"\n query GetSubscriptions(\n $page: PageInput!,\n $filters: SubscriptionsFilterInput!,\n $orderBy: SubscriptionsOrderInput!\n) {\n subscriptions(\n pagination: {\n page: $page\n }\n filters: $filters\n orderBy: $orderBy\n ) {\n nodes {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetSubscriptionsDocument,
|
||||||
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument,
|
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filters: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filters\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument,
|
||||||
"\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n": types.DeleteSubscriptionsDocument,
|
"\n mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filters)\n }\n": types.DeleteSubscriptionsDocument,
|
||||||
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument,
|
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument,
|
||||||
"\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n": types.CreateSubscriptionDocument,
|
"\n mutation CreateSubscription($input: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $input) {\n id\n displayName\n sourceUrl\n enabled\n category\n }\n }\n": types.CreateSubscriptionDocument,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export function gql(source: "\n mutation DeleteSubscriptions($filters: Subscr
|
|||||||
/**
|
/**
|
||||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n homepage\n }\n }\n }\n }\n}\n"];
|
export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filters: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n rawName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n savePath\n deleted\n homepage\n }\n }\n }\n }\n}\n"];
|
||||||
/**
|
/**
|
||||||
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
@ -14,12 +14,12 @@ export type Scalars = {
|
|||||||
Boolean: { input: boolean; output: boolean; }
|
Boolean: { input: boolean; output: boolean; }
|
||||||
Int: { input: number; output: number; }
|
Int: { input: number; output: number; }
|
||||||
Float: { input: number; output: number; }
|
Float: { input: number; output: number; }
|
||||||
JsonbFilterInput: { input: any; output: any; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Bangumi = {
|
export type Bangumi = {
|
||||||
__typename?: 'Bangumi';
|
__typename?: 'Bangumi';
|
||||||
createdAt: Scalars['String']['output'];
|
createdAt: Scalars['String']['output'];
|
||||||
|
deleted: Scalars['Boolean']['output'];
|
||||||
displayName: Scalars['String']['output'];
|
displayName: Scalars['String']['output'];
|
||||||
episode: EpisodesConnection;
|
episode: EpisodesConnection;
|
||||||
fansub?: Maybe<Scalars['String']['output']>;
|
fansub?: Maybe<Scalars['String']['output']>;
|
||||||
@ -64,6 +64,7 @@ export type BangumiSubscriptionBangumiArgs = {
|
|||||||
export type BangumiBasic = {
|
export type BangumiBasic = {
|
||||||
__typename?: 'BangumiBasic';
|
__typename?: 'BangumiBasic';
|
||||||
createdAt: Scalars['String']['output'];
|
createdAt: Scalars['String']['output'];
|
||||||
|
deleted: Scalars['Boolean']['output'];
|
||||||
displayName: Scalars['String']['output'];
|
displayName: Scalars['String']['output'];
|
||||||
fansub?: Maybe<Scalars['String']['output']>;
|
fansub?: Maybe<Scalars['String']['output']>;
|
||||||
homepage?: Maybe<Scalars['String']['output']>;
|
homepage?: Maybe<Scalars['String']['output']>;
|
||||||
@ -97,6 +98,7 @@ export type BangumiEdge = {
|
|||||||
export type BangumiFilterInput = {
|
export type BangumiFilterInput = {
|
||||||
and?: InputMaybe<Array<BangumiFilterInput>>;
|
and?: InputMaybe<Array<BangumiFilterInput>>;
|
||||||
createdAt?: InputMaybe<TextFilterInput>;
|
createdAt?: InputMaybe<TextFilterInput>;
|
||||||
|
deleted?: InputMaybe<BooleanFilterInput>;
|
||||||
displayName?: InputMaybe<StringFilterInput>;
|
displayName?: InputMaybe<StringFilterInput>;
|
||||||
fansub?: InputMaybe<StringFilterInput>;
|
fansub?: InputMaybe<StringFilterInput>;
|
||||||
homepage?: InputMaybe<StringFilterInput>;
|
homepage?: InputMaybe<StringFilterInput>;
|
||||||
@ -116,6 +118,7 @@ export type BangumiFilterInput = {
|
|||||||
|
|
||||||
export type BangumiInsertInput = {
|
export type BangumiInsertInput = {
|
||||||
createdAt?: InputMaybe<Scalars['String']['input']>;
|
createdAt?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
deleted: Scalars['Boolean']['input'];
|
||||||
displayName: Scalars['String']['input'];
|
displayName: Scalars['String']['input'];
|
||||||
fansub?: InputMaybe<Scalars['String']['input']>;
|
fansub?: InputMaybe<Scalars['String']['input']>;
|
||||||
homepage?: InputMaybe<Scalars['String']['input']>;
|
homepage?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -133,6 +136,7 @@ export type BangumiInsertInput = {
|
|||||||
|
|
||||||
export type BangumiOrderInput = {
|
export type BangumiOrderInput = {
|
||||||
createdAt?: InputMaybe<OrderByEnum>;
|
createdAt?: InputMaybe<OrderByEnum>;
|
||||||
|
deleted?: InputMaybe<OrderByEnum>;
|
||||||
displayName?: InputMaybe<OrderByEnum>;
|
displayName?: InputMaybe<OrderByEnum>;
|
||||||
extra?: InputMaybe<OrderByEnum>;
|
extra?: InputMaybe<OrderByEnum>;
|
||||||
fansub?: InputMaybe<OrderByEnum>;
|
fansub?: InputMaybe<OrderByEnum>;
|
||||||
@ -153,6 +157,7 @@ export type BangumiOrderInput = {
|
|||||||
|
|
||||||
export type BangumiUpdateInput = {
|
export type BangumiUpdateInput = {
|
||||||
createdAt?: InputMaybe<Scalars['String']['input']>;
|
createdAt?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
deleted?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
displayName?: InputMaybe<Scalars['String']['input']>;
|
displayName?: InputMaybe<Scalars['String']['input']>;
|
||||||
fansub?: InputMaybe<Scalars['String']['input']>;
|
fansub?: InputMaybe<Scalars['String']['input']>;
|
||||||
homepage?: InputMaybe<Scalars['String']['input']>;
|
homepage?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -473,6 +478,7 @@ export type Episodes = {
|
|||||||
bangumi?: Maybe<Bangumi>;
|
bangumi?: Maybe<Bangumi>;
|
||||||
bangumiId: Scalars['Int']['output'];
|
bangumiId: Scalars['Int']['output'];
|
||||||
createdAt: Scalars['String']['output'];
|
createdAt: Scalars['String']['output'];
|
||||||
|
deleted: Scalars['Boolean']['output'];
|
||||||
displayName: Scalars['String']['output'];
|
displayName: Scalars['String']['output'];
|
||||||
download: SubscriptionsConnection;
|
download: SubscriptionsConnection;
|
||||||
episodeIndex: Scalars['Int']['output'];
|
episodeIndex: Scalars['Int']['output'];
|
||||||
@ -520,6 +526,7 @@ export type EpisodesBasic = {
|
|||||||
__typename?: 'EpisodesBasic';
|
__typename?: 'EpisodesBasic';
|
||||||
bangumiId: Scalars['Int']['output'];
|
bangumiId: Scalars['Int']['output'];
|
||||||
createdAt: Scalars['String']['output'];
|
createdAt: Scalars['String']['output'];
|
||||||
|
deleted: Scalars['Boolean']['output'];
|
||||||
displayName: Scalars['String']['output'];
|
displayName: Scalars['String']['output'];
|
||||||
episodeIndex: Scalars['Int']['output'];
|
episodeIndex: Scalars['Int']['output'];
|
||||||
fansub?: Maybe<Scalars['String']['output']>;
|
fansub?: Maybe<Scalars['String']['output']>;
|
||||||
@ -556,6 +563,7 @@ export type EpisodesFilterInput = {
|
|||||||
and?: InputMaybe<Array<EpisodesFilterInput>>;
|
and?: InputMaybe<Array<EpisodesFilterInput>>;
|
||||||
bangumiId?: InputMaybe<IntegerFilterInput>;
|
bangumiId?: InputMaybe<IntegerFilterInput>;
|
||||||
createdAt?: InputMaybe<TextFilterInput>;
|
createdAt?: InputMaybe<TextFilterInput>;
|
||||||
|
deleted?: InputMaybe<BooleanFilterInput>;
|
||||||
displayName?: InputMaybe<StringFilterInput>;
|
displayName?: InputMaybe<StringFilterInput>;
|
||||||
episodeIndex?: InputMaybe<IntegerFilterInput>;
|
episodeIndex?: InputMaybe<IntegerFilterInput>;
|
||||||
fansub?: InputMaybe<StringFilterInput>;
|
fansub?: InputMaybe<StringFilterInput>;
|
||||||
@ -578,6 +586,7 @@ export type EpisodesFilterInput = {
|
|||||||
export type EpisodesInsertInput = {
|
export type EpisodesInsertInput = {
|
||||||
bangumiId: Scalars['Int']['input'];
|
bangumiId: Scalars['Int']['input'];
|
||||||
createdAt?: InputMaybe<Scalars['String']['input']>;
|
createdAt?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
deleted: Scalars['Boolean']['input'];
|
||||||
displayName: Scalars['String']['input'];
|
displayName: Scalars['String']['input'];
|
||||||
episodeIndex: Scalars['Int']['input'];
|
episodeIndex: Scalars['Int']['input'];
|
||||||
fansub?: InputMaybe<Scalars['String']['input']>;
|
fansub?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -598,6 +607,7 @@ export type EpisodesInsertInput = {
|
|||||||
export type EpisodesOrderInput = {
|
export type EpisodesOrderInput = {
|
||||||
bangumiId?: InputMaybe<OrderByEnum>;
|
bangumiId?: InputMaybe<OrderByEnum>;
|
||||||
createdAt?: InputMaybe<OrderByEnum>;
|
createdAt?: InputMaybe<OrderByEnum>;
|
||||||
|
deleted?: InputMaybe<OrderByEnum>;
|
||||||
displayName?: InputMaybe<OrderByEnum>;
|
displayName?: InputMaybe<OrderByEnum>;
|
||||||
episodeIndex?: InputMaybe<OrderByEnum>;
|
episodeIndex?: InputMaybe<OrderByEnum>;
|
||||||
extra?: InputMaybe<OrderByEnum>;
|
extra?: InputMaybe<OrderByEnum>;
|
||||||
@ -620,6 +630,7 @@ export type EpisodesOrderInput = {
|
|||||||
export type EpisodesUpdateInput = {
|
export type EpisodesUpdateInput = {
|
||||||
bangumiId?: InputMaybe<Scalars['Int']['input']>;
|
bangumiId?: InputMaybe<Scalars['Int']['input']>;
|
||||||
createdAt?: InputMaybe<Scalars['String']['input']>;
|
createdAt?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
deleted?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
displayName?: InputMaybe<Scalars['String']['input']>;
|
displayName?: InputMaybe<Scalars['String']['input']>;
|
||||||
episodeIndex?: InputMaybe<Scalars['Int']['input']>;
|
episodeIndex?: InputMaybe<Scalars['Int']['input']>;
|
||||||
fansub?: InputMaybe<Scalars['String']['input']>;
|
fansub?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -671,10 +682,6 @@ export type Mutation = {
|
|||||||
episodesCreateOne: EpisodesBasic;
|
episodesCreateOne: EpisodesBasic;
|
||||||
episodesDelete: Scalars['Int']['output'];
|
episodesDelete: Scalars['Int']['output'];
|
||||||
episodesUpdate: Array<EpisodesBasic>;
|
episodesUpdate: Array<EpisodesBasic>;
|
||||||
subscriberTasksCreateBatch: Array<SubscriberTasksBasic>;
|
|
||||||
subscriberTasksCreateOne: SubscriberTasksBasic;
|
|
||||||
subscriberTasksDelete: Scalars['Int']['output'];
|
|
||||||
subscriberTasksUpdate: Array<SubscriberTasksBasic>;
|
|
||||||
subscriptionBangumiCreateBatch: Array<SubscriptionBangumiBasic>;
|
subscriptionBangumiCreateBatch: Array<SubscriptionBangumiBasic>;
|
||||||
subscriptionBangumiCreateOne: SubscriptionBangumiBasic;
|
subscriptionBangumiCreateOne: SubscriptionBangumiBasic;
|
||||||
subscriptionBangumiDelete: Scalars['Int']['output'];
|
subscriptionBangumiDelete: Scalars['Int']['output'];
|
||||||
@ -683,7 +690,6 @@ export type Mutation = {
|
|||||||
subscriptionEpisodeCreateOne: SubscriptionEpisodeBasic;
|
subscriptionEpisodeCreateOne: SubscriptionEpisodeBasic;
|
||||||
subscriptionEpisodeDelete: Scalars['Int']['output'];
|
subscriptionEpisodeDelete: Scalars['Int']['output'];
|
||||||
subscriptionEpisodeUpdate: Array<SubscriptionEpisodeBasic>;
|
subscriptionEpisodeUpdate: Array<SubscriptionEpisodeBasic>;
|
||||||
subscriptionSyncOneSources: SyncOneSubscriptionInfo;
|
|
||||||
subscriptionsCreateBatch: Array<SubscriptionsBasic>;
|
subscriptionsCreateBatch: Array<SubscriptionsBasic>;
|
||||||
subscriptionsCreateOne: SubscriptionsBasic;
|
subscriptionsCreateOne: SubscriptionsBasic;
|
||||||
subscriptionsDelete: Scalars['Int']['output'];
|
subscriptionsDelete: Scalars['Int']['output'];
|
||||||
@ -775,27 +781,6 @@ export type MutationEpisodesUpdateArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriberTasksCreateBatchArgs = {
|
|
||||||
data: Array<SubscriberTasksInsertInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriberTasksCreateOneArgs = {
|
|
||||||
data: SubscriberTasksInsertInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriberTasksDeleteArgs = {
|
|
||||||
filter?: InputMaybe<SubscriberTasksFilterInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriberTasksUpdateArgs = {
|
|
||||||
data: SubscriberTasksUpdateInput;
|
|
||||||
filter?: InputMaybe<SubscriberTasksFilterInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriptionBangumiCreateBatchArgs = {
|
export type MutationSubscriptionBangumiCreateBatchArgs = {
|
||||||
data: Array<SubscriptionBangumiInsertInput>;
|
data: Array<SubscriptionBangumiInsertInput>;
|
||||||
};
|
};
|
||||||
@ -838,11 +823,6 @@ export type MutationSubscriptionEpisodeUpdateArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriptionSyncOneSourcesArgs = {
|
|
||||||
filter: SyncOneSubscriptionFilterInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationSubscriptionsCreateBatchArgs = {
|
export type MutationSubscriptionsCreateBatchArgs = {
|
||||||
data: Array<SubscriptionsInsertInput>;
|
data: Array<SubscriptionsInsertInput>;
|
||||||
};
|
};
|
||||||
@ -907,12 +887,9 @@ export type Query = {
|
|||||||
downloaders: DownloadersConnection;
|
downloaders: DownloadersConnection;
|
||||||
downloads: DownloadsConnection;
|
downloads: DownloadsConnection;
|
||||||
episodes: EpisodesConnection;
|
episodes: EpisodesConnection;
|
||||||
subscriberTasks: SubscriberTasksConnection;
|
|
||||||
subscribers: SubscribersConnection;
|
subscribers: SubscribersConnection;
|
||||||
subscriptionBangumi: SubscriptionBangumiConnection;
|
subscriptionBangumi: SubscriptionBangumiConnection;
|
||||||
subscriptionEpisode: SubscriptionEpisodeConnection;
|
subscriptionEpisode: SubscriptionEpisodeConnection;
|
||||||
subscriptionSyncOneFeedsFull: SyncOneSubscriptionInfo;
|
|
||||||
subscriptionSyncOneFeedsIncremental: SyncOneSubscriptionInfo;
|
|
||||||
subscriptions: SubscriptionsConnection;
|
subscriptions: SubscriptionsConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -950,13 +927,6 @@ export type QueryEpisodesArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QuerySubscriberTasksArgs = {
|
|
||||||
filters?: InputMaybe<SubscriberTasksFilterInput>;
|
|
||||||
orderBy?: InputMaybe<SubscriberTasksOrderInput>;
|
|
||||||
pagination?: InputMaybe<PaginationInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type QuerySubscribersArgs = {
|
export type QuerySubscribersArgs = {
|
||||||
filters?: InputMaybe<SubscribersFilterInput>;
|
filters?: InputMaybe<SubscribersFilterInput>;
|
||||||
orderBy?: InputMaybe<SubscribersOrderInput>;
|
orderBy?: InputMaybe<SubscribersOrderInput>;
|
||||||
@ -978,16 +948,6 @@ export type QuerySubscriptionEpisodeArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QuerySubscriptionSyncOneFeedsFullArgs = {
|
|
||||||
filter: SyncOneSubscriptionFilterInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type QuerySubscriptionSyncOneFeedsIncrementalArgs = {
|
|
||||||
filter: SyncOneSubscriptionFilterInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type QuerySubscriptionsArgs = {
|
export type QuerySubscriptionsArgs = {
|
||||||
filters?: InputMaybe<SubscriptionsFilterInput>;
|
filters?: InputMaybe<SubscriptionsFilterInput>;
|
||||||
orderBy?: InputMaybe<SubscriptionsOrderInput>;
|
orderBy?: InputMaybe<SubscriptionsOrderInput>;
|
||||||
@ -1018,109 +978,6 @@ export type SubscriberIdFilterInput = {
|
|||||||
eq?: InputMaybe<Scalars['Int']['input']>;
|
eq?: InputMaybe<Scalars['Int']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SubscriberTasks = {
|
|
||||||
__typename?: 'SubscriberTasks';
|
|
||||||
attempts: Scalars['Int']['output'];
|
|
||||||
doneAt?: Maybe<Scalars['String']['output']>;
|
|
||||||
id: Scalars['String']['output'];
|
|
||||||
lastError?: Maybe<Scalars['String']['output']>;
|
|
||||||
lockAt?: Maybe<Scalars['String']['output']>;
|
|
||||||
lockBy?: Maybe<Scalars['String']['output']>;
|
|
||||||
maxAttempts: Scalars['Int']['output'];
|
|
||||||
priority: Scalars['Int']['output'];
|
|
||||||
runAt: Scalars['String']['output'];
|
|
||||||
status: Scalars['String']['output'];
|
|
||||||
subscriber?: Maybe<Subscribers>;
|
|
||||||
subscriberId: Scalars['Int']['output'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksBasic = {
|
|
||||||
__typename?: 'SubscriberTasksBasic';
|
|
||||||
attempts: Scalars['Int']['output'];
|
|
||||||
doneAt?: Maybe<Scalars['String']['output']>;
|
|
||||||
id: Scalars['String']['output'];
|
|
||||||
lastError?: Maybe<Scalars['String']['output']>;
|
|
||||||
lockAt?: Maybe<Scalars['String']['output']>;
|
|
||||||
lockBy?: Maybe<Scalars['String']['output']>;
|
|
||||||
maxAttempts: Scalars['Int']['output'];
|
|
||||||
priority: Scalars['Int']['output'];
|
|
||||||
runAt: Scalars['String']['output'];
|
|
||||||
status: Scalars['String']['output'];
|
|
||||||
subscriberId: Scalars['Int']['output'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksConnection = {
|
|
||||||
__typename?: 'SubscriberTasksConnection';
|
|
||||||
edges: Array<SubscriberTasksEdge>;
|
|
||||||
nodes: Array<SubscriberTasks>;
|
|
||||||
pageInfo: PageInfo;
|
|
||||||
paginationInfo?: Maybe<PaginationInfo>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksEdge = {
|
|
||||||
__typename?: 'SubscriberTasksEdge';
|
|
||||||
cursor: Scalars['String']['output'];
|
|
||||||
node: SubscriberTasks;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksFilterInput = {
|
|
||||||
and?: InputMaybe<Array<SubscriberTasksFilterInput>>;
|
|
||||||
attempts?: InputMaybe<IntegerFilterInput>;
|
|
||||||
doneAt?: InputMaybe<TextFilterInput>;
|
|
||||||
id?: InputMaybe<StringFilterInput>;
|
|
||||||
job?: InputMaybe<Scalars['JsonbFilterInput']['input']>;
|
|
||||||
lastError?: InputMaybe<StringFilterInput>;
|
|
||||||
lockAt?: InputMaybe<TextFilterInput>;
|
|
||||||
lockBy?: InputMaybe<StringFilterInput>;
|
|
||||||
maxAttempts?: InputMaybe<IntegerFilterInput>;
|
|
||||||
or?: InputMaybe<Array<SubscriberTasksFilterInput>>;
|
|
||||||
priority?: InputMaybe<IntegerFilterInput>;
|
|
||||||
runAt?: InputMaybe<TextFilterInput>;
|
|
||||||
status?: InputMaybe<StringFilterInput>;
|
|
||||||
subscriberId?: InputMaybe<SubscriberIdFilterInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksInsertInput = {
|
|
||||||
attempts: Scalars['Int']['input'];
|
|
||||||
doneAt?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
id?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
lastError?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
lockAt?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
lockBy?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
maxAttempts: Scalars['Int']['input'];
|
|
||||||
priority: Scalars['Int']['input'];
|
|
||||||
runAt: Scalars['String']['input'];
|
|
||||||
status: Scalars['String']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksOrderInput = {
|
|
||||||
attempts?: InputMaybe<OrderByEnum>;
|
|
||||||
doneAt?: InputMaybe<OrderByEnum>;
|
|
||||||
id?: InputMaybe<OrderByEnum>;
|
|
||||||
job?: InputMaybe<OrderByEnum>;
|
|
||||||
lastError?: InputMaybe<OrderByEnum>;
|
|
||||||
lockAt?: InputMaybe<OrderByEnum>;
|
|
||||||
lockBy?: InputMaybe<OrderByEnum>;
|
|
||||||
maxAttempts?: InputMaybe<OrderByEnum>;
|
|
||||||
priority?: InputMaybe<OrderByEnum>;
|
|
||||||
runAt?: InputMaybe<OrderByEnum>;
|
|
||||||
status?: InputMaybe<OrderByEnum>;
|
|
||||||
subscriberId?: InputMaybe<OrderByEnum>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubscriberTasksUpdateInput = {
|
|
||||||
attempts?: InputMaybe<Scalars['Int']['input']>;
|
|
||||||
doneAt?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
id?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
lastError?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
lockAt?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
lockBy?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
maxAttempts?: InputMaybe<Scalars['Int']['input']>;
|
|
||||||
priority?: InputMaybe<Scalars['Int']['input']>;
|
|
||||||
runAt?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
status?: InputMaybe<Scalars['String']['input']>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Subscribers = {
|
export type Subscribers = {
|
||||||
__typename?: 'Subscribers';
|
__typename?: 'Subscribers';
|
||||||
bangumi: BangumiConnection;
|
bangumi: BangumiConnection;
|
||||||
@ -1194,7 +1051,6 @@ export type SubscriptionBangumi = {
|
|||||||
bangumi?: Maybe<Bangumi>;
|
bangumi?: Maybe<Bangumi>;
|
||||||
bangumiId: Scalars['Int']['output'];
|
bangumiId: Scalars['Int']['output'];
|
||||||
id: Scalars['Int']['output'];
|
id: Scalars['Int']['output'];
|
||||||
subscriber?: Maybe<Subscribers>;
|
|
||||||
subscriberId: Scalars['Int']['output'];
|
subscriberId: Scalars['Int']['output'];
|
||||||
subscription?: Maybe<Subscriptions>;
|
subscription?: Maybe<Subscriptions>;
|
||||||
subscriptionId: Scalars['Int']['output'];
|
subscriptionId: Scalars['Int']['output'];
|
||||||
@ -1252,9 +1108,7 @@ export type SubscriptionBangumiUpdateInput = {
|
|||||||
|
|
||||||
export enum SubscriptionCategoryEnum {
|
export enum SubscriptionCategoryEnum {
|
||||||
Manual = 'manual',
|
Manual = 'manual',
|
||||||
MikanBangumi = 'mikan_bangumi',
|
Mikan = 'mikan'
|
||||||
MikanSeason = 'mikan_season',
|
|
||||||
MikanSubscriber = 'mikan_subscriber'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SubscriptionCategoryEnumFilterInput = {
|
export type SubscriptionCategoryEnumFilterInput = {
|
||||||
@ -1275,7 +1129,6 @@ export type SubscriptionEpisode = {
|
|||||||
episode?: Maybe<Episodes>;
|
episode?: Maybe<Episodes>;
|
||||||
episodeId: Scalars['Int']['output'];
|
episodeId: Scalars['Int']['output'];
|
||||||
id: Scalars['Int']['output'];
|
id: Scalars['Int']['output'];
|
||||||
subscriber?: Maybe<Subscribers>;
|
|
||||||
subscriberId: Scalars['Int']['output'];
|
subscriberId: Scalars['Int']['output'];
|
||||||
subscription?: Maybe<Subscriptions>;
|
subscription?: Maybe<Subscriptions>;
|
||||||
subscriptionId: Scalars['Int']['output'];
|
subscriptionId: Scalars['Int']['output'];
|
||||||
@ -1452,17 +1305,6 @@ export type SubscriptionsUpdateInput = {
|
|||||||
updatedAt?: InputMaybe<Scalars['String']['input']>;
|
updatedAt?: InputMaybe<Scalars['String']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The input of the subscriptionSyncOne series of mutations */
|
|
||||||
export type SyncOneSubscriptionFilterInput = {
|
|
||||||
subscriptionId: Scalars['Int']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The output of the subscriptionSyncOne series of mutations */
|
|
||||||
export type SyncOneSubscriptionInfo = {
|
|
||||||
__typename?: 'SyncOneSubscriptionInfo';
|
|
||||||
taskId: Scalars['String']['output'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TextFilterInput = {
|
export type TextFilterInput = {
|
||||||
between?: InputMaybe<Array<Scalars['String']['input']>>;
|
between?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||||
eq?: InputMaybe<Scalars['String']['input']>;
|
eq?: InputMaybe<Scalars['String']['input']>;
|
||||||
@ -1507,7 +1349,7 @@ export type GetSubscriptionDetailQueryVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetSubscriptionDetailQuery = { __typename?: 'Query', subscriptions: { __typename?: 'SubscriptionsConnection', nodes: Array<{ __typename?: 'Subscriptions', id: number, displayName: string, createdAt: string, updatedAt: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean, bangumi: { __typename?: 'BangumiConnection', nodes: Array<{ __typename?: 'Bangumi', createdAt: string, updatedAt: string, id: number, mikanBangumiId?: string | null, displayName: string, rawName: string, season: number, seasonRaw?: string | null, fansub?: string | null, mikanFansubId?: string | null, rssLink?: string | null, posterLink?: string | null, savePath?: string | null, homepage?: string | null }> } }> } };
|
export type GetSubscriptionDetailQuery = { __typename?: 'Query', subscriptions: { __typename?: 'SubscriptionsConnection', nodes: Array<{ __typename?: 'Subscriptions', id: number, displayName: string, createdAt: string, updatedAt: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean, bangumi: { __typename?: 'BangumiConnection', nodes: Array<{ __typename?: 'Bangumi', createdAt: string, updatedAt: string, id: number, mikanBangumiId?: string | null, displayName: string, rawName: string, season: number, seasonRaw?: string | null, fansub?: string | null, mikanFansubId?: string | null, rssLink?: string | null, posterLink?: string | null, savePath?: string | null, deleted: boolean, homepage?: string | null }> } }> } };
|
||||||
|
|
||||||
export type CreateSubscriptionMutationVariables = Exact<{
|
export type CreateSubscriptionMutationVariables = Exact<{
|
||||||
input: SubscriptionsInsertInput;
|
input: SubscriptionsInsertInput;
|
||||||
@ -1520,5 +1362,5 @@ export type CreateSubscriptionMutation = { __typename?: 'Mutation', subscription
|
|||||||
export const GetSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsOrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionsQuery, GetSubscriptionsQueryVariables>;
|
export const GetSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsOrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionsQuery, GetSubscriptionsQueryVariables>;
|
||||||
export const UpdateSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsUpdateInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]} as unknown as DocumentNode<UpdateSubscriptionsMutation, UpdateSubscriptionsMutationVariables>;
|
export const UpdateSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsUpdateInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]} as unknown as DocumentNode<UpdateSubscriptionsMutation, UpdateSubscriptionsMutationVariables>;
|
||||||
export const DeleteSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}]}]}}]} as unknown as DocumentNode<DeleteSubscriptionsMutation, DeleteSubscriptionsMutationVariables>;
|
export const DeleteSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}]}]}}]} as unknown as DocumentNode<DeleteSubscriptionsMutation, DeleteSubscriptionsMutationVariables>;
|
||||||
export const GetSubscriptionDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptionDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"bangumi"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mikanBangumiId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"rawName"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"seasonRaw"}},{"kind":"Field","name":{"kind":"Name","value":"fansub"}},{"kind":"Field","name":{"kind":"Name","value":"mikanFansubId"}},{"kind":"Field","name":{"kind":"Name","value":"rssLink"}},{"kind":"Field","name":{"kind":"Name","value":"posterLink"}},{"kind":"Field","name":{"kind":"Name","value":"savePath"}},{"kind":"Field","name":{"kind":"Name","value":"homepage"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionDetailQuery, GetSubscriptionDetailQueryVariables>;
|
export const GetSubscriptionDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptionDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"bangumi"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mikanBangumiId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"rawName"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"seasonRaw"}},{"kind":"Field","name":{"kind":"Name","value":"fansub"}},{"kind":"Field","name":{"kind":"Name","value":"mikanFansubId"}},{"kind":"Field","name":{"kind":"Name","value":"rssLink"}},{"kind":"Field","name":{"kind":"Name","value":"posterLink"}},{"kind":"Field","name":{"kind":"Name","value":"savePath"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"homepage"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionDetailQuery, GetSubscriptionDetailQueryVariables>;
|
||||||
export const CreateSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsInsertInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsCreateOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"category"}}]}}]}}]} as unknown as DocumentNode<CreateSubscriptionMutation, CreateSubscriptionMutationVariables>;
|
export const CreateSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsInsertInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsCreateOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"category"}}]}}]}}]} as unknown as DocumentNode<CreateSubscriptionMutation, CreateSubscriptionMutationVariables>;
|
@ -35,11 +35,8 @@ qbit-rs = { git = "https://github.com/lonelyhentxi/qbit.git", rev = "72d53138ebe
|
|||||||
"builder",
|
"builder",
|
||||||
] }
|
] }
|
||||||
merge-struct = "0.1"
|
merge-struct = "0.1"
|
||||||
librqbit-core = { git = "https://github.com/ikatson/rqbit.git", rev = "0936730" }
|
librqbit-core = "4"
|
||||||
librqbit = { git = "https://github.com/ikatson/rqbit.git", rev = "0936730", features = [
|
librqbit = { version = "8", features = ["async-bt", "watch"] }
|
||||||
"async-bt",
|
|
||||||
"watch",
|
|
||||||
] }
|
|
||||||
|
|
||||||
util = { workspace = true }
|
util = { workspace = true }
|
||||||
testing-torrents = { workspace = true, optional = true }
|
testing-torrents = { workspace = true, optional = true }
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "util-derive"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
snafu = { workspace = true }
|
|
||||||
convert_case = { workspace = true }
|
|
||||||
|
|
||||||
quote = "1"
|
|
||||||
syn = "2"
|
|
||||||
darling = "0.20"
|
|
||||||
proc-macro2 = { version = "1" }
|
|
@ -1,162 +0,0 @@
|
|||||||
extern crate proc_macro;
|
|
||||||
|
|
||||||
use convert_case::{Case, Casing};
|
|
||||||
use darling::{FromDeriveInput, FromField, ast::Data, util::Ignored};
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use syn::{Attribute, DeriveInput, Generics, Ident, parse_macro_input};
|
|
||||||
|
|
||||||
#[derive(snafu::Snafu, Debug)]
|
|
||||||
enum GeneratorError {
|
|
||||||
#[snafu(transparent)]
|
|
||||||
Syn { source: syn::Error },
|
|
||||||
|
|
||||||
#[snafu(transparent)]
|
|
||||||
Darling { source: darling::Error },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GeneratorError {
|
|
||||||
fn write_errors(self) -> proc_macro2::TokenStream {
|
|
||||||
match self {
|
|
||||||
GeneratorError::Syn { source } => source.to_compile_error(),
|
|
||||||
GeneratorError::Darling { source } => source.write_errors(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, FromField)]
|
|
||||||
#[darling(attributes(dyngql))]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct DynamicGraphqlFieldInfo {
|
|
||||||
ident: Option<Ident>,
|
|
||||||
ty: syn::Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromDeriveInput)]
|
|
||||||
#[darling(attributes(dyngql), forward_attrs(doc))]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct DynamicGraphqlInfo {
|
|
||||||
pub ident: Ident,
|
|
||||||
pub attrs: Vec<Attribute>,
|
|
||||||
pub generics: Generics,
|
|
||||||
pub data: Data<Ignored, DynamicGraphqlFieldInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynamicGraphqlInfo {
|
|
||||||
fn expand(&self) -> Result<TokenStream, GeneratorError> {
|
|
||||||
let struct_name = &self.ident;
|
|
||||||
let enum_name = format_ident!("{}FieldEnum", struct_name);
|
|
||||||
|
|
||||||
let fields = self.data.as_ref().take_struct().unwrap();
|
|
||||||
|
|
||||||
let enum_variants = fields
|
|
||||||
.iter()
|
|
||||||
.filter_map(|field| field.ident.as_ref())
|
|
||||||
.map(|field_ident| {
|
|
||||||
let variant_name = Ident::new(
|
|
||||||
&field_ident.to_string().to_case(Case::Pascal),
|
|
||||||
field_ident.span(),
|
|
||||||
);
|
|
||||||
quote! { #variant_name }
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let as_str_arms: Vec<_> = fields
|
|
||||||
.iter()
|
|
||||||
.filter_map(|field| field.ident.as_ref())
|
|
||||||
.map(|field_ident| {
|
|
||||||
let variant_name = Ident::new(
|
|
||||||
&field_ident.to_string().to_case(Case::Pascal),
|
|
||||||
field_ident.span(),
|
|
||||||
);
|
|
||||||
let field_name_str = field_ident.to_string().to_case(Case::Camel);
|
|
||||||
quote! {
|
|
||||||
Self::#variant_name => #field_name_str,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let from_str_arms: Vec<_> = fields
|
|
||||||
.iter()
|
|
||||||
.filter_map(|field| field.ident.as_ref())
|
|
||||||
.map(|field_ident| {
|
|
||||||
let variant_name = Ident::new(
|
|
||||||
&field_ident.to_string().to_case(Case::Pascal),
|
|
||||||
field_ident.span(),
|
|
||||||
);
|
|
||||||
let field_name_str = field_ident.to_string().to_case(Case::Camel);
|
|
||||||
quote! {
|
|
||||||
#field_name_str => Some(Self::#variant_name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let all_field_names: Vec<_> = fields
|
|
||||||
.iter()
|
|
||||||
.filter_map(|field| field.ident.as_ref())
|
|
||||||
.map(|field_ident| field_ident.to_string().to_case(Case::Camel))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let result = quote! {
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum #enum_name {
|
|
||||||
#(#enum_variants),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #enum_name {
|
|
||||||
pub fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
#(#as_str_arms),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_str(s: &str) -> Option<Self> {
|
|
||||||
match s {
|
|
||||||
#(#from_str_arms),* ,
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all_field_names() -> Vec<&'static str> {
|
|
||||||
vec![#(#all_field_names),*]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for #enum_name {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<#enum_name> for String {
|
|
||||||
fn from(value: #enum_name) -> Self {
|
|
||||||
value.as_str().to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::str::FromStr for #enum_name {
|
|
||||||
type Err = String;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Self::from_str(s).ok_or_else(|| format!("Unknown field name: {s}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(result.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(DynamicGraphql, attributes(dyngql))]
|
|
||||||
pub fn derive_dynamic_graphql(input: TokenStream) -> TokenStream {
|
|
||||||
let opts =
|
|
||||||
match DynamicGraphqlInfo::from_derive_input(&parse_macro_input!(input as DeriveInput)) {
|
|
||||||
Ok(opts) => opts,
|
|
||||||
Err(err) => return TokenStream::from(err.write_errors()),
|
|
||||||
};
|
|
||||||
match opts.expand() {
|
|
||||||
Ok(token_stream) => token_stream,
|
|
||||||
Err(err) => err.write_errors().into(),
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user