11 Commits

Author SHA1 Message Date
GW_MC
6f5596dc69 Enforce deny unwrap_used
All checks were successful
Test / test-frontend (pull_request) Successful in 26s
Test / lint-frontend (pull_request) Successful in 28s
Test / frontend-build (pull_request) Successful in 32s
Verify / verify-generated-database-code (pull_request) Successful in 1m7s
Verify / verify-generated-agent-code (pull_request) Successful in 1m10s
Verify / verify-openapi-spec (pull_request) Successful in 1m9s
Verify / verify-frontend-api-client (pull_request) Successful in 7s
Test / test-crates (pull_request) Successful in 54s
Test / lint-crates (pull_request) Successful in 1m10s
2025-12-28 19:07:17 +08:00
GW_MC
96e7f36731 feat: integrate AgentService into app service and enhance configuration handling
All checks were successful
Test / test-frontend (pull_request) Successful in 25s
Test / lint-frontend (pull_request) Successful in 28s
Test / frontend-build (pull_request) Successful in 32s
Verify / verify-generated-database-code (pull_request) Successful in 1m7s
Verify / verify-generated-agent-code (pull_request) Successful in 1m11s
Verify / verify-openapi-spec (pull_request) Successful in 1m13s
Verify / verify-frontend-api-client (pull_request) Successful in 8s
Test / test-crates (pull_request) Successful in 55s
Test / lint-crates (pull_request) Successful in 1m9s
2025-12-28 18:35:53 +08:00
GW_MC
410328a2af refactor app service 2025-12-28 18:28:28 +08:00
GW_MC
9f122566d0 feat: add agent settings configuration and update agent client service 2025-12-28 18:08:55 +08:00
GW_MC
c65dc3af47 feat: Basic agent-client service
Some checks failed
Test / test-frontend (pull_request) Successful in 27s
Test / frontend-build (pull_request) Successful in 33s
Verify / verify-openapi-spec (pull_request) Successful in 7s
Verify / verify-generated-agent-code (pull_request) Successful in 1m7s
Verify / verify-generated-database-code (pull_request) Successful in 2m14s
Verify / verify-frontend-api-client (pull_request) Successful in 6s
Test / lint-crates (pull_request) Failing after 1m28s
Test / test-crates (pull_request) Successful in 2m40s
Test / lint-frontend (pull_request) Successful in 29s
2025-12-28 17:53:27 +08:00
GW_MC
6f395ed1ae rename workflows jobs 2025-12-28 16:42:12 +08:00
GW_MC
e6e85564e7 fix: incorrect pnpm cache
All checks were successful
Test / test-frontend (pull_request) Successful in 1m17s
Test / lint-frontend (pull_request) Successful in 1m19s
Test / frontend-build (pull_request) Successful in 1m55s
Verify / verify-openapi-spec (pull_request) Successful in 31s
Verify / verify-generated-agent-code (pull_request) Successful in 2m54s
Verify / verify-generated-code (pull_request) Successful in 3m57s
Verify / verify-frontend-api-client (pull_request) Successful in 8s
Test / lint (pull_request) Successful in 1m25s
Test / test (pull_request) Successful in 3m26s
2025-12-28 16:29:31 +08:00
GW_MC
c7a090f78a fix: frontend deps not installed 2025-12-28 16:23:10 +08:00
GW_MC
8cc2775fe4 feat: auto format generated code, and ignore clippy lint in agent-client 2025-12-28 16:22:41 +08:00
GW_MC
0b64538754 added verify for untracked generated files 2025-12-28 15:56:04 +08:00
GW_MC
8009ca20ff feat: added verify generated agent code in action workflows 2025-12-28 15:55:30 +08:00
31 changed files with 7196 additions and 7222 deletions

View File

@@ -12,11 +12,8 @@ on:
jobs: jobs:
# setup is now handled by a composite action used by downstream jobs to keep
# the workflow DRY. The composite action performs checkout, cache restore and
# toolchain setup.
test: test-crates:
needs: frontend-build needs: frontend-build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -39,7 +36,7 @@ jobs:
- name: Run tests - name: Run tests
run: cargo test --all-features run: cargo test --all-features
lint: lint-crates:
needs: frontend-build needs: frontend-build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -83,7 +80,7 @@ jobs:
with: with:
node-version: 22 node-version: 22
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: apps/frontend/pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
@@ -114,7 +111,7 @@ jobs:
with: with:
node-version: 22 node-version: 22
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: apps/frontend/pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
@@ -142,12 +139,12 @@ jobs:
with: with:
node-version: 22 node-version: 22
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: apps/frontend/pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: Install frontend dependencies - name: Install frontend dependencies
run: | run: |
cd apps/frontend cd apps/frontend
pnpm install pnpm install --frozen-lockfile
- name: Build frontend - name: Build frontend
run: | run: |

View File

