From e0cacee57504fc2263f342732cf28fa81a3ada6e Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Mon, 23 Sep 2024 23:01:44 +0300 Subject: [PATCH 1/9] init: agent_api --- .gitignore | 3 +- .json | 16 +++++++++ src/agent_api/dto/mod.rs | 0 src/agent_api/mod.rs | 2 ++ src/agent_api/requests/create_client.rs | 46 +++++++++++++++++++++++++ src/agent_api/requests/mod.rs | 3 ++ src/main.rs | 1 + src/state.rs | 9 +++++ test.py | 2 ++ 9 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 .json create mode 100644 src/agent_api/dto/mod.rs create mode 100644 src/agent_api/mod.rs create mode 100644 src/agent_api/requests/create_client.rs create mode 100644 src/agent_api/requests/mod.rs create mode 100644 test.py diff --git a/.gitignore b/.gitignore index 9e897c5..1a75808 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target .env .env.production -.env.test \ No newline at end of file +.env.test +.idea \ No newline at end of file diff --git a/.json b/.json new file mode 100644 index 0000000..a47e35c --- /dev/null +++ b/.json @@ -0,0 +1,16 @@ +{ + "id": "inbound_id", + "settings": { + "clients": [ + { + "id": "Uuid::new_v4()", + "flow": "", + "email": "a", + "limitIp": 0, + "totalGB": 0, + "expiryTime": 0, + "enable": true + } + ] + } +} diff --git a/src/agent_api/dto/mod.rs b/src/agent_api/dto/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/agent_api/mod.rs b/src/agent_api/mod.rs new file mode 100644 index 0000000..92bd3c1 --- /dev/null +++ b/src/agent_api/mod.rs @@ -0,0 +1,2 @@ +mod dto; +mod requests; diff --git a/src/agent_api/requests/create_client.rs b/src/agent_api/requests/create_client.rs new file mode 100644 index 0000000..d3b0837 --- /dev/null +++ b/src/agent_api/requests/create_client.rs @@ -0,0 +1,46 @@ +use serde_json::json; +use uuid::Uuid; + +struct Client { + inbound_id: u32, + link: String, +} +enum Protocol { + VLESS, + Shadowsocks, +} + +pub async fn create_client( + client: reqwest::Client, + protocol: Protocol, + url: &str, + inbound_id: u32, + device_id: &Uuid, +) { + match protocol { + Protocol::VLESS => { + let res = client + .post(format!("http://{url}/panel/api/inbounds/addClient")) + .json(&json!({ + "id": inbound_id, + "settings": { + "clients": [ + { + "id": Uuid::new_v4(), + "flow": "", + "email": device_id, + "limitIp": 0, + "totalGB": 0, + "expiryTime": 0, + "enable": true + } + ] + } + } + )) + .send() + .await; + } + Protocol::Shadowsocks => {} + } +} diff --git a/src/agent_api/requests/mod.rs b/src/agent_api/requests/mod.rs new file mode 100644 index 0000000..8a2f750 --- /dev/null +++ b/src/agent_api/requests/mod.rs @@ -0,0 +1,3 @@ +mod create_client; + +pub use create_client::create_client; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 110eae1..6ff0e2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ extern crate openssl; #[macro_use] extern crate diesel; +mod agent_api; mod config; mod data; mod dto; diff --git a/src/state.rs b/src/state.rs index 971d945..5296793 100644 --- a/src/state.rs +++ b/src/state.rs @@ -69,6 +69,7 @@ pub struct AppState { pub pool: Pool, pub oauth_providers: OAuthProviders, pub resend: Resend, + pub reqwest_client: reqwest::Client, } impl AppState { @@ -76,10 +77,12 @@ impl AppState { let manager = Manager::new(&ENV.database_url, deadpool_diesel::Runtime::Tokio1); let pool = Pool::builder(manager).build().unwrap(); let resend = Resend::new(&ENV.resend_api_key); + let reqwest_client = reqwest::Client::new(); Self { pool, resend, oauth_providers: OAuthProviders::default(), + reqwest_client, } } } @@ -101,3 +104,9 @@ impl FromRef for Resend { state.resend.clone() } } + +impl FromRef for reqwest::Client { + fn from_ref(state: &AppState) -> Self { + state.reqwest_client.clone() + } +} diff --git a/test.py b/test.py new file mode 100644 index 0000000..2a1bad3 --- /dev/null +++ b/test.py @@ -0,0 +1,2 @@ +import base64 + From 4746e5bd13cb9401f2d885443731e6bdd85facdb Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Fri, 27 Sep 2024 20:47:53 +0300 Subject: [PATCH 2/9] feature: basic requests --- Cargo.lock | 13 +-- Cargo.toml | 5 +- src/agent_api/dto/agent_response.rs | 9 ++ src/agent_api/dto/client.rs | 8 ++ src/agent_api/dto/mod.rs | 5 + src/agent_api/enums/mod.rs | 3 + src/agent_api/enums/protocol.rs | 5 + src/agent_api/mod.rs | 9 +- src/agent_api/requests/create_client.rs | 100 ++++++++++++------ src/agent_api/requests/delete_client.rs | 40 +++++++ src/agent_api/requests/get_client_stats.rs | 39 +++++++ src/agent_api/requests/login.rs | 34 ++++++ src/agent_api/requests/mod.rs | 8 +- src/agent_api/state.rs | 74 +++++++++++++ src/agent_api/utils/client_json/mod.rs | 5 + .../utils/client_json/shadowsocks.rs | 13 +++ src/agent_api/utils/client_json/vless.rs | 14 +++ src/agent_api/utils/link/mod.rs | 5 + src/agent_api/utils/link/shadowsocks.rs | 18 ++++ src/agent_api/utils/link/vless.rs | 8 ++ src/agent_api/utils/mod.rs | 3 + src/agent_api/utils/password_generator/mod.rs | 3 + .../utils/password_generator/shadowsocks.rs | 7 ++ src/data/enums/country.rs | 2 +- src/enums/errors/internal/agent_api.rs | 14 +++ src/enums/errors/internal/mod.rs | 4 + src/main.rs | 2 +- src/state.rs | 6 +- test.py | 2 - 29 files changed, 405 insertions(+), 53 deletions(-) create mode 100644 src/agent_api/dto/agent_response.rs create mode 100644 src/agent_api/dto/client.rs create mode 100644 src/agent_api/enums/mod.rs create mode 100644 src/agent_api/enums/protocol.rs create mode 100644 src/agent_api/requests/delete_client.rs create mode 100644 src/agent_api/requests/get_client_stats.rs create mode 100644 src/agent_api/requests/login.rs create mode 100644 src/agent_api/state.rs create mode 100644 src/agent_api/utils/client_json/mod.rs create mode 100644 src/agent_api/utils/client_json/shadowsocks.rs create mode 100644 src/agent_api/utils/client_json/vless.rs create mode 100644 src/agent_api/utils/link/mod.rs create mode 100644 src/agent_api/utils/link/shadowsocks.rs create mode 100644 src/agent_api/utils/link/vless.rs create mode 100644 src/agent_api/utils/mod.rs create mode 100644 src/agent_api/utils/password_generator/mod.rs create mode 100644 src/agent_api/utils/password_generator/shadowsocks.rs create mode 100644 src/enums/errors/internal/agent_api.rs delete mode 100644 test.py diff --git a/Cargo.lock b/Cargo.lock index 25df2d1..b018fd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,6 +906,7 @@ dependencies = [ "oauth2", "once_cell", "openssl", + "rand", "rand_core", "reqwest 0.12.7", "resend-rs", @@ -2380,9 +2381,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -2489,12 +2490,12 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" dependencies = [ "async-compression", - "base64 0.21.7", + "base64 0.22.1", "bitflags 2.6.0", "bytes", "futures-core", @@ -2511,7 +2512,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower 0.4.13", + "tower 0.5.0", "tower-layer", "tower-service", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 2c9266b..155c535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,14 +26,15 @@ mime = "0.3" oauth2 = "4.1" once_cell = "1.19.0" openssl = "*" +rand = "0.8" rand_core = { version = "0.6.3", features = ["std"] } reqwest = { version = "0.12", features = ["json"] } resend-rs = "0.9" serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = { version = "1.39", features = ["full"] } +tokio = { version = "1.40", features = ["full"] } tower = { version = "0.5", features = ["full"] } -tower-http = { version = "0.5.0", features = ["full"] } +tower-http = { version = "0.6", features = ["full"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } uuid = { version = "1.10", features = [ diff --git a/src/agent_api/dto/agent_response.rs b/src/agent_api/dto/agent_response.rs new file mode 100644 index 0000000..3c374e4 --- /dev/null +++ b/src/agent_api/dto/agent_response.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Debug, Serialize, Deserialize)] +pub struct AgentResponse { + pub success: bool, + pub msg: String, + pub obj: Option, +} diff --git a/src/agent_api/dto/client.rs b/src/agent_api/dto/client.rs new file mode 100644 index 0000000..eec08f8 --- /dev/null +++ b/src/agent_api/dto/client.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + + +#[derive(Debug, Serialize, Deserialize)] +pub struct Client { + pub inbound_id: u32, + pub link: String, +} diff --git a/src/agent_api/dto/mod.rs b/src/agent_api/dto/mod.rs index e69de29..eaf46e9 100644 --- a/src/agent_api/dto/mod.rs +++ b/src/agent_api/dto/mod.rs @@ -0,0 +1,5 @@ +mod agent_response; +mod client; + +pub use agent_response::AgentResponse; +pub use client::Client; diff --git a/src/agent_api/enums/mod.rs b/src/agent_api/enums/mod.rs new file mode 100644 index 0000000..8ad2ffc --- /dev/null +++ b/src/agent_api/enums/mod.rs @@ -0,0 +1,3 @@ +mod protocol; + +pub use protocol::Protocol; \ No newline at end of file diff --git a/src/agent_api/enums/protocol.rs b/src/agent_api/enums/protocol.rs new file mode 100644 index 0000000..f9c49e3 --- /dev/null +++ b/src/agent_api/enums/protocol.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Copy)] +pub enum Protocol { + Vless, + Shadowsocks, +} diff --git a/src/agent_api/mod.rs b/src/agent_api/mod.rs index 92bd3c1..d60fc0d 100644 --- a/src/agent_api/mod.rs +++ b/src/agent_api/mod.rs @@ -1,2 +1,7 @@ -mod dto; -mod requests; +pub mod dto; +pub mod enums; +pub mod requests; +mod state; +pub mod utils; + +pub use state::AgentState; diff --git a/src/agent_api/requests/create_client.rs b/src/agent_api/requests/create_client.rs index d3b0837..495c460 100644 --- a/src/agent_api/requests/create_client.rs +++ b/src/agent_api/requests/create_client.rs @@ -1,46 +1,76 @@ use serde_json::json; use uuid::Uuid; -struct Client { - inbound_id: u32, - link: String, -} -enum Protocol { - VLESS, - Shadowsocks, -} +use crate::{ + agent_api::{ + dto::{AgentResponse, Client}, + enums::Protocol, + utils, AgentState, + }, + data::enums::Country, + enums::errors::internal::{AgentAPI, Error, Result}, +}; pub async fn create_client( - client: reqwest::Client, + agent_state: AgentState, + country: Country, + host: &str, + port: u16, protocol: Protocol, - url: &str, inbound_id: u32, device_id: &Uuid, -) { - match protocol { - Protocol::VLESS => { - let res = client - .post(format!("http://{url}/panel/api/inbounds/addClient")) - .json(&json!({ - "id": inbound_id, - "settings": { - "clients": [ - { - "id": Uuid::new_v4(), - "flow": "", - "email": device_id, - "limitIp": 0, - "totalGB": 0, - "expiryTime": 0, - "enable": true - } - ] - } - } - )) - .send() - .await; +) -> Result { + let cookie = agent_state.get_or_refresh_cookie(&country).await?; + let agent = agent_state + .agents + .get(&country) + .ok_or(Error::AgentAPI(AgentAPI::Internal))?; + let client = agent_state.client; + + let (client_body, link) = match protocol { + Protocol::Vless => { + let client_id = Uuid::new_v4(); + let client_body = utils::client_json::vless(&client_id, device_id); + let link = utils::link::vless(&client_id, host, agent.vless_config.port, device_id); + + (client_body, link) } - Protocol::Shadowsocks => {} + Protocol::Shadowsocks => { + let password = utils::password_generator::shadowsocks(); + let client_body = utils::client_json::shadowsocks(&password, device_id); + let link = utils::link::shadowsocks( + &agent.password, + &password, + host, + agent.shadowsocks_config.port, + device_id, + ); + + (client_body, link) + } + }; + + let res = client + .post(format!( + "http://{}:{}/{}/panel/api/inbounds/addClient", + host, port, agent.secure_path + )) + .header("Cookie", cookie) + .json(&json!({ + "id": inbound_id, + "settings": { + "clients": [client_body] + } + } + )) + .send() + .await? + .json::() + .await?; + + if !res.success { + return Err(Error::AgentAPI(AgentAPI::Internal)); } + + Ok(Client { inbound_id, link }) } diff --git a/src/agent_api/requests/delete_client.rs b/src/agent_api/requests/delete_client.rs new file mode 100644 index 0000000..eda6acf --- /dev/null +++ b/src/agent_api/requests/delete_client.rs @@ -0,0 +1,40 @@ +use uuid::Uuid; + +use crate::{ + agent_api::{dto::AgentResponse, AgentState}, + data::enums::Country, + enums::errors::internal::{AgentAPI, Error, Result}, +}; + +pub async fn delete_client( + agent_state: AgentState, + country: Country, + host: &str, + port: u16, + inbound_id: u32, + device_id: &Uuid, +) -> Result<()> { + let cookie = agent_state.get_or_refresh_cookie(&country).await?; + let agent = agent_state + .agents + .get(&country) + .ok_or(Error::AgentAPI(AgentAPI::Internal))?; + let client = agent_state.client; + + let res = client + .post(format!( + "http://{}:{}/{}/panel/api/inbounds/{}/delClient/{}", + host, port, agent.secure_path, inbound_id, device_id + )) + .header("Cookie", cookie) + .send() + .await? + .json::() + .await?; + + if !res.success { + return Err(Error::AgentAPI(AgentAPI::Internal)); + } + + Ok(()) +} diff --git a/src/agent_api/requests/get_client_stats.rs b/src/agent_api/requests/get_client_stats.rs new file mode 100644 index 0000000..8395d30 --- /dev/null +++ b/src/agent_api/requests/get_client_stats.rs @@ -0,0 +1,39 @@ +use uuid::Uuid; + +use crate::{ + agent_api::{dto::AgentResponse, AgentState}, + data::enums::Country, + enums::errors::internal::{AgentAPI, Error, Result}, +}; + +pub async fn get_client_stats( + agent_state: AgentState, + country: Country, + host: &str, + port: u16, + device_id: &Uuid, +) -> Result<()> { + let cookie = agent_state.get_or_refresh_cookie(&country).await?; + let agent = agent_state + .agents + .get(&country) + .ok_or(Error::AgentAPI(AgentAPI::Internal))?; + let client = agent_state.client; + + let res = client + .get(format!( + "http://{}:{}/{}/panel/api/inbounds/getClientTaffics/{}", + host, port, agent.secure_path, device_id + )) + .header("Cookie", cookie) + .send() + .await? + .json::() + .await?; + + if !res.success { + return Err(Error::AgentAPI(AgentAPI::Internal)); + } + + Ok(()) +} diff --git a/src/agent_api/requests/login.rs b/src/agent_api/requests/login.rs new file mode 100644 index 0000000..b4c5d53 --- /dev/null +++ b/src/agent_api/requests/login.rs @@ -0,0 +1,34 @@ +use std::collections::HashMap; + +use reqwest::header::SET_COOKIE; +use tracing::error; + +use crate::{ + agent_api::dto::AgentResponse, + enums::errors::internal::{AgentAPI, Error, Result}, +}; + +pub async fn login( + client: &reqwest::Client, + url: &str, + username: &str, + password: &str, +) -> Result { + let params = [("username", username), ("password", password)]; + + let res = client + .post(format!("http://{url}/login")) + .form(¶ms) + .send() + .await?; + + let cookies = res.headers().get_all(SET_COOKIE).iter().last().cloned(); + let body = res.json::().await?; + + if !body.success || cookies.is_none() { + error!("Login failed: {}", body.msg); + return Err(Error::AgentAPI(AgentAPI::Internal)); + } + + Ok(cookies.unwrap().to_str().unwrap().to_string()) +} diff --git a/src/agent_api/requests/mod.rs b/src/agent_api/requests/mod.rs index 8a2f750..58b5d2d 100644 --- a/src/agent_api/requests/mod.rs +++ b/src/agent_api/requests/mod.rs @@ -1,3 +1,9 @@ mod create_client; +mod delete_client; +mod get_client_stats; +mod login; -pub use create_client::create_client; \ No newline at end of file +pub use create_client::create_client; +pub use delete_client::delete_client; +pub use get_client_stats::get_client_stats; +pub use login::login; diff --git a/src/agent_api/state.rs b/src/agent_api/state.rs new file mode 100644 index 0000000..1b5bde5 --- /dev/null +++ b/src/agent_api/state.rs @@ -0,0 +1,74 @@ +use std::collections::HashMap; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use tokio::sync::RwLock; + +use crate::{data::enums::Country, enums::errors::internal::Result}; + +use super::requests::login; + +#[derive(Clone)] +pub struct CookieInfo { + pub cookie: String, + pub expires_at: Instant, +} + +#[derive(Clone)] +pub struct ShadowsocksConfig { + pub port: u16, + pub method: String, + pub password: String, +} + +#[derive(Clone)] +pub struct VlessConfig { + pub port: u16, +} + +#[derive(Clone)] +pub struct Agent { + pub host: String, + pub secure_path: String, + pub username: String, + pub password: String, + pub shadowsocks_config: ShadowsocksConfig, + pub vless_config: VlessConfig, + pub cookie: Arc>, +} + +#[derive(Clone)] +pub struct AgentState { + pub client: reqwest::Client, + pub agents: HashMap, +} + +impl AgentState { + pub fn new(servers: HashMap) -> Self { + AgentState { + agents: servers, + client: reqwest::Client::new(), + } + } + + pub async fn get_or_refresh_cookie(&self, country: &Country) -> Result { + let agent = self.agents.get(country).unwrap(); + let cookie = agent.cookie.read().await; + + if cookie.expires_at < Instant::now() { + let new_cookie = login( + &self.client, + &agent.secure_path, + &agent.username, + &agent.password, + ) + .await?; + + let mut cookie = agent.cookie.write().await; + cookie.cookie = new_cookie; + cookie.expires_at = Instant::now() + Duration::from_secs(60 * 59); + } + + Ok(cookie.cookie.clone()) + } +} diff --git a/src/agent_api/utils/client_json/mod.rs b/src/agent_api/utils/client_json/mod.rs new file mode 100644 index 0000000..c53499c --- /dev/null +++ b/src/agent_api/utils/client_json/mod.rs @@ -0,0 +1,5 @@ +mod shadowsocks; +mod vless; + +pub use shadowsocks::shadowsocks; +pub use vless::vless; diff --git a/src/agent_api/utils/client_json/shadowsocks.rs b/src/agent_api/utils/client_json/shadowsocks.rs new file mode 100644 index 0000000..c243928 --- /dev/null +++ b/src/agent_api/utils/client_json/shadowsocks.rs @@ -0,0 +1,13 @@ +use serde_json::json; +use uuid::Uuid; + +pub fn shadowsocks(password: &str, device_id: &Uuid) -> serde_json::Value { + json!({ + "email": device_id, + "password": password, + "limitIp": 0, + "totalGB": 0, + "expiryTime": 0, + "enable": true + }) +} diff --git a/src/agent_api/utils/client_json/vless.rs b/src/agent_api/utils/client_json/vless.rs new file mode 100644 index 0000000..b8dd057 --- /dev/null +++ b/src/agent_api/utils/client_json/vless.rs @@ -0,0 +1,14 @@ +use serde_json::json; +use uuid::Uuid; + +pub fn vless(client_id: &Uuid, device_id: &Uuid) -> serde_json::Value { + json!({ + "id": client_id, + "flow": "", + "email": device_id, + "limitIp": 0, + "totalGB": 0, + "expiryTime": 0, + "enable": true + }) +} diff --git a/src/agent_api/utils/link/mod.rs b/src/agent_api/utils/link/mod.rs new file mode 100644 index 0000000..3032b84 --- /dev/null +++ b/src/agent_api/utils/link/mod.rs @@ -0,0 +1,5 @@ +mod shadowsocks; +mod vless; + +pub use shadowsocks::shadowsocks; +pub use vless::vless; \ No newline at end of file diff --git a/src/agent_api/utils/link/shadowsocks.rs b/src/agent_api/utils/link/shadowsocks.rs new file mode 100644 index 0000000..d4ca3e9 --- /dev/null +++ b/src/agent_api/utils/link/shadowsocks.rs @@ -0,0 +1,18 @@ +use uuid::Uuid; + +pub fn shadowsocks( + inbound_password: &str, + client_password: &str, + host: &str, + port: u16, + device_id: &Uuid, +) -> String { + let hash = openssl::base64::encode_block( + format!( + "2022-blake3-aes-256-gcm:{}:{}", + inbound_password, client_password + ) + .as_bytes(), + ); + format!("ss://{}@{}:{}?type=tcp#{}", hash, host, port, device_id) +} diff --git a/src/agent_api/utils/link/vless.rs b/src/agent_api/utils/link/vless.rs new file mode 100644 index 0000000..7675a1a --- /dev/null +++ b/src/agent_api/utils/link/vless.rs @@ -0,0 +1,8 @@ +use uuid::Uuid; + +pub fn vless(client_id: &Uuid, host: &str, port: u16, device_id: &Uuid) -> String { + format!( + "vless://{}@{}:{}?type=tcp&security=none#{}", + client_id, host, port, device_id + ) +} diff --git a/src/agent_api/utils/mod.rs b/src/agent_api/utils/mod.rs new file mode 100644 index 0000000..c51f754 --- /dev/null +++ b/src/agent_api/utils/mod.rs @@ -0,0 +1,3 @@ +pub mod client_json; +pub mod password_generator; +pub mod link; \ No newline at end of file diff --git a/src/agent_api/utils/password_generator/mod.rs b/src/agent_api/utils/password_generator/mod.rs new file mode 100644 index 0000000..a036a37 --- /dev/null +++ b/src/agent_api/utils/password_generator/mod.rs @@ -0,0 +1,3 @@ +mod shadowsocks; + +pub use shadowsocks::shadowsocks; \ No newline at end of file diff --git a/src/agent_api/utils/password_generator/shadowsocks.rs b/src/agent_api/utils/password_generator/shadowsocks.rs new file mode 100644 index 0000000..f2ff1a8 --- /dev/null +++ b/src/agent_api/utils/password_generator/shadowsocks.rs @@ -0,0 +1,7 @@ +use rand::Rng; + +pub fn shadowsocks() -> String { + let mut rng = rand::thread_rng(); + let random_numbers: Vec = (0..32).map(|_| rng.gen_range(0..=255)).collect(); + openssl::base64::encode_block(&random_numbers) +} diff --git a/src/data/enums/country.rs b/src/data/enums/country.rs index 0e96127..2966fbf 100644 --- a/src/data/enums/country.rs +++ b/src/data/enums/country.rs @@ -8,7 +8,7 @@ use diesel::{ }; use serde::{Deserialize, Serialize}; -#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)] #[diesel(sql_type = crate::data::schema::sql_types::Country)] #[allow(clippy::upper_case_acronyms)] pub enum Country { diff --git a/src/enums/errors/internal/agent_api.rs b/src/enums/errors/internal/agent_api.rs new file mode 100644 index 0000000..1b1431e --- /dev/null +++ b/src/enums/errors/internal/agent_api.rs @@ -0,0 +1,14 @@ +#[derive(Debug, Clone)] +pub enum AgentAPI { + Internal, + LoginFailed, +} + +impl std::fmt::Display for AgentAPI { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AgentAPI::LoginFailed => write!(f, "Login failed"), + AgentAPI::Internal => write!(f, "Internal error"), + } + } +} \ No newline at end of file diff --git a/src/enums/errors/internal/mod.rs b/src/enums/errors/internal/mod.rs index e5d71ad..8a81e51 100644 --- a/src/enums/errors/internal/mod.rs +++ b/src/enums/errors/internal/mod.rs @@ -1,3 +1,4 @@ +mod agent_api; mod auth; mod database; mod device; @@ -7,6 +8,7 @@ mod resend; mod session; mod token; +pub use agent_api::AgentAPI; pub use auth::Auth; pub use database::Database; pub use device::Device; @@ -26,6 +28,7 @@ pub enum Error { Reqwest(Reqwest), Resend(Resend), Device(Device), + AgentAPI(AgentAPI), } impl std::fmt::Display for Error { @@ -39,6 +42,7 @@ impl std::fmt::Display for Error { Error::Reqwest(e) => write!(f, "{}", e), Error::Resend(e) => write!(f, "{}", e), Error::Device(e) => write!(f, "{}", e), + Error::AgentAPI(e) => write!(f, "{}", e), } } } diff --git a/src/main.rs b/src/main.rs index 6ff0e2b..4f9b542 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ mod tests; mod utils; async fn start_main_server() { - let state = AppState::default(); + let state = AppState::new(); let app = app_router(state.clone()).with_state(state); let listener = TcpListener::bind(&ENV.master_backend_url).await.unwrap(); diff --git a/src/state.rs b/src/state.rs index 5296793..6741958 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,7 +13,7 @@ pub struct OAuthProviders { } impl OAuthProviders { - pub fn default() -> Self { + pub fn new() -> Self { Self { discord: Self::client( &ENV.discord_client_id, @@ -73,7 +73,7 @@ pub struct AppState { } impl AppState { - pub fn default() -> Self { + pub fn new() -> Self { let manager = Manager::new(&ENV.database_url, deadpool_diesel::Runtime::Tokio1); let pool = Pool::builder(manager).build().unwrap(); let resend = Resend::new(&ENV.resend_api_key); @@ -81,7 +81,7 @@ impl AppState { Self { pool, resend, - oauth_providers: OAuthProviders::default(), + oauth_providers: OAuthProviders::new(), reqwest_client, } } diff --git a/test.py b/test.py deleted file mode 100644 index 2a1bad3..0000000 --- a/test.py +++ /dev/null @@ -1,2 +0,0 @@ -import base64 - From 573e8d182c1079818d1e1878ad32e6d2cef82b16 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Fri, 27 Sep 2024 21:36:45 +0300 Subject: [PATCH 3/9] feature: integrate two states --- .json | 16 ------ src/agent_api/dto/client.rs | 1 - src/agent_api/enums/mod.rs | 2 +- src/agent_api/enums/protocol.rs | 1 - src/agent_api/mod.rs | 2 +- src/agent_api/requests/login.rs | 2 - src/agent_api/state/agent.rs | 54 +++++++++++++++++++ src/agent_api/state/configs/mod.rs | 5 ++ src/agent_api/state/configs/shadowsocks.rs | 9 ++++ src/agent_api/state/configs/vless.rs | 7 +++ src/agent_api/state/cookie.rs | 7 +++ src/agent_api/{state.rs => state/mod.rs} | 36 ++----------- src/agent_api/utils/link/mod.rs | 2 +- src/agent_api/utils/mod.rs | 2 +- src/agent_api/utils/password_generator/mod.rs | 2 +- src/config.rs | 2 + src/data/enums/country.rs | 4 +- src/enums/errors/internal/agent_api.rs | 2 +- src/main.rs | 2 +- src/state.rs | 24 +++++++-- 20 files changed, 119 insertions(+), 63 deletions(-) delete mode 100644 .json create mode 100644 src/agent_api/state/agent.rs create mode 100644 src/agent_api/state/configs/mod.rs create mode 100644 src/agent_api/state/configs/shadowsocks.rs create mode 100644 src/agent_api/state/configs/vless.rs create mode 100644 src/agent_api/state/cookie.rs rename src/agent_api/{state.rs => state/mod.rs} (63%) diff --git a/.json b/.json deleted file mode 100644 index a47e35c..0000000 --- a/.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "id": "inbound_id", - "settings": { - "clients": [ - { - "id": "Uuid::new_v4()", - "flow": "", - "email": "a", - "limitIp": 0, - "totalGB": 0, - "expiryTime": 0, - "enable": true - } - ] - } -} diff --git a/src/agent_api/dto/client.rs b/src/agent_api/dto/client.rs index eec08f8..377e610 100644 --- a/src/agent_api/dto/client.rs +++ b/src/agent_api/dto/client.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; - #[derive(Debug, Serialize, Deserialize)] pub struct Client { pub inbound_id: u32, diff --git a/src/agent_api/enums/mod.rs b/src/agent_api/enums/mod.rs index 8ad2ffc..89c7f30 100644 --- a/src/agent_api/enums/mod.rs +++ b/src/agent_api/enums/mod.rs @@ -1,3 +1,3 @@ mod protocol; -pub use protocol::Protocol; \ No newline at end of file +pub use protocol::Protocol; diff --git a/src/agent_api/enums/protocol.rs b/src/agent_api/enums/protocol.rs index f9c49e3..ec85319 100644 --- a/src/agent_api/enums/protocol.rs +++ b/src/agent_api/enums/protocol.rs @@ -1,4 +1,3 @@ -#[derive(Debug, Clone, Copy)] pub enum Protocol { Vless, Shadowsocks, diff --git a/src/agent_api/mod.rs b/src/agent_api/mod.rs index d60fc0d..2afa059 100644 --- a/src/agent_api/mod.rs +++ b/src/agent_api/mod.rs @@ -1,7 +1,7 @@ pub mod dto; pub mod enums; pub mod requests; -mod state; +pub mod state; pub mod utils; pub use state::AgentState; diff --git a/src/agent_api/requests/login.rs b/src/agent_api/requests/login.rs index b4c5d53..3ab911c 100644 --- a/src/agent_api/requests/login.rs +++ b/src/agent_api/requests/login.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use reqwest::header::SET_COOKIE; use tracing::error; diff --git a/src/agent_api/state/agent.rs b/src/agent_api/state/agent.rs new file mode 100644 index 0000000..6016078 --- /dev/null +++ b/src/agent_api/state/agent.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; + +use serde::{Deserialize, Serialize}; +use tokio::{sync::RwLock, time::Instant}; + +use super::{configs, cookie::Cookie}; + +#[derive(Clone)] +pub struct Agent { + pub host: String, + pub port: u16, + pub secure_path: String, + pub username: String, + pub password: String, + pub shadowsocks_config: configs::Shadowsocks, + pub vless_config: configs::Vless, + pub cookie: Arc>, +} + +#[derive(Clone, Serialize, Deserialize)] +struct AgentBuilder { + host: String, + port: u16, + secure_path: String, + username: String, + password: String, + shadowsocks_config: configs::Shadowsocks, + vless_config: configs::Vless, +} + +impl From for Agent { + fn from(builder: AgentBuilder) -> Self { + Agent { + host: builder.host, + port: builder.port, + secure_path: builder.secure_path, + username: builder.username, + password: builder.password, + shadowsocks_config: builder.shadowsocks_config, + vless_config: builder.vless_config, + cookie: Arc::new(RwLock::new(Cookie { + cookie: String::new(), + expires_at: Instant::now(), + })), + } + } +} + +impl From<&str> for Agent { + fn from(s: &str) -> Self { + let builder: AgentBuilder = serde_json::from_str(s).expect("Agent config is invalid"); + builder.into() + } +} diff --git a/src/agent_api/state/configs/mod.rs b/src/agent_api/state/configs/mod.rs new file mode 100644 index 0000000..c65c3ff --- /dev/null +++ b/src/agent_api/state/configs/mod.rs @@ -0,0 +1,5 @@ +mod shadowsocks; +mod vless; + +pub use shadowsocks::Shadowsocks; +pub use vless::Vless; diff --git a/src/agent_api/state/configs/shadowsocks.rs b/src/agent_api/state/configs/shadowsocks.rs new file mode 100644 index 0000000..a0e875e --- /dev/null +++ b/src/agent_api/state/configs/shadowsocks.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize)] +pub struct Shadowsocks { + pub inbound_id: u32, + pub port: u16, + pub encryption: String, + pub password: String, +} diff --git a/src/agent_api/state/configs/vless.rs b/src/agent_api/state/configs/vless.rs new file mode 100644 index 0000000..573da83 --- /dev/null +++ b/src/agent_api/state/configs/vless.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize)] +pub struct Vless { + pub inbound_id: u32, + pub port: u16, +} diff --git a/src/agent_api/state/cookie.rs b/src/agent_api/state/cookie.rs new file mode 100644 index 0000000..41036db --- /dev/null +++ b/src/agent_api/state/cookie.rs @@ -0,0 +1,7 @@ +use tokio::time::Instant; + +#[derive(Clone)] +pub struct Cookie { + pub cookie: String, + pub expires_at: Instant, +} diff --git a/src/agent_api/state.rs b/src/agent_api/state/mod.rs similarity index 63% rename from src/agent_api/state.rs rename to src/agent_api/state/mod.rs index 1b5bde5..5086aca 100644 --- a/src/agent_api/state.rs +++ b/src/agent_api/state/mod.rs @@ -1,41 +1,15 @@ use std::collections::HashMap; -use std::sync::Arc; -use std::time::{Duration, Instant}; +use tokio::time::{Duration, Instant}; -use tokio::sync::RwLock; +pub use agent::Agent; use crate::{data::enums::Country, enums::errors::internal::Result}; use super::requests::login; -#[derive(Clone)] -pub struct CookieInfo { - pub cookie: String, - pub expires_at: Instant, -} - -#[derive(Clone)] -pub struct ShadowsocksConfig { - pub port: u16, - pub method: String, - pub password: String, -} - -#[derive(Clone)] -pub struct VlessConfig { - pub port: u16, -} - -#[derive(Clone)] -pub struct Agent { - pub host: String, - pub secure_path: String, - pub username: String, - pub password: String, - pub shadowsocks_config: ShadowsocksConfig, - pub vless_config: VlessConfig, - pub cookie: Arc>, -} +mod agent; +mod configs; +mod cookie; #[derive(Clone)] pub struct AgentState { diff --git a/src/agent_api/utils/link/mod.rs b/src/agent_api/utils/link/mod.rs index 3032b84..c53499c 100644 --- a/src/agent_api/utils/link/mod.rs +++ b/src/agent_api/utils/link/mod.rs @@ -2,4 +2,4 @@ mod shadowsocks; mod vless; pub use shadowsocks::shadowsocks; -pub use vless::vless; \ No newline at end of file +pub use vless::vless; diff --git a/src/agent_api/utils/mod.rs b/src/agent_api/utils/mod.rs index c51f754..f00eeb5 100644 --- a/src/agent_api/utils/mod.rs +++ b/src/agent_api/utils/mod.rs @@ -1,3 +1,3 @@ pub mod client_json; +pub mod link; pub mod password_generator; -pub mod link; \ No newline at end of file diff --git a/src/agent_api/utils/password_generator/mod.rs b/src/agent_api/utils/password_generator/mod.rs index a036a37..c742812 100644 --- a/src/agent_api/utils/password_generator/mod.rs +++ b/src/agent_api/utils/password_generator/mod.rs @@ -1,3 +1,3 @@ mod shadowsocks; -pub use shadowsocks::shadowsocks; \ No newline at end of file +pub use shadowsocks::shadowsocks; diff --git a/src/config.rs b/src/config.rs index 55a9362..172b846 100644 --- a/src/config.rs +++ b/src/config.rs @@ -35,6 +35,7 @@ pub struct Config { pub google_client_id: String, pub resend_api_key: String, pub server_mode: ServerMode, + pub agent_config_uk: String, } pub static ENV: Lazy = Lazy::new(|| { @@ -60,6 +61,7 @@ pub static ENV: Lazy = Lazy::new(|| { .expect("SERVER_MODE must be set") .parse() .unwrap(), + agent_config_uk: env::var("AGENT_CONFIG_UK").expect("AGENT_CONFIG_UK must be set"), } }); diff --git a/src/data/enums/country.rs b/src/data/enums/country.rs index 2966fbf..7343534 100644 --- a/src/data/enums/country.rs +++ b/src/data/enums/country.rs @@ -8,7 +8,9 @@ use diesel::{ }; use serde::{Deserialize, Serialize}; -#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)] +#[derive( + Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash, +)] #[diesel(sql_type = crate::data::schema::sql_types::Country)] #[allow(clippy::upper_case_acronyms)] pub enum Country { diff --git a/src/enums/errors/internal/agent_api.rs b/src/enums/errors/internal/agent_api.rs index 1b1431e..4f0e881 100644 --- a/src/enums/errors/internal/agent_api.rs +++ b/src/enums/errors/internal/agent_api.rs @@ -11,4 +11,4 @@ impl std::fmt::Display for AgentAPI { AgentAPI::Internal => write!(f, "Internal error"), } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 4f9b542..6ff0e2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ mod tests; mod utils; async fn start_main_server() { - let state = AppState::new(); + let state = AppState::default(); let app = app_router(state.clone()).with_state(state); let listener = TcpListener::bind(&ENV.master_backend_url).await.unwrap(); diff --git a/src/state.rs b/src/state.rs index 6741958..94c09c3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,9 +1,15 @@ +use std::collections::HashMap; + use axum::extract::FromRef; use deadpool_diesel::postgres::{Manager, Pool}; use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; use resend_rs::Resend; -use crate::{config::ENV, data::enums::OAuthProvider}; +use crate::{ + agent_api::{state, AgentState}, + config::ENV, + data::enums::{Country, OAuthProvider}, +}; #[derive(Clone)] pub struct OAuthProviders { @@ -13,7 +19,7 @@ pub struct OAuthProviders { } impl OAuthProviders { - pub fn new() -> Self { + pub fn default() -> Self { Self { discord: Self::client( &ENV.discord_client_id, @@ -70,19 +76,29 @@ pub struct AppState { pub oauth_providers: OAuthProviders, pub resend: Resend, pub reqwest_client: reqwest::Client, + pub agent_state: AgentState, } impl AppState { - pub fn new() -> Self { + pub fn default() -> Self { let manager = Manager::new(&ENV.database_url, deadpool_diesel::Runtime::Tokio1); let pool = Pool::builder(manager).build().unwrap(); let resend = Resend::new(&ENV.resend_api_key); let reqwest_client = reqwest::Client::new(); + let mut servers: HashMap = HashMap::new(); + servers.insert( + Country::UK, + state::Agent::from(ENV.agent_config_uk.as_str()), + ); + + let agent_state = AgentState::new(servers); + Self { pool, resend, - oauth_providers: OAuthProviders::new(), + oauth_providers: OAuthProviders::default(), reqwest_client, + agent_state, } } } From b2cddcd0d80f3231f7266fe9d3f145a798de4324 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Fri, 27 Sep 2024 23:13:18 +0300 Subject: [PATCH 4/9] refactor: update create session service --- src/agent_api/dto/agent_response.rs | 5 +- src/agent_api/dto/client.rs | 1 - src/agent_api/dto/client_stats.rs | 4 ++ src/agent_api/dto/mod.rs | 2 + src/agent_api/enums/mod.rs | 3 - src/agent_api/enums/protocol.rs | 4 -- src/agent_api/mod.rs | 1 - src/agent_api/requests/create_client.rs | 28 ++++----- src/agent_api/requests/delete_client.rs | 15 +++-- src/agent_api/requests/get_client_stats.rs | 35 ++++++++--- src/agent_api/requests/login.rs | 25 +++++--- src/agent_api/state/mod.rs | 8 +-- src/config.rs | 2 +- src/data/enums/mod.rs | 21 +++---- src/data/enums/protocol.rs | 48 ++++++++++++++ src/data/models/config.rs | 20 ------ src/data/models/device.rs | 1 - src/data/models/mod.rs | 4 -- src/data/models/server.rs | 18 ------ src/data/models/session.rs | 15 +++-- src/data/schema.rs | 49 +++------------ src/dto/session/interface.rs | 6 +- src/dto/session/internal/mod.rs | 6 +- src/dto/session/internal/new_session.rs | 11 +++- .../query/active_session_and_device.rs | 13 ++-- ...on_and_device_and_country_and_protocol.rs} | 28 ++++----- src/dto/session/query/mod.rs | 8 +-- src/dto/session/response/session.rs | 16 ++--- src/services/config/get_by_country.rs | 36 ----------- src/services/config/mod.rs | 3 - src/services/session/create.rs | 63 ++++++++++--------- 31 files changed, 224 insertions(+), 275 deletions(-) create mode 100644 src/agent_api/dto/client_stats.rs delete mode 100644 src/agent_api/enums/mod.rs delete mode 100644 src/agent_api/enums/protocol.rs create mode 100644 src/data/enums/protocol.rs delete mode 100644 src/data/models/config.rs delete mode 100644 src/data/models/server.rs rename src/dto/session/query/{active_session_and_device_and_country.rs => active_session_and_device_and_country_and_protocol.rs} (58%) delete mode 100644 src/services/config/get_by_country.rs delete mode 100644 src/services/config/mod.rs diff --git a/src/agent_api/dto/agent_response.rs b/src/agent_api/dto/agent_response.rs index 3c374e4..bc2b65b 100644 --- a/src/agent_api/dto/agent_response.rs +++ b/src/agent_api/dto/agent_response.rs @@ -1,9 +1,8 @@ use serde::{Deserialize, Serialize}; -use serde_json::Value; #[derive(Debug, Serialize, Deserialize)] -pub struct AgentResponse { +pub struct AgentResponse { pub success: bool, pub msg: String, - pub obj: Option, + pub obj: Option, } diff --git a/src/agent_api/dto/client.rs b/src/agent_api/dto/client.rs index 377e610..485b597 100644 --- a/src/agent_api/dto/client.rs +++ b/src/agent_api/dto/client.rs @@ -2,6 +2,5 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Client { - pub inbound_id: u32, pub link: String, } diff --git a/src/agent_api/dto/client_stats.rs b/src/agent_api/dto/client_stats.rs new file mode 100644 index 0000000..ce13c3f --- /dev/null +++ b/src/agent_api/dto/client_stats.rs @@ -0,0 +1,4 @@ +pub struct ClientStats { + pub up: i32, + pub down: i32, +} diff --git a/src/agent_api/dto/mod.rs b/src/agent_api/dto/mod.rs index eaf46e9..50810e5 100644 --- a/src/agent_api/dto/mod.rs +++ b/src/agent_api/dto/mod.rs @@ -1,5 +1,7 @@ mod agent_response; mod client; +mod client_stats; pub use agent_response::AgentResponse; pub use client::Client; +pub use client_stats::ClientStats; diff --git a/src/agent_api/enums/mod.rs b/src/agent_api/enums/mod.rs deleted file mode 100644 index 89c7f30..0000000 --- a/src/agent_api/enums/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod protocol; - -pub use protocol::Protocol; diff --git a/src/agent_api/enums/protocol.rs b/src/agent_api/enums/protocol.rs deleted file mode 100644 index ec85319..0000000 --- a/src/agent_api/enums/protocol.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub enum Protocol { - Vless, - Shadowsocks, -} diff --git a/src/agent_api/mod.rs b/src/agent_api/mod.rs index 2afa059..3c0a8c7 100644 --- a/src/agent_api/mod.rs +++ b/src/agent_api/mod.rs @@ -1,5 +1,4 @@ pub mod dto; -pub mod enums; pub mod requests; pub mod state; pub mod utils; diff --git a/src/agent_api/requests/create_client.rs b/src/agent_api/requests/create_client.rs index 495c460..eaab78b 100644 --- a/src/agent_api/requests/create_client.rs +++ b/src/agent_api/requests/create_client.rs @@ -1,23 +1,19 @@ -use serde_json::json; +use serde_json::{json, Value}; use uuid::Uuid; use crate::{ agent_api::{ dto::{AgentResponse, Client}, - enums::Protocol, utils, AgentState, }, - data::enums::Country, + data::enums::{Country, Protocol}, enums::errors::internal::{AgentAPI, Error, Result}, }; pub async fn create_client( agent_state: AgentState, country: Country, - host: &str, - port: u16, protocol: Protocol, - inbound_id: u32, device_id: &Uuid, ) -> Result { let cookie = agent_state.get_or_refresh_cookie(&country).await?; @@ -27,13 +23,14 @@ pub async fn create_client( .ok_or(Error::AgentAPI(AgentAPI::Internal))?; let client = agent_state.client; - let (client_body, link) = match protocol { + let (inbound_id, client_body, link) = match protocol { Protocol::Vless => { let client_id = Uuid::new_v4(); let client_body = utils::client_json::vless(&client_id, device_id); - let link = utils::link::vless(&client_id, host, agent.vless_config.port, device_id); - - (client_body, link) + let link = + utils::link::vless(&client_id, &agent.host, agent.vless_config.port, device_id); + let inbound_id = agent.vless_config.inbound_id; + (inbound_id, client_body, link) } Protocol::Shadowsocks => { let password = utils::password_generator::shadowsocks(); @@ -41,19 +38,20 @@ pub async fn create_client( let link = utils::link::shadowsocks( &agent.password, &password, - host, + &agent.host, agent.shadowsocks_config.port, device_id, ); + let inbound_id = agent.shadowsocks_config.inbound_id; - (client_body, link) + (inbound_id, client_body, link) } }; let res = client .post(format!( "http://{}:{}/{}/panel/api/inbounds/addClient", - host, port, agent.secure_path + agent.host, agent.port, agent.secure_path )) .header("Cookie", cookie) .json(&json!({ @@ -65,12 +63,12 @@ pub async fn create_client( )) .send() .await? - .json::() + .json::>() .await?; if !res.success { return Err(Error::AgentAPI(AgentAPI::Internal)); } - Ok(Client { inbound_id, link }) + Ok(Client { link }) } diff --git a/src/agent_api/requests/delete_client.rs b/src/agent_api/requests/delete_client.rs index eda6acf..31dbd44 100644 --- a/src/agent_api/requests/delete_client.rs +++ b/src/agent_api/requests/delete_client.rs @@ -1,17 +1,16 @@ +use serde_json::Value; use uuid::Uuid; use crate::{ agent_api::{dto::AgentResponse, AgentState}, - data::enums::Country, + data::enums::{Country, Protocol}, enums::errors::internal::{AgentAPI, Error, Result}, }; pub async fn delete_client( agent_state: AgentState, country: Country, - host: &str, - port: u16, - inbound_id: u32, + protocol: Protocol, device_id: &Uuid, ) -> Result<()> { let cookie = agent_state.get_or_refresh_cookie(&country).await?; @@ -20,16 +19,20 @@ pub async fn delete_client( .get(&country) .ok_or(Error::AgentAPI(AgentAPI::Internal))?; let client = agent_state.client; + let inbound_id = match protocol { + Protocol::Vless => agent.vless_config.inbound_id, + Protocol::Shadowsocks => agent.shadowsocks_config.inbound_id, + }; let res = client .post(format!( "http://{}:{}/{}/panel/api/inbounds/{}/delClient/{}", - host, port, agent.secure_path, inbound_id, device_id + agent.host, agent.port, agent.secure_path, inbound_id, device_id )) .header("Cookie", cookie) .send() .await? - .json::() + .json::>() .await?; if !res.success { diff --git a/src/agent_api/requests/get_client_stats.rs b/src/agent_api/requests/get_client_stats.rs index 8395d30..a61aba1 100644 --- a/src/agent_api/requests/get_client_stats.rs +++ b/src/agent_api/requests/get_client_stats.rs @@ -1,18 +1,34 @@ +use serde::Deserialize; use uuid::Uuid; use crate::{ - agent_api::{dto::AgentResponse, AgentState}, + agent_api::{ + dto::{AgentResponse, ClientStats}, + AgentState, + }, data::enums::Country, enums::errors::internal::{AgentAPI, Error, Result}, }; +#[derive(Deserialize, Clone)] +#[allow(non_snake_case)] +struct ClientStatsResponse { + pub id: u32, + pub inboundId: u32, + pub enable: bool, + pub email: String, + pub up: i32, + pub down: i32, + pub expiryTime: u32, + pub total: u32, + pub reset: u32, +} + pub async fn get_client_stats( agent_state: AgentState, country: Country, - host: &str, - port: u16, device_id: &Uuid, -) -> Result<()> { +) -> Result { let cookie = agent_state.get_or_refresh_cookie(&country).await?; let agent = agent_state .agents @@ -23,17 +39,22 @@ pub async fn get_client_stats( let res = client .get(format!( "http://{}:{}/{}/panel/api/inbounds/getClientTaffics/{}", - host, port, agent.secure_path, device_id + agent.host, agent.port, agent.secure_path, device_id )) .header("Cookie", cookie) .send() .await? - .json::() + .json::>() .await?; if !res.success { return Err(Error::AgentAPI(AgentAPI::Internal)); } - Ok(()) + let stats = ClientStats { + up: res.obj.clone().unwrap().up, + down: res.obj.unwrap().down, + }; + + Ok(stats) } diff --git a/src/agent_api/requests/login.rs b/src/agent_api/requests/login.rs index 3ab911c..19bb874 100644 --- a/src/agent_api/requests/login.rs +++ b/src/agent_api/requests/login.rs @@ -1,27 +1,32 @@ use reqwest::header::SET_COOKIE; +use serde_json::Value; use tracing::error; use crate::{ - agent_api::dto::AgentResponse, + agent_api::{dto::AgentResponse, AgentState}, + data::enums::Country, enums::errors::internal::{AgentAPI, Error, Result}, }; -pub async fn login( - client: &reqwest::Client, - url: &str, - username: &str, - password: &str, -) -> Result { - let params = [("username", username), ("password", password)]; +pub async fn login(agent_state: AgentState, country: &Country) -> Result { + let client = agent_state.client; + let agent = agent_state + .agents + .get(&country) + .ok_or(Error::AgentAPI(AgentAPI::Internal))?; + let params = [("username", &agent.username), ("password", &agent.password)]; let res = client - .post(format!("http://{url}/login")) + .post(format!( + "http://{}:{}/{}/login", + agent.host, agent.port, agent.secure_path + )) .form(¶ms) .send() .await?; let cookies = res.headers().get_all(SET_COOKIE).iter().last().cloned(); - let body = res.json::().await?; + let body = res.json::>().await?; if !body.success || cookies.is_none() { error!("Login failed: {}", body.msg); diff --git a/src/agent_api/state/mod.rs b/src/agent_api/state/mod.rs index 5086aca..cde987d 100644 --- a/src/agent_api/state/mod.rs +++ b/src/agent_api/state/mod.rs @@ -30,13 +30,7 @@ impl AgentState { let cookie = agent.cookie.read().await; if cookie.expires_at < Instant::now() { - let new_cookie = login( - &self.client, - &agent.secure_path, - &agent.username, - &agent.password, - ) - .await?; + let new_cookie = login(self.clone(), country).await?; let mut cookie = agent.cookie.write().await; cookie.cookie = new_cookie; diff --git a/src/config.rs b/src/config.rs index 172b846..aa9eade 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,7 +15,7 @@ impl std::str::FromStr for ServerMode { match s { "development" => Ok(ServerMode::Development), "production" => Ok(ServerMode::Production), - _ => Err("Unknown server type".to_string()), + _ => Err("Unknown server mode".to_string()), } } } diff --git a/src/data/enums/mod.rs b/src/data/enums/mod.rs index 4ab8f72..bfa69aa 100644 --- a/src/data/enums/mod.rs +++ b/src/data/enums/mod.rs @@ -1,20 +1,15 @@ -pub mod config_status; -pub use config_status::ConfigStatus; - pub mod country; -pub use country::Country; - pub mod device_status; -pub use device_status::DeviceStatus; - +pub mod oauth_provider; pub mod os; -pub use os::OS; - +pub mod protocol; pub mod session_status; -pub use session_status::SessionStatus; - pub mod user_status; -pub use user_status::UserStatus; -pub mod oauth_provider; +pub use country::Country; +pub use device_status::DeviceStatus; pub use oauth_provider::OAuthProvider; +pub use os::OS; +pub use protocol::Protocol; +pub use session_status::SessionStatus; +pub use user_status::UserStatus; diff --git a/src/data/enums/protocol.rs b/src/data/enums/protocol.rs new file mode 100644 index 0000000..7d77b44 --- /dev/null +++ b/src/data/enums/protocol.rs @@ -0,0 +1,48 @@ +use std::io::Write; + +use diesel::{ + deserialize::{self, FromSql, FromSqlRow}, + expression::AsExpression, + pg::{Pg, PgValue}, + serialize::{self, IsNull, Output, ToSql}, +}; +use serde::{Deserialize, Serialize}; + +#[derive( + Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash, +)] +#[diesel(sql_type = crate::data::schema::sql_types::Protocol)] +#[allow(clippy::upper_case_acronyms)] +pub enum Protocol { + Vless, + Shadowsocks, +} + +impl ToSql for Protocol { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + match *self { + Protocol::Vless => out.write_all(b"Vless")?, + Protocol::Shadowsocks => out.write_all(b"Shadowsocks")?, + } + Ok(IsNull::No) + } +} + +impl FromSql for Protocol { + fn from_sql(bytes: PgValue) -> deserialize::Result { + match bytes.as_bytes() { + b"Vless" => Ok(Protocol::Vless), + b"Shadowsocks" => Ok(Protocol::Shadowsocks), + _ => Err("Unrecognized enum variant".into()), + } + } +} + +impl std::fmt::Display for Protocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Protocol::Vless => write!(f, "Vless"), + Protocol::Shadowsocks => write!(f, "Shadowsocks"), + } + } +} diff --git a/src/data/models/config.rs b/src/data/models/config.rs deleted file mode 100644 index 3dc8cc9..0000000 --- a/src/data/models/config.rs +++ /dev/null @@ -1,20 +0,0 @@ -use chrono::NaiveDateTime; -use diesel::{Queryable, Selectable}; -use serde::Serialize; -use uuid::Uuid; - -use crate::data::{enums::ConfigStatus, schema}; - -#[derive(Queryable, Selectable, Debug, Clone, Serialize)] -#[diesel(table_name = schema::config)] -#[diesel(belongs_to(crate::dto::server::Server,))] -#[diesel(check_for_backend(diesel::pg::Pg))] -pub struct Config { - pub id: Uuid, - pub private_key: String, - pub user_ip: String, - pub server_id: Uuid, - pub status: ConfigStatus, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, -} diff --git a/src/data/models/device.rs b/src/data/models/device.rs index cb9590d..a3395b1 100644 --- a/src/data/models/device.rs +++ b/src/data/models/device.rs @@ -10,7 +10,6 @@ use crate::data::{ #[derive(Queryable, Selectable, Debug, Clone, Serialize)] #[diesel(table_name = schema::device)] -#[diesel(belongs_to(crate::dto::user::User))] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Device { pub id: Uuid, diff --git a/src/data/models/mod.rs b/src/data/models/mod.rs index 345dfb9..514fb1a 100644 --- a/src/data/models/mod.rs +++ b/src/data/models/mod.rs @@ -1,17 +1,13 @@ mod classic_auth; -mod config; mod device; mod email_confirmation; mod oauth; -mod server; mod session; mod user; pub use classic_auth::ClassicAuth; -pub use config::Config; pub use device::Device; pub use email_confirmation::EmailConfirmation; pub use oauth::OAuth; -pub use server::Server; pub use session::Session; pub use user::User; diff --git a/src/data/models/server.rs b/src/data/models/server.rs deleted file mode 100644 index d5ce0ac..0000000 --- a/src/data/models/server.rs +++ /dev/null @@ -1,18 +0,0 @@ -use chrono::NaiveDateTime; -use diesel::{Queryable, Selectable}; -use uuid::Uuid; - -use crate::data::{enums::Country, schema}; - -#[derive(Queryable, Selectable, Debug, Clone)] -#[diesel(table_name = schema::server)] -#[diesel(check_for_backend(diesel::pg::Pg))] -#[allow(dead_code)] -pub struct Server { - pub id: Uuid, - pub public_key: String, - pub wireguard_uri: String, - pub country: Country, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, -} diff --git a/src/data/models/session.rs b/src/data/models/session.rs index f4d1136..075044a 100644 --- a/src/data/models/session.rs +++ b/src/data/models/session.rs @@ -2,19 +2,24 @@ use chrono::NaiveDateTime; use diesel::{Queryable, Selectable}; use uuid::Uuid; -use crate::data::{enums::SessionStatus, schema}; +use crate::data::{ + enums::{Country, Protocol, SessionStatus}, + schema, +}; #[derive(Queryable, Selectable, Debug, Clone)] #[diesel(table_name = schema::session)] -#[diesel(belongs_to(crate::dto::device::Device))] -#[diesel(belongs_to(crate::dto::config::Config))] #[diesel(check_for_backend(diesel::pg::Pg))] #[allow(dead_code)] pub struct Session { pub id: Uuid, + pub device_id: Uuid, + pub country: Country, + pub protocol: Protocol, + pub link: String, pub status: SessionStatus, + pub up: Option, + pub down: Option, pub opened_at: NaiveDateTime, pub closed_at: Option, - pub device_id: Uuid, - pub config_id: Uuid, } diff --git a/src/data/schema.rs b/src/data/schema.rs index b1c734f..2d52a99 100644 --- a/src/data/schema.rs +++ b/src/data/schema.rs @@ -1,10 +1,6 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] - #[diesel(postgres_type(name = "ConfigStatus"))] - pub struct ConfigStatus; - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "Country"))] pub struct Country; @@ -28,21 +24,10 @@ pub mod sql_types { #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "OAuthProvider"))] pub struct OAuthProvider; -} - -diesel::table! { - use diesel::sql_types::*; - use super::sql_types::ConfigStatus; - config (id) { - id -> Uuid, - private_key -> Text, - user_ip -> Text, - server_id -> Uuid, - status -> ConfigStatus, - created_at -> Timestamp, - updated_at -> Timestamp, - } + #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] + #[diesel(postgres_type(name = "Protocol"))] + pub struct Protocol; } diesel::table! { @@ -65,29 +50,19 @@ diesel::table! { diesel::table! { use diesel::sql_types::*; - use super::sql_types::Country; - - server (id) { - id -> Uuid, - public_key -> Text, - wireguard_uri -> Text, - country -> Country, - created_at -> Timestamp, - updated_at -> Timestamp, - } -} - -diesel::table! { - use diesel::sql_types::*; - use super::sql_types::SessionStatus; + use super::sql_types::{SessionStatus, Country, Protocol}; session (id) { id -> Uuid, + device_id -> Uuid, + protocol -> Protocol, + country -> Country, + link -> Text, status -> SessionStatus, + up -> Nullable, + down -> Nullable, opened_at -> Timestamp, closed_at -> Nullable, - device_id -> Uuid, - config_id -> Uuid, } } @@ -147,15 +122,11 @@ diesel::table! { diesel::joinable!(classic_auth -> user (user_id)); diesel::joinable!(oauth -> user (user_id)); diesel::joinable!(email_confirmation -> user (user_id)); -diesel::joinable!(config -> server (server_id)); diesel::joinable!(device -> user (user_id)); -diesel::joinable!(session -> config (config_id)); diesel::joinable!(session -> device (device_id)); diesel::allow_tables_to_appear_in_same_query!( - config, device, - server, session, user, classic_auth, diff --git a/src/dto/session/interface.rs b/src/dto/session/interface.rs index cc56e59..ed93472 100644 --- a/src/dto/session/interface.rs +++ b/src/dto/session/interface.rs @@ -1,5 +1,5 @@ use crate::{ - data::models::{Config, Device, Server, Session}, + data::models::{Device, Session}, enums::errors::internal::Error, }; @@ -7,12 +7,12 @@ pub trait SessionBy { async fn get_session<'a>( &self, pool: &'a deadpool_diesel::postgres::Pool, - ) -> Result<(Session, Device, Config, Server), Error>; + ) -> Result<(Session, Device), Error>; } pub async fn get_session( pool: &deadpool_diesel::postgres::Pool, by: T, -) -> Result<(Session, Device, Config, Server), Error> { +) -> Result<(Session, Device), Error> { by.get_session(pool).await } diff --git a/src/dto/session/internal/mod.rs b/src/dto/session/internal/mod.rs index 5c0ad29..3309bca 100644 --- a/src/dto/session/internal/mod.rs +++ b/src/dto/session/internal/mod.rs @@ -1,5 +1,5 @@ -pub mod new_session; -pub use new_session::NewSession; +mod new_session; +mod session_history; -pub mod session_history; +pub use new_session::NewSession; pub use session_history::SessionHistory; diff --git a/src/dto/session/internal/new_session.rs b/src/dto/session/internal/new_session.rs index 4a0b981..c78f049 100644 --- a/src/dto/session/internal/new_session.rs +++ b/src/dto/session/internal/new_session.rs @@ -1,12 +1,17 @@ use diesel::prelude::*; use uuid::Uuid; -use crate::data::{enums::SessionStatus, schema}; +use crate::data::{ + enums::{Country, Protocol, SessionStatus}, + schema, +}; #[derive(Insertable, Clone)] #[diesel(table_name = schema::session)] pub struct NewSession { - pub status: SessionStatus, + pub session_status: SessionStatus, pub device_id: Uuid, - pub config_id: Uuid, + pub country: Country, + pub protocol: Protocol, + pub link: String, } diff --git a/src/dto/session/query/active_session_and_device.rs b/src/dto/session/query/active_session_and_device.rs index 61bb692..eb671f5 100644 --- a/src/dto/session/query/active_session_and_device.rs +++ b/src/dto/session/query/active_session_and_device.rs @@ -5,7 +5,7 @@ use uuid::Uuid; use crate::{ data::{ enums::SessionStatus, - models::{Config, Device, Server, Session}, + models::{Device, Session}, schema, }, dto::session::SessionBy, @@ -20,24 +20,21 @@ impl SessionBy for ActiveSessionAndDevice { async fn get_session<'a>( &self, pool: &'a deadpool_diesel::postgres::Pool, - ) -> Result<(Session, Device, Config, Server), Error> { + ) -> Result<(Session, Device), Error> { let conn = pool.get().await?; let device_id = self.device_id; - let result: Vec<(Session, Device, Config, Server)> = conn + let result: Vec<(Session, Device)> = conn .interact(move |conn| { schema::session::table .inner_join(schema::device::table) - .inner_join(schema::config::table.inner_join(schema::server::table)) .filter(schema::session::device_id.eq(device_id)) .filter(schema::session::status.eq(SessionStatus::Active)) .select(( Session::as_select(), - Device::as_select(), - Config::as_select(), - Server::as_select(), + Device::as_select() )) - .load::<(Session, Device, Config, Server)>(conn) + .load::<(Session, Device)>(conn) }) .await??; if result.len() != 1 { diff --git a/src/dto/session/query/active_session_and_device_and_country.rs b/src/dto/session/query/active_session_and_device_and_country_and_protocol.rs similarity index 58% rename from src/dto/session/query/active_session_and_device_and_country.rs rename to src/dto/session/query/active_session_and_device_and_country_and_protocol.rs index 69e1b94..a814c16 100644 --- a/src/dto/session/query/active_session_and_device_and_country.rs +++ b/src/dto/session/query/active_session_and_device_and_country_and_protocol.rs @@ -4,42 +4,38 @@ use uuid::Uuid; use crate::{ data::{ - enums::{Country, SessionStatus}, - models::{Config, Device, Server, Session}, + enums::{Country, Protocol, SessionStatus}, + models::{Device, Session}, schema, }, dto::session::SessionBy, enums::errors::internal::{self, Error}, }; -pub struct ActiveSessionAndDeviceAndCountry { +pub struct ActiveSessionAndDeviceAndCountryAndProtocol { pub device_id: Uuid, pub country: Country, + pub protocol: Protocol, } -impl SessionBy for ActiveSessionAndDeviceAndCountry { +impl SessionBy for ActiveSessionAndDeviceAndCountryAndProtocol { async fn get_session<'a>( &self, pool: &'a deadpool_diesel::postgres::Pool, - ) -> Result<(Session, Device, Config, Server), Error> { + ) -> Result<(Session, Device), Error> { let conn = pool.get().await?; - let (device_id, country) = (self.device_id, self.country); - let result: Vec<(Session, Device, Config, Server)> = conn + let (device_id, country, protocol) = (self.device_id, self.country, self.protocol); + let result: Vec<(Session, Device)> = conn .interact(move |conn| { schema::session::table .inner_join(schema::device::table) - .inner_join(schema::config::table.inner_join(schema::server::table)) .filter(schema::session::device_id.eq(device_id)) .filter(schema::session::status.eq(SessionStatus::Active)) - .filter(schema::server::country.eq(country)) - .select(( - Session::as_select(), - Device::as_select(), - Config::as_select(), - Server::as_select(), - )) - .load::<(Session, Device, Config, Server)>(conn) + .filter(schema::session::country.eq(country)) + .filter(schema::session::protocol.eq(protocol)) + .select((Session::as_select(), Device::as_select())) + .load::<(Session, Device)>(conn) }) .await??; diff --git a/src/dto/session/query/mod.rs b/src/dto/session/query/mod.rs index a4a349e..3874f50 100644 --- a/src/dto/session/query/mod.rs +++ b/src/dto/session/query/mod.rs @@ -1,5 +1,5 @@ -pub mod active_session_and_device; -pub use active_session_and_device::ActiveSessionAndDevice; +mod active_session_and_device; +mod active_session_and_device_and_country_and_protocol; -pub mod active_session_and_device_and_country; -pub use active_session_and_device_and_country::ActiveSessionAndDeviceAndCountry; +pub use active_session_and_device::ActiveSessionAndDevice; +pub use active_session_and_device_and_country_and_protocol::ActiveSessionAndDeviceAndCountryAndProtocol; diff --git a/src/dto/session/response/session.rs b/src/dto/session/response/session.rs index 31bed5f..e58af91 100644 --- a/src/dto/session/response/session.rs +++ b/src/dto/session/response/session.rs @@ -6,20 +6,14 @@ use crate::data::models; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Session { pub session_id: Uuid, - pub server_public_key: String, - pub wireguard_uri: String, - pub user_ip: String, - pub user_private_key: String, + pub link: String, } -impl Session { - pub fn new(session: models::Session, server: models::Server, config: models::Config) -> Self { - Self { +impl From for Session { + fn from(session: models::Session) -> Self { + Session { session_id: session.id, - server_public_key: server.public_key, - wireguard_uri: server.wireguard_uri, - user_ip: config.user_ip, - user_private_key: config.private_key, + link: session.link, } } } diff --git a/src/services/config/get_by_country.rs b/src/services/config/get_by_country.rs deleted file mode 100644 index 64ee0ac..0000000 --- a/src/services/config/get_by_country.rs +++ /dev/null @@ -1,36 +0,0 @@ -use diesel::{prelude::*, QueryDsl}; -use tracing::info; - -use crate::{ - data::{ - enums::{ConfigStatus, Country}, - models::{Config, Server}, - schema, - }, - enums::errors::internal::Result, -}; - -pub async fn get_by_country( - pool: &deadpool_diesel::postgres::Pool, - country: &Country, -) -> Result<(Config, Server)> { - let conn = pool.get().await?; - let country = country.to_owned(); - - let result: Vec<(Config, Server)> = conn - .interact(move |conn| { - schema::config::table - .inner_join(schema::server::table) - .filter(schema::server::country.eq(country)) - .filter(schema::config::status.eq(ConfigStatus::NotInUse)) - .select((Config::as_select(), Server::as_select())) - .load::<(Config, Server)>(conn) - }) - .await??; - - let (config, server) = result.first().unwrap().clone(); - - info!("Got config by country: {:?}", country); - - Ok((config, server)) -} diff --git a/src/services/config/mod.rs b/src/services/config/mod.rs deleted file mode 100644 index 60632df..0000000 --- a/src/services/config/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod get_by_country; - -pub use get_by_country::get_by_country; diff --git a/src/services/session/create.rs b/src/services/session/create.rs index 0235a41..535a1a1 100644 --- a/src/services/session/create.rs +++ b/src/services/session/create.rs @@ -1,70 +1,73 @@ -use diesel::prelude::*; use tracing::info; use uuid::Uuid; use super::close_by_id; use crate::{ + agent_api, data::{ - enums::{ConfigStatus, Country, SessionStatus}, + enums::{Country, Protocol, SessionStatus}, models::Session, schema, }, dto::session::{ interface::get_session, internal::NewSession, - query::{ActiveSessionAndDevice, ActiveSessionAndDeviceAndCountry}, + query::{ActiveSessionAndDevice, ActiveSessionAndDeviceAndCountryAndProtocol}, response, }, enums::errors::internal::Result, - services::config::get_by_country, }; pub async fn create( pool: &deadpool_diesel::postgres::Pool, - device_id: &Uuid, + agent_state: agent_api::AgentState, country: &Country, + protocol: &Protocol, + device_id: &Uuid, ) -> Result { let conn = pool.get().await?; - let (device_id, country) = (*device_id, *country); + let (device_id, country, protocol) = (*device_id, *country, *protocol); if let Ok(current_session) = get_session( pool, - ActiveSessionAndDeviceAndCountry { device_id, country }, + ActiveSessionAndDeviceAndCountryAndProtocol { + device_id, + country, + protocol, + }, ) .await { - let (session, _device, config, server) = current_session; - let response = response::Session::new(session.clone(), server, config); - info!("Found active session with the same country: {}", session.id); + let (session, _device) = current_session; + let response = response::Session::from(session); + info!( + "Found active session with the same country and protocol: {}", + session.id + ); return Ok(response); } - if let Ok((session, _, _, _)) = get_session(pool, ActiveSessionAndDevice { device_id }).await { + if let Ok((session, _)) = get_session(pool, ActiveSessionAndDevice { device_id }).await { let _ = close_by_id(pool, &session.id).await?; } - let (config, server) = get_by_country(pool, &country).await?; - let new_session = NewSession { - status: SessionStatus::Active, - device_id, - config_id: config.id, - }; - - let session: Session = conn + let new_client = + agent_api::requests::create_client(agent_state, country, protocol, &device_id).await?; + let new_session: Session = conn .interact(move |conn| { - let session = diesel::insert_into(schema::session::table) - .values(&new_session) - .get_result::(conn); - let _ = diesel::update(schema::config::table) - .filter(schema::config::id.eq(config.id)) - .set(schema::config::status.eq(ConfigStatus::InUse)) - .execute(conn); - - session + diesel::insert_into(schema::session::table) + .values(&NewSession { + session_status: SessionStatus::Active, + device_id, + country, + protocol, + link: new_client.link, + }) + .get_result(conn) }) .await??; - info!("Created session: {}", session.id); + info!("Created session: {}", new_session.id); - Ok(response::Session::new(session, server, config)) + Ok(response::Session::from(new_session)) } From 32f09235769e1001e11e48e2af185be6f09830dd Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Fri, 27 Sep 2024 23:49:57 +0300 Subject: [PATCH 5/9] minor fix --- .../2024-09-27-203615_new_sessions/down.sql | 1 + .../2024-09-27-203615_new_sessions/up.sql | 1 + src/agent_api/requests/create_client.rs | 7 +++--- src/agent_api/requests/delete_client.rs | 7 +++--- src/agent_api/requests/get_client_stats.rs | 5 +++-- src/agent_api/requests/login.rs | 3 ++- src/agent_api/state/mod.rs | 2 +- src/data/enums/protocol.rs | 5 +---- src/data/models/session.rs | 1 - src/data/schema.rs | 7 +++--- src/dto/session/internal/new_session.rs | 2 +- src/services/session/close.rs | 14 ++++++++---- src/services/session/close_by_id.rs | 8 +++---- src/services/session/create.rs | 22 ++++++++++--------- 14 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 migrations/2024-09-27-203615_new_sessions/down.sql create mode 100644 migrations/2024-09-27-203615_new_sessions/up.sql diff --git a/migrations/2024-09-27-203615_new_sessions/down.sql b/migrations/2024-09-27-203615_new_sessions/down.sql new file mode 100644 index 0000000..d9a93fe --- /dev/null +++ b/migrations/2024-09-27-203615_new_sessions/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/migrations/2024-09-27-203615_new_sessions/up.sql b/migrations/2024-09-27-203615_new_sessions/up.sql new file mode 100644 index 0000000..b37f10d --- /dev/null +++ b/migrations/2024-09-27-203615_new_sessions/up.sql @@ -0,0 +1 @@ +-- Your SQL goes here diff --git a/src/agent_api/requests/create_client.rs b/src/agent_api/requests/create_client.rs index eaab78b..3c829dc 100644 --- a/src/agent_api/requests/create_client.rs +++ b/src/agent_api/requests/create_client.rs @@ -11,11 +11,12 @@ use crate::{ }; pub async fn create_client( - agent_state: AgentState, - country: Country, - protocol: Protocol, + agent_state: &AgentState, + country: &Country, + protocol: &Protocol, device_id: &Uuid, ) -> Result { + let agent_state = agent_state.clone(); let cookie = agent_state.get_or_refresh_cookie(&country).await?; let agent = agent_state .agents diff --git a/src/agent_api/requests/delete_client.rs b/src/agent_api/requests/delete_client.rs index 31dbd44..eceeb72 100644 --- a/src/agent_api/requests/delete_client.rs +++ b/src/agent_api/requests/delete_client.rs @@ -8,11 +8,12 @@ use crate::{ }; pub async fn delete_client( - agent_state: AgentState, - country: Country, - protocol: Protocol, + agent_state: &AgentState, + country: &Country, + protocol: &Protocol, device_id: &Uuid, ) -> Result<()> { + let agent_state = agent_state.clone(); let cookie = agent_state.get_or_refresh_cookie(&country).await?; let agent = agent_state .agents diff --git a/src/agent_api/requests/get_client_stats.rs b/src/agent_api/requests/get_client_stats.rs index a61aba1..c5d5028 100644 --- a/src/agent_api/requests/get_client_stats.rs +++ b/src/agent_api/requests/get_client_stats.rs @@ -25,10 +25,11 @@ struct ClientStatsResponse { } pub async fn get_client_stats( - agent_state: AgentState, - country: Country, + agent_state: &AgentState, + country: &Country, device_id: &Uuid, ) -> Result { + let agent_state = agent_state.clone(); let cookie = agent_state.get_or_refresh_cookie(&country).await?; let agent = agent_state .agents diff --git a/src/agent_api/requests/login.rs b/src/agent_api/requests/login.rs index 19bb874..c866cd9 100644 --- a/src/agent_api/requests/login.rs +++ b/src/agent_api/requests/login.rs @@ -8,7 +8,8 @@ use crate::{ enums::errors::internal::{AgentAPI, Error, Result}, }; -pub async fn login(agent_state: AgentState, country: &Country) -> Result { +pub async fn login(agent_state: &AgentState, country: &Country) -> Result { + let agent_state = agent_state.clone(); let client = agent_state.client; let agent = agent_state .agents diff --git a/src/agent_api/state/mod.rs b/src/agent_api/state/mod.rs index cde987d..be9ed70 100644 --- a/src/agent_api/state/mod.rs +++ b/src/agent_api/state/mod.rs @@ -30,7 +30,7 @@ impl AgentState { let cookie = agent.cookie.read().await; if cookie.expires_at < Instant::now() { - let new_cookie = login(self.clone(), country).await?; + let new_cookie = login(self, country).await?; let mut cookie = agent.cookie.write().await; cookie.cookie = new_cookie; diff --git a/src/data/enums/protocol.rs b/src/data/enums/protocol.rs index 7d77b44..1910b01 100644 --- a/src/data/enums/protocol.rs +++ b/src/data/enums/protocol.rs @@ -8,11 +8,8 @@ use diesel::{ }; use serde::{Deserialize, Serialize}; -#[derive( - Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash, -)] +#[derive(Debug, AsExpression, FromSqlRow, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] #[diesel(sql_type = crate::data::schema::sql_types::Protocol)] -#[allow(clippy::upper_case_acronyms)] pub enum Protocol { Vless, Shadowsocks, diff --git a/src/data/models/session.rs b/src/data/models/session.rs index 075044a..8755ea4 100644 --- a/src/data/models/session.rs +++ b/src/data/models/session.rs @@ -10,7 +10,6 @@ use crate::data::{ #[derive(Queryable, Selectable, Debug, Clone)] #[diesel(table_name = schema::session)] #[diesel(check_for_backend(diesel::pg::Pg))] -#[allow(dead_code)] pub struct Session { pub id: Uuid, pub device_id: Uuid, diff --git a/src/data/schema.rs b/src/data/schema.rs index 2d52a99..ea624f0 100644 --- a/src/data/schema.rs +++ b/src/data/schema.rs @@ -32,8 +32,7 @@ pub mod sql_types { diesel::table! { use diesel::sql_types::*; - use super::sql_types::Os; - use super::sql_types::DeviceStatus; + use super::sql_types::{Os, DeviceStatus}; device (id) { id -> Uuid, @@ -59,8 +58,8 @@ diesel::table! { country -> Country, link -> Text, status -> SessionStatus, - up -> Nullable, - down -> Nullable, + up -> Nullable, + down -> Nullable, opened_at -> Timestamp, closed_at -> Nullable, } diff --git a/src/dto/session/internal/new_session.rs b/src/dto/session/internal/new_session.rs index c78f049..dfc352b 100644 --- a/src/dto/session/internal/new_session.rs +++ b/src/dto/session/internal/new_session.rs @@ -9,7 +9,7 @@ use crate::data::{ #[derive(Insertable, Clone)] #[diesel(table_name = schema::session)] pub struct NewSession { - pub session_status: SessionStatus, + pub status: SessionStatus, pub device_id: Uuid, pub country: Country, pub protocol: Protocol, diff --git a/src/services/session/close.rs b/src/services/session/close.rs index fc1a70a..5d0f255 100644 --- a/src/services/session/close.rs +++ b/src/services/session/close.rs @@ -1,19 +1,25 @@ use uuid::Uuid; -use super::close_by_id; use crate::{ + agent_api, dto::session::{interface::get_session, query::ActiveSessionAndDevice}, enums::errors::internal::Result, }; -pub async fn close(pool: &deadpool_diesel::postgres::Pool, device_id: &Uuid) -> Result { - let (session, _, _, _) = get_session( +use super::close_by_id; + +pub async fn close( + pool: &deadpool_diesel::postgres::Pool, + agent_state: agent_api::AgentState, + device_id: &Uuid, +) -> Result { + let (session, _) = get_session( pool, ActiveSessionAndDevice { device_id: *device_id, }, ) .await?; - let session_id = close_by_id(pool, &session.id).await?; + let session_id = close_by_id(pool, agent_state, &session.id).await?; Ok(session_id) } diff --git a/src/services/session/close_by_id.rs b/src/services/session/close_by_id.rs index fb6e147..b9a6aff 100644 --- a/src/services/session/close_by_id.rs +++ b/src/services/session/close_by_id.rs @@ -3,16 +3,14 @@ use tracing::info; use uuid::Uuid; use crate::{ - data::{ - enums::{ConfigStatus, SessionStatus}, - models::Session, - schema, - }, + agent_api, + data::{enums::SessionStatus, models::Session, schema}, enums::errors::internal::{self, Error, Result}, }; pub async fn close_by_id( pool: &deadpool_diesel::postgres::Pool, + agent_state: &agent_api::AgentState, session_id: &Uuid, ) -> Result { let conn = pool.get().await?; diff --git a/src/services/session/create.rs b/src/services/session/create.rs index 535a1a1..9aa4cf0 100644 --- a/src/services/session/create.rs +++ b/src/services/session/create.rs @@ -1,3 +1,4 @@ +use diesel::prelude::*; use tracing::info; use uuid::Uuid; @@ -20,7 +21,7 @@ use crate::{ pub async fn create( pool: &deadpool_diesel::postgres::Pool, - agent_state: agent_api::AgentState, + agent_state: &agent_api::AgentState, country: &Country, protocol: &Protocol, device_id: &Uuid, @@ -48,21 +49,22 @@ pub async fn create( } if let Ok((session, _)) = get_session(pool, ActiveSessionAndDevice { device_id }).await { - let _ = close_by_id(pool, &session.id).await?; + let _ = close_by_id(pool, agent_state, &session.id).await?; } let new_client = - agent_api::requests::create_client(agent_state, country, protocol, &device_id).await?; + agent_api::requests::create_client(&agent_state, &country, &protocol, &device_id).await?; + let data = NewSession { + status: SessionStatus::Active, + device_id, + country, + protocol, + link: new_client.link, + }; let new_session: Session = conn .interact(move |conn| { diesel::insert_into(schema::session::table) - .values(&NewSession { - session_status: SessionStatus::Active, - device_id, - country, - protocol, - link: new_client.link, - }) + .values(&data) .get_result(conn) }) .await??; From 402d6d9d14059f7a46af6841d832bb2a4583cb03 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Fri, 27 Sep 2024 23:59:55 +0300 Subject: [PATCH 6/9] fix create service --- src/data/models/session.rs | 2 +- src/data/schema.rs | 4 ++-- src/services/session/create.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/models/session.rs b/src/data/models/session.rs index 8755ea4..3935f95 100644 --- a/src/data/models/session.rs +++ b/src/data/models/session.rs @@ -13,8 +13,8 @@ use crate::data::{ pub struct Session { pub id: Uuid, pub device_id: Uuid, - pub country: Country, pub protocol: Protocol, + pub country: Country, pub link: String, pub status: SessionStatus, pub up: Option, diff --git a/src/data/schema.rs b/src/data/schema.rs index ea624f0..d25ace5 100644 --- a/src/data/schema.rs +++ b/src/data/schema.rs @@ -58,8 +58,8 @@ diesel::table! { country -> Country, link -> Text, status -> SessionStatus, - up -> Nullable, - down -> Nullable, + up -> Nullable, + down -> Nullable, opened_at -> Timestamp, closed_at -> Nullable, } diff --git a/src/services/session/create.rs b/src/services/session/create.rs index 9aa4cf0..d7d8d98 100644 --- a/src/services/session/create.rs +++ b/src/services/session/create.rs @@ -40,7 +40,7 @@ pub async fn create( .await { let (session, _device) = current_session; - let response = response::Session::from(session); + let response = response::Session::from(session.clone()); info!( "Found active session with the same country and protocol: {}", session.id From f20ef79fee8b7d84a79b3088121f5d5e048ae462 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Sat, 28 Sep 2024 13:20:08 +0300 Subject: [PATCH 7/9] feature: finish all session associated requests and methods --- src/dto/session/request/create_session.rs | 3 +- src/handlers/device/revoke_device.rs | 6 +-- src/handlers/session/close_session.rs | 6 +-- src/handlers/session/create_session.rs | 13 ++++-- src/services/device/revoke.rs | 6 ++- src/services/mod.rs | 1 - src/services/session/close.rs | 2 +- src/services/session/close_by_id.rs | 41 +++++++---------- src/services/session/get_history.rs | 56 +---------------------- 9 files changed, 42 insertions(+), 92 deletions(-) diff --git a/src/dto/session/request/create_session.rs b/src/dto/session/request/create_session.rs index 07a60f5..59153f7 100644 --- a/src/dto/session/request/create_session.rs +++ b/src/dto/session/request/create_session.rs @@ -1,8 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::data::enums::Country; +use crate::data::enums::{Country, Protocol}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CreateSession { pub country: Country, + pub protocol: Protocol, } diff --git a/src/handlers/device/revoke_device.rs b/src/handlers/device/revoke_device.rs index b4fbb8a..5be72be 100644 --- a/src/handlers/device/revoke_device.rs +++ b/src/handlers/device/revoke_device.rs @@ -2,7 +2,6 @@ use axum::{ extract::{Path, State}, http::StatusCode, }; -use deadpool_diesel::postgres::Pool; use tracing::info; use uuid::Uuid; @@ -10,14 +9,15 @@ use crate::{ dto::{auth::internal::AccessToken, response::success::Response}, enums::errors::external::Result, services::device, + state::AppState, }; pub async fn revoke_device( claims: AccessToken, - State(pool): State, + State(state): State, Path(device_id): Path, ) -> Result> { - device::revoke(&pool, claims, &device_id).await?; + device::revoke(&state.pool, &state.agent_state, claims, &device_id).await?; info!("Device revoked successfully: {:?}", device_id); diff --git a/src/handlers/session/close_session.rs b/src/handlers/session/close_session.rs index 3ed8577..d2cf4f2 100644 --- a/src/handlers/session/close_session.rs +++ b/src/handlers/session/close_session.rs @@ -1,18 +1,18 @@ use axum::{extract::State, http::StatusCode}; -use deadpool_diesel::postgres::Pool; use tracing::info; use crate::{ dto::{auth::internal::AccessToken, response::success::Response}, enums::errors::external::Result, services::session, + state::AppState, }; pub async fn close_session( claims: AccessToken, - State(pool): State, + State(state): State, ) -> Result> { - let session_id = session::close(&pool, &claims.device_id).await?; + let session_id = session::close(&state.pool, &state.agent_state, &claims.device_id).await?; info!("Closed session successfully: {}", session_id); diff --git a/src/handlers/session/create_session.rs b/src/handlers/session/create_session.rs index 2ba4f29..4c68e43 100644 --- a/src/handlers/session/create_session.rs +++ b/src/handlers/session/create_session.rs @@ -1,5 +1,4 @@ use axum::{extract::State, http::StatusCode}; -use deadpool_diesel::postgres::Pool; use tracing::info; use crate::{ @@ -11,14 +10,22 @@ use crate::{ enums::errors::external::Result, extractors::Json, services::session, + state::AppState, }; pub async fn create_session( claims: AccessToken, - State(pool): State, + State(state): State, Json(payload): Json, ) -> Result> { - let session = session::create(&pool, &claims.device_id, &payload.country).await?; + let session = session::create( + &state.pool, + &state.agent_state, + &payload.country, + &payload.protocol, + &claims.device_id, + ) + .await?; info!("Session created successfully: {}", session.session_id); diff --git a/src/services/device/revoke.rs b/src/services/device/revoke.rs index 2dd47aa..afb3428 100644 --- a/src/services/device/revoke.rs +++ b/src/services/device/revoke.rs @@ -3,6 +3,7 @@ use tracing::info; use uuid::Uuid; use crate::{ + agent_api, data::{enums::DeviceStatus, schema}, dto::{ auth::internal::AccessToken, @@ -14,6 +15,7 @@ use crate::{ pub async fn revoke( pool: &deadpool_diesel::postgres::Pool, + agent_state: &agent_api::AgentState, author: AccessToken, device_id: &Uuid, ) -> Result<()> { @@ -36,8 +38,8 @@ pub async fn revoke( return Err(Error::Device(Device::AlreadyRevoked)); } - if let Ok((session, _, _, _)) = get_session(pool, ActiveSessionAndDevice { device_id }).await { - let _ = services::session::close_by_id(pool, &session.id).await?; + if let Ok((session, _)) = get_session(pool, ActiveSessionAndDevice { device_id }).await { + let _ = services::session::close_by_id(pool, agent_state, &session.id).await?; } conn.interact(move |conn| { diff --git a/src/services/mod.rs b/src/services/mod.rs index d8a8df7..aa8f2fe 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,4 +1,3 @@ -pub mod config; pub mod device; pub mod email; pub mod oauth_providers; diff --git a/src/services/session/close.rs b/src/services/session/close.rs index 5d0f255..0865db3 100644 --- a/src/services/session/close.rs +++ b/src/services/session/close.rs @@ -10,7 +10,7 @@ use super::close_by_id; pub async fn close( pool: &deadpool_diesel::postgres::Pool, - agent_state: agent_api::AgentState, + agent_state: &agent_api::AgentState, device_id: &Uuid, ) -> Result { let (session, _) = get_session( diff --git a/src/services/session/close_by_id.rs b/src/services/session/close_by_id.rs index b9a6aff..68f6f34 100644 --- a/src/services/session/close_by_id.rs +++ b/src/services/session/close_by_id.rs @@ -4,7 +4,7 @@ use uuid::Uuid; use crate::{ agent_api, - data::{enums::SessionStatus, models::Session, schema}, + data::{models::Session, schema}, enums::errors::internal::{self, Error, Result}, }; @@ -16,29 +16,22 @@ pub async fn close_by_id( let conn = pool.get().await?; let session_id = *session_id; - conn.interact(move |conn| { - let session = match diesel::update(schema::session::table) - .filter(schema::session::id.eq(session_id)) - .set(( - schema::session::status.eq(SessionStatus::Closed), - schema::session::closed_at.eq(diesel::dsl::now), - )) - .get_result::(conn) - .map_err(|_| Error::Session(internal::Session::NotFound)) - { - Ok(session) => { - info!("Found session: {}", session.id); - session - } - Err(_) => return Err(Error::Session(internal::Session::NotFound)), - }; - let _ = diesel::update(schema::config::table) - .filter(schema::config::id.eq(session.config_id)) - .set(schema::config::status.eq(ConfigStatus::NotInUse)) - .execute(conn); - Ok(()) - }) - .await??; + let session = conn + .interact(move |conn| { + schema::session::table + .filter(schema::session::id.eq(session_id)) + .first::(conn) + }) + .await? + .map_err(|_| Error::Session(internal::Session::NotFound))?; + + agent_api::requests::delete_client( + agent_state, + &session.country, + &session.protocol, + &session.device_id, + ) + .await?; info!("Closed session: {}", session_id); diff --git a/src/services/session/get_history.rs b/src/services/session/get_history.rs index dca5448..9655447 100644 --- a/src/services/session/get_history.rs +++ b/src/services/session/get_history.rs @@ -4,7 +4,7 @@ use uuid::Uuid; use crate::{ data::{ enums::{Country, SessionStatus}, - models::{Config, Server, Session}, + models::Session, schema, }, dto::session::{internal::SessionHistory, request::Params}, @@ -16,57 +16,5 @@ pub async fn get_history( user_id: &Uuid, params: &Params, ) -> Result> { - let conn = pool.get().await?; - - let limit = params.limit.unwrap_or(10); - let offset = params.offset.unwrap_or(0); - let devices = params.devices.clone(); - let countries = params.countries.clone(); - let user_id = *user_id; - - let data: Vec<(Session, Config, Server)> = conn - .interact(move |conn| { - schema::session::table - .inner_join(schema::config::table.inner_join(schema::server::table)) - .filter(schema::session::status.eq(SessionStatus::Closed)) - .filter( - schema::server::country.eq_any( - countries.unwrap_or( - schema::server::table - .select(schema::server::country) - .load::(conn)?, - ), - ), - ) - .filter( - schema::session::device_id.eq_any( - devices.unwrap_or( - schema::device::table - .select(schema::device::id) - .filter(schema::device::user_id.eq(user_id)) - .load::(conn)?, - ), - ), - ) - .limit(limit) - .offset(offset) - .select(( - Session::as_select(), - Config::as_select(), - Server::as_select(), - )) - .load::<(Session, Config, Server)>(conn) - }) - .await??; - - let history = data.iter().map(|(session, _, server)| SessionHistory { - id: session.id, - device_id: session.device_id, - opened_at: session.opened_at, - closed_at: session.closed_at.unwrap(), - duration: (session.closed_at.unwrap() - session.opened_at).num_seconds(), - country: server.country, - }); - - Ok(history.collect()) + todo!() } From 7d05337fe069cb3464a7073e3565e45a2e66cbf6 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Sat, 28 Sep 2024 13:20:29 +0300 Subject: [PATCH 8/9] fmt --- src/dto/session/query/active_session_and_device.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/dto/session/query/active_session_and_device.rs b/src/dto/session/query/active_session_and_device.rs index eb671f5..a711d0b 100644 --- a/src/dto/session/query/active_session_and_device.rs +++ b/src/dto/session/query/active_session_and_device.rs @@ -30,10 +30,7 @@ impl SessionBy for ActiveSessionAndDevice { .inner_join(schema::device::table) .filter(schema::session::device_id.eq(device_id)) .filter(schema::session::status.eq(SessionStatus::Active)) - .select(( - Session::as_select(), - Device::as_select() - )) + .select((Session::as_select(), Device::as_select())) .load::<(Session, Device)>(conn) }) .await??; From 9742f0d2773c33629015ec4ecf96aedbb5e321d0 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Sat, 28 Sep 2024 15:44:10 +0300 Subject: [PATCH 9/9] add migration --- .vscode/launch.json | 45 +++++ .vscode/settings.json | 3 +- migrations/2024-08-02-114025_initial/down.sql | 66 ------- migrations/2024-08-02-114025_initial/up.sql | 166 ------------------ .../down.sql | 9 - .../up.sql | 23 --- .../2024-09-27-203615_new_sessions/down.sql | 1 - .../2024-09-27-203615_new_sessions/up.sql | 1 - migrations/2024-09-28-102954_initial/down.sql | 62 +++++++ migrations/2024-09-28-102954_initial/up.sql | 136 ++++++++++++++ src/agent_api/dto/client_stats.rs | 4 +- src/agent_api/requests/create_client.rs | 4 +- src/agent_api/requests/get_client_stats.rs | 4 +- src/agent_api/requests/login.rs | 13 +- src/agent_api/state/mod.rs | 2 +- src/data/models/device.rs | 2 - src/data/models/session.rs | 4 +- src/data/models/user.rs | 2 - src/data/schema.rs | 24 ++- src/services/session/create.rs | 2 +- src/tests/e2e/sql/seed.sql | 14 +- 21 files changed, 280 insertions(+), 307 deletions(-) create mode 100644 .vscode/launch.json delete mode 100644 migrations/2024-08-02-114025_initial/down.sql delete mode 100644 migrations/2024-08-02-114025_initial/up.sql delete mode 100644 migrations/2024-08-11-120853_email_confirmation/down.sql delete mode 100644 migrations/2024-08-11-120853_email_confirmation/up.sql delete mode 100644 migrations/2024-09-27-203615_new_sessions/down.sql delete mode 100644 migrations/2024-09-27-203615_new_sessions/up.sql create mode 100644 migrations/2024-09-28-102954_initial/down.sql create mode 100644 migrations/2024-09-28-102954_initial/up.sql diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2d889ed --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'helios-master-backend'", + "cargo": { + "args": [ + "build", + "--bin=helios-master-backend", + "--package=helios-master-backend" + ], + "filter": { + "name": "helios-master-backend", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'helios-master-backend'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=helios-master-backend", + "--package=helios-master-backend" + ], + "filter": { + "name": "helios-master-backend", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 37d0524..112d793 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true - } + }, + "Prettier-SQL.SQLFlavourOverride": "postgresql" } diff --git a/migrations/2024-08-02-114025_initial/down.sql b/migrations/2024-08-02-114025_initial/down.sql deleted file mode 100644 index ba993c1..0000000 --- a/migrations/2024-08-02-114025_initial/down.sql +++ /dev/null @@ -1,66 +0,0 @@ --- Drop triggers -DROP TRIGGER table_updated_at_trigger ON "server"; - -DROP TRIGGER table_updated_at_trigger ON "config"; - -DROP TRIGGER table_updated_at_trigger ON "user"; - -DROP TRIGGER table_updated_at_trigger ON "device"; - --- Drop function -DROP FUNCTION update_updated_at (); - --- Drop foreign key constraints -ALTER TABLE "session" -DROP CONSTRAINT "session_config_id_fkey"; - -ALTER TABLE "session" -DROP CONSTRAINT "session_device_id_fkey"; - -ALTER TABLE "device" -DROP CONSTRAINT "device_user_id_fkey"; - -ALTER TABLE "config" -DROP CONSTRAINT "config_server_id_fkey"; - --- Drop indexes -DROP INDEX "session_configId_key"; - -DROP INDEX "session_deviceId_key"; - -DROP INDEX "user_email_key"; - -DROP INDEX "config_privateKey_key"; - -DROP INDEX "server_wireguardUri_key"; - -DROP INDEX "server_backendUri_key"; - -DROP INDEX "server_publicKey_key"; - --- Drop tables -DROP TABLE "session"; - -DROP TABLE "device"; - -DROP TABLE "user"; - -DROP TABLE "config"; - -DROP TABLE "server"; - --- Drop types -DROP TYPE "UserStatus"; - -DROP TYPE "DeviceStatus"; - -DROP TYPE "OS"; - -DROP TYPE "ConfigStatus"; - -DROP TYPE "SessionStatus"; - -DROP TYPE "Country"; - --- Drop extension -DROP EXTENSION "uuid-ossp"; \ No newline at end of file diff --git a/migrations/2024-08-02-114025_initial/up.sql b/migrations/2024-08-02-114025_initial/up.sql deleted file mode 100644 index 40252f9..0000000 --- a/migrations/2024-08-02-114025_initial/up.sql +++ /dev/null @@ -1,166 +0,0 @@ -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -CREATE TYPE "Country" AS ENUM ('UK', 'USA', 'Germany'); - -CREATE TYPE "SessionStatus" AS ENUM ('Active', 'Closed'); - -CREATE TYPE "ConfigStatus" AS ENUM ('InUse', 'NotInUse'); - -CREATE TYPE "OS" AS ENUM ( - 'Windows', - 'Linux', - 'MacOS', - 'IOS', - 'Android', - 'Unknown' -); - -CREATE TYPE "DeviceStatus" AS ENUM ('LoggedIn', 'LoggedOut'); - -CREATE TYPE "UserStatus" AS ENUM ( - 'Active', - 'Banned', - 'PermanentlyBanned', - 'Deleted' -); - -CREATE TYPE "OAuthProvider" AS ENUM ('Google', 'Discord', 'GitHub'); - -CREATE TABLE - "server" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "public_key" TEXT NOT NULL, - "wireguard_uri" TEXT NOT NULL, - "country" "Country" NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE - "config" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "private_key" TEXT NOT NULL, - "user_ip" TEXT NOT NULL, - "server_id" uuid NOT NULL, - "status" "ConfigStatus" NOT NULL DEFAULT 'NotInUse', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE - "user" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "email" TEXT NOT NULL, - "banned_at" TIMESTAMP(3), - "banned_till" TIMESTAMP(3), - "status" "UserStatus" NOT NULL DEFAULT 'Active', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE - "classic_auth" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "user_id" uuid NOT NULL, - "password_hash" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE - "oauth" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "user_id" uuid NOT NULL, - "provider" TEXT NOT NULL, - "metadata" JSONB NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE - "device" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "name" TEXT NOT NULL, - "os" "OS" NOT NULL, - "user_id" uuid NOT NULL, - "banned_at" TIMESTAMP(3), - "banned_till" TIMESTAMP(3), - "status" "DeviceStatus" NOT NULL DEFAULT 'LoggedIn', - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE - "session" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "status" "SessionStatus" NOT NULL DEFAULT 'Active', - "opened_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "closed_at" TIMESTAMP(3), - "device_id" uuid NOT NULL, - "config_id" uuid NOT NULL - ); - -CREATE UNIQUE INDEX "server_publicKey_key" ON "server" ("public_key"); - -CREATE UNIQUE INDEX "server_wireguardUri_key" ON "server" ("wireguard_uri"); - -CREATE INDEX "config_privateKey_key" ON "config" ("private_key"); - -CREATE UNIQUE INDEX "user_email_key" ON "user" ("email"); - -CREATE INDEX "session_deviceId_key" ON "session" ("device_id"); - -CREATE INDEX "session_configId_key" ON "session" ("config_id"); - -ALTER TABLE "config" ADD CONSTRAINT "config_server_id_fkey" FOREIGN KEY ("server_id") REFERENCES "server" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -ALTER TABLE "device" ADD CONSTRAINT "device_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -ALTER TABLE "session" ADD CONSTRAINT "session_device_id_fkey" FOREIGN KEY ("device_id") REFERENCES "device" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -ALTER TABLE "session" ADD CONSTRAINT "session_config_id_fkey" FOREIGN KEY ("config_id") REFERENCES "config" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -ALTER TABLE "classic_auth" ADD CONSTRAINT "classic_auth_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -ALTER TABLE "oauth" ADD CONSTRAINT "oauth_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -CREATE OR REPLACE FUNCTION update_updated_at() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER table_updated_at_trigger -BEFORE UPDATE ON "server" -FOR EACH ROW -EXECUTE FUNCTION update_updated_at(); - - -CREATE TRIGGER table_updated_at_trigger -BEFORE UPDATE ON "config" -FOR EACH ROW -EXECUTE FUNCTION update_updated_at(); - - -CREATE TRIGGER table_updated_at_trigger -BEFORE UPDATE ON "user" -FOR EACH ROW -EXECUTE FUNCTION update_updated_at(); - - -CREATE TRIGGER table_updated_at_trigger -BEFORE UPDATE ON "device" -FOR EACH ROW -EXECUTE FUNCTION update_updated_at(); - -CREATE TRIGGER table_updated_at_trigger -BEFORE UPDATE ON "classic_auth" -FOR EACH ROW -EXECUTE FUNCTION update_updated_at(); - -CREATE TRIGGER table_updated_at_trigger -BEFORE UPDATE ON "oauth" -FOR EACH ROW -EXECUTE FUNCTION update_updated_at(); diff --git a/migrations/2024-08-11-120853_email_confirmation/down.sql b/migrations/2024-08-11-120853_email_confirmation/down.sql deleted file mode 100644 index d4ae949..0000000 --- a/migrations/2024-08-11-120853_email_confirmation/down.sql +++ /dev/null @@ -1,9 +0,0 @@ -DROP TRIGGER table_confirmed_at_trigger ON "email_confirmation"; - -DROP FUNCTION confirmed_at (); - -DROP -ALTER TABLE "email_confirmation" -DROP CONSTRAINT "fk_email_confirmation_user_id"; - -DROP TABLE "email_confirmation"; \ No newline at end of file diff --git a/migrations/2024-08-11-120853_email_confirmation/up.sql b/migrations/2024-08-11-120853_email_confirmation/up.sql deleted file mode 100644 index e2edda1..0000000 --- a/migrations/2024-08-11-120853_email_confirmation/up.sql +++ /dev/null @@ -1,23 +0,0 @@ -CREATE TABLE - "email_confirmation" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), - "user_id" uuid NOT NULL, - "confirmed" BOOLEAN NOT NULL DEFAULT FALSE, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "confirmed_at" TIMESTAMP(3) - ); - -ALTER TABLE "email_confirmation" ADD CONSTRAINT "fk_email_confirmation_user_id" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE CASCADE; - -CREATE OR REPLACE FUNCTION confirmed_at() -RETURNS TRIGGER AS $$ -BEGIN - NEW.confirmed_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER table_confirmed_at_trigger -BEFORE UPDATE ON "email_confirmation" -FOR EACH ROW -EXECUTE FUNCTION confirmed_at(); diff --git a/migrations/2024-09-27-203615_new_sessions/down.sql b/migrations/2024-09-27-203615_new_sessions/down.sql deleted file mode 100644 index d9a93fe..0000000 --- a/migrations/2024-09-27-203615_new_sessions/down.sql +++ /dev/null @@ -1 +0,0 @@ --- This file should undo anything in `up.sql` diff --git a/migrations/2024-09-27-203615_new_sessions/up.sql b/migrations/2024-09-27-203615_new_sessions/up.sql deleted file mode 100644 index b37f10d..0000000 --- a/migrations/2024-09-27-203615_new_sessions/up.sql +++ /dev/null @@ -1 +0,0 @@ --- Your SQL goes here diff --git a/migrations/2024-09-28-102954_initial/down.sql b/migrations/2024-09-28-102954_initial/down.sql new file mode 100644 index 0000000..8001dda --- /dev/null +++ b/migrations/2024-09-28-102954_initial/down.sql @@ -0,0 +1,62 @@ +DROP TRIGGER IF EXISTS table_updated_at_trigger ON "user"; + +DROP TRIGGER IF EXISTS table_updated_at_trigger ON "device"; + +DROP TRIGGER IF EXISTS table_updated_at_trigger ON "classic_auth"; + +DROP TRIGGER IF EXISTS table_updated_at_trigger ON "oauth"; + +DROP TRIGGER IF EXISTS table_confirmed_at_trigger ON "email_confirmation"; + +DROP FUNCTION IF EXISTS update_updated_at (); + +DROP FUNCTION IF EXISTS confirmed_at (); + +ALTER TABLE "device" +DROP CONSTRAINT IF EXISTS "device_user_id_fkey"; + +ALTER TABLE "session" +DROP CONSTRAINT IF EXISTS "session_device_id_fkey"; + +ALTER TABLE "classic_auth" +DROP CONSTRAINT IF EXISTS "classic_auth_user_id_fkey"; + +ALTER TABLE "oauth" +DROP CONSTRAINT IF EXISTS "oauth_user_id_fkey"; + +ALTER TABLE "email_confirmation" +DROP CONSTRAINT IF EXISTS "fk_email_confirmation_user_id"; + +DROP INDEX IF EXISTS "user_email_key"; + +DROP INDEX IF EXISTS "session_device_id_key"; + +DROP INDEX IF EXISTS "session_device_id_active_status_key"; + +DROP TABLE IF EXISTS "session"; + +DROP TABLE IF EXISTS "device"; + +DROP TABLE IF EXISTS "email_confirmation"; + +DROP TABLE IF EXISTS "oauth"; + +DROP TABLE IF EXISTS "classic_auth"; + +DROP TABLE IF EXISTS "user"; + +DROP TYPE IF EXISTS "Protocol"; + +DROP TYPE IF EXISTS "OAuthProvider"; + +DROP TYPE IF EXISTS "UserStatus"; + +DROP TYPE IF EXISTS "SessionStatus"; + +DROP TYPE IF EXISTS "OS"; + +DROP TYPE IF EXISTS "DeviceStatus"; + +DROP TYPE IF EXISTS "Country"; + +DROP EXTENSION IF EXISTS "uuid-ossp"; \ No newline at end of file diff --git a/migrations/2024-09-28-102954_initial/up.sql b/migrations/2024-09-28-102954_initial/up.sql new file mode 100644 index 0000000..bf23fcd --- /dev/null +++ b/migrations/2024-09-28-102954_initial/up.sql @@ -0,0 +1,136 @@ +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +CREATE TYPE "Country" AS ENUM('UK', 'USA', 'Germany'); + +CREATE TYPE "DeviceStatus" AS ENUM('LoggedIn', 'LoggedOut'); + +CREATE TYPE "OS" AS ENUM('Windows', 'Linux', 'MacOS', 'IOS', 'Android', 'Unknown'); + +CREATE TYPE "SessionStatus" AS ENUM('Active', 'Closed'); + +CREATE TYPE "UserStatus" AS ENUM('Active', 'Banned', 'PermanentlyBanned', 'Deleted'); + +CREATE TYPE "OAuthProvider" AS ENUM('Google', 'Discord', 'GitHub'); + +CREATE TYPE "Protocol" AS ENUM('Vless', 'Shadowsocks'); + +CREATE TABLE + "user" ( + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), + "email" TEXT NOT NULL, + "status" "UserStatus" NOT NULL DEFAULT 'Active', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + +CREATE TABLE + "classic_auth" ( + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), + "user_id" uuid NOT NULL, + "password_hash" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + +CREATE TABLE + "oauth" ( + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), + "user_id" uuid NOT NULL, + "provider" TEXT NOT NULL, + "metadata" JSONB NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + +CREATE TABLE + "email_confirmation" ( + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), + "user_id" uuid NOT NULL, + "confirmed" BOOLEAN NOT NULL DEFAULT FALSE, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "confirmed_at" TIMESTAMP(3) + ); + +CREATE TABLE + "device" ( + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), + "name" TEXT NOT NULL, + "os" "OS" NOT NULL, + "user_id" uuid NOT NULL, + "status" "DeviceStatus" NOT NULL DEFAULT 'LoggedIn', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP + ); + +CREATE TABLE + "session" ( + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4 (), + "device_id" uuid NOT NULL, + "link" TEXT NOT NULL, + "protocol" "Protocol" NOT NULL, + "country" "Country" NOT NULL, + "status" "SessionStatus" NOT NULL DEFAULT 'Active', + "up" BIGINT, + "down" BIGINT, + "opened_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "closed_at" TIMESTAMP(3) + ); + +CREATE UNIQUE INDEX "user_email_key" ON "user" ("email"); + +CREATE INDEX "session_device_id_key" ON "session" ("device_id"); + +CREATE UNIQUE INDEX "session_device_id_active_status_key" ON "session" ("device_id") +WHERE + status = 'Active'; + +ALTER TABLE "device" +ADD CONSTRAINT "device_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +ALTER TABLE "session" +ADD CONSTRAINT "session_device_id_fkey" FOREIGN KEY ("device_id") REFERENCES "device" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +ALTER TABLE "classic_auth" +ADD CONSTRAINT "classic_auth_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +ALTER TABLE "oauth" +ADD CONSTRAINT "oauth_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +ALTER TABLE "email_confirmation" +ADD CONSTRAINT "fk_email_confirmation_user_id" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE CASCADE; + +CREATE +OR REPLACE FUNCTION confirmed_at () RETURNS TRIGGER AS $$ +BEGIN + NEW.confirmed_at = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER table_confirmed_at_trigger BEFORE +UPDATE ON "email_confirmation" FOR EACH ROW +EXECUTE FUNCTION confirmed_at (); + +CREATE +OR REPLACE FUNCTION update_updated_at () RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER table_updated_at_trigger BEFORE +UPDATE ON "user" FOR EACH ROW +EXECUTE FUNCTION update_updated_at (); + +CREATE TRIGGER table_updated_at_trigger BEFORE +UPDATE ON "device" FOR EACH ROW +EXECUTE FUNCTION update_updated_at (); + +CREATE TRIGGER table_updated_at_trigger BEFORE +UPDATE ON "classic_auth" FOR EACH ROW +EXECUTE FUNCTION update_updated_at (); + +CREATE TRIGGER table_updated_at_trigger BEFORE +UPDATE ON "oauth" FOR EACH ROW +EXECUTE FUNCTION update_updated_at (); \ No newline at end of file diff --git a/src/agent_api/dto/client_stats.rs b/src/agent_api/dto/client_stats.rs index ce13c3f..3624f18 100644 --- a/src/agent_api/dto/client_stats.rs +++ b/src/agent_api/dto/client_stats.rs @@ -1,4 +1,4 @@ pub struct ClientStats { - pub up: i32, - pub down: i32, + pub up: i64, + pub down: i64, } diff --git a/src/agent_api/requests/create_client.rs b/src/agent_api/requests/create_client.rs index 3c829dc..5f82318 100644 --- a/src/agent_api/requests/create_client.rs +++ b/src/agent_api/requests/create_client.rs @@ -17,10 +17,10 @@ pub async fn create_client( device_id: &Uuid, ) -> Result { let agent_state = agent_state.clone(); - let cookie = agent_state.get_or_refresh_cookie(&country).await?; + let cookie = agent_state.get_or_refresh_cookie(country).await?; let agent = agent_state .agents - .get(&country) + .get(country) .ok_or(Error::AgentAPI(AgentAPI::Internal))?; let client = agent_state.client; diff --git a/src/agent_api/requests/get_client_stats.rs b/src/agent_api/requests/get_client_stats.rs index c5d5028..49a9ce1 100644 --- a/src/agent_api/requests/get_client_stats.rs +++ b/src/agent_api/requests/get_client_stats.rs @@ -17,8 +17,8 @@ struct ClientStatsResponse { pub inboundId: u32, pub enable: bool, pub email: String, - pub up: i32, - pub down: i32, + pub up: i64, + pub down: i64, pub expiryTime: u32, pub total: u32, pub reset: u32, diff --git a/src/agent_api/requests/login.rs b/src/agent_api/requests/login.rs index c866cd9..607245e 100644 --- a/src/agent_api/requests/login.rs +++ b/src/agent_api/requests/login.rs @@ -13,7 +13,7 @@ pub async fn login(agent_state: &AgentState, country: &Country) -> Result Result>().await?; @@ -34,5 +38,8 @@ pub async fn login(agent_state: &AgentState, country: &Country) -> Result, - pub banned_till: Option, pub status: DeviceStatus, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, diff --git a/src/data/models/session.rs b/src/data/models/session.rs index 3935f95..4d31e13 100644 --- a/src/data/models/session.rs +++ b/src/data/models/session.rs @@ -17,8 +17,8 @@ pub struct Session { pub country: Country, pub link: String, pub status: SessionStatus, - pub up: Option, - pub down: Option, + pub up: Option, + pub down: Option, pub opened_at: NaiveDateTime, pub closed_at: Option, } diff --git a/src/data/models/user.rs b/src/data/models/user.rs index 56203c2..99db605 100644 --- a/src/data/models/user.rs +++ b/src/data/models/user.rs @@ -11,8 +11,6 @@ use crate::data::{enums::UserStatus, schema}; pub struct User { pub id: Uuid, pub email: String, - pub banned_at: Option, - pub banned_till: Option, pub status: UserStatus, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, diff --git a/src/data/schema.rs b/src/data/schema.rs index d25ace5..c2e25c1 100644 --- a/src/data/schema.rs +++ b/src/data/schema.rs @@ -13,14 +13,6 @@ pub mod sql_types { #[diesel(postgres_type(name = "OS"))] pub struct Os; - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] - #[diesel(postgres_type(name = "SessionStatus"))] - pub struct SessionStatus; - - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] - #[diesel(postgres_type(name = "UserStatus"))] - pub struct UserStatus; - #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "OAuthProvider"))] pub struct OAuthProvider; @@ -28,6 +20,14 @@ pub mod sql_types { #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "Protocol"))] pub struct Protocol; + + #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] + #[diesel(postgres_type(name = "SessionStatus"))] + pub struct SessionStatus; + + #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)] + #[diesel(postgres_type(name = "UserStatus"))] + pub struct UserStatus; } diesel::table! { @@ -39,8 +39,6 @@ diesel::table! { name -> Text, os -> Os, user_id -> Uuid, - banned_at -> Nullable, - banned_till -> Nullable, status -> DeviceStatus, created_at -> Timestamp, updated_at -> Timestamp, @@ -58,8 +56,8 @@ diesel::table! { country -> Country, link -> Text, status -> SessionStatus, - up -> Nullable, - down -> Nullable, + up -> Nullable, + down -> Nullable, opened_at -> Timestamp, closed_at -> Nullable, } @@ -72,8 +70,6 @@ diesel::table! { user (id) { id -> Uuid, email -> Text, - banned_at -> Nullable, - banned_till -> Nullable, status -> UserStatus, created_at -> Timestamp, updated_at -> Timestamp, diff --git a/src/services/session/create.rs b/src/services/session/create.rs index d7d8d98..74900e4 100644 --- a/src/services/session/create.rs +++ b/src/services/session/create.rs @@ -53,7 +53,7 @@ pub async fn create( } let new_client = - agent_api::requests::create_client(&agent_state, &country, &protocol, &device_id).await?; + agent_api::requests::create_client(agent_state, &country, &protocol, &device_id).await?; let data = NewSession { status: SessionStatus::Active, device_id, diff --git a/src/tests/e2e/sql/seed.sql b/src/tests/e2e/sql/seed.sql index f9d5df7..337dc1f 100644 --- a/src/tests/e2e/sql/seed.sql +++ b/src/tests/e2e/sql/seed.sql @@ -1,11 +1,7 @@ -INSERT INTO "server" ("public_key", "wireguard_uri", "country") +INSERT INTO + "user" (email) VALUES - ('public_key_1', 'wireguard_uri_1', 'UK'), - ('public_key_2', 'wireguard_uri_2', 'USA'), - ('public_key_3', 'wireguard_uri_3', 'Germany'); + ('test@email.com'); -INSERT INTO "config" ("private_key", "user_ip", "server_id") -VALUES - ('private_key_1', '192.168.1.1', (SELECT "id" FROM "server" WHERE "public_key" = 'public_key_1')), - ('private_key_2', '192.168.1.2', (SELECT "id" FROM "server" WHERE "public_key" = 'public_key_2')), - ('private_key_3', '192.168.1.3', (SELECT "id" FROM "server" WHERE "public_key" = 'public_key_3')); +INSERT INTO "device" (name, os, user_id) +VALUES ('Pixel 8a', 'Android', (SELECT id from "user" WHERE email = 'test@email.com'));