@@ -11,11 +11,8 @@ on:
jobs: jobs:
# setup is now handled by a composite action used by downstream jobs to keep
# the workflow DRY. The composite action performs checkout, cache restore and
# toolchain setup.
verify-generated-code: verify-generated-database-code:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -32,14 +29,62 @@ jobs:
cargo run -- db:migrate_and_generate --output-path ../../public/database/src/generated/entities cargo run -- db:migrate_and_generate --output-path ../../public/database/src/generated/entities
- name: Check for uncommitted changes in /generated/ - name: Check for uncommitted changes in /generated/
run: | run: |
if [[ -n $(git status --porcelain | grep '^ M .*\/generated\/') ]]; then if [[ -n $(git status --porcelain --untracked-files=all | grep '/generated/') ]]; then
echo "Generated code is not up to date. Please run the code generation locally and commit the changes." echo "Generated code is not up to date. Please run the code generation locally and commit the changes."
git status --porcelain | grep '^ M .*\/generated\/' git status --porcelain --untracked-files=all | grep '/generated/'
exit 1 exit 1
else else
echo "Generated code is up to date." echo "Generated code is up to date."
fi fi
verify-generated-agent-code:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
- name: Setup Rust, checkout and restore caches
uses: ./.github/actions/setup-rust
- name: Setup PNPM
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml
- name: Install root dependencies
run: |
pnpm install --frozen-lockfile
- name: generate agent client code
run: |
pnpm just generate-agent-client
- name: Check for uncommitted changes in agent client code
run: |
if [[ -n $(git status --porcelain --untracked-files=all | grep 'public/agent-client/') ]]; then
echo "Agent client code is not up to date. Please run the agent client code generation locally and commit the changes."
git status --porcelain --untracked-files=all | grep 'public/agent-client/'
exit 1
else
echo "Agent client code is up to date."
fi
verify-openapi-spec: verify-openapi-spec:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -99,9 +144,9 @@ jobs:
- name: Check for uncommitted changes in swagger.json - name: Check for uncommitted changes in swagger.json
if: steps.check_changes.outputs.changed == 'true' if: steps.check_changes.outputs.changed == 'true'
run: | run: |
if [[ -n $(git status --porcelain | grep '^ M apps/api/swagger.json') ]]; then if [[ -n $(git status --porcelain --untracked-files=all | grep 'apps/api/swagger.json') ]]; then
echo "OpenAPI spec is not up to date. Please run the OpenAPI generation locally and commit the changes." echo "OpenAPI spec is not up to date. Please run the OpenAPI generation locally and commit the changes."
git status --porcelain | grep '^ M apps/api/swagger.json' git status --porcelain --untracked-files=all | grep 'apps/api/swagger.json'
exit 1 exit 1
else else
echo "OpenAPI spec is up to date." echo "OpenAPI spec is up to date."
@@ -165,7 +210,7 @@ jobs:
with: with:
node-version: 22 node-version: 22
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: apps/frontend/pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: Install frontend dependencies - name: Install frontend dependencies
if: steps.check_swagger_changes.outputs.changed == 'true' if: steps.check_swagger_changes.outputs.changed == 'true'
@@ -182,9 +227,9 @@ jobs:
- name: Check for uncommitted changes in frontend API client - name: Check for uncommitted changes in frontend API client
if: steps.check_swagger_changes.outputs.changed == 'true' if: steps.check_swagger_changes.outputs.changed == 'true'
run: | run: |
if [[ -n $(git status --porcelain | grep '^ M apps/frontend/app/generated/api-client') ]]; then if [[ -n $(git status --porcelain --untracked-files=all | grep 'apps/frontend/app/generated/api-client') ]]; then
echo "Frontend API client is not up to date. Please run the API client generation locally and commit the changes." echo "Frontend API client is not up to date. Please run the API client generation locally and commit the changes."
git status --porcelain | grep '^ M apps/frontend/app/generated/api-client' git status --porcelain --untracked-files=all | grep 'apps/frontend/app/generated/api-client'
exit 1 exit 1
else else
echo "Frontend API client is up to date." echo "Frontend API client is up to date."

82
Cargo.lock generated
View File

@@ -542,7 +542,7 @@ dependencies = [
"num-traits", "num-traits",
"serde", "serde",
"wasm-bindgen", "wasm-bindgen",
"windows-link", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@@ -1709,9 +1709,11 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
] ]
[[package]] [[package]]
@@ -2468,7 +2470,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-link", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@@ -3073,8 +3075,10 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"http-body-util", "http-body-util",
@@ -3084,6 +3088,7 @@ dependencies = [
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"mime",
"mime_guess", "mime_guess",
"native-tls", "native-tls",
"percent-encoding", "percent-encoding",
@@ -4193,6 +4198,27 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "tap" name = "tap"
version = "1.0.1" version = "1.0.1"
@@ -5008,9 +5034,9 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [ dependencies = [
"windows-implement", "windows-implement",
"windows-interface", "windows-interface",
"windows-link", "windows-link 0.2.1",
"windows-result", "windows-result 0.4.1",
"windows-strings", "windows-strings 0.5.1",
] ]
[[package]] [[package]]
@@ -5035,19 +5061,54 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-registry"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
dependencies = [
"windows-link 0.1.3",
"windows-result 0.3.4",
"windows-strings 0.4.2",
]
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.4.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.1",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link 0.1.3",
] ]
[[package]] [[package]]
@@ -5056,7 +5117,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@@ -5101,7 +5162,7 @@ version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.1",
] ]
[[package]] [[package]]
@@ -5141,7 +5202,7 @@ version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.1",
"windows_aarch64_gnullvm 0.53.1", "windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1", "windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1", "windows_i686_gnu 0.53.1",
@@ -5367,6 +5428,7 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
name = "yet-another-nginx-proxy-manager" name = "yet-another-nginx-proxy-manager"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"agent_client",
"argon2", "argon2",
"async-trait", "async-trait",
"axum", "axum",
@@ -5380,9 +5442,11 @@ dependencies = [
"migration", "migration",
"mime_guess", "mime_guess",
"once_cell", "once_cell",
"reqwest",
"sea-orm", "sea-orm",
"serde", "serde",
"serde_json", "serde_json",
"tempfile",
"tokio", "tokio",
"tower", "tower",
"tower-http", "tower-http",

View File

@@ -6,6 +6,7 @@ edition = "2024"
[dependencies] [dependencies]
database = { path = "../../public/database" } database = { path = "../../public/database" }
migration = { path = "../../public/migration" } migration = { path = "../../public/migration" }
agent_client = { path = "../../public/agent-client" }
axum = { version = "0.8.7", features = ["form", "http1", "http2", "json", "matched-path", "original-uri", "query", "tokio", "tower-log", "tracing", "macros"] } axum = { version = "0.8.7", features = ["form", "http1", "http2", "json", "matched-path", "original-uri", "query", "tokio", "tower-log", "tracing", "macros"] }
axum-extra = { version = "0.12.2", features = ["cookie"] } axum-extra = { version = "0.12.2", features = ["cookie"] }
@@ -28,4 +29,10 @@ argon2 = { version = "0.5.3", features = ["std"] }
jsonwebtoken = { version = "10.2.0", features = ["rust_crypto"] } jsonwebtoken = { version = "10.2.0", features = ["rust_crypto"] }
uuid = { version = "1.19.0", features = ["v4", "serde", "fast-rng"] } uuid = { version = "1.19.0", features = ["v4", "serde", "fast-rng"] }
tower-http = { version = "0.6.8", features = ["cors"] } tower-http = { version = "0.6.8", features = ["cors"] }
reqwest = { version = "^0.12", features = ["json", "multipart", "stream"] }
[dev-dependencies]
tempfile = "3"
[lints.clippy]
unwrap_used = "deny"

View File

@@ -28,7 +28,7 @@ fn action(
_matches: &clap::ArgMatches, _matches: &clap::ArgMatches,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>> { ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>> {
let output_path = _matches.get_one::<String>("output_path"); let output_path = _matches.get_one::<String>("output_path");
let output_path = output_path.unwrap().to_string(); let output_path = output_path.expect("output_path is required").to_string();
Box::pin(async move { Box::pin(async move {
tracing::subscriber::with_default(log::make_temporary_subscriber(), || { tracing::subscriber::with_default(log::make_temporary_subscriber(), || {

View File

@@ -11,15 +11,8 @@ use crate::{
cmd::CliCommand, cmd::CliCommand,
configs::{ProgramSettings, get_program_settings, logging::LoggingSettings}, configs::{ProgramSettings, get_program_settings, logging::LoggingSettings},
log, log,
routes::{self, AppService, AppState}, routes::{self, AppState},
services::{ services::get_app_service,
auth::{
authentication::{AuthenticationServiceImpl, strategies::password::PasswordStrategy},
user::UserServiceImpl,
},
server_state::ServerStateService,
settings::SettingsService,
},
tasks, tasks,
}; };
@@ -148,19 +141,7 @@ fn get_app_state(
AppState { AppState {
database_connection: db_connection.clone(), database_connection: db_connection.clone(),
config: Arc::new(settings.clone()), config: Arc::new(settings.clone()),
service: Arc::new(AppService { service: Arc::new(get_app_service(db_connection, settings)),
server_state: Arc::new(ServerStateService::new(db_connection.clone())),
settings: Arc::new(SettingsService::new(db_connection.clone())),
auth_state: routes::AuthState {
strategy: routes::AuthStrategy {
password: Arc::new(PasswordStrategy::new(db_connection.clone())),
},
authentication: Arc::new(AuthenticationServiceImpl::new(
settings.auth.jwt_secret.clone(),
)),
},
user: Arc::new(UserServiceImpl::new(db_connection.clone())),
}),
} }
} }

View File

@@ -1,3 +1,4 @@
pub mod agent;
pub mod auth; pub mod auth;
pub mod database; pub mod database;
pub mod logging; pub mod logging;
@@ -21,6 +22,7 @@ pub struct ProgramSettings {
pub database: database::DatabaseSettings, pub database: database::DatabaseSettings,
pub server: server::ServerSettings, pub server: server::ServerSettings,
pub auth: auth::AuthSettings, pub auth: auth::AuthSettings,
pub agent: agent::AgentSettings,
} }
impl FromConfig for ProgramSettings { impl FromConfig for ProgramSettings {
@@ -30,6 +32,7 @@ impl FromConfig for ProgramSettings {
database: database::DatabaseSettings::from_config(_config)?, database: database::DatabaseSettings::from_config(_config)?,
server: server::ServerSettings::from_config(_config)?, server: server::ServerSettings::from_config(_config)?,
auth: auth::AuthSettings::from_config(_config)?, auth: auth::AuthSettings::from_config(_config)?,
agent: agent::AgentSettings::from_config(_config)?,
}; };
config.validate()?; config.validate()?;
Ok(config) Ok(config)
@@ -50,6 +53,7 @@ impl FromConfig for ProgramSettings {
database: database::DatabaseSettings::mock(), database: database::DatabaseSettings::mock(),
server: server::ServerSettings::mock(), server: server::ServerSettings::mock(),
auth: auth::AuthSettings::mock(), auth: auth::AuthSettings::mock(),
agent: agent::AgentSettings::mock(),
} }
} }
} }

View File

@@ -0,0 +1,58 @@
use config::Config;
use tracing::error;
use crate::configs::key::AGENT_SOCK_PATH_KEY;
use super::FromConfig;
#[derive(Debug, Clone)]
pub struct AgentSettings {
pub socket_path: String,
}
impl FromConfig for AgentSettings {
fn from_config(_config: &Config) -> Result<Self, String> {
Ok(AgentSettings {
socket_path: _config.get_string(AGENT_SOCK_PATH_KEY).map_err(|err| {
format!(
"Failed to get {} from configuration. Err: {}",
AGENT_SOCK_PATH_KEY, err
)
})?,
})
}
fn validate(&self) -> Result<(), String> {
// ensure socket_path exists and is readable and writable
if !std::path::Path::new(&self.socket_path).exists() {
let msg = format!("Agent socket path '{}' does not exist", self.socket_path);
error!("{}", msg);
return Err(msg);
}
if std::path::Path::new(&self.socket_path)
.metadata()
.map(|meta| {
let permissions = meta.permissions();
// Check read and write permissions for the owner
!permissions.readonly()
})
.unwrap_or(false)
{
Ok(())
} else {
let msg = format!(
"Agent socket path '{}' is not readable/writable",
self.socket_path
);
error!("{}", msg);
Err(msg)
}
}
#[cfg(test)]
fn mock() -> Self {
AgentSettings {
socket_path: "/tmp/agent.sock".to_string(),
}
}
}

View File

@@ -14,3 +14,5 @@ pub(crate) const DATABASE_MIGRATE_ON_STARTUP_KEY: &str = "DATABASE.MIGRATION.MIG
pub(crate) const AUTH_JWT_SECRET_KEY: &str = "AUTH.JWT_SECRET"; pub(crate) const AUTH_JWT_SECRET_KEY: &str = "AUTH.JWT_SECRET";
pub(crate) const AUTH_DEFAULT_ADMIN_USERNAME_KEY: &str = "AUTH.DEFAULT_ADMIN_USERNAME"; pub(crate) const AUTH_DEFAULT_ADMIN_USERNAME_KEY: &str = "AUTH.DEFAULT_ADMIN_USERNAME";
pub(crate) const AUTH_DEFAULT_ADMIN_PASSWORD_KEY: &str = "AUTH.DEFAULT_ADMIN_PASSWORD"; pub(crate) const AUTH_DEFAULT_ADMIN_PASSWORD_KEY: &str = "AUTH.DEFAULT_ADMIN_PASSWORD";
//
pub(crate) const AGENT_SOCK_PATH_KEY: &str = "AGENT.SOCK.PATH";

View File

@@ -121,7 +121,7 @@ impl FromConfig for ServerSettings {
#[cfg(test)] #[cfg(test)]
fn mock() -> Self { fn mock() -> Self {
ServerSettings { ServerSettings {
address: "0.0.0.0".parse().unwrap(), address: "0.0.0.0".parse().expect("Failed to parse mock IP address"),
port: 8080, port: 8080,
serve_openapi: false, serve_openapi: false,
cors: CORSSettings { cors: CORSSettings {

View File

@@ -12,12 +12,8 @@ use crate::{
configs::{ProgramSettings, server::CORSSettings}, configs::{ProgramSettings, server::CORSSettings},
middlewares, middlewares,
services::{ services::{
auth::{ AppService, ServiceState,
authentication::{AuthenticationService, strategies::password::PasswordStrategy}, auth::authentication::{AuthenticationService, strategies::password::PasswordStrategy},
user::UserService,
},
server_state::ServerStateStore,
settings::SettingsStore,
}, },
}; };
@@ -28,8 +24,6 @@ pub struct AppState {
pub config: Arc<ProgramSettings>, pub config: Arc<ProgramSettings>,
} }
pub type ServiceState<T> = Arc<T>;
pub struct AuthStrategy { pub struct AuthStrategy {
pub password: ServiceState<PasswordStrategy>, pub password: ServiceState<PasswordStrategy>,
} }
@@ -39,13 +33,6 @@ pub struct AuthState {
pub authentication: ServiceState<dyn AuthenticationService>, pub authentication: ServiceState<dyn AuthenticationService>,
} }
pub struct AppService {
pub settings: ServiceState<dyn SettingsStore>,
pub auth_state: AuthState,
pub user: ServiceState<dyn UserService>,
pub server_state: ServiceState<dyn ServerStateStore>,
}
pub fn get_root_router( pub fn get_root_router(
state: impl Into<Arc<AppState>>, state: impl Into<Arc<AppState>>,
cors_settings: Arc<CORSSettings>, cors_settings: Arc<CORSSettings>,

View File

@@ -79,6 +79,7 @@ pub async fn get_health_info(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::configs::FromConfig; use crate::configs::FromConfig;
use crate::services::agent_client::AgentService;
use crate::{ use crate::{
routes::{AppState, api::health::state::HealthState}, routes::{AppState, api::health::state::HealthState},
services::{ services::{
@@ -94,6 +95,7 @@ mod test {
}; };
use super::*; use super::*;
use agent_client::apis::configuration::Configuration;
use axum::body::to_bytes; use axum::body::to_bytes;
use axum::{ use axum::{
Router, Router,
@@ -124,6 +126,7 @@ mod test {
}, },
user: Arc::new(UserServiceImpl::new(db.clone())), user: Arc::new(UserServiceImpl::new(db.clone())),
server_state: Arc::new(ServerStateService::new(db.clone())), server_state: Arc::new(ServerStateService::new(db.clone())),
agent_client: Arc::new(AgentService::new(Configuration::default())),
}), }),
}); });
@@ -135,13 +138,21 @@ mod test {
})); }));
let response = app let response = app
.oneshot(Request::builder().uri("/info").body(Body::empty()).unwrap()) .oneshot(
Request::builder()
.uri("/info")
.body(Body::empty())
.expect("Failed to build request"),
)
.await .await
.unwrap(); .unwrap();
assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.status(), StatusCode::OK);
let body = to_bytes(response.into_body(), 1024 * 1024).await.unwrap(); // Set limit to 1 MB let body = to_bytes(response.into_body(), 1024 * 1024)
let health_info: HealthInfo = serde_json::from_slice(&body).unwrap(); .await
.expect("Failed to read response body"); // Set limit to 1 MB
let health_info: HealthInfo =
serde_json::from_slice(&body).expect("Failed to deserialize response body");
assert_eq!(health_info.status, STATUS_HEALTHY); assert_eq!(health_info.status, STATUS_HEALTHY);
assert_eq!(health_info.version, env!("CARGO_PKG_VERSION")); assert_eq!(health_info.version, env!("CARGO_PKG_VERSION"));
assert!(health_info.errors.is_none()); assert!(health_info.errors.is_none());

View File

@@ -1,3 +1,54 @@
pub mod agent_client;
pub mod auth; pub mod auth;
pub mod server_state; pub mod server_state;
pub mod settings; pub mod settings;
use std::sync::Arc;
use ::agent_client::apis::configuration::Configuration;
use crate::{
configs::ProgramSettings,
routes::{self, AuthState},
services::{
auth::{
authentication::{AuthenticationServiceImpl, strategies::password::PasswordStrategy},
user::{UserService, UserServiceImpl},
},
server_state::{ServerStateService, ServerStateStore},
settings::{SettingsService, SettingsStore},
},
};
pub type ServiceState<T> = Arc<T>;
pub struct AppService {
pub settings: ServiceState<dyn SettingsStore>,
pub auth_state: AuthState,
pub user: ServiceState<dyn UserService>,
pub server_state: ServiceState<dyn ServerStateStore>,
#[allow(dead_code)]
pub agent_client: ServiceState<agent_client::AgentService>,
}
pub fn get_app_service(
db_connection: &Arc<sea_orm::DatabaseConnection>,
settings: &ProgramSettings,
) -> AppService {
AppService {
server_state: Arc::new(ServerStateService::new(db_connection.clone())),
settings: Arc::new(SettingsService::new(db_connection.clone())),
auth_state: routes::AuthState {
strategy: routes::AuthStrategy {
password: Arc::new(PasswordStrategy::new(db_connection.clone())),
},
authentication: Arc::new(AuthenticationServiceImpl::new(
settings.auth.jwt_secret.clone(),
)),
},
user: Arc::new(UserServiceImpl::new(db_connection.clone())),
agent_client: Arc::new(agent_client::AgentService::new(Configuration::from(
settings.agent.clone(),
))),
}
}

View File

@@ -0,0 +1,114 @@
use std::sync::Arc;
use agent_client::apis::{ApiClient, configuration::Configuration};
use tracing::warn;
use crate::configs::agent::AgentSettings;
pub struct AgentService {
client: Arc<ApiClient>,
}
impl From<AgentSettings> for Configuration {
fn from(settings: AgentSettings) -> Self {
let mut config = Configuration::default();
let mut builder = reqwest::Client::builder();
let url = settings.socket_path;
if url.starts_with("unix://") {
builder = builder.unix_socket(url.to_string());
config.client = builder.build().expect("Failed to build reqwest client");
} else {
warn!("AgentSettings contains a non-unix socket path: {}", url);
config.base_path = url;
}
config
}
}
impl AgentService {
pub fn new(config: impl Into<Arc<Configuration>>) -> Self {
let client = ApiClient::new(config.into());
AgentService {
client: Arc::new(client),
}
}
#[allow(dead_code)]
pub fn get_client(&self) -> Arc<ApiClient> {
Arc::clone(&self.client)
}
}
#[cfg(test)]
mod tests {
use super::*;
use agent_client::{
apis::{Api, nginx_agent_api::StatusSuccess},
models::StatusResp,
};
use axum::{http::StatusCode, response::Json};
use std::time::Duration;
use tempfile::tempdir;
use tokio::time::sleep;
#[test]
fn test_agent_service_creation() {
let config = Configuration::default();
let service = AgentService::new(config);
let client = service.get_client();
assert!(Arc::ptr_eq(&client, &service.client));
}
#[tokio::test]
async fn test_agent_socket_support() {
// create temporary socket path
let dir = tempdir().expect("Failed to create temp dir");
let socket_path = dir.path().join("agent.sock");
// create axum app with a simple /status route
let app = axum::Router::new().route(
"/status",
axum::routing::get(|| async {
let result: (StatusCode, StatusResp) = (StatusCode::OK, StatusResp { ok: true });
(result.0, Json(result.1))
}),
);
// bind tokio unix listener and serve in background
let listener =
tokio::net::UnixListener::bind(&socket_path).expect("Failed to bind to unix socket");
let server_fut = axum::serve::serve(listener, app);
let _srv = tokio::spawn(async move {
let _ = server_fut.await;
});
// give server a moment to start
sleep(Duration::from_millis(50)).await;
let client: ApiClient = ApiClient::new(Arc::new(Configuration {
base_path: "http://localhost".to_string(),
client: reqwest::Client::builder()
.unix_socket(socket_path.clone())
.build()
.expect("Failed to build reqwest client"),
..Default::default()
}));
let res = client
.nginx_agent_api()
.status()
.await
.expect("Failed to get status");
let body = res.entity.expect("Response entity is missing");
assert!(res.status.is_success());
if let StatusSuccess::Status200(body) = body {
assert!(body.ok);
} else {
panic!("Unexpected response body");
}
}
}

View File

@@ -197,14 +197,17 @@ mod tests {
let (token, _) = service let (token, _) = service
.generate_jwt(user_id, 60) .generate_jwt(user_id, 60)
.await .await
.expect("generate jwt"); .expect("Failed to generate jwt");
let valid = service let valid = service
.is_valid_jwt(&token, None) .is_valid_jwt(&token, None)
.await .await
.expect("validate jwt"); .expect("Failed to validate jwt");
assert!(valid.is_some(), "Generated token should be valid"); assert!(valid.is_some(), "Generated token should be valid");
let claims = service.parse_jwt(&token).await.expect("parse jwt"); let claims = service
.parse_jwt(&token)
.await
.expect("Failed to parse jwt");
assert_eq!(claims.sub, user_id.to_string()); assert_eq!(claims.sub, user_id.to_string());
} }
@@ -213,10 +216,16 @@ mod tests {
let service = AuthenticationServiceImpl::new(Some("secret".to_string())); let service = AuthenticationServiceImpl::new(Some("secret".to_string()));
let user_id = Uuid::new_v4(); let user_id = Uuid::new_v4();
let (token, _) = service.generate_jwt(user_id, 60).await.unwrap(); let (token, _) = service
.generate_jwt(user_id, 60)
.await
.expect("Failed to generate jwt");
let other_sub = Uuid::new_v4().to_string(); let other_sub = Uuid::new_v4().to_string();
let valid = service.is_valid_jwt(&token, Some(other_sub)).await.unwrap(); let valid = service
.is_valid_jwt(&token, Some(other_sub))
.await
.expect("jwt is not valid");
assert!( assert!(
valid.is_none(), valid.is_none(),
"Token should be invalid for a different subject" "Token should be invalid for a different subject"
@@ -236,10 +245,19 @@ mod tests {
let service = AuthenticationServiceImpl::new(Some("secret".to_string())); let service = AuthenticationServiceImpl::new(Some("secret".to_string()));
let user_id = Uuid::new_v4(); let user_id = Uuid::new_v4();
let (token, _) = service.generate_jwt(user_id, 60).await.unwrap(); let (token, _) = service
let new_token = service.refresh_jwt(&token, 120).await.unwrap(); .generate_jwt(user_id, 60)
.await
.expect("Failed to generate jwt");
let new_token = service
.refresh_jwt(&token, 120)
.await
.expect("Failed to refresh jwt");
let claims = service.parse_jwt(&new_token).await.unwrap(); let claims = service
.parse_jwt(&new_token)
.await
.expect("Failed to parse refreshed jwt");
assert_eq!(claims.sub, user_id.to_string()); assert_eq!(claims.sub, user_id.to_string());
assert_eq!(claims.exp - claims.iat, 120); assert_eq!(claims.exp - claims.iat, 120);
} }
@@ -249,10 +267,16 @@ mod tests {
let service = AuthenticationServiceImpl::new(Some("secret".to_string())); let service = AuthenticationServiceImpl::new(Some("secret".to_string()));
let user_id = Uuid::new_v4(); let user_id = Uuid::new_v4();
let (token, claims) = service.generate_jwt(user_id, 1).await.unwrap(); let (token, claims) = service
.generate_jwt(user_id, 1)
.await
.expect("Failed to generate jwt");
sleep(Duration::from_secs(2)).await; sleep(Duration::from_secs(2)).await;
let valid = service.is_valid_jwt(&token, None).await.unwrap(); let valid = service
.is_valid_jwt(&token, None)
.await
.expect("Failed to validate jwt");
assert!( assert!(
valid.is_none(), valid.is_none(),
"Token should be expired and thus invalid. Current time: {:?}. Diff: {}", "Token should be expired and thus invalid. Current time: {:?}. Diff: {}",
@@ -266,9 +290,15 @@ mod tests {
let service = AuthenticationServiceImpl::new(Some("secret".to_string())); let service = AuthenticationServiceImpl::new(Some("secret".to_string()));
let user_id = Uuid::new_v4(); let user_id = Uuid::new_v4();
let (token, _) = service.generate_jwt(user_id, 1).await.unwrap(); let (token, _) = service
.generate_jwt(user_id, 1)
.await
.expect("Failed to generate jwt");
service.invalidate_jwt(&token).await.unwrap(); service
.invalidate_jwt(&token)
.await
.expect("Failed to invalidate jwt");
// ensure entry is present // ensure entry is present
{ {

View File

@@ -236,7 +236,7 @@ mod test {
"CorrectPassword".as_bytes(), "CorrectPassword".as_bytes(),
&SaltString::generate(&mut OsRng), &SaltString::generate(&mut OsRng),
) )
.unwrap() .expect("Failed to hash password")
.to_string(); .to_string();
let db = MockDatabase::new(sea_orm::DatabaseBackend::Sqlite) let db = MockDatabase::new(sea_orm::DatabaseBackend::Sqlite)
.append_query_results(vec![vec![user::Model { .append_query_results(vec![vec![user::Model {
@@ -281,7 +281,7 @@ mod test {
"CorrectPassword".as_bytes(), "CorrectPassword".as_bytes(),
&SaltString::generate(&mut OsRng), &SaltString::generate(&mut OsRng),
) )
.unwrap() .expect("Failed to hash password")
.to_string(); .to_string();
let db = MockDatabase::new(sea_orm::DatabaseBackend::Sqlite) let db = MockDatabase::new(sea_orm::DatabaseBackend::Sqlite)
.append_query_results(vec![vec![user::Model { .append_query_results(vec![vec![user::Model {

File diff suppressed because it is too large Load Diff

View File

@@ -67,6 +67,12 @@ generate-agent-client:
--additional-properties=supportMultipleResponses=true \ --additional-properties=supportMultipleResponses=true \
--additional-properties=topLevelApiClient=true \ --additional-properties=topLevelApiClient=true \
--additional-properties=useSingleRequestParameter=true --additional-properties=useSingleRequestParameter=true
# format generated code
cd public/agent-client && \
cargo fmt
# append lint allows/forbids to the end of Cargo.toml to disable warnings in generated code and forbid unsafe code
cd public/agent-client && \
echo '\n[lints.clippy]\nall = "allow"\n[lints.rust]\nunsafe_code = "forbid"\n' >> Cargo.toml
generate-all: generate-entity generate-openapi generate-agent-client generate-all: generate-entity generate-openapi generate-agent-client

View File

@@ -1,5 +1,6 @@
{ {
"dependencies": { "devDependencies": {
"@openapitools/openapi-generator-cli": "^2.26.0" "@openapitools/openapi-generator-cli": "^2.26.0",
"rust-just": "^1.44.0"
} }
} }

6606
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,7 @@
packages:
- apps/frontend
onlyBuiltDependencies: onlyBuiltDependencies:
- '@nestjs/core' - '@nestjs/core'
- '@openapitools/openapi-generator-cli' - '@openapitools/openapi-generator-cli'
- esbuild

View File

@@ -19,3 +19,9 @@ default = ["native-tls"]
native-tls = ["reqwest/native-tls"] native-tls = ["reqwest/native-tls"]
rustls-tls = ["reqwest/rustls-tls"] rustls-tls = ["reqwest/rustls-tls"]
mockall = ["dep:mockall"] mockall = ["dep:mockall"]
[lints.clippy]
all = "allow"
[lints.rust]
unsafe_code = "forbid"

View File

@@ -8,8 +8,6 @@
* Generated by: https://openapi-generator.tech * Generated by: https://openapi-generator.tech
*/ */
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Configuration { pub struct Configuration {
pub base_path: String, pub base_path: String,
@@ -29,7 +27,6 @@ pub struct ApiKey {
pub key: String, pub key: String,
} }
impl Configuration { impl Configuration {
pub fn new() -> Configuration { pub fn new() -> Configuration {
Configuration::default() Configuration::default()

View File

@@ -16,7 +16,7 @@ pub enum Error<T> {
ResponseError(ResponseContent<T>), ResponseError(ResponseContent<T>),
} }
impl <T> fmt::Display for Error<T> { impl<T> fmt::Display for Error<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (module, e) = match self { let (module, e) = match self {
Error::Reqwest(e) => ("reqwest", e.to_string()), Error::Reqwest(e) => ("reqwest", e.to_string()),
@@ -28,7 +28,7 @@ impl <T> fmt::Display for Error<T> {
} }
} }
impl <T: fmt::Debug> error::Error for Error<T> { impl<T: fmt::Debug> error::Error for Error<T> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match self { Some(match self {
Error::Reqwest(e) => e, Error::Reqwest(e) => e,
@@ -39,19 +39,19 @@ impl <T: fmt::Debug> error::Error for Error<T> {
} }
} }
impl <T> From<reqwest::Error> for Error<T> { impl<T> From<reqwest::Error> for Error<T> {
fn from(e: reqwest::Error) -> Self { fn from(e: reqwest::Error) -> Self {
Error::Reqwest(e) Error::Reqwest(e)
} }
} }
impl <T> From<serde_json::Error> for Error<T> { impl<T> From<serde_json::Error> for Error<T> {
fn from(e: serde_json::Error) -> Self { fn from(e: serde_json::Error) -> Self {
Error::Serde(e) Error::Serde(e)
} }
} }
impl <T> From<std::io::Error> for Error<T> { impl<T> From<std::io::Error> for Error<T> {
fn from(e: std::io::Error) -> Self { fn from(e: std::io::Error) -> Self {
Error::Io(e) Error::Io(e)
} }
@@ -78,8 +78,10 @@ pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String
value, value,
)); ));
} }
}, }
serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), serde_json::Value::String(s) => {
params.push((format!("{}[{}]", prefix, key), s.clone()))
}
_ => params.push((format!("{}[{}]", prefix, key), value.to_string())), _ => params.push((format!("{}[{}]", prefix, key), value.to_string())),
} }
} }
@@ -96,7 +98,7 @@ pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String
enum ContentType { enum ContentType {
Json, Json,
Text, Text,
Unsupported(String) Unsupported(String),
} }
impl From<&str> for ContentType { impl From<&str> for ContentType {
@@ -128,7 +130,9 @@ pub struct ApiClient {
impl ApiClient { impl ApiClient {
pub fn new(configuration: Arc<configuration::Configuration>) -> Self { pub fn new(configuration: Arc<configuration::Configuration>) -> Self {
Self { Self {
nginx_agent_api: Box::new(nginx_agent_api::NginxAgentApiClient::new(configuration.clone())), nginx_agent_api: Box::new(nginx_agent_api::NginxAgentApiClient::new(
configuration.clone(),
)),
} }
} }
} }
@@ -159,4 +163,3 @@ impl Api for MockApiClient {
&self.nginx_agent_api_mock &self.nginx_agent_api_mock
} }
} }

View File

@@ -8,44 +8,51 @@
* Generated by: https://openapi-generator.tech * Generated by: https://openapi-generator.tech
*/ */
use super::{configuration, Error};
use crate::apis::ContentType;
use crate::{apis::ResponseContent, models};
use async_trait::async_trait; use async_trait::async_trait;
#[cfg(feature = "mockall")] #[cfg(feature = "mockall")]
use mockall::automock; use mockall::automock;
use reqwest; use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
use serde::{Deserialize, Serialize, de::Error as _};
use crate::{apis::ResponseContent, models};
use super::{Error, configuration};
use crate::apis::ContentType;
#[cfg_attr(feature = "mockall", automock)] #[cfg_attr(feature = "mockall", automock)]
#[async_trait] #[async_trait]
pub trait NginxAgentApi: Send + Sync { pub trait NginxAgentApi: Send + Sync {
/// GET /status /// GET /status
/// ///
/// ///
async fn status(&self, ) -> Result<ResponseContent<StatusSuccess>, Error<StatusError>>; async fn status(&self) -> Result<ResponseContent<StatusSuccess>, Error<StatusError>>;
/// POST /validate /// POST /validate
/// ///
/// ///
async fn validate(&self, params: ValidateParams ) -> Result<ResponseContent<ValidateSuccess>, Error<ValidateError>>; async fn validate(
&self,
params: ValidateParams,
) -> Result<ResponseContent<ValidateSuccess>, Error<ValidateError>>;
/// POST /validate_and_reload /// POST /validate_and_reload
/// ///
/// ///
async fn validate_and_reload(&self, params: ValidateAndReloadParams ) -> Result<ResponseContent<ValidateAndReloadSuccess>, Error<ValidateAndReloadError>>; async fn validate_and_reload(
&self,
params: ValidateAndReloadParams,
) -> Result<ResponseContent<ValidateAndReloadSuccess>, Error<ValidateAndReloadError>>;
/// POST /write_config /// POST /write_config
/// ///
/// ///
async fn write_config(&self, params: WriteConfigParams ) -> Result<ResponseContent<WriteConfigSuccess>, Error<WriteConfigError>>; async fn write_config(
&self,
params: WriteConfigParams,
) -> Result<ResponseContent<WriteConfigSuccess>, Error<WriteConfigError>>;
} }
pub struct NginxAgentApiClient { pub struct NginxAgentApiClient {
configuration: Arc<configuration::Configuration> configuration: Arc<configuration::Configuration>,
} }
impl NginxAgentApiClient { impl NginxAgentApiClient {
@@ -54,40 +61,38 @@ impl NginxAgentApiClient {
} }
} }
/// struct for passing parameters to the method [`NginxAgentApi::validate`] /// struct for passing parameters to the method [`NginxAgentApi::validate`]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ValidateParams { pub struct ValidateParams {
pub validate_body: models::ValidateBody pub validate_body: models::ValidateBody,
} }
/// struct for passing parameters to the method [`NginxAgentApi::validate_and_reload`] /// struct for passing parameters to the method [`NginxAgentApi::validate_and_reload`]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ValidateAndReloadParams { pub struct ValidateAndReloadParams {
pub validate_and_reload_body: models::ValidateAndReloadBody pub validate_and_reload_body: models::ValidateAndReloadBody,
} }
/// struct for passing parameters to the method [`NginxAgentApi::write_config`] /// struct for passing parameters to the method [`NginxAgentApi::write_config`]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct WriteConfigParams { pub struct WriteConfigParams {
pub write_config_body: models::WriteConfigBody pub write_config_body: models::WriteConfigBody,
} }
#[async_trait] #[async_trait]
impl NginxAgentApi for NginxAgentApiClient { impl NginxAgentApi for NginxAgentApiClient {
async fn status(&self, ) -> Result<ResponseContent<StatusSuccess>, Error<StatusError>> { async fn status(&self) -> Result<ResponseContent<StatusSuccess>, Error<StatusError>> {
let local_var_configuration = &self.configuration; let local_var_configuration = &self.configuration;
let local_var_client = &local_var_configuration.client; let local_var_client = &local_var_configuration.client;
let local_var_uri_str = format!("{}/status", local_var_configuration.base_path); let local_var_uri_str = format!("{}/status", local_var_configuration.base_path);
let mut local_var_req_builder = local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); let mut local_var_req_builder =
local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str());
if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { if let Some(ref local_var_user_agent) = local_var_configuration.user_agent {
local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); local_var_req_builder = local_var_req_builder
.header(reqwest::header::USER_AGENT, local_var_user_agent.clone());
} }
let local_var_req = local_var_req_builder.build()?; let local_var_req = local_var_req_builder.build()?;
@@ -97,32 +102,43 @@ impl NginxAgentApi for NginxAgentApiClient {
let local_var_content = local_var_resp.text().await?; let local_var_content = local_var_resp.text().await?;
if !local_var_status.is_client_error() && !local_var_status.is_server_error() { if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
let local_var_entity: Option<StatusSuccess> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<StatusSuccess> =
let local_var_result = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_result = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Ok(local_var_result) Ok(local_var_result)
} else { } else {
let local_var_entity: Option<StatusError> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<StatusError> =
let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_error = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Err(Error::ResponseError(local_var_error)) Err(Error::ResponseError(local_var_error))
} }
} }
async fn validate(&self, params: ValidateParams ) -> Result<ResponseContent<ValidateSuccess>, Error<ValidateError>> { async fn validate(
&self,
let ValidateParams { params: ValidateParams,
validate_body, ) -> Result<ResponseContent<ValidateSuccess>, Error<ValidateError>> {
} = params; let ValidateParams { validate_body } = params;
let local_var_configuration = &self.configuration; let local_var_configuration = &self.configuration;
let local_var_client = &local_var_configuration.client; let local_var_client = &local_var_configuration.client;
let local_var_uri_str = format!("{}/validate", local_var_configuration.base_path); let local_var_uri_str = format!("{}/validate", local_var_configuration.base_path);
let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); let mut local_var_req_builder =
local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str());
if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { if let Some(ref local_var_user_agent) = local_var_configuration.user_agent {
local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); local_var_req_builder = local_var_req_builder
.header(reqwest::header::USER_AGENT, local_var_user_agent.clone());
} }
local_var_req_builder = local_var_req_builder.json(&validate_body); local_var_req_builder = local_var_req_builder.json(&validate_body);
@@ -133,32 +149,46 @@ impl NginxAgentApi for NginxAgentApiClient {
let local_var_content = local_var_resp.text().await?; let local_var_content = local_var_resp.text().await?;
if !local_var_status.is_client_error() && !local_var_status.is_server_error() { if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
let local_var_entity: Option<ValidateSuccess> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<ValidateSuccess> =
let local_var_result = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_result = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Ok(local_var_result) Ok(local_var_result)
} else { } else {
let local_var_entity: Option<ValidateError> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<ValidateError> =
let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_error = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Err(Error::ResponseError(local_var_error)) Err(Error::ResponseError(local_var_error))
} }
} }
async fn validate_and_reload(&self, params: ValidateAndReloadParams ) -> Result<ResponseContent<ValidateAndReloadSuccess>, Error<ValidateAndReloadError>> { async fn validate_and_reload(
&self,
params: ValidateAndReloadParams,
) -> Result<ResponseContent<ValidateAndReloadSuccess>, Error<ValidateAndReloadError>> {
let ValidateAndReloadParams { let ValidateAndReloadParams {
validate_and_reload_body, validate_and_reload_body,
} = params; } = params;
let local_var_configuration = &self.configuration; let local_var_configuration = &self.configuration;
let local_var_client = &local_var_configuration.client; let local_var_client = &local_var_configuration.client;
let local_var_uri_str = format!("{}/validate_and_reload", local_var_configuration.base_path); let local_var_uri_str =
let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); format!("{}/validate_and_reload", local_var_configuration.base_path);
let mut local_var_req_builder =
local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str());
if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { if let Some(ref local_var_user_agent) = local_var_configuration.user_agent {
local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); local_var_req_builder = local_var_req_builder
.header(reqwest::header::USER_AGENT, local_var_user_agent.clone());
} }
local_var_req_builder = local_var_req_builder.json(&validate_and_reload_body); local_var_req_builder = local_var_req_builder.json(&validate_and_reload_body);
@@ -169,32 +199,43 @@ impl NginxAgentApi for NginxAgentApiClient {
let local_var_content = local_var_resp.text().await?; let local_var_content = local_var_resp.text().await?;
if !local_var_status.is_client_error() && !local_var_status.is_server_error() { if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
let local_var_entity: Option<ValidateAndReloadSuccess> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<ValidateAndReloadSuccess> =
let local_var_result = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_result = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Ok(local_var_result) Ok(local_var_result)
} else { } else {
let local_var_entity: Option<ValidateAndReloadError> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<ValidateAndReloadError> =
let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_error = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Err(Error::ResponseError(local_var_error)) Err(Error::ResponseError(local_var_error))
} }
} }
async fn write_config(&self, params: WriteConfigParams ) -> Result<ResponseContent<WriteConfigSuccess>, Error<WriteConfigError>> { async fn write_config(
&self,
let WriteConfigParams { params: WriteConfigParams,
write_config_body, ) -> Result<ResponseContent<WriteConfigSuccess>, Error<WriteConfigError>> {
} = params; let WriteConfigParams { write_config_body } = params;
let local_var_configuration = &self.configuration; let local_var_configuration = &self.configuration;
let local_var_client = &local_var_configuration.client; let local_var_client = &local_var_configuration.client;
let local_var_uri_str = format!("{}/write_config", local_var_configuration.base_path); let local_var_uri_str = format!("{}/write_config", local_var_configuration.base_path);
let mut local_var_req_builder = local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str()); let mut local_var_req_builder =
local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str());
if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { if let Some(ref local_var_user_agent) = local_var_configuration.user_agent {
local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); local_var_req_builder = local_var_req_builder
.header(reqwest::header::USER_AGENT, local_var_user_agent.clone());
} }
local_var_req_builder = local_var_req_builder.json(&write_config_body); local_var_req_builder = local_var_req_builder.json(&write_config_body);
@@ -205,16 +246,25 @@ impl NginxAgentApi for NginxAgentApiClient {
let local_var_content = local_var_resp.text().await?; let local_var_content = local_var_resp.text().await?;
if !local_var_status.is_client_error() && !local_var_status.is_server_error() { if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
let local_var_entity: Option<WriteConfigSuccess> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<WriteConfigSuccess> =
let local_var_result = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_result = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Ok(local_var_result) Ok(local_var_result)
} else { } else {
let local_var_entity: Option<WriteConfigError> = serde_json::from_str(&local_var_content).ok(); let local_var_entity: Option<WriteConfigError> =
let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; serde_json::from_str(&local_var_content).ok();
let local_var_error = ResponseContent {
status: local_var_status,
content: local_var_content,
entity: local_var_entity,
};
Err(Error::ResponseError(local_var_error)) Err(Error::ResponseError(local_var_error))
} }
} }
} }
/// struct for typed successes of method [`NginxAgentApi::status`] /// struct for typed successes of method [`NginxAgentApi::status`]
@@ -277,4 +327,3 @@ pub enum WriteConfigError {
Status500(serde_json::Value), Status500(serde_json::Value),
UnknownValue(serde_json::Value), UnknownValue(serde_json::Value),
} }

View File

@@ -1,9 +1,9 @@
#![allow(unused_imports)] #![allow(unused_imports)]
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
extern crate serde_repr;
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate serde_repr;
extern crate url; extern crate url;
pub mod apis; pub mod apis;

View File

@@ -19,9 +19,6 @@ pub struct StatusResp {
impl StatusResp { impl StatusResp {
pub fn new(ok: bool) -> StatusResp { pub fn new(ok: bool) -> StatusResp {
StatusResp { StatusResp { ok }
ok,
}
} }
} }

View File

@@ -27,4 +27,3 @@ impl ValidateAndReloadBody {
} }
} }
} }

View File

@@ -21,10 +21,6 @@ pub struct ValidateAndReloadResp {
impl ValidateAndReloadResp { impl ValidateAndReloadResp {
pub fn new(rc: i32, ro: String) -> ValidateAndReloadResp { pub fn new(rc: i32, ro: String) -> ValidateAndReloadResp {
ValidateAndReloadResp { ValidateAndReloadResp { rc, ro }
rc,
ro,
}
} }
} }

View File

@@ -27,4 +27,3 @@ impl ValidateBody {
} }
} }
} }

View File

@@ -30,4 +30,3 @@ impl WriteConfigBody {
} }
} }
} }