From 6a726b736c90bb692c12386dda2e4321808eb4a4 Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 25 Sep 2023 18:10:24 +0300 Subject: [PATCH 01/73] Use forms module, adjust struct types --- src/forms/mod.rs | 4 + src/forms/stack.rs | 260 ++++++++++++++++++++++++++++++++++++++++ src/models/mod.rs | 2 + src/models/stack.rs | 245 ------------------------------------- src/routes/stack/add.rs | 22 ++-- 5 files changed, 277 insertions(+), 256 deletions(-) create mode 100644 src/forms/stack.rs diff --git a/src/forms/mod.rs b/src/forms/mod.rs index 7a10a3e..f4280a7 100644 --- a/src/forms/mod.rs +++ b/src/forms/mod.rs @@ -1,3 +1,7 @@ mod rating; +pub(crate) mod stack; + pub use rating::*; + +pub use stack::*; diff --git a/src/forms/stack.rs b/src/forms/stack.rs new file mode 100644 index 0000000..c8ea0c4 --- /dev/null +++ b/src/forms/stack.rs @@ -0,0 +1,260 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use serde_valid::Validate; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] +#[serde(rename_all = "camelCase")] +pub struct StackForm { + pub common_domain: String, + pub domain_list: Option, + pub region: String, + pub zone: Option, + pub server: String, + pub os: String, + pub ssl: String, + pub vars: Option>, + #[serde(rename = "integrated_features")] + pub integrated_features: Option>, + #[serde(rename = "extended_features")] + pub extended_features: Option>, + pub subscriptions: Option>, + #[serde(rename = "save_token")] + pub save_token: bool, + #[serde(rename = "cloud_token")] + pub cloud_token: String, + pub provider: String, + #[serde(rename = "stack_code")] + pub stack_code: String, + #[serde(rename = "selected_plan")] + pub selected_plan: String, + pub custom: Custom, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DomainList { +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Var { +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Price { + pub value: f64 +} +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] +#[serde(rename_all = "camelCase")] +pub struct Custom { + pub web: Vec, + pub feature: Option>, + pub service: Option>, + #[serde(rename = "servers_count")] + pub servers_count: i64, + #[serde(rename = "custom_stack_name")] + pub custom_stack_name: String, + #[serde(rename = "custom_stack_code")] + pub custom_stack_code: String, + #[serde(rename = "custom_stack_git_url")] + pub custom_stack_git_url: Option, + #[serde(rename = "custom_stack_category")] + pub custom_stack_category: Option>, + #[serde(rename = "custom_stack_short_description")] + pub custom_stack_short_description: Option, + #[serde(rename = "custom_stack_description")] + pub custom_stack_description: Option, + #[serde(rename = "project_name")] + pub project_name: String, + #[serde(rename = "project_overview")] + pub project_overview: Option, + #[serde(rename = "project_description")] + pub project_description: Option, +} + + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Web { + pub name: String, + pub code: String, + pub domain: Option, + pub shared_ports: Option>, + pub versions: Option>, + pub custom: bool, + #[serde(rename = "type")] + pub type_field: String, + pub main: bool, + #[serde(rename = "_id")] + pub id: String, + #[serde(rename = "dockerhub_user")] + pub dockerhub_user: String, + #[serde(rename = "dockerhub_name")] + pub dockerhub_name: String, + pub url_app: Option, + pub url_git: Option, + #[serde(rename = "disk_size")] + pub disk_size: String, + #[serde(rename = "ram_size")] + pub ram_size: String, + pub cpu: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Feature { + #[serde(rename = "_etag")] + pub etag: Option, + #[serde(rename = "_id")] + pub id: u32, + #[serde(rename = "_created")] + pub created: Option, + #[serde(rename = "_updated")] + pub updated: Option, + pub name: String, + pub code: String, + pub role: Vec, + #[serde(rename = "type")] + pub type_field: String, + pub default: Option, + pub popularity: Option, + pub descr: Option, + pub ports: Option, + pub commercial: Option, + pub subscription: Option, + pub autodeploy: Option, + pub suggested: Option, + pub dependency: Option, + #[serde(rename = "avoid_render")] + pub avoid_render: Option, + pub price: Option, + pub icon: Option, + #[serde(rename = "category_id")] + pub category_id: Option, + #[serde(rename = "parent_app_id")] + pub parent_app_id: Option, + #[serde(rename = "full_description")] + pub full_description: Option, + pub description: Option, + #[serde(rename = "plan_type")] + pub plan_type: Option, + #[serde(rename = "ansible_var")] + pub ansible_var: Option, + #[serde(rename = "repo_dir")] + pub repo_dir: Option, + pub cpu: String, + #[serde(rename = "ram_size")] + pub ram_size: String, + #[serde(rename = "disk_size")] + pub disk_size: String, + #[serde(rename = "dockerhub_image")] + pub dockerhub_image: Option, + pub versions: Option>, + pub domain: Option, + pub shared_ports: Option>, + pub main: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Ports { + pub public: Vec, +} + + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Icon { + pub light: IconLight, + pub dark: IconDark, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct IconLight { + pub width: i64, + pub height: i64, + pub image: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct IconDark { +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Version { + #[serde(rename = "_etag")] + pub etag: Option, + #[serde(rename = "_id")] + pub id: i64, + #[serde(rename = "_created")] + pub created: Option, + #[serde(rename = "_updated")] + pub updated: Option, + #[serde(rename = "app_id")] + pub app_id: i64, + pub name: String, + pub version: String, + #[serde(rename = "update_status")] + pub update_status: Option, + pub tag: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Service { + #[serde(rename = "_etag")] + pub etag: Option, + #[serde(rename = "_id")] + pub id: i64, + #[serde(rename = "_created")] + pub created: Option, + #[serde(rename = "_updated")] + pub updated: Option, + pub name: String, + pub code: String, + pub role: Option>, + #[serde(rename = "type")] + pub type_field: String, + pub default: Option, + pub popularity: Option, + pub descr: Option, + pub ports: Option, + pub commercial: Option, + pub subscription: Option, + pub autodeploy: Option, + pub suggested: Option, + pub dependency: Option, + #[serde(rename = "avoid_render")] + pub avoid_render: Option, + pub price: Option, + pub icon: Option, + #[serde(rename = "category_id")] + pub category_id: Option, + #[serde(rename = "parent_app_id")] + pub parent_app_id: Option, + #[serde(rename = "full_description")] + pub full_description: Option, + pub description: Option, + #[serde(rename = "plan_type")] + pub plan_type: Option, + #[serde(rename = "ansible_var")] + pub ansible_var: Option, + #[serde(rename = "repo_dir")] + pub repo_dir: Option, + pub cpu: String, + #[serde(rename = "ram_size")] + pub ram_size: String, + #[serde(rename = "disk_size")] + pub disk_size: String, + #[serde(rename = "dockerhub_image")] + pub dockerhub_image: Option, + pub versions: Option>, + pub domain: String, + pub shared_ports: Option>, + pub main: bool, +} + diff --git a/src/models/mod.rs b/src/models/mod.rs index c3cbfed..bc4add4 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -3,3 +3,5 @@ pub mod stack; pub mod user; pub use rating::*; + +pub use stack::*; \ No newline at end of file diff --git a/src/models/stack.rs b/src/models/stack.rs index 8cb79b1..40255dd 100644 --- a/src/models/stack.rs +++ b/src/models/stack.rs @@ -1,8 +1,5 @@ use uuid::Uuid; use chrono::{DateTime, Utc}; -use serde_derive::Deserialize; -use serde_derive::Serialize; -use serde_json::Value; pub struct Stack { pub id: Uuid, // id - is a unique identifier for the app stack @@ -15,245 +12,3 @@ pub struct Stack { } -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct FormData { - pub common_domain: String, - pub domain_list: DomainList, - pub region: String, - pub zone: Value, - pub server: String, - pub os: String, - pub ssl: String, - pub vars: Vec, - #[serde(rename = "integrated_features")] - pub integrated_features: Vec, - #[serde(rename = "extended_features")] - pub extended_features: Vec, - pub subscriptions: Vec, - #[serde(rename = "save_token")] - pub save_token: bool, - #[serde(rename = "cloud_token")] - pub cloud_token: String, - pub provider: String, - #[serde(rename = "stack_code")] - pub stack_code: String, - #[serde(rename = "selected_plan")] - pub selected_plan: String, - pub custom: Custom, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DomainList { -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Custom { - pub web: Vec, - pub feature: Vec, - pub service: Vec, - #[serde(rename = "servers_count")] - pub servers_count: i64, - #[serde(rename = "custom_stack_name")] - pub custom_stack_name: String, - #[serde(rename = "custom_stack_code")] - pub custom_stack_code: String, - #[serde(rename = "custom_stack_git_url")] - pub custom_stack_git_url: String, - #[serde(rename = "custom_stack_category")] - pub custom_stack_category: Vec, - #[serde(rename = "custom_stack_short_description")] - pub custom_stack_short_description: String, - #[serde(rename = "custom_stack_description")] - pub custom_stack_description: String, - #[serde(rename = "project_name")] - pub project_name: String, - #[serde(rename = "project_overview")] - pub project_overview: String, - #[serde(rename = "project_description")] - pub project_description: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Web { - pub name: String, - pub code: String, - pub domain: String, - pub shared_ports: Vec, - pub versions: Vec, - pub custom: bool, - #[serde(rename = "type")] - pub type_field: String, - pub main: bool, - #[serde(rename = "_id")] - pub id: String, - #[serde(rename = "dockerhub_user")] - pub dockerhub_user: String, - #[serde(rename = "dockerhub_name")] - pub dockerhub_name: String, - #[serde(rename = "ram_size")] - pub ram_size: String, - pub cpu: i64, - #[serde(rename = "disk_size")] - pub disk_size: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Feature { - #[serde(rename = "_etag")] - pub etag: Value, - #[serde(rename = "_id")] - pub id: i64, - #[serde(rename = "_created")] - pub created: String, - #[serde(rename = "_updated")] - pub updated: String, - pub name: String, - pub code: String, - pub role: Vec, - #[serde(rename = "type")] - pub type_field: String, - pub default: Value, - pub popularity: Value, - pub descr: Value, - pub ports: Ports, - pub commercial: Value, - pub subscription: Value, - pub autodeploy: Value, - pub suggested: Value, - pub dependency: Value, - #[serde(rename = "avoid_render")] - pub avoid_render: Value, - pub price: Value, - pub icon: Icon, - #[serde(rename = "category_id")] - pub category_id: i64, - #[serde(rename = "parent_app_id")] - pub parent_app_id: Value, - #[serde(rename = "full_description")] - pub full_description: Value, - pub description: String, - #[serde(rename = "plan_type")] - pub plan_type: Value, - #[serde(rename = "ansible_var")] - pub ansible_var: Value, - #[serde(rename = "repo_dir")] - pub repo_dir: Value, - pub cpu: String, - #[serde(rename = "ram_size")] - pub ram_size: String, - #[serde(rename = "disk_size")] - pub disk_size: String, - #[serde(rename = "dockerhub_image")] - pub dockerhub_image: String, - pub versions: Vec, - pub domain: String, - pub shared_ports: Vec, - pub main: bool, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Ports { - pub public: Vec, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Icon { - pub light: IconLight, - pub dark: IconDark, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct IconLight { - pub width: i64, - pub height: i64, - pub image: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct IconDark { -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Version { - #[serde(rename = "_etag")] - pub etag: Option, - #[serde(rename = "_id")] - pub id: i64, - #[serde(rename = "_created")] - pub created: Option, - #[serde(rename = "_updated")] - pub updated: String, - #[serde(rename = "app_id")] - pub app_id: i64, - pub name: String, - pub version: String, - #[serde(rename = "update_status")] - pub update_status: String, - pub tag: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Service { - #[serde(rename = "_etag")] - pub etag: Value, - #[serde(rename = "_id")] - pub id: i64, - #[serde(rename = "_created")] - pub created: String, - #[serde(rename = "_updated")] - pub updated: String, - pub name: String, - pub code: String, - pub role: Vec, - #[serde(rename = "type")] - pub type_field: String, - pub default: Value, - pub popularity: Value, - pub descr: Value, - pub ports: Value, - pub commercial: Value, - pub subscription: Value, - pub autodeploy: Value, - pub suggested: Value, - pub dependency: Value, - #[serde(rename = "avoid_render")] - pub avoid_render: Value, - pub price: Value, - pub icon: Icon, - #[serde(rename = "category_id")] - pub category_id: Value, - #[serde(rename = "parent_app_id")] - pub parent_app_id: Value, - #[serde(rename = "full_description")] - pub full_description: Value, - pub description: Value, - #[serde(rename = "plan_type")] - pub plan_type: Value, - #[serde(rename = "ansible_var")] - pub ansible_var: Value, - #[serde(rename = "repo_dir")] - pub repo_dir: Value, - pub cpu: Value, - #[serde(rename = "ram_size")] - pub ram_size: Value, - #[serde(rename = "disk_size")] - pub disk_size: Value, - #[serde(rename = "dockerhub_image")] - pub dockerhub_image: String, - pub versions: Vec, - pub domain: String, - pub shared_ports: Vec, - pub main: bool, -} - diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 67379a5..1cb190d 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,14 +1,14 @@ -use std::io::Read; +// use std::io::Read; use actix_web::{web::{Data, Bytes, Json}, HttpResponse, HttpRequest, Responder, Result}; -use actix_web::error::{Error, JsonPayloadError, PayloadError}; -use sqlx::PgPool; -use tracing::Instrument; -use uuid::Uuid; -use chrono::Utc; -use crate::models::stack::{FormData}; -use crate::startup::AppState; +// use actix_web::error::{Error, JsonPayloadError, PayloadError}; +// use sqlx::PgPool; +// use tracing::Instrument; +// use uuid::Uuid; +// use chrono::Utc; +use crate::forms::stack::StackForm; +// use crate::startup::AppState; use std::str; -use actix_web::web::Form; +// use actix_web::web::Form; // pub async fn add(req: HttpRequest, app_state: Data, pool: @@ -26,11 +26,11 @@ pub async fn add(body: Bytes) -> Result { // let app_state = serde_json::from_str::(body_str).unwrap(); // println!("request: {:?}", app_state); - let stack = serde_json::from_str::(body_str).unwrap(); + let stack = serde_json::from_str::(body_str).unwrap(); println!("app: {:?}", stack); // println!("user_id: {:?}", data.user_id); // tracing::info!("we are here"); - // match Json::::extract(&req).await { + // match Json::::extract(&req).await { // Ok(form) => println!("Hello from {:?}!", form), // Err(err) => println!("error={:?}", err), // }; From df2bcb066314ba9363305c522f2c8dddc3657bfa Mon Sep 17 00:00:00 2001 From: vsilent Date: Tue, 26 Sep 2023 21:53:18 +0300 Subject: [PATCH 02/73] save stack info into database, move JsonResponse to utils/json.rs, make user_stack table id serial, and stack name must be unique --- .idea/dataSources.xml | 12 ++ .idea/sqldialects.xml | 7 + .idea/vcs.xml | 6 + Cargo.lock | 1 + Cargo.toml | 3 +- custom-stack-payload-3.json | 1 + ...0230905145525_creating_stack_tables.up.sql | 6 +- src/lib.rs | 1 + src/models/stack.rs | 11 +- src/routes/mod.rs | 3 +- src/routes/rating.rs | 9 +- src/routes/stack/add.rs | 166 ++++++++++-------- src/utils/json.rs | 8 + src/utils/mod.rs | 2 + 14 files changed, 142 insertions(+), 94 deletions(-) create mode 100644 .idea/dataSources.xml create mode 100644 .idea/sqldialects.xml create mode 100644 .idea/vcs.xml create mode 100644 custom-stack-payload-3.json create mode 100644 src/utils/json.rs create mode 100644 src/utils/mod.rs diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..a9f6743 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/stacker + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..7692097 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 908b64a..2e1553c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1939,6 +1939,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", + "serde_json", "sha2", "sqlx-core", "sqlx-rt", diff --git a/Cargo.toml b/Cargo.toml index 342ca3d..cb91e85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,5 +36,6 @@ features = [ "postgres", "uuid", "tls", - "chrono" + "chrono", + "json" ] diff --git a/custom-stack-payload-3.json b/custom-stack-payload-3.json new file mode 100644 index 0000000..4008848 --- /dev/null +++ b/custom-stack-payload-3.json @@ -0,0 +1 @@ +{"commonDomain":"","domainList":{},"region":"fsn1","zone":null,"server":"cx21","os":"ubuntu-20.04","ssl":"letsencrypt","vars":[],"integrated_features":[],"extended_features":[],"subscriptions":["stack_migration"],"save_token":false,"cloud_token":"r6LAjqrynVt7pUwctVkzBlJmKjLOCxJIWjZFMLTkPYCCB4rsgphhEVhiL4DuO757","provider":"htz","stack_code":"custom-stack","selected_plan":"plan-individual-monthly","custom":{"web":[{"name":"Smarty Bot","code":"smarty-bot","domain":"smartybot.xyz","sharedPorts":["8000"],"versions":[],"custom":true,"type":"web","main":true,"_id":"lltkpq6p347kystct","dockerhub_user":"trydirect","dockerhub_name":"smarty-bot","url_app":"smartybot.xyz","url_git":"https://github.com/vsilent/smarty.git","disk_size":"1Gb","ram_size":"1Gb","cpu":1}],"feature":[{"_etag":null,"_id":198,"_created":"2022-04-27T14:10:27.280327","_updated":"2023-08-03T08:24:18.958721","name":"Portainer CE Feature","code":"portainer_ce_feature","role":["portainer-ce-feature"],"type":"feature","default":null,"popularity":null,"descr":null,"ports":{"public":["9000","8000"]},"commercial":null,"subscription":null,"autodeploy":null,"suggested":null,"dependency":null,"avoid_render":null,"price":null,"icon":{"light":{"width":1138,"height":1138,"image":"08589075-44e6-430e-98a5-f9dcf711e054.svg"},"dark":{}},"category_id":2,"parent_app_id":null,"full_description":null,"description":"

Portainer is a lightweight management UI which allows you to easily manage your different Docker environments (Docker hosts or Swarm clusters)

","plan_type":null,"ansible_var":null,"repo_dir":null,"cpu":"0.6","ram_size":"1Gb","disk_size":"1Gb","dockerhub_image":"portainer-ce-feature","versions":[{"_etag":null,"_id":456,"_created":"2022-04-25T12:44:30.964547","_updated":"2023-03-17T13:46:51.433539","app_id":198,"name":"latest","version":"latest","update_status":"published","tag":"latest"}],"domain":"","sharedPorts":["9000"],"main":true,"version":{"_etag":null,"_id":456,"_created":"2022-04-25T12:44:30.964547","_updated":"2023-03-17T13:46:51.433539","app_id":198,"name":"latest","version":"latest","update_status":"published","tag":"latest"}}],"service":[{"_etag":null,"_id":230,"_created":"2023-05-24T12:51:52.108972","_updated":"2023-08-04T12:18:34.670194","name":"pgrst","code":"pgrst","role":null,"type":"service","default":null,"popularity":null,"descr":null,"ports":null,"commercial":null,"subscription":null,"autodeploy":null,"suggested":null,"dependency":null,"avoid_render":null,"price":null,"icon":null,"category_id":null,"parent_app_id":null,"full_description":null,"description":"

PostgREST description

","plan_type":null,"ansible_var":null,"repo_dir":null,"cpu":"1","ram_size":"1Gb","disk_size":"1Gb","dockerhub_image":"pgrst","versions":[{"_etag":"566","_id":566,"_created":"2023-08-15T12:10:44","_updated":"2023-08-15T12:10:44.905249","app_id":230,"name":"PostgreSQL","version":"15_4","update_status":"ready_for_testing","tag":"unstable"},{"_etag":null,"_id":563,"_created":null,"_updated":"2023-05-24T12:52:15.351522","app_id":230,"name":"0.0.5","version":"0.0.5","update_status":"ready_for_testing","tag":"0.0.5"}],"domain":"","sharedPorts":["9999"],"main":true,"version":{"_etag":"566","_id":566,"_created":"2023-08-15T12:10:44","_updated":"2023-08-15T12:10:44.905249","app_id":230,"name":"PostgreSQL","version":"15_4","update_status":"ready_for_testing","tag":"unstable"}}],"servers_count":3,"custom_stack_name":"mysampleproject","custom_stack_code":"another-bot","custom_stack_category":["New"],"custom_stack_short_description":"sample short description","custom_stack_description":"stack description","custom_stack_publish":false,"project_name":"Smarty Bot","project_git_url":"https://github.com/vsilent/smarty.git","project_overview":"my product 1","project_description":"my product 1"}} diff --git a/migrations/20230905145525_creating_stack_tables.up.sql b/migrations/20230905145525_creating_stack_tables.up.sql index eab6cf2..eba00f9 100644 --- a/migrations/20230905145525_creating_stack_tables.up.sql +++ b/migrations/20230905145525_creating_stack_tables.up.sql @@ -1,10 +1,10 @@ -- Add up migration script here -- Add migration script here CREATE TABLE user_stack ( - id integer NOT NULL, PRIMARY KEY(id), - stack_id integer NOT NULL, + id serial, + stack_id uuid NOT NULL, user_id integer NOT NULL, - name TEXT NOT NULL, + name TEXT NOT NULL UNIQUE, body JSON NOT NULL, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL diff --git a/src/lib.rs b/src/lib.rs index 9d3cc9b..c8fb96b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,4 @@ pub mod routes; pub mod services; pub mod startup; pub mod telemetry; +pub mod utils; diff --git a/src/models/stack.rs b/src/models/stack.rs index 40255dd..f567eaf 100644 --- a/src/models/stack.rs +++ b/src/models/stack.rs @@ -1,12 +1,17 @@ use uuid::Uuid; use chrono::{DateTime, Utc}; +use serde_json::Value; +// #[derive(sqlx::Type, Debug, Clone, Copy)] +// #[sqlx(rename_all = "lowercase", type_name = "json")] +#[derive(Debug)] pub struct Stack { - pub id: Uuid, // id - is a unique identifier for the app stack + pub id: i32, // id - is a unique identifier for the app stack pub stack_id: Uuid, // external stack ID - pub user_id: Uuid, // external unique identifier for the user + pub user_id: i32, // external unique identifier for the user pub name: String, - pub body: String, + // pub body: sqlx::types::Json, + pub body: Value, pub created_at: DateTime, pub updated_at: DateTime, } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index c9a46e5..e04b518 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -4,4 +4,5 @@ mod rating; pub use health_checks::*; pub use rating::*; pub(crate) mod stack; -pub use stack::*; \ No newline at end of file +pub use stack::*; + diff --git a/src/routes/rating.rs b/src/routes/rating.rs index e930624..48cf730 100644 --- a/src/routes/rating.rs +++ b/src/routes/rating.rs @@ -2,24 +2,17 @@ use crate::forms; use crate::models; use crate::startup::AppState; use actix_web::{web, HttpResponse, Responder, Result}; -use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; use uuid::Uuid; use crate::models::RateCategory; +use crate::utils::json::JsonResponse; // workflow // add, update, list, get(user_id), ACL, // ACL - access to func for a user // ACL - access to objects for a user -#[derive(Serialize)] -struct JsonResponse { - status: String, - message: String, - code: u32, - id: Option -} pub async fn rating( app_state: web::Data, diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 1cb190d..7ea7a13 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,92 +1,102 @@ -// use std::io::Read; use actix_web::{web::{Data, Bytes, Json}, HttpResponse, HttpRequest, Responder, Result}; -// use actix_web::error::{Error, JsonPayloadError, PayloadError}; -// use sqlx::PgPool; -// use tracing::Instrument; -// use uuid::Uuid; -// use chrono::Utc; +use sqlx::PgPool; +use tracing::Instrument; +use uuid::Uuid; +use chrono::Utc; use crate::forms::stack::StackForm; -// use crate::startup::AppState; +use crate::startup::AppState; use std::str; -// use actix_web::web::Form; +use crate::models::Stack; +use crate::utils::json::JsonResponse; // pub async fn add(req: HttpRequest, app_state: Data, pool: -pub async fn add(body: Bytes) -> Result { - // None::.expect("my error"); - // return Err(JsonPayloadError::Payload(PayloadError::Overflow).into()); +pub async fn add(body: Bytes, app_state: Data, pool: Data) -> Result { // let content_type = req.headers().get("content-type"); - // println!("=================== Request Content-Type: {:?}", content_type); + // println!("Request Content-Type: {:?}", content_type); let body_bytes = actix_web::body::to_bytes(body).await.unwrap(); let body_str = str::from_utf8(&body_bytes).unwrap(); - // method 1 - // let app_state: AppState = serde_json::from_str(body_str).unwrap(); - // method 2 - // let app_state = serde_json::from_str::(body_str).unwrap(); - // println!("request: {:?}", app_state); + // method 1 let app_state: AppState = serde_json::from_str(body_str).unwrap(); + // method 2 let app_state = serde_json::from_str::(body_str).unwrap(); + let form = serde_json::from_str::(body_str).unwrap(); + // println!("app: {:?}", form); - let stack = serde_json::from_str::(body_str).unwrap(); - println!("app: {:?}", stack); - // println!("user_id: {:?}", data.user_id); - // tracing::info!("we are here"); - // match Json::::extract(&req).await { - // Ok(form) => println!("Hello from {:?}!", form), - // Err(err) => println!("error={:?}", err), - // }; + let user_id = app_state.user_id; + let request_id = Uuid::new_v4(); + let request_span = tracing::info_span!( + "Validating a new stack", %request_id, + commonDomain=?&form.common_domain, + region=?&form.region, + domainList=?&form.domain_list + ); + // using `enter` is an async function + let _request_span_guard = request_span.enter(); // ->exit - // let user_id = app_state.user_id; - // let request_id = Uuid::new_v4(); - // let request_span = tracing::info_span!( - // "Validating a new stack", %request_id, - // commonDomain=?form.common_domain, - // region=?form.region, - // domainList=?form.domain_list - // ); - // - // // using `enter` is an async function - // let _request_span_guard = request_span.enter(); // ->exit - // - // tracing::info!( - // "request_id {} Adding '{}' '{}' as a new stack", - // request_id, - // form.common_domain, - // form.region - // ); - // - // let query_span = tracing::info_span!( - // "Saving new stack details into the database" - // ); - // - // // match sqlx::query!( - // // r#" - // // INSERT INTO user_stack (id, user_id, name, created_at, updated_at) - // // VALUES ($1, $2, $3, $4, $5) - // // "#, - // // 0_i32, - // // user_id, - // // form.common_domain, - // // Utc::now(), - // // Utc::now() - // // ) - // // .execute(pool.get_ref()) - // // .instrument(query_span) - // // .await - // // { - // // Ok(_) => { - // // tracing::info!( - // // "req_id: {} New stack details have been saved to database", - // // request_id - // // ); - // // HttpResponse::Ok().finish() - // // } - // // Err(e) => { - // // tracing::error!("req_id: {} Failed to execute query: {:?}", request_id, e); - // // HttpResponse::InternalServerError().finish() - // // } - // // } + tracing::info!( + "request_id {} Adding '{}' '{}' as a new stack", + request_id, + form.common_domain, + form.region + ); + + let query_span = tracing::info_span!( + "Saving new stack details into the database" + ); + + let stack = Stack { + id: 0_i32, // internal stack id + stack_id: Uuid::new_v4(), // public uuid of the stack + // user_id: Uuid::from_u128(user_id as u128), + user_id: user_id, // + name: form.custom.custom_stack_code.clone(), + body: serde_json::to_value::(form).unwrap_or_default(), + // body: body_str.to_string(), + created_at: Utc::now(), + updated_at: Utc::now() + }; + + println!("stack object {:?}", stack); + return match sqlx::query!( + r#" + INSERT INTO user_stack (id, stack_id, user_id, name, body, created_at, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING id; + "#, + 0_i32, + stack.stack_id, + stack.user_id, + stack.name, + // sqlx::types::Json(stack.body), + stack.body, + stack.created_at, + stack.updated_at + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(record) => { + tracing::info!( + "req_id: {} New stack details have been saved to database", + request_id + ); + Ok(Json(JsonResponse { + status: "OK".to_string(), + code: 200, + message: format!("Object saved"), + id: Some(record.id) + })) + } + Err(e) => { + tracing::error!("req_id: {} Failed to execute query: {:?}", request_id, e); + Ok(Json(JsonResponse { + status: "Error".to_string(), + code: 400, + message: e.to_string(), + id: None + })) + } + } - // HttpResponse::Ok().finish() - Ok(Json(stack)) - // Ok(HttpResponse::Ok().finish()) } diff --git a/src/utils/json.rs b/src/utils/json.rs new file mode 100644 index 0000000..171a7c4 --- /dev/null +++ b/src/utils/json.rs @@ -0,0 +1,8 @@ +use serde_derive::Serialize; +#[derive(Serialize)] +pub(crate) struct JsonResponse { + pub(crate) status: String, + pub(crate) message: String, + pub(crate) code: u32, + pub(crate) id: Option +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..b919165 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod json; +pub use json::*; From 815dcbfa5b4e2b2b328546061f521143f9cf2872 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 1 Oct 2023 09:22:49 +0300 Subject: [PATCH 03/73] tracing on err --- custom-stack-payload-singleapp.json | 1 + src/forms/stack.rs | 21 +++++++++++++---- src/models/stack.rs | 2 +- src/routes/stack/add.rs | 35 +++++++++++++++++++++++++---- src/routes/stack/get.rs | 6 ----- src/utils/mod.rs | 2 ++ src/utils/validator/mod.rs | 0 7 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 custom-stack-payload-singleapp.json create mode 100644 src/utils/validator/mod.rs diff --git a/custom-stack-payload-singleapp.json b/custom-stack-payload-singleapp.json new file mode 100644 index 0000000..e1b3998 --- /dev/null +++ b/custom-stack-payload-singleapp.json @@ -0,0 +1 @@ +{"commonDomain":"","domainList":{},"region":"fsn1","zone":null,"server":"cx11","os":"ubuntu-20.04","ssl":"letsencrypt","vars":[],"integrated_features":[],"extended_features":[],"subscriptions":[],"save_token":false,"cloud_token":"nUDKdUk0b6fUOcW6I4zhmdMfhH8kR4nJrxWjRPxrfqTJ9smOSoKB4qZpsYjS8As6","provider":"htz","stack_code":"custom-stack","selected_plan":"plan-free-periodically","custom":{"web":[{"name":"Smarty Bot","code":"smarty-bot","domain":"smartybot.com","sharedPorts":["8000"],"versions":[],"custom":true,"type":"web","main":true,"_id":"lmg1mg6c1acxn9bs7","dockerhub_user":"vsilent","dockerhub_name":"smarty"}],"feature":[],"service":[],"servers_count":3,"project_name":"sample1","custom_stack_code":"sample1"}} diff --git a/src/forms/stack.rs b/src/forms/stack.rs index c8ea0c4..93a1be8 100644 --- a/src/forms/stack.rs +++ b/src/forms/stack.rs @@ -74,7 +74,7 @@ pub struct Custom { } -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] #[serde(rename_all = "camelCase")] pub struct Web { pub name: String, @@ -94,14 +94,21 @@ pub struct Web { pub dockerhub_name: String, pub url_app: Option, pub url_git: Option, + #[validate(min_length=1)] + #[validate(max_length=10)] + #[validate(pattern = r"^\d+G$")] #[serde(rename = "disk_size")] pub disk_size: String, #[serde(rename = "ram_size")] + #[validate(min_length=1)] + #[validate(max_length=10)] + #[validate(pattern = r"^\d+G$")] pub ram_size: String, - pub cpu: i64, + #[validate(minimum=0.1)] + pub cpu: f64, } -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] #[serde(rename_all = "camelCase")] pub struct Feature { #[serde(rename = "_etag")] @@ -143,9 +150,12 @@ pub struct Feature { pub ansible_var: Option, #[serde(rename = "repo_dir")] pub repo_dir: Option, + #[validate(min_length=1)] pub cpu: String, + #[validate(min_length=1)] #[serde(rename = "ram_size")] pub ram_size: String, + #[validate(min_length=1)] #[serde(rename = "disk_size")] pub disk_size: String, #[serde(rename = "dockerhub_image")] @@ -203,7 +213,7 @@ pub struct Version { pub tag: String, } -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] #[serde(rename_all = "camelCase")] pub struct Service { #[serde(rename = "_etag")] @@ -245,10 +255,13 @@ pub struct Service { pub ansible_var: Option, #[serde(rename = "repo_dir")] pub repo_dir: Option, + #[validate(min_length=1)] pub cpu: String, #[serde(rename = "ram_size")] + #[validate(min_length=1)] pub ram_size: String, #[serde(rename = "disk_size")] + #[validate(min_length=1)] pub disk_size: String, #[serde(rename = "dockerhub_image")] pub dockerhub_image: Option, diff --git a/src/models/stack.rs b/src/models/stack.rs index f567eaf..526f369 100644 --- a/src/models/stack.rs +++ b/src/models/stack.rs @@ -11,7 +11,7 @@ pub struct Stack { pub user_id: i32, // external unique identifier for the user pub name: String, // pub body: sqlx::types::Json, - pub body: Value, + pub body: Value, //json type pub created_at: DateTime, pub updated_at: DateTime, } diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 7ea7a13..84ad67b 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,4 +1,4 @@ -use actix_web::{web::{Data, Bytes, Json}, HttpResponse, HttpRequest, Responder, Result}; +use actix_web::{web::{Data, Bytes, Json}, Responder, Result}; use sqlx::PgPool; use tracing::Instrument; use uuid::Uuid; @@ -6,11 +6,11 @@ use chrono::Utc; use crate::forms::stack::StackForm; use crate::startup::AppState; use std::str; +use serde_json::Value; use crate::models::Stack; use crate::utils::json::JsonResponse; -// pub async fn add(req: HttpRequest, app_state: Data, pool: pub async fn add(body: Bytes, app_state: Data, pool: Data) -> Result { // let content_type = req.headers().get("content-type"); // println!("Request Content-Type: {:?}", content_type); @@ -19,7 +19,20 @@ pub async fn add(body: Bytes, app_state: Data, pool: Data) -> let body_str = str::from_utf8(&body_bytes).unwrap(); // method 1 let app_state: AppState = serde_json::from_str(body_str).unwrap(); // method 2 let app_state = serde_json::from_str::(body_str).unwrap(); - let form = serde_json::from_str::(body_str).unwrap(); + let form = match serde_json::from_str::(body_str) { + Ok(f) => { + println!("fine"); + f + } + Err(err) => { + return Ok(Json(JsonResponse { + status: "Error".to_string(), + code: 400, + message: err.to_string(), + id: None + })); + } + }; // println!("app: {:?}", form); let user_id = app_state.user_id; @@ -44,13 +57,27 @@ pub async fn add(body: Bytes, app_state: Data, pool: Data) -> "Saving new stack details into the database" ); + let body = match serde_json::to_value::(form) { + Ok(body) => { + body + } + Err(err) => { + tracing::error!( + "request_id {} unwrap body {:?}", + request_id, + err + ); + serde_json::to_value::(StackForm::default()) + } + }; + let stack = Stack { id: 0_i32, // internal stack id stack_id: Uuid::new_v4(), // public uuid of the stack // user_id: Uuid::from_u128(user_id as u128), user_id: user_id, // name: form.custom.custom_stack_code.clone(), - body: serde_json::to_value::(form).unwrap_or_default(), + body: body, // body: body_str.to_string(), created_at: Utc::now(), updated_at: Utc::now() diff --git a/src/routes/stack/get.rs b/src/routes/stack/get.rs index 734f22b..c5cf6ec 100644 --- a/src/routes/stack/get.rs +++ b/src/routes/stack/get.rs @@ -30,9 +30,3 @@ pub async fn get( } } } - - -pub async fn validate_stack () -> HttpResponse { - unimplemented!(); -} - diff --git a/src/utils/mod.rs b/src/utils/mod.rs index b919165..1ca1b3c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,2 +1,4 @@ pub(crate) mod json; +mod validator; + pub use json::*; diff --git a/src/utils/validator/mod.rs b/src/utils/validator/mod.rs new file mode 100644 index 0000000..e69de29 From e235d7c6d92c192aded155c43e0d233a3c5204e0 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 15 Oct 2023 10:42:53 +0300 Subject: [PATCH 04/73] API client structure --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a708c47..c95694e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Application development will include: - Back-end RESTful API, includes: - [ ] Security module. - [ ] User Authorization - - [ ] Application Management + - [ ] Restful API client Application Management - [ ] Application Key Management - [ ] Cloud Provider Key Management - [ ] docker-compose.yml generator @@ -20,6 +20,35 @@ Application development will include: ## How to start + +## Structure + +Stacker (User's dashboard) - Management +---------- +Authentication through TryDirect OAuth +/api/auth checks client's creds, api token, api secret +/apiclient (Create/Manage user's api clients) example: BeerMaster (email, callback) +/rating + + +Stacker (API) - Serves API clients +---------- +Authentication made through TryDirect OAuth, here we have only client +Database (Read only) +Logging/Tracing (Files) / Quickwit for future +/stack (WebUI, as a result we have a JSON) +/stack/deploy -> sends deploy command to TryDirect Install service +/stack/deploy/status - get installation progress (rabbitmq client), + +#### TODO +Find out how to get user's token for queue +Limit Requests Frequency (Related to user's settings/role/group etc) +Callback event, example: subscribe on get_report (internally from rabbitmq client), + + +main client (AllMasters) -> client (Beermaster) + + #### Run db migration ``` From 8530aeb27be159ca72f1ed374154e582881451fb Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 16 Oct 2023 16:56:02 +0300 Subject: [PATCH 05/73] ignore .idea files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index eb5a316..c507849 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +.idea From c552af2ee0ffd68de16812e55d76d6cf865dbdb4 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 18 Oct 2023 12:58:05 +0300 Subject: [PATCH 06/73] fix borrowing --- configuration.yaml | 3 ++- src/forms/stack.rs | 4 ++-- src/routes/rating/add.rs | 1 - src/routes/stack/add.rs | 7 ++++--- src/startup.rs | 6 ++++-- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/configuration.yaml b/configuration.yaml index b3918f1..1b288e0 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,5 +1,6 @@ application_port: 8000 -auth_url: https://65190108818c4e98ac6000e4.mockapi.io/user/1 +#auth_url: https://65190108818c4e98ac6000e4.mockapi.io/user/1 +auth_url: https://dev.try.direct/server/user/oauth_server/api/me database: host: localhost port: 5432 diff --git a/src/forms/stack.rs b/src/forms/stack.rs index 93a1be8..72436d3 100644 --- a/src/forms/stack.rs +++ b/src/forms/stack.rs @@ -96,13 +96,13 @@ pub struct Web { pub url_git: Option, #[validate(min_length=1)] #[validate(max_length=10)] - #[validate(pattern = r"^\d+G$")] + //#[validate(pattern = r"^\d+G$")] #[serde(rename = "disk_size")] pub disk_size: String, #[serde(rename = "ram_size")] #[validate(min_length=1)] #[validate(max_length=10)] - #[validate(pattern = r"^\d+G$")] + //#[validate(pattern = r"^\d+G$")] pub ram_size: String, #[validate(minimum=0.1)] pub cpu: f64, diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs index 70e40c1..44ab829 100644 --- a/src/routes/rating/add.rs +++ b/src/routes/rating/add.rs @@ -4,7 +4,6 @@ use crate::models::user::User; use crate::models::RateCategory; use actix_web::post; use actix_web::{web, Responder, Result}; -use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; use crate::utils::json::JsonResponse; diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 39edfc5..0e73f88 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -38,7 +38,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R }; // println!("app: {:?}", form); - let user_id = user.user_id; + let user_id = user.id; let request_id = Uuid::new_v4(); let request_span = tracing::info_span!( "Validating a new stack", %request_id, @@ -60,6 +60,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R "Saving new stack details into the database" ); + let custom_stack_code = form.custom.custom_stack_code.clone(); let body = match serde_json::to_value::(form) { Ok(body) => { body @@ -70,7 +71,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R request_id, err ); - serde_json::to_value::(StackForm::default()) + serde_json::to_value::(StackForm::default()).unwrap() } }; @@ -79,7 +80,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R stack_id: Uuid::new_v4(), // public uuid of the stack // user_id: Uuid::from_u128(user_id as u128), user_id: user_id, // - name: form.custom.custom_stack_code.clone(), + name: custom_stack_code, body: body, // body: body_str.to_string(), created_at: Utc::now(), diff --git a/src/startup.rs b/src/startup.rs index a00c02d..6f1f7c2 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -98,9 +98,11 @@ pub async fn run(settings: Settings) -> Result { // .route(web::post() // .to(crate::routes::stack::add)), // ) - .service(web::resource("/stack").route(web::post().to(crate::routes::stack::add::add))) + .service(web::resource("/stack") + .route(web::post().to(crate::routes::stack::add::add))) .service( - web::resource("/stack/deploy").route(web::post().to(crate::routes::stack::deploy)), + web::resource("/stack/deploy") + .route(web::post().to(crate::routes::stack::deploy)), ) .app_data(db_pool.clone()) .app_data(settings.clone()) From 849d1492e6060e53823fca0abbe4f2364a07761f Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 18 Oct 2023 19:55:28 +0300 Subject: [PATCH 07/73] issue-auth rating path --- Cargo.lock | 936 +++++++++++++++++++++------------------ src/routes/mod.rs | 6 +- src/routes/rating/add.rs | 3 +- src/routes/rating/mod.rs | 5 +- src/routes/stack/add.rs | 61 ++- src/startup.rs | 12 +- 6 files changed, 539 insertions(+), 484 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9bf68a..9910db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,19 @@ version = 3 [[package]] name = "actix-codec" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-sink", - "log", "memchr", "pin-project-lite", "tokio", "tokio-util", + "tracing", ] [[package]] @@ -36,17 +36,17 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.3.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", "ahash 0.8.3", - "base64 0.21.0", - "bitflags", + "base64 0.21.4", + "bitflags 2.4.1", "brotli", "bytes", "bytestring", @@ -75,12 +75,12 @@ dependencies = [ [[package]] name = "actix-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -98,9 +98,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" dependencies = [ "futures-core", "tokio", @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "actix-server" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" dependencies = [ "actix-rt", "actix-service", @@ -118,8 +118,7 @@ dependencies = [ "futures-core", "futures-util", "mio", - "num_cpus", - "socket2", + "socket2 0.5.4", "tokio", "tracing", ] @@ -147,9 +146,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.3.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" +checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9" dependencies = [ "actix-codec", "actix-http", @@ -160,7 +159,7 @@ dependencies = [ "actix-service", "actix-utils", "actix-web-codegen", - "ahash 0.7.6", + "ahash 0.8.3", "bytes", "bytestring", "cfg-if", @@ -169,7 +168,6 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http", "itoa", "language-tags", "log", @@ -181,21 +179,21 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2", - "time 0.3.21", + "socket2 0.5.4", + "time 0.3.30", "url", ] [[package]] name = "actix-web-codegen" -version = "4.2.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" +checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -206,13 +204,22 @@ checksum = "1d613edf08a42ccc6864c941d30fe14e1b676a77d16f1dbadc1174d065a0a775" dependencies = [ "actix-utils", "actix-web", - "base64 0.21.0", + "base64 0.21.4", "futures-core", "futures-util", "log", "pin-project-lite", ] +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -244,9 +251,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -266,6 +273,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -283,13 +296,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] @@ -307,6 +320,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -315,9 +343,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bitflags" @@ -325,6 +353,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.10.4" @@ -336,9 +370,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.3.4" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -347,9 +381,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "da74e2b81409b1b743f8f0c62cc6254afefb8b8e50bbfe3735550f7aeefa3448" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -357,21 +391,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bytestring" @@ -384,11 +418,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -399,9 +434,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "d87d9d13be47a5b7c3907137f1290b0459a7f80efb26be8c52afb11963bccb02" dependencies = [ "android-tzdata", "iana-time-zone", @@ -409,7 +444,7 @@ dependencies = [ "num-traits", "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -444,7 +479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.21", + "time 0.3.30", "version_check", ] @@ -466,9 +501,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -509,9 +544,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -526,6 +561,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -541,9 +585,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -584,38 +628,27 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "cc", "libc", + "windows-sys", ] [[package]] @@ -626,18 +659,21 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -666,9 +702,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -748,20 +784,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -787,20 +829,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" dependencies = [ "ahash 0.8.3", + "allocator-api2", ] [[package]] name = "hashlink" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.1", ] [[package]] @@ -814,18 +857,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -881,15 +915,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -902,7 +936,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -924,16 +958,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -947,9 +981,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -975,22 +1009,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itertools" @@ -1001,26 +1024,35 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1050,9 +1082,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.143" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linked-hash-map" @@ -1062,19 +1094,18 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "local-channel" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +checksum = "e0a493488de5f18c8ffcba89eebb8532ffc562dc400490eb65b84893fae0b178" dependencies = [ "futures-core", "futures-sink", - "futures-util", "local-waker", ] @@ -1086,9 +1117,9 @@ checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1096,9 +1127,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matchers" @@ -1106,23 +1137,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ + "cfg-if", "digest", ] [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" @@ -1147,14 +1179,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1197,36 +1229,45 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -1243,7 +1284,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] @@ -1254,9 +1295,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -1298,7 +1339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.9", ] [[package]] @@ -1317,22 +1358,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.4.1", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" @@ -1342,25 +1383,26 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" dependencies = [ "pest", "pest_generator", @@ -1368,22 +1410,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" dependencies = [ "once_cell", "pest", @@ -1407,14 +1449,14 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1428,6 +1470,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1460,9 +1508,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1512,7 +1560,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1521,7 +1569,16 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -1537,13 +1594,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1555,6 +1613,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -1563,17 +1632,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.0", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -1594,6 +1663,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -1613,12 +1683,26 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce3045ffa7c981a6ee93f640b538952e155f1ae3a1a02b84547fc7a56b7059a" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys", +] + [[package]] name = "ron" version = "0.7.1" @@ -1626,7 +1710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "serde", ] @@ -1640,6 +1724,12 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1651,59 +1741,58 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct", "webpki", ] [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.0", + "base64 0.21.4", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -1711,17 +1800,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "security-framework" -version = "2.9.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1730,9 +1819,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -1740,35 +1829,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1794,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0adc7a19d45e581abc6d169c865a0b14b84bb43a9e966d1cca4d733e70f7f35a" dependencies = [ "indexmap", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "paste", @@ -1833,9 +1922,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -1844,9 +1933,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -1855,9 +1944,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -1873,18 +1962,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -1896,19 +1985,35 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "sqlformat" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" dependencies = [ - "itertools", + "itertools 0.11.0", "nom", "unicode_categories", ] @@ -1932,7 +2037,7 @@ dependencies = [ "ahash 0.7.6", "atoi", "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "chrono", @@ -2036,10 +2141,11 @@ dependencies = [ [[package]] name = "stringprep" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", ] @@ -2052,9 +2158,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -2069,46 +2175,67 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.31" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] @@ -2134,11 +2261,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ + "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -2146,15 +2275,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -2176,11 +2305,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -2188,9 +2317,9 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2201,7 +2330,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] @@ -2238,9 +2367,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -2267,11 +2396,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2292,27 +2420,27 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", ] [[package]] name = "tracing-bunyan-formatter" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464ce79ea7f689ca56d90a9c5563e803a4b61b2695e789205644ed8e8101e6bf" +checksum = "b5c266b9ac83dedf0e0385ad78514949e6d89491269e7065bee51d2bb8ec7373" dependencies = [ "ahash 0.8.3", "gethostname", "log", "serde", "serde_json", - "time 0.3.21", + "time 0.3.30", "tracing", "tracing-core", "tracing-log", @@ -2321,9 +2449,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2366,15 +2494,15 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" @@ -2384,9 +2512,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2415,11 +2543,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -2428,9 +2562,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.3.4" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom", ] @@ -2455,11 +2589,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2477,9 +2610,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2487,24 +2620,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -2514,9 +2647,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2524,28 +2657,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -2553,12 +2686,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.4", + "untrusted 0.9.0", ] [[package]] @@ -2572,9 +2705,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" dependencies = [ "wasm-bindgen", "web-sys", @@ -2603,36 +2736,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -2641,130 +2750,74 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys", ] [[package]] @@ -2778,18 +2831,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", @@ -2797,11 +2850,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/src/routes/mod.rs b/src/routes/mod.rs index e04b518..15e2852 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,8 +1,6 @@ -mod health_checks; -mod rating; +pub mod health_checks; +pub mod rating; pub use health_checks::*; -pub use rating::*; pub(crate) mod stack; pub use stack::*; - diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs index 70e40c1..96a8192 100644 --- a/src/routes/rating/add.rs +++ b/src/routes/rating/add.rs @@ -2,12 +2,11 @@ use crate::forms; use crate::models; use crate::models::user::User; use crate::models::RateCategory; +use crate::utils::json::JsonResponse; use actix_web::post; use actix_web::{web, Responder, Result}; -use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; -use crate::utils::json::JsonResponse; // workflow // add, update, list, get(user_id), ACL, diff --git a/src/routes/rating/mod.rs b/src/routes/rating/mod.rs index 0b54a65..b13668a 100644 --- a/src/routes/rating/mod.rs +++ b/src/routes/rating/mod.rs @@ -1,4 +1,5 @@ -mod add; -mod get; +pub mod add; +pub mod get; + pub use add::*; pub use get::*; diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 39edfc5..9b9f306 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,18 +1,24 @@ -use actix_web::{web::{Data, Bytes, Json}, Responder, Result, web}; +use actix_web::{ + web, + web::{Bytes, Data, Json}, + Responder, Result, +}; +use crate::forms::stack::StackForm; +use crate::models::user::User; +use crate::models::Stack; +use crate::utils::json::JsonResponse; use chrono::Utc; use sqlx::PgPool; use std::str; use tracing::Instrument; use uuid::Uuid; -use crate::forms::stack::StackForm; -use crate::models::Stack; -use crate::models::user::User; -use crate::utils::json::JsonResponse; - - -pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> Result { +pub async fn add( + body: Bytes, + user: web::ReqData, + pool: Data, +) -> Result { // None::.expect("my error"); // return Err(JsonPayloadError::Payload(PayloadError::Overflow).into()); // let content_type = req.headers().get("content-type"); @@ -32,13 +38,13 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R status: "Error".to_string(), code: 400, message: err.to_string(), - id: None + id: None, })); } }; // println!("app: {:?}", form); - let user_id = user.user_id; + let user_id = user.id; let request_id = Uuid::new_v4(); let request_span = tracing::info_span!( "Validating a new stack", %request_id, @@ -56,34 +62,26 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R form.region ); - let query_span = tracing::info_span!( - "Saving new stack details into the database" - ); + let query_span = tracing::info_span!("Saving new stack details into the database"); let body = match serde_json::to_value::(form) { - Ok(body) => { - body - } + Ok(body) => body, Err(err) => { - tracing::error!( - "request_id {} unwrap body {:?}", - request_id, - err - ); + tracing::error!("request_id {} unwrap body {:?}", request_id, err); serde_json::to_value::(StackForm::default()) } }; let stack = Stack { - id: 0_i32, // internal stack id - stack_id: Uuid::new_v4(), // public uuid of the stack + id: 0_i32, // internal stack id + stack_id: Uuid::new_v4(), // public uuid of the stack // user_id: Uuid::from_u128(user_id as u128), - user_id: user_id, // + user_id: user_id, // name: form.custom.custom_stack_code.clone(), body: body, // body: body_str.to_string(), created_at: Utc::now(), - updated_at: Utc::now() + updated_at: Utc::now(), }; println!("stack object {:?}", stack); @@ -102,9 +100,9 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R stack.created_at, stack.updated_at ) - .fetch_one(pool.get_ref()) - .instrument(query_span) - .await + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await { Ok(record) => { tracing::info!( @@ -115,7 +113,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R status: "OK".to_string(), code: 200, message: format!("Object saved"), - id: Some(record.id) + id: Some(record.id), })) } Err(e) => { @@ -124,9 +122,8 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R status: "Error".to_string(), code: 400, message: e.to_string(), - id: None + id: None, })) } - } - + }; } diff --git a/src/startup.rs b/src/startup.rs index a00c02d..71786dc 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -82,12 +82,20 @@ pub async fn run(settings: Settings) -> Result { App::new() .wrap(TracingLogger::default()) .service(web::scope("/health_check").service(crate::routes::health_check)) + /* + .service( + web::scope("/client") + .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(Cors::permissive()) + .service(crate::routes::add_handler), + ) + */ .service( web::scope("/rating") .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) - .service(crate::routes::add_handler) - .service(crate::routes::get_handler), + .service(crate::routes::rating::add_handler) + .service(crate::routes::rating::get_handler), ) // .service( // web::resource("/stack/{id}") From 6166024a8125b53d4f915faea22f6510e453fa82 Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 18 Oct 2023 20:02:27 +0300 Subject: [PATCH 08/73] issue-auth add regex --- Cargo.lock | 1 + Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9910db6..8ffc8a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2123,6 +2123,7 @@ dependencies = [ "actix-web-httpauth", "chrono", "config", + "regex", "reqwest", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index f7df3c7..82d4753 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ serde_derive = "1.0.188" actix-web-httpauth = "0.8.1" actix-cors = "0.6.4" tracing-actix-web = "0.7.7" +regex = "1.10.2" [dependencies.sqlx] version = "0.6.3" From 6a5c1e13dc0ca57b8a3e19f534a18d6db50ca924 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 22 Oct 2023 08:58:08 +0300 Subject: [PATCH 09/73] Dockerfile and docker-compose , non-functional yet --- .env | 6 +- .idea/misc.xml | 3 + Dockerfile | 55 +++ Makefile | 26 ++ configuration.yaml | 2 +- docker-compose.yml | 42 ++ docker/postgres.conf | 798 ++++++++++++++++++++++++++++++++++++++ postgresql.conf | 822 ++++++++++++++++++++++++++++++++++++++++ rustfmt.toml | 1 + src/models/user.rs | 122 +++++- src/routes/mod.rs | 2 +- src/routes/stack/add.rs | 56 ++- src/startup.rs | 35 +- 13 files changed, 1924 insertions(+), 46 deletions(-) create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 docker-compose.yml create mode 100644 docker/postgres.conf create mode 100644 postgresql.conf create mode 100644 rustfmt.toml diff --git a/.env b/.env index f6e3b68..dffc672 100644 --- a/.env +++ b/.env @@ -1 +1,5 @@ -DATABASE_URL=postgres://postgres:postgres@localhost:5432/stacker \ No newline at end of file +DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/stacker +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=stacker +POSTGRES_PORT=5432 \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index d56657a..812ab5a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b763c62 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +FROM rust:slim as build + +RUN apt-get update; \ + apt-get install --no-install-recommends -y libpq-dev libssl-dev pkg-config; \ + rm -rf /var/lib/apt/lists/*; \ + USER=root cargo new --bin app; + +WORKDIR /app + +# copy manifests +COPY ./Cargo.toml . +COPY ./rustfmt.toml . +COPY ./Makefile . +COPY .env . + +RUN cargo install sqlx-cli +# build this project to cache dependencies +#ENV DATABASE_URL=postgres://postgres:postgres@172.0.0.1:5432/stacker +RUN sqlx database create && sqlx migrate run + +RUN cargo build --release; \ + rm src/*.rs + +# copy project source and necessary files +COPY ./src ./src + +# add .env and secret.key for Docker env +#RUN touch .env; + +# rebuild app with project source +RUN rm -rf ./target/release/deps/stacker*; \ + cargo build --release + + +# deploy stage +FROM debian:bullseye-slim as production + +# create app directory +WORKDIR /app +RUN mkdir ./files && chmod 0777 ./files + +# install libpq +#RUN apt-get update; \ +# apt-get install --no-install-recommends -y libpq-dev libssl-dev; \ +# rm -rf /var/lib/apt/lists/* + +# copy binary and configuration files +#COPY --from=builder ~/.cargo/bin/sqlx-cli sqlx-cli +COPY --from=build /app/target/release/stacker . +COPY --from=build /app/.env . + +EXPOSE 8080 + +# run the binary +ENTRYPOINT ["/app/stacker"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b99ff96 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +build: + @cargo build + +clean: + @cargo clean + +TESTS = "" +test: + @cargo test $(TESTS) --offline --lib -- --color=always --test-threads=1 --nocapture + +docs: build + @cargo doc --no-deps + +style-check: + @rustup component add rustfmt 2> /dev/null + cargo fmt --all -- --check + +lint: + @rustup component add clippy 2> /dev/null + touch src/** + cargo clippy --all-targets --all-features -- -D warnings + +dev: + @cargo run + +.PHONY: build test docs style-check lint diff --git a/configuration.yaml b/configuration.yaml index 1b288e0..06a1988 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -5,5 +5,5 @@ database: host: localhost port: 5432 username: postgres - password: "postgres" + password: postgres database_name: stacker diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0d23c86 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +version: "2.2" + +volumes: + stackerdb: + driver: local + +services: + + stacker: + image: trydirect/stacker:0.0.1 + build: . + container_name: stacker + restart: always + volumes: + - ./stacker/files:/app/files + ports: + - "8000:8000" + env_file: + - .env + environment: + - RUST_LOG="trace" + depends_on: + stackerdb: + condition: service_healthy + + + stackerdb: + container_name: stackerdb + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + image: postgres:16.0 + restart: always + ports: + - 127.0.0.1:5432:5432 + env_file: + - .env + volumes: + - stackerdb:/var/lib/postgresql/data + - ./docker/postgresql.conf:/etc/postgresql/postgresql.conf \ No newline at end of file diff --git a/docker/postgres.conf b/docker/postgres.conf new file mode 100644 index 0000000..4e89674 --- /dev/null +++ b/docker/postgres.conf @@ -0,0 +1,798 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: us = microseconds +# kB = kilobytes ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +#max_connections = 100 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/tmp' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +#client_connection_check_interval = 0 # time between checks for client + # disconnection while running queries; + # 0 for never + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = scram-sha-256 # scram-sha-256 or md5 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab' +#krb_caseins_users = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_crl_dir = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1.2' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +#shared_buffers = 32MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#huge_page_size = 0 # zero for system default + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#hash_mem_multiplier = 1.0 # 1-1000.0 multiplier on hash table work_mem +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#logical_decoding_work_mem = 64MB # min 64kB +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +#dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) +#min_dynamic_shared_memory = 0MB # (change requires restart) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kilobytes, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 64 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 2 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 0 # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#backend_flush_after = 0 # measured in pages, 0 disables +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#parallel_leader_participation = on +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux and FreeBSD) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_compression = off # enable compression of full-page writes +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables +#wal_skip_threshold = 2MB + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 0 # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables +#max_wal_size = 1GB +#min_wal_size = 80MB + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_command = '' # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a logfile segment switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived logfile segment + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the primary and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#wal_keep_size = 0 # in megabytes; 0 disables +#max_slot_wal_keep_size = -1 # in megabytes; -1 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Primary Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a primary server. + +#primary_conninfo = '' # connection string to sending server +#primary_slot_name = '' # replication slot on sending server +#promote_trigger_file = '' # file name whose presence ends recovery +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name + # is not set +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from primary + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_async_append = on +#enable_bitmapscan = on +#enable_gathermerge = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_incremental_sort = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_memoize = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_parallel_hash = on +#enable_partition_pruning = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#jit = on # allow JIT compilation +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (Windows): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements + # and their durations, > 0 logs only a sample of + # statements running at least this number + # of milliseconds; + # sample fraction is determined by log_statement_sample_rate + +#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding + # log_min_duration_sample to be logged; + # 1.0 logs all such statements, 0.0 never logs + + +#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements + # are logged regardless of their duration; 1.0 logs all + # statements from all transactions, 0.0 never logs + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_autovacuum_min_duration = -1 # log autovacuum activity; + # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %b = backend type + # %p = process ID + # %P = process ID of parallel group leader + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %Q = query ID (0 if none or not computed) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_recovery_conflict_waits = off # log standby recovery conflict waits + # >= deadlock_timeout +#log_parameter_max_length = -1 # when logging statements, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_parameter_max_length_on_error = 0 # when logging an error, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +#log_timezone = 'GMT' + + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Query and Index Statistics Collector - + +#track_activities = on +#track_activity_query_size = 1024 # (change requires restart) +#track_counts = on +#track_io_timing = off +#track_wal_io_timing = off +#track_functions = none # none, pl, all +#stats_temp_directory = 'pg_stat_tmp' + + +# - Monitoring - + +#compute_query_id = auto +#log_statement_stats = off +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts + # before vacuum; -1 disables insert + # vacuums +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table + # size before insert vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_table_access_method = 'heap' +#default_tablespace = '' # a tablespace name, '' uses the default +#default_toast_compression = 'pglz' # 'pglz' or 'lz4' +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#idle_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_table_age = 150000000 +#vacuum_freeze_min_age = 50000000 +#vacuum_failsafe_age = 1600000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_failsafe_age = 1600000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +#datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +#timezone = 'GMT' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +#lc_messages = 'C' # locale for system error message + # strings +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting + +# default configuration for text search +#default_text_search_config = 'pg_catalog.simple' + +# - Shared Library Preloading - + +#local_preload_libraries = '' +#session_preload_libraries = '' +#shared_preload_libraries = '' # (change requires restart) +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#extension_destdir = '' # prepend path when loading extensions + # and shared objects (added by Debian) +#gin_fuzzy_search_limit = 0 + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) +#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/postgresql.conf b/postgresql.conf new file mode 100644 index 0000000..e2acada --- /dev/null +++ b/postgresql.conf @@ -0,0 +1,822 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: us = microseconds +# kB = kilobytes ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +max_connections = 100 # (change requires restart) +#reserved_connections = 0 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +#client_connection_check_interval = 0 # time between checks for client + # disconnection while running queries; + # 0 for never + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = scram-sha-256 # scram-sha-256 or md5 +#scram_iterations = 4096 +#db_user_namespace = off + +# GSSAPI using Kerberos +#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab' +#krb_caseins_users = off +#gss_accept_delegation = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_crl_dir = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1.2' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +shared_buffers = 128MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#huge_page_size = 0 # zero for system default + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#hash_mem_multiplier = 2.0 # 1-1000.0 multiplier on hash table work_mem +#maintenance_work_mem = 64MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#logical_decoding_work_mem = 64MB # min 64kB +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +dynamic_shared_memory_type = posix # the default is usually the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) +#min_dynamic_shared_memory = 0MB # (change requires restart) +#vacuum_buffer_usage_limit = 256kB # size of vacuum and analyze buffer access strategy ring; + # 0 to disable vacuum buffer access strategy; + # range 128kB to 16GB + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kilobytes, or -1 for no limit + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 64 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 2 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 512kB # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#backend_flush_after = 0 # measured in pages, 0 disables +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 # (change requires restart) +#max_parallel_workers_per_gather = 2 # taken from max_parallel_workers +#max_parallel_maintenance_workers = 2 # taken from max_parallel_workers +#max_parallel_workers = 8 # maximum number of max_worker_processes that + # can be used in parallel operations +#parallel_leader_participation = on +#old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate + # (change requires restart) + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux and FreeBSD) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_compression = off # enables compression of full-page writes; + # off, pglz, lz4, zstd, or on +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables +#wal_skip_threshold = 2MB + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 256kB # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables +max_wal_size = 1GB +min_wal_size = 80MB + +# - Prefetching during recovery - + +#recovery_prefetch = try # prefetch pages referenced in the WAL? +#wal_decode_buffer_size = 512kB # lookahead window used for prefetching + # (change requires restart) + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_library = '' # library to use to archive a WAL file + # (empty string indicates archive_command should + # be used) +#archive_command = '' # command to use to archive a WAL file + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a WAL file switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived WAL file + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the primary and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#wal_keep_size = 0 # in megabytes; 0 disables +#max_slot_wal_keep_size = -1 # in megabytes; -1 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Primary Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all + +# - Standby Servers - + +# These settings are ignored on a primary server. + +#primary_conninfo = '' # connection string to sending server +#primary_slot_name = '' # replication slot on sending server +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name + # is not set +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from primary + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers +#max_parallel_apply_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_async_append = on +#enable_bitmapscan = on +#enable_gathermerge = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_incremental_sort = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_memoize = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_parallel_hash = on +#enable_partition_pruning = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_presorted_aggregate = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#jit = on # allow JIT compilation +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan +#recursive_worktable_factor = 10.0 # range 0.001-1000000 + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, jsonlog, syslog, and + # eventlog, depending on platform. + # csvlog and jsonlog require + # logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr, jsonlog, + # and csvlog into log files. Required + # to be on for csvlogs and jsonlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (Windows): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements + # and their durations, > 0 logs only a sample of + # statements running at least this number + # of milliseconds; + # sample fraction is determined by log_statement_sample_rate + +#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding + # log_min_duration_sample to be logged; + # 1.0 logs all such statements, 0.0 never logs + + +#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements + # are logged regardless of their duration; 1.0 logs all + # statements from all transactions, 0.0 never logs + +#log_startup_progress_interval = 10s # Time between progress updates for + # long-running startup operations. + # 0 disables the feature, > 0 indicates + # the interval in milliseconds. + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_autovacuum_min_duration = 10min # log autovacuum activity; + # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#log_checkpoints = on +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %b = backend type + # %p = process ID + # %P = process ID of parallel group leader + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %Q = query ID (0 if none or not computed) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_recovery_conflict_waits = off # log standby recovery conflict waits + # >= deadlock_timeout +#log_parameter_max_length = -1 # when logging statements, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_parameter_max_length_on_error = 0 # when logging an error, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +log_timezone = 'Etc/UTC' + +# - Process Title - + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Cumulative Query and Index Statistics - + +#track_activities = on +#track_activity_query_size = 1024 # (change requires restart) +#track_counts = on +#track_io_timing = off +#track_wal_io_timing = off +#track_functions = none # none, pl, all +#stats_fetch_consistency = cache # cache, none, snapshot + + +# - Monitoring - + +#compute_query_id = auto +#log_statement_stats = off +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts + # before vacuum; -1 disables insert + # vacuums +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table + # size before insert vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_table_access_method = 'heap' +#default_tablespace = '' # a tablespace name, '' uses the default +#default_toast_compression = 'pglz' # 'pglz' or 'lz4' +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#idle_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_table_age = 150000000 +#vacuum_freeze_min_age = 50000000 +#vacuum_failsafe_age = 1600000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_failsafe_age = 1600000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_pending_list_limit = 4MB +#createrole_self_grant = '' # set and/or inherit + +# - Locale and Formatting - + +datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +timezone = 'Etc/UTC' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +lc_messages = 'en_US.utf8' # locale for system error message + # strings +lc_monetary = 'en_US.utf8' # locale for monetary formatting +lc_numeric = 'en_US.utf8' # locale for number formatting +lc_time = 'en_US.utf8' # locale for time formatting + +#icu_validation_level = warning # report ICU locale validation + # errors at the given level + +# default configuration for text search +default_text_search_config = 'pg_catalog.english' + +# - Shared Library Preloading - + +#local_preload_libraries = '' +#session_preload_libraries = '' +#shared_preload_libraries = '' # (change requires restart) +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#extension_destdir = '' # prepend path when loading extensions + # and shared objects (added by Debian) +#gin_fuzzy_search_limit = 0 + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) +#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..32a9786 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +edition = "2018" diff --git a/src/models/user.rs b/src/models/user.rs index f345e40..5133c47 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,6 +1,124 @@ -use serde::Deserialize; +use serde_derive::{Serialize, Deserialize}; +use serde_json::Value; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Root { + pub user: User, +} #[derive(Debug, Copy, Clone, Deserialize)] +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct User { - pub id: i32, + #[serde(rename = "_id")] + pub id: String, + #[serde(rename = "first_name")] + pub first_name: String, + #[serde(rename = "last_name")] + pub last_name: String, + pub created: String, + pub updated: String, + pub email: String, + #[serde(rename = "email_confirmed")] + pub email_confirmed: bool, + pub social: bool, + pub website: String, + pub currency: Value, + pub phone: String, + #[serde(rename = "password_change_required")] + pub password_change_required: Value, + pub photo: String, + pub country: String, + #[serde(rename = "billing_first_name")] + pub billing_first_name: Value, + #[serde(rename = "billing_last_name")] + pub billing_last_name: Value, + #[serde(rename = "billing_postcode")] + pub billing_postcode: String, + #[serde(rename = "billing_address_1")] + pub billing_address_1: String, + #[serde(rename = "billing_address_2")] + pub billing_address_2: String, + #[serde(rename = "billing_city")] + pub billing_city: String, + #[serde(rename = "billing_country_code")] + pub billing_country_code: String, + #[serde(rename = "billing_country_area")] + pub billing_country_area: String, + pub tokens: Vec, + pub subscriptions: Vec, + pub plan: Plan, + #[serde(rename = "deployments_left")] + pub deployments_left: Value, + #[serde(rename = "suspension_hints")] + pub suspension_hints: SuspensionHints, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Token { + pub provider: String, + pub expired: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Subscription { + #[serde(rename = "subscription_id")] + pub subscription_id: i64, + #[serde(rename = "user_id")] + pub user_id: i64, + #[serde(rename = "date_created")] + pub date_created: String, + #[serde(rename = "date_updated")] + pub date_updated: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Plan { + #[serde(rename = "supported_stacks")] + pub supported_stacks: SupportedStacks, + #[serde(rename = "date_end")] + pub date_end: Value, + pub name: String, + pub code: String, + pub includes: Vec, + pub team: String, + #[serde(rename = "billing_email")] + pub billing_email: String, + #[serde(rename = "date_of_purchase")] + pub date_of_purchase: String, + pub currency: String, + pub price: String, + pub period: String, + #[serde(rename = "date_start")] + pub date_start: String, + pub active: bool, + #[serde(rename = "billing_id")] + pub billing_id: String, } + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SupportedStacks { + pub monthly: i64, + pub annually: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Include { + pub name: String, + pub code: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SuspensionHints { + pub days: i64, + pub reason: String, +} + + diff --git a/src/routes/mod.rs b/src/routes/mod.rs index e04b518..c80fca5 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,5 +1,5 @@ mod health_checks; -mod rating; +pub(crate) mod rating; pub use health_checks::*; pub use rating::*; diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 0e73f88..f803d3c 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,16 +1,19 @@ use actix_web::{web::{Data, Bytes, Json}, Responder, Result, web}; +use crate::forms::stack::StackForm; +use crate::models::user::User; +use crate::models::Stack; +use crate::utils::json::JsonResponse; use chrono::Utc; use sqlx::PgPool; use std::str; use tracing::Instrument; use uuid::Uuid; -use crate::forms::stack::StackForm; -use crate::models::Stack; -use crate::models::user::User; -use crate::utils::json::JsonResponse; - +use serde_json::Value; +use actix_web::post; +#[tracing::instrument(name = "Add stack.")] +#[post("")] pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> Result { // None::.expect("my error"); @@ -32,7 +35,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R status: "Error".to_string(), code: 400, message: err.to_string(), - id: None + id: None, })); } }; @@ -56,35 +59,27 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R form.region ); - let query_span = tracing::info_span!( - "Saving new stack details into the database" - ); + let query_span = tracing::info_span!("Saving new stack details into the database"); - let custom_stack_code = form.custom.custom_stack_code.clone(); - let body = match serde_json::to_value::(form) { - Ok(body) => { - body - } + let stack_name = form.custom.custom_stack_code.clone(); + let body: Value = match serde_json::to_value::(form) { + Ok(body) => body, Err(err) => { - tracing::error!( - "request_id {} unwrap body {:?}", - request_id, - err - ); + tracing::error!("request_id {} unwrap body {:?}", request_id, err); serde_json::to_value::(StackForm::default()).unwrap() } }; let stack = Stack { - id: 0_i32, // internal stack id - stack_id: Uuid::new_v4(), // public uuid of the stack + id: 0_i32, // internal stack id + stack_id: Uuid::new_v4(), // public uuid of the stack // user_id: Uuid::from_u128(user_id as u128), - user_id: user_id, // - name: custom_stack_code, + user_id: user_id, // + name: stack_name, body: body, // body: body_str.to_string(), created_at: Utc::now(), - updated_at: Utc::now() + updated_at: Utc::now(), }; println!("stack object {:?}", stack); @@ -103,9 +98,9 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R stack.created_at, stack.updated_at ) - .fetch_one(pool.get_ref()) - .instrument(query_span) - .await + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await { Ok(record) => { tracing::info!( @@ -116,7 +111,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R status: "OK".to_string(), code: 200, message: format!("Object saved"), - id: Some(record.id) + id: Some(record.id), })) } Err(e) => { @@ -125,9 +120,8 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R status: "Error".to_string(), code: 400, message: e.to_string(), - id: None + id: None, })) } - } - + }; } diff --git a/src/startup.rs b/src/startup.rs index 6f1f7c2..d0b6917 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -1,3 +1,4 @@ +use reqwest::Url; use crate::configuration::Settings; use actix_cors::Cors; use actix_web::dev::{Server, ServiceRequest}; @@ -25,21 +26,34 @@ async fn bearer_guard( ) -> Result { let settings = req.app_data::>().unwrap(); + let url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); + // let data_url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); + tracing::debug!("URL :::: {:?}", url); + + let client = reqwest::Client::new(); let resp = client - .get(&settings.auth_url) + // .get(&settings.auth_url) + .get(url) .bearer_auth(credentials.token()) .header(CONTENT_TYPE, "application/json") .header(ACCEPT, "application/json") .send() .await; + + // tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); + let resp = match resp { - Ok(resp) if resp.status().is_success() => resp, Ok(resp) => { - tracing::error!("Authentication service returned no success {:?}", resp); - return Err((ErrorUnauthorized(""), req)); + //if resp.status().is_success() + tracing::debug!("{:?}", resp); + resp } + // Ok(resp) => { + // tracing::error!("Authentication service returned no success {:?}", resp); + // return Err((ErrorUnauthorized(""), req)); + // } Err(err) => { tracing::error!("error from reqwest {:?}", err); return Err((ErrorInternalServerError(""), req)); @@ -86,8 +100,8 @@ pub async fn run(settings: Settings) -> Result { web::scope("/rating") .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) - .service(crate::routes::add_handler) - .service(crate::routes::get_handler), + .service(crate::routes::rating::add_handler) + .service(crate::routes::rating::get_handler), ) // .service( // web::resource("/stack/{id}") @@ -98,11 +112,12 @@ pub async fn run(settings: Settings) -> Result { // .route(web::post() // .to(crate::routes::stack::add)), // ) - .service(web::resource("/stack") - .route(web::post().to(crate::routes::stack::add::add))) .service( - web::resource("/stack/deploy") - .route(web::post().to(crate::routes::stack::deploy)), + web::scope("/stack") + .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(Cors::permissive()) + .service(crate::routes::stack::add::add) + //.service(crate::routes::stack::deploy), ) .app_data(db_pool.clone()) .app_data(settings.clone()) From 6d7ed85ab5670ef3eb8585d7a010612294cf3548 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 22 Oct 2023 08:59:33 +0300 Subject: [PATCH 10/73] Dockerfile and docker-compose , non-functional yet --- docker/{postgres.conf => postgresql.conf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker/{postgres.conf => postgresql.conf} (100%) diff --git a/docker/postgres.conf b/docker/postgresql.conf similarity index 100% rename from docker/postgres.conf rename to docker/postgresql.conf From 2d411a695b711cbaadbb25dbdcdc006beb224ad0 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 22 Oct 2023 10:40:33 +0300 Subject: [PATCH 11/73] user id to string (/me returns type string), User models, forms and struct adjusted --- Cargo.lock | 29 +++- Cargo.toml | 2 + ...230903063840_creating_rating_tables.up.sql | 2 +- ...0230905145525_creating_stack_tables.up.sql | 2 +- src/forms/mod.rs | 1 + src/forms/stack.rs | 4 +- src/forms/user.rs | 138 ++++++++++++++++++ src/models/rating.rs | 2 +- src/models/stack.rs | 2 +- src/models/user.rs | 128 ++-------------- src/routes/stack/add.rs | 3 +- src/startup.rs | 11 +- 12 files changed, 189 insertions(+), 135 deletions(-) create mode 100644 src/forms/user.rs diff --git a/Cargo.lock b/Cargo.lock index a9bf68a..e874895 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1106,7 +1106,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -1120,9 +1120,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" @@ -1537,13 +1537,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1555,6 +1556,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -1563,9 +1575,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" @@ -2018,6 +2030,7 @@ dependencies = [ "actix-web-httpauth", "chrono", "config", + "regex", "reqwest", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index f7df3c7..0608a4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ serde_derive = "1.0.188" actix-web-httpauth = "0.8.1" actix-cors = "0.6.4" tracing-actix-web = "0.7.7" +regex = "1.10.2" + [dependencies.sqlx] version = "0.6.3" diff --git a/migrations/20230903063840_creating_rating_tables.up.sql b/migrations/20230903063840_creating_rating_tables.up.sql index 4aaeb3f..450e365 100644 --- a/migrations/20230903063840_creating_rating_tables.up.sql +++ b/migrations/20230903063840_creating_rating_tables.up.sql @@ -10,7 +10,7 @@ CREATE TABLE product ( CREATE TABLE rating ( id serial, - user_id integer NOT NULL, + user_id VARCHAR(50) NOT NULL, product_id integer NOT NULL, category VARCHAR(255) NOT NULL, comment TEXT DEFAULT NULL, diff --git a/migrations/20230905145525_creating_stack_tables.up.sql b/migrations/20230905145525_creating_stack_tables.up.sql index eba00f9..e908e97 100644 --- a/migrations/20230905145525_creating_stack_tables.up.sql +++ b/migrations/20230905145525_creating_stack_tables.up.sql @@ -3,7 +3,7 @@ CREATE TABLE user_stack ( id serial, stack_id uuid NOT NULL, - user_id integer NOT NULL, + user_id VARCHAR(50) NOT NULL, name TEXT NOT NULL UNIQUE, body JSON NOT NULL, created_at timestamptz NOT NULL, diff --git a/src/forms/mod.rs b/src/forms/mod.rs index f4280a7..957c91d 100644 --- a/src/forms/mod.rs +++ b/src/forms/mod.rs @@ -1,6 +1,7 @@ mod rating; pub(crate) mod stack; +pub mod user; pub use rating::*; diff --git a/src/forms/stack.rs b/src/forms/stack.rs index 72436d3..93a1be8 100644 --- a/src/forms/stack.rs +++ b/src/forms/stack.rs @@ -96,13 +96,13 @@ pub struct Web { pub url_git: Option, #[validate(min_length=1)] #[validate(max_length=10)] - //#[validate(pattern = r"^\d+G$")] + #[validate(pattern = r"^\d+G$")] #[serde(rename = "disk_size")] pub disk_size: String, #[serde(rename = "ram_size")] #[validate(min_length=1)] #[validate(max_length=10)] - //#[validate(pattern = r"^\d+G$")] + #[validate(pattern = r"^\d+G$")] pub ram_size: String, #[validate(minimum=0.1)] pub cpu: f64, diff --git a/src/forms/user.rs b/src/forms/user.rs new file mode 100644 index 0000000..21fba4a --- /dev/null +++ b/src/forms/user.rs @@ -0,0 +1,138 @@ +use serde_derive::{Serialize, Deserialize}; +use serde_json::Value; +use serde_valid::{Validate}; +use tracing_subscriber::fmt::format; +use crate::models::user::User as UserModel; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserForm { + pub user: User, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] +#[serde(rename_all = "camelCase")] +pub struct User { + #[serde(rename = "_id")] + pub id: String, + #[serde(rename = "first_name")] + pub first_name: String, + #[serde(rename = "last_name")] + pub last_name: String, + pub created: String, + pub updated: String, + pub email: String, + #[serde(rename = "email_confirmed")] + pub email_confirmed: bool, + pub social: bool, + pub website: String, + pub currency: Value, + pub phone: String, + #[serde(rename = "password_change_required")] + pub password_change_required: Value, + pub photo: String, + pub country: String, + #[serde(rename = "billing_first_name")] + pub billing_first_name: Value, + #[serde(rename = "billing_last_name")] + pub billing_last_name: Value, + #[serde(rename = "billing_postcode")] + pub billing_postcode: String, + #[serde(rename = "billing_address_1")] + pub billing_address_1: String, + #[serde(rename = "billing_address_2")] + pub billing_address_2: String, + #[serde(rename = "billing_city")] + pub billing_city: String, + #[serde(rename = "billing_country_code")] + pub billing_country_code: String, + #[serde(rename = "billing_country_area")] + pub billing_country_area: String, + pub tokens: Vec, + pub subscriptions: Vec, + pub plan: Plan, + #[serde(rename = "deployments_left")] + pub deployments_left: Value, + #[serde(rename = "suspension_hints")] + pub suspension_hints: SuspensionHints, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Token { + pub provider: String, + pub expired: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Subscription { + #[serde(rename = "subscription_id")] + pub subscription_id: i64, + #[serde(rename = "user_id")] + pub user_id: i64, + #[serde(rename = "date_created")] + pub date_created: String, + #[serde(rename = "date_updated")] + pub date_updated: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Plan { + #[serde(rename = "supported_stacks")] + pub supported_stacks: SupportedStacks, + #[serde(rename = "date_end")] + pub date_end: Value, + pub name: String, + pub code: String, + pub includes: Vec, + pub team: String, + #[serde(rename = "billing_email")] + pub billing_email: String, + #[serde(rename = "date_of_purchase")] + pub date_of_purchase: String, + pub currency: String, + pub price: String, + pub period: String, + #[serde(rename = "date_start")] + pub date_start: String, + pub active: bool, + #[serde(rename = "billing_id")] + pub billing_id: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SupportedStacks { + pub monthly: i64, + pub annually: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Include { + pub name: String, + pub code: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SuspensionHints { + pub days: i64, + pub reason: String, +} + + +impl TryInto for UserForm { + type Error = String; + fn try_into(self) -> Result { + // let id = self.id.parse::().map_err( + // |msg| { format!("{:?}", msg) } + // )?; + Ok(UserModel { + id: self.user.id + }) + } + +} \ No newline at end of file diff --git a/src/models/rating.rs b/src/models/rating.rs index 6e914f0..54a6a2f 100644 --- a/src/models/rating.rs +++ b/src/models/rating.rs @@ -19,7 +19,7 @@ pub struct Product { #[derive(Debug, Serialize)] pub struct Rating { pub id: i32, - pub user_id: i32, // external user_id, 100, taken using token (middleware?) + pub user_id: String, // external user_id, 100, taken using token (middleware?) pub product_id: i32, //primary key, for better data management pub category: String, // rating of product | rating of service etc pub comment: Option, // always linked to a product diff --git a/src/models/stack.rs b/src/models/stack.rs index 35ffe5c..c3bf934 100644 --- a/src/models/stack.rs +++ b/src/models/stack.rs @@ -9,7 +9,7 @@ use uuid::Uuid; pub struct Stack { pub id: i32, // id - is a unique identifier for the app stack pub stack_id: Uuid, // external stack ID - pub user_id: i32, // external unique identifier for the user + pub user_id: String, // external unique identifier for the user pub name: String, // pub body: sqlx::types::Json, pub body: Value, //json type diff --git a/src/models/user.rs b/src/models/user.rs index 5133c47..db2d87a 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,124 +1,14 @@ -use serde_derive::{Serialize, Deserialize}; -use serde_json::Value; +use serde::Deserialize; -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Root { - pub user: User, -} - -#[derive(Debug, Copy, Clone, Deserialize)] -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Deserialize)] pub struct User { - #[serde(rename = "_id")] pub id: String, - #[serde(rename = "first_name")] - pub first_name: String, - #[serde(rename = "last_name")] - pub last_name: String, - pub created: String, - pub updated: String, - pub email: String, - #[serde(rename = "email_confirmed")] - pub email_confirmed: bool, - pub social: bool, - pub website: String, - pub currency: Value, - pub phone: String, - #[serde(rename = "password_change_required")] - pub password_change_required: Value, - pub photo: String, - pub country: String, - #[serde(rename = "billing_first_name")] - pub billing_first_name: Value, - #[serde(rename = "billing_last_name")] - pub billing_last_name: Value, - #[serde(rename = "billing_postcode")] - pub billing_postcode: String, - #[serde(rename = "billing_address_1")] - pub billing_address_1: String, - #[serde(rename = "billing_address_2")] - pub billing_address_2: String, - #[serde(rename = "billing_city")] - pub billing_city: String, - #[serde(rename = "billing_country_code")] - pub billing_country_code: String, - #[serde(rename = "billing_country_area")] - pub billing_country_area: String, - pub tokens: Vec, - pub subscriptions: Vec, - pub plan: Plan, - #[serde(rename = "deployments_left")] - pub deployments_left: Value, - #[serde(rename = "suspension_hints")] - pub suspension_hints: SuspensionHints, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Token { - pub provider: String, - pub expired: bool, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Subscription { - #[serde(rename = "subscription_id")] - pub subscription_id: i64, - #[serde(rename = "user_id")] - pub user_id: i64, - #[serde(rename = "date_created")] - pub date_created: String, - #[serde(rename = "date_updated")] - pub date_updated: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Plan { - #[serde(rename = "supported_stacks")] - pub supported_stacks: SupportedStacks, - #[serde(rename = "date_end")] - pub date_end: Value, - pub name: String, - pub code: String, - pub includes: Vec, - pub team: String, - #[serde(rename = "billing_email")] - pub billing_email: String, - #[serde(rename = "date_of_purchase")] - pub date_of_purchase: String, - pub currency: String, - pub price: String, - pub period: String, - #[serde(rename = "date_start")] - pub date_start: String, - pub active: bool, - #[serde(rename = "billing_id")] - pub billing_id: String, } -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SupportedStacks { - pub monthly: i64, - pub annually: i64, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Include { - pub name: String, - pub code: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SuspensionHints { - pub days: i64, - pub reason: String, -} - - +impl Clone for User { + fn clone(&self) -> Self { + User { + id: self.id.clone() + } + } +} \ No newline at end of file diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index f803d3c..77e185c 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,6 +1,7 @@ use actix_web::{web::{Data, Bytes, Json}, Responder, Result, web}; use crate::forms::stack::StackForm; +use crate::forms::user::UserForm; use crate::models::user::User; use crate::models::Stack; use crate::utils::json::JsonResponse; @@ -41,7 +42,7 @@ pub async fn add(body: Bytes, user: web::ReqData, pool: Data) -> R }; // println!("app: {:?}", form); - let user_id = user.id; + let user_id = user.id.clone(); let request_id = Uuid::new_v4(); let request_span = tracing::info_span!( "Validating a new stack", %request_id, diff --git a/src/startup.rs b/src/startup.rs index d0b6917..ba3f394 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -16,6 +16,7 @@ use reqwest::header::{ACCEPT, CONTENT_TYPE}; use sqlx::PgPool; use std::sync::Arc; use tracing_actix_web::TracingLogger; +use crate::forms::user::UserForm; use crate::models::user::User; @@ -60,7 +61,7 @@ async fn bearer_guard( } }; - let user: User = match resp.json().await { + let user_form: UserForm = match resp.json().await { Ok(user) => { tracing::info!("unpacked user {user:?}"); user @@ -71,6 +72,14 @@ async fn bearer_guard( } }; + let user:User = match user_form.try_into() // try to convert UserForm into User model + { + Ok(user) => { user } + Err(err) => { + tracing::error!("Could not create User from form data: {:?}", err); + return Err((ErrorUnauthorized(""), req)); + } + }; let existent_user = req.extensions_mut().insert(user); if existent_user.is_some() { tracing::error!("already logged {existent_user:?}"); From 102e1da188a921ce8488bd14f294a65a49cb28b7 Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 25 Oct 2023 18:17:58 +0300 Subject: [PATCH 12/73] issue-auth - logic --- src/routes/client/add.rs | 19 +++++++++++++++++++ src/routes/client/mod.rs | 3 +++ src/routes/mod.rs | 1 + src/startup.rs | 21 +++++++++------------ 4 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 src/routes/client/add.rs create mode 100644 src/routes/client/mod.rs diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs new file mode 100644 index 0000000..d6b85be --- /dev/null +++ b/src/routes/client/add.rs @@ -0,0 +1,19 @@ +use crate::models; +use crate::models::user::User; +use crate::models::RateCategory; +use crate::utils::json::JsonResponse; +use actix_web::post; +use actix_web::{web, Responder, Result}; +use sqlx::PgPool; +use tracing::Instrument; + +#[tracing::instrument(name = "Add client.")] +#[post("")] +pub async fn add_handler( + user: web::ReqData, + pool: web::Data, +) -> Result { + //todo generate client + //save client in DB + //return the client as a response +} diff --git a/src/routes/client/mod.rs b/src/routes/client/mod.rs new file mode 100644 index 0000000..0cc183a --- /dev/null +++ b/src/routes/client/mod.rs @@ -0,0 +1,3 @@ +pub mod add; + +pub use add::*; diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 11b8536..fbb81d4 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod client; pub mod health_checks; pub(crate) mod rating; diff --git a/src/startup.rs b/src/startup.rs index 10f3991..13b2d9f 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -1,5 +1,5 @@ -use reqwest::Url; use crate::configuration::Settings; +use crate::forms::user::UserForm; use actix_cors::Cors; use actix_web::dev::{Server, ServiceRequest}; use actix_web::error::{ErrorInternalServerError, ErrorUnauthorized}; @@ -13,10 +13,10 @@ use actix_web::{ }; use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; use reqwest::header::{ACCEPT, CONTENT_TYPE}; +use reqwest::Url; use sqlx::PgPool; use std::sync::Arc; use tracing_actix_web::TracingLogger; -use crate::forms::user::UserForm; use crate::models::user::User; @@ -31,7 +31,6 @@ async fn bearer_guard( // let data_url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); tracing::debug!("URL :::: {:?}", url); - let client = reqwest::Client::new(); let resp = client // .get(&settings.auth_url) @@ -42,7 +41,6 @@ async fn bearer_guard( .send() .await; - // tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); let resp = match resp { @@ -72,7 +70,7 @@ async fn bearer_guard( } }; - let user:User = match user_form.try_into() // try to convert UserForm into User model + let user: User = match user_form.try_into() // try to convert UserForm into User model { Ok(user) => { user } Err(err) => { @@ -105,14 +103,14 @@ pub async fn run(settings: Settings) -> Result { App::new() .wrap(TracingLogger::default()) .service(web::scope("/health_check").service(crate::routes::health_check)) - /* .service( web::scope("/client") - .wrap(HttpAuthentication::bearer(bearer_guard)) - .wrap(Cors::permissive()) - .service(crate::routes::add_handler), + /* + .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(Cors::permissive()) + */ + .service(crate::routes::client::add_handler), ) - */ .service( web::scope("/rating") .wrap(HttpAuthentication::bearer(bearer_guard)) @@ -133,8 +131,7 @@ pub async fn run(settings: Settings) -> Result { web::scope("/stack") .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) - .service(crate::routes::stack::add::add) - //.service(crate::routes::stack::deploy), + .service(crate::routes::stack::add::add), //.service(crate::routes::stack::deploy), ) .app_data(db_pool.clone()) .app_data(settings.clone()) From ffbb411cfb466aa762aa94e75e726a54019b7871 Mon Sep 17 00:00:00 2001 From: Petru Date: Thu, 26 Oct 2023 18:11:31 +0300 Subject: [PATCH 13/73] issue-auth StatusCode::OK --- configuration.yaml | 1 - src/routes/client/add.rs | 15 +++++++++++++-- src/routes/rating/get.rs | 3 +-- src/startup.rs | 21 +++++---------------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/configuration.yaml b/configuration.yaml index 06a1988..3b9d945 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,5 +1,4 @@ application_port: 8000 -#auth_url: https://65190108818c4e98ac6000e4.mockapi.io/user/1 auth_url: https://dev.try.direct/server/user/oauth_server/api/me database: host: localhost diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index d6b85be..9f59b99 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -2,18 +2,29 @@ use crate::models; use crate::models::user::User; use crate::models::RateCategory; use crate::utils::json::JsonResponse; -use actix_web::post; -use actix_web::{web, Responder, Result}; +use actix_web::{post, web, Responder, Result}; +use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; +#[derive(Serialize)] +struct ClientAddResponse { + status: String, + code: u32, +} + #[tracing::instrument(name = "Add client.")] #[post("")] pub async fn add_handler( user: web::ReqData, pool: web::Data, ) -> Result { + //todo how many clients can an user have? //todo generate client //save client in DB //return the client as a response + return Ok(web::Json(ClientAddResponse { + status: "success".to_string(), + code: 200, + })); } diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs index 505e66d..d8e4216 100644 --- a/src/routes/rating/get.rs +++ b/src/routes/rating/get.rs @@ -1,6 +1,5 @@ use crate::models; -use actix_web::get; -use actix_web::{web, Responder, Result}; +use actix_web::{get, web, Responder, Result}; use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; diff --git a/src/startup.rs b/src/startup.rs index 13b2d9f..95ed932 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -26,33 +26,21 @@ async fn bearer_guard( credentials: BearerAuth, ) -> Result { let settings = req.app_data::>().unwrap(); - - let url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); - // let data_url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); - tracing::debug!("URL :::: {:?}", url); - let client = reqwest::Client::new(); let resp = client - // .get(&settings.auth_url) - .get(url) + .get(&settings.auth_url) .bearer_auth(credentials.token()) .header(CONTENT_TYPE, "application/json") .header(ACCEPT, "application/json") .send() .await; - // tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); - let resp = match resp { + Ok(resp) if resp.status().is_success() => resp, Ok(resp) => { - //if resp.status().is_success() - tracing::debug!("{:?}", resp); - resp + tracing::error!("Authentication service returned no success {:?}", resp); + return Err((ErrorUnauthorized(""), req)); } - // Ok(resp) => { - // tracing::error!("Authentication service returned no success {:?}", resp); - // return Err((ErrorUnauthorized(""), req)); - // } Err(err) => { tracing::error!("error from reqwest {:?}", err); return Err((ErrorInternalServerError(""), req)); @@ -106,6 +94,7 @@ pub async fn run(settings: Settings) -> Result { .service( web::scope("/client") /* + * todo .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) */ From b16384d4aa1ff12700c8b2df1a1c75125b32fe8b Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 28 Oct 2023 18:37:54 +0300 Subject: [PATCH 14/73] issue-auth bearer_guard --- src/startup.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/startup.rs b/src/startup.rs index 95ed932..ae719e7 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -93,11 +93,8 @@ pub async fn run(settings: Settings) -> Result { .service(web::scope("/health_check").service(crate::routes::health_check)) .service( web::scope("/client") - /* - * todo - .wrap(HttpAuthentication::bearer(bearer_guard)) - .wrap(Cors::permissive()) - */ + .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(Cors::permissive()) .service(crate::routes::client::add_handler), ) .service( From ae4f07bf7c888f74268ab7b9a8053ced2df9aa72 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 28 Oct 2023 19:23:22 +0300 Subject: [PATCH 15/73] issue-auth - client migration --- migrations/20231028161917_client.down.sql | 2 ++ migrations/20231028161917_client.up.sql | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 migrations/20231028161917_client.down.sql create mode 100644 migrations/20231028161917_client.up.sql diff --git a/migrations/20231028161917_client.down.sql b/migrations/20231028161917_client.down.sql new file mode 100644 index 0000000..800b06e --- /dev/null +++ b/migrations/20231028161917_client.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +DROP TABLE client; diff --git a/migrations/20231028161917_client.up.sql b/migrations/20231028161917_client.up.sql new file mode 100644 index 0000000..3befeca --- /dev/null +++ b/migrations/20231028161917_client.up.sql @@ -0,0 +1,7 @@ +-- Add up migration script here +CREATE TABLE public.client ( + id serial4 NOT NULL, + user_id varchar(50) NOT NULL, + "password" varchar(255) NOT NULL, + CONSTRAINT client_pkey PRIMARY KEY (id) +); From a4818a3735d3acd0cfb217840e3c95d6d9d9a147 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 28 Oct 2023 19:44:04 +0300 Subject: [PATCH 16/73] issue-auth client entity --- migrations/20231028161917_client.up.sql | 2 +- src/models/client.rs | 8 ++++ src/models/mod.rs | 5 +- src/routes/client/add.rs | 62 ++++++++++++++++++++++--- 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/models/client.rs diff --git a/migrations/20231028161917_client.up.sql b/migrations/20231028161917_client.up.sql index 3befeca..a9b74c7 100644 --- a/migrations/20231028161917_client.up.sql +++ b/migrations/20231028161917_client.up.sql @@ -2,6 +2,6 @@ CREATE TABLE public.client ( id serial4 NOT NULL, user_id varchar(50) NOT NULL, - "password" varchar(255) NOT NULL, + secret varchar(255) NOT NULL, CONSTRAINT client_pkey PRIMARY KEY (id) ); diff --git a/src/models/client.rs b/src/models/client.rs new file mode 100644 index 0000000..d6676dd --- /dev/null +++ b/src/models/client.rs @@ -0,0 +1,8 @@ +use serde::Serialize; + +#[derive(Default, Serialize)] +pub struct Client { + pub id: i32, + pub user_id: String, + pub secret: String, +} diff --git a/src/models/mod.rs b/src/models/mod.rs index bc4add4..32ecf55 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,7 +1,8 @@ +mod client; pub mod rating; pub mod stack; pub mod user; +pub use client::*; pub use rating::*; - -pub use stack::*; \ No newline at end of file +pub use stack::*; diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 9f59b99..fd99f98 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -1,16 +1,14 @@ -use crate::models; use crate::models::user::User; -use crate::models::RateCategory; -use crate::utils::json::JsonResponse; +use crate::models::Client; use actix_web::{post, web, Responder, Result}; -use serde_derive::Serialize; +use serde::Serialize; use sqlx::PgPool; -use tracing::Instrument; #[derive(Serialize)] struct ClientAddResponse { status: String, code: u32, + client: Client, } #[tracing::instrument(name = "Add client.")] @@ -20,11 +18,63 @@ pub async fn add_handler( pool: web::Data, ) -> Result { //todo how many clients can an user have? - //todo generate client + let mut client = Client::default(); + client.id = 1; + client.user_id = user.id.clone(); + client.secret = "secret".to_string(); + //todo 1. genereate random secret. 255symbols + //todo 2. save it to database + //todo 3. update entity with the database's generated id + //todo 4. it throws 500 when the AS is not reachable. it should just return 401 + + /* + let query_span = tracing::info_span!("Saving new rating details into the database"); + // Insert rating + match sqlx::query!( + r#" + INSERT INTO rating (user_id, product_id, category, comment, hidden,rate, + created_at, + updated_at) + VALUES ($1, $2, $3, $4, $5, $6, NOW() at time zone 'utc', NOW() at time zone 'utc') + RETURNING id + "#, + user.id, + form.obj_id, + form.category as models::RateCategory, + form.comment, + false, + form.rate + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(result) => { + tracing::info!("New rating {} have been saved to database", result.id); + + Ok(web::Json(JsonResponse { + status: "ok".to_string(), + code: 200, + message: "Saved".to_string(), + id: Some(result.id), + })) + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + Ok(web::Json(JsonResponse { + status: "error".to_string(), + code: 500, + message: "Failed to insert".to_string(), + id: None, + })) + } + } + */ //save client in DB //return the client as a response return Ok(web::Json(ClientAddResponse { status: "success".to_string(), code: 200, + client: client, })); } From bafa9feaa37d9c798b53cf42567cf2dedb274afb Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 28 Oct 2023 23:30:20 +0300 Subject: [PATCH 17/73] issue-auth generate secret --- Cargo.lock | 1 + Cargo.toml | 1 + src/routes/client/add.rs | 17 +++++++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ffc8a5..7903f67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2123,6 +2123,7 @@ dependencies = [ "actix-web-httpauth", "chrono", "config", + "rand", "regex", "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index 82d4753..25bf2e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ actix-web-httpauth = "0.8.1" actix-cors = "0.6.4" tracing-actix-web = "0.7.7" regex = "1.10.2" +rand = "0.8.5" [dependencies.sqlx] version = "0.6.3" diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index fd99f98..9ec56b1 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -1,6 +1,7 @@ use crate::models::user::User; use crate::models::Client; use actix_web::{post, web, Responder, Result}; +use rand::Rng; use serde::Serialize; use sqlx::PgPool; @@ -11,6 +12,19 @@ struct ClientAddResponse { client: Client, } +fn generate_secret(len: usize) -> String { + const CHARSET: &[u8] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)(*&^%$#@!~"; + let mut rng = rand::thread_rng(); + + (0..len) + .map(|_| { + let idx = rng.gen_range(0..CHARSET.len()); + CHARSET[idx] as char + }) + .collect() +} + #[tracing::instrument(name = "Add client.")] #[post("")] pub async fn add_handler( @@ -21,8 +35,7 @@ pub async fn add_handler( let mut client = Client::default(); client.id = 1; client.user_id = user.id.clone(); - client.secret = "secret".to_string(); - //todo 1. genereate random secret. 255symbols + client.secret = generate_secret(255); //todo 2. save it to database //todo 3. update entity with the database's generated id //todo 4. it throws 500 when the AS is not reachable. it should just return 401 From 90702a962c2506b05c1ce5fb69bd379ebf1d88e3 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 28 Oct 2023 23:43:03 +0300 Subject: [PATCH 18/73] issue-auth save client into db --- migrations/20231028161917_client.up.sql | 10 +++-- src/routes/client/add.rs | 50 +++++++++---------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/migrations/20231028161917_client.up.sql b/migrations/20231028161917_client.up.sql index a9b74c7..ea03bba 100644 --- a/migrations/20231028161917_client.up.sql +++ b/migrations/20231028161917_client.up.sql @@ -1,7 +1,9 @@ -- Add up migration script here CREATE TABLE public.client ( - id serial4 NOT NULL, - user_id varchar(50) NOT NULL, - secret varchar(255) NOT NULL, - CONSTRAINT client_pkey PRIMARY KEY (id) + id serial4 NOT NULL, + user_id varchar(50) NOT NULL, + secret varchar(255) NOT NULL, + created_at timestamptz NOT NULL, + updated_at timestamptz NOT NULL, + CONSTRAINT client_pkey PRIMARY KEY (id) ); diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 9ec56b1..f73ad5f 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -4,12 +4,14 @@ use actix_web::{post, web, Responder, Result}; use rand::Rng; use serde::Serialize; use sqlx::PgPool; +use tracing::Instrument; #[derive(Serialize)] struct ClientAddResponse { status: String, + message: String, code: u32, - client: Client, + client: Option, } fn generate_secret(len: usize) -> String { @@ -36,58 +38,42 @@ pub async fn add_handler( client.id = 1; client.user_id = user.id.clone(); client.secret = generate_secret(255); - //todo 2. save it to database //todo 3. update entity with the database's generated id //todo 4. it throws 500 when the AS is not reachable. it should just return 401 - /* - let query_span = tracing::info_span!("Saving new rating details into the database"); - // Insert rating + let query_span = tracing::info_span!("Saving new client into the database"); match sqlx::query!( r#" - INSERT INTO rating (user_id, product_id, category, comment, hidden,rate, - created_at, - updated_at) - VALUES ($1, $2, $3, $4, $5, $6, NOW() at time zone 'utc', NOW() at time zone 'utc') + INSERT INTO client (user_id, secret, created_at, updated_at) + VALUES ($1, $2, NOW() at time zone 'utc', NOW() at time zone 'utc') RETURNING id "#, - user.id, - form.obj_id, - form.category as models::RateCategory, - form.comment, - false, - form.rate + client.user_id.clone(), + client.secret, ) .fetch_one(pool.get_ref()) .instrument(query_span) .await { Ok(result) => { - tracing::info!("New rating {} have been saved to database", result.id); - - Ok(web::Json(JsonResponse { - status: "ok".to_string(), + tracing::info!("New client {} have been saved to database", result.id); + client.id = result.id; + Ok(web::Json(ClientAddResponse { + status: "success".to_string(), + message: "".to_string(), code: 200, - message: "Saved".to_string(), - id: Some(result.id), + client: Some(client), })) } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - Ok(web::Json(JsonResponse { + + return Ok(web::Json(ClientAddResponse { status: "error".to_string(), code: 500, message: "Failed to insert".to_string(), - id: None, - })) + client: None, + })); } } - */ - //save client in DB - //return the client as a response - return Ok(web::Json(ClientAddResponse { - status: "success".to_string(), - code: 200, - client: client, - })); } From 7b0ecc0b8b50dd0dbb75e8bf2dcdcf8b0641ec99 Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 29 Oct 2023 00:07:08 +0300 Subject: [PATCH 19/73] issue-auth test/deploy endpoint --- src/routes/client/add.rs | 2 -- src/routes/mod.rs | 1 + src/routes/test/deploy.rs | 15 +++++++++++++++ src/routes/test/mod.rs | 1 + src/startup.rs | 1 + 5 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/routes/test/deploy.rs create mode 100644 src/routes/test/mod.rs diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index f73ad5f..5fc12d1 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -38,8 +38,6 @@ pub async fn add_handler( client.id = 1; client.user_id = user.id.clone(); client.secret = generate_secret(255); - //todo 3. update entity with the database's generated id - //todo 4. it throws 500 when the AS is not reachable. it should just return 401 let query_span = tracing::info_span!("Saving new client into the database"); match sqlx::query!( diff --git a/src/routes/mod.rs b/src/routes/mod.rs index fbb81d4..cab3e46 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,6 +1,7 @@ pub(crate) mod client; pub mod health_checks; pub(crate) mod rating; +pub(crate) mod test; pub use health_checks::*; pub(crate) mod stack; diff --git a/src/routes/test/deploy.rs b/src/routes/test/deploy.rs new file mode 100644 index 0000000..9e4ee85 --- /dev/null +++ b/src/routes/test/deploy.rs @@ -0,0 +1,15 @@ +use actix_web::{post, web, Responder, Result}; +use serde::Serialize; + +#[derive(Serialize)] +struct DeployResponse { + status: String, +} + +#[tracing::instrument(name = "Add rating.")] +#[post("/deploy")] +pub async fn handler() -> Result { + Ok(DeployResponse { + status: "success".to_string(), + }) +} diff --git a/src/routes/test/mod.rs b/src/routes/test/mod.rs new file mode 100644 index 0000000..40149b1 --- /dev/null +++ b/src/routes/test/mod.rs @@ -0,0 +1 @@ +pub mod deploy; diff --git a/src/startup.rs b/src/startup.rs index ae719e7..7a3bcd2 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -97,6 +97,7 @@ pub async fn run(settings: Settings) -> Result { .wrap(Cors::permissive()) .service(crate::routes::client::add_handler), ) + .service(web::scope("/test").service(crate::routes::test::deploy::handler)) .service( web::scope("/rating") .wrap(HttpAuthentication::bearer(bearer_guard)) From 903e1cea22c4eff6a94a33373367f40ff71f4bad Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 29 Oct 2023 00:12:30 +0300 Subject: [PATCH 20/73] issue-auth todos --- src/routes/test/deploy.rs | 5 +++-- src/startup.rs | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/routes/test/deploy.rs b/src/routes/test/deploy.rs index 9e4ee85..60895f6 100644 --- a/src/routes/test/deploy.rs +++ b/src/routes/test/deploy.rs @@ -6,10 +6,11 @@ struct DeployResponse { status: String, } +//todo inject client through enpoint's inputs #[tracing::instrument(name = "Add rating.")] #[post("/deploy")] pub async fn handler() -> Result { - Ok(DeployResponse { + Ok(web::Json(DeployResponse { status: "success".to_string(), - }) + })) } diff --git a/src/startup.rs b/src/startup.rs index 7a3bcd2..1b9dbcb 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -97,7 +97,14 @@ pub async fn run(settings: Settings) -> Result { .wrap(Cors::permissive()) .service(crate::routes::client::add_handler), ) - .service(web::scope("/test").service(crate::routes::test::deploy::handler)) + .service( + //todo 1. add client_guard. it should fetch client_id and hash from headers. based on db's + //client secret and input body valiates the input. the client is to be handed over + //to the http endpoint + //todo 2. the generation secret and the client bearer to be separated in a separate + //utils module + web::scope("/test").service(crate::routes::test::deploy::handler), + ) .service( web::scope("/rating") .wrap(HttpAuthentication::bearer(bearer_guard)) From 8dc569a2edff9be8f20eb01233c05ffc02588e20 Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 30 Oct 2023 09:12:34 +0200 Subject: [PATCH 21/73] Last development session updates, testing server fix, mock auth server --- src/main.rs | 16 +++++++-- src/startup.rs | 13 ++------ tests/health_check.rs | 78 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 50788f2..105939b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,25 @@ +use std::net::TcpListener; +use sqlx::PgPool; use stacker::configuration::get_configuration; use stacker::startup::run; use stacker::telemetry::{get_subscriber, init_subscriber}; + #[actix_web::main] async fn main() -> std::io::Result<()> { let subscriber = get_subscriber("stacker".into(), "info".into()); init_subscriber(subscriber); - let configuration = get_configuration().expect("Failed to read configuration."); + let settings = get_configuration().expect("Failed to read configuration."); + + let db_pool = PgPool::connect(&settings.database.connection_string()) + .await + .expect("Failed to connect to database."); + + let address = format!("127.0.0.1:{}", settings.application_port); + tracing::info!("Start server at {:?}", &address); + let listener = TcpListener::bind(address) + .expect(&format!("failed to bind to {}", settings.application_port)); - run(configuration).await?.await + run(listener, db_pool, settings).await?.await } diff --git a/src/startup.rs b/src/startup.rs index 1b9dbcb..a55f595 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -14,8 +14,9 @@ use actix_web::{ use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; use reqwest::header::{ACCEPT, CONTENT_TYPE}; use reqwest::Url; -use sqlx::PgPool; +use sqlx::{Pool, Postgres}; use std::sync::Arc; +use std::net::TcpListener; use tracing_actix_web::TracingLogger; use crate::models::user::User; @@ -75,18 +76,10 @@ async fn bearer_guard( Ok(req) } -pub async fn run(settings: Settings) -> Result { +pub async fn run(listener: TcpListener, db_pool: Pool, settings: Settings) -> Result { let settings = Arc::new(settings); - let db_pool = PgPool::connect(&settings.database.connection_string()) - .await - .expect("Failed to connect to database."); let db_pool = web::Data::new(db_pool); - let address = format!("127.0.0.1:{}", settings.application_port); - tracing::info!("Start server at {:?}", &address); - let listener = std::net::TcpListener::bind(address) - .expect(&format!("failed to bind to {}", settings.application_port)); - let server = HttpServer::new(move || { App::new() .wrap(TracingLogger::default()) diff --git a/tests/health_check.rs b/tests/health_check.rs index f2a8323..0b858bf 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,7 +1,31 @@ //#[actix_rt::test] +use std::net::TcpListener; +use actix_web::{App, HttpServer, web, Responder, get}; use sqlx::{Connection, Executor, PgConnection, PgPool}; use stacker::configuration::{get_configuration, DatabaseSettings}; +use stacker::forms; + + +#[get("")] +async fn mock_auth() -> actix_web::Result { + println!("Starting auth server in test mode ..."); + // 1. set user id + // 2. add token to header / hardcoded + Ok(web::Json(forms::user::UserForm::default())) +} + +async fn mock_auth_server(listener:TcpListener) -> actix_web::dev::Server { + + HttpServer::new(|| { + App::new() + .service(web::scope("/me") + .service(mock_auth)) + }) + .listen(listener) + .unwrap() + .run() +} #[tokio::test] async fn health_check_works() { @@ -61,29 +85,67 @@ pub async fn configure_database(config: &DatabaseSettings) -> PgPool { connection_pool } + // we have to run server in another thread async fn spawn_app() -> TestApp { // Future - let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); + + // let mut rt = tokio::runtime::Runtime::new().unwrap(); + // rt.spawn(mock_auth_server(listener)).expect("Could not spawn auth server"); + let mut configuration = get_configuration().expect("Failed to get configuration"); + + let listener = std::net::TcpListener::bind("127.0.0.1:0") + .expect("Failed to bind port for testing auth server"); + + configuration.auth_url = format!("http://127.0.0.1:{}/me", listener.local_addr().unwrap().port()); + println!("Auth Server is running on: {}", configuration.auth_url); + + let handle = tokio::spawn(mock_auth_server(listener)); + handle.await.expect("Auth Server can not be started"); + + let listener = std::net::TcpListener::bind("127.0.0.1:0") + .expect("Failed to bind random port"); let port = listener.local_addr().unwrap().port(); let address = format!("http://127.0.0.1:{}", port); - let mut configuration = get_configuration().expect("Failed to get configuration"); configuration.database.database_name = uuid::Uuid::new_v4().to_string(); let connection_pool = configure_database(&configuration.database).await; - //let connection_pool = PgPool::connect(&configuration.database.connection_string()) - //.await - //.expect("Failed to connect to database"); - let server = stacker::startup::run(listener, connection_pool.clone()) - .expect("Failed to bind address."); + let server = stacker::startup::run(listener, connection_pool.clone(), configuration) + .await.expect("Failed to bind address."); let _ = tokio::spawn(server); println!("Used Port: {}", port); - //format!("http://127.0.0.1:{}", port) + TestApp { address, db_pool: connection_pool, } } + +#[tokio::test] +async fn add_rating_returns_a_200_for_valid_form_data() { + // Arrange + let app = spawn_app().await; + let client = reqwest::Client::new(); + + // let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; // %20 - space, %40 - @ + // let response = client + // .post(&format!("{}/subscriptions", &app.address)) + // .header("Content-Type", "application/x-www-form-urlencoded") + // .body(body) + // .send() + // .await + // .expect("Failed to execute request."); + // + // assert_eq!(200, response.status().as_u16()); + // + // let saved = sqlx::query!("SELECT email, name FROM subscriptions",) + // .fetch_one(&app.db_pool) + // .await + // .expect("Failed to fetch saved subscription."); + // + // assert_eq!(saved.email, "ursula_le_guin@gmail.com"); + // assert_eq!(saved.name, "le guin"); +} \ No newline at end of file From 5e6ba685e39afd483c4f4bada30df5c2c99715a2 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 30 Oct 2023 17:16:53 +0200 Subject: [PATCH 22/73] issue-auth max_clients_number --- configuration.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configuration.yaml b/configuration.yaml index 3b9d945..2bba7e7 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,5 +1,6 @@ application_port: 8000 auth_url: https://dev.try.direct/server/user/oauth_server/api/me +max_clients_number: 2 database: host: localhost port: 5432 From 82e9e5dcb841e76f06dba0c700ab5f4816df005e Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 30 Oct 2023 17:48:29 +0200 Subject: [PATCH 23/73] issue-auth max_clients_number --- src/configuration.rs | 2 +- src/routes/client/add.rs | 47 +++++++++++++++++++++++++++++++++++++++- src/startup.rs | 4 ++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index 9787660..bd62125 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -5,6 +5,7 @@ pub struct Settings { pub database: DatabaseSettings, pub application_port: u16, pub auth_url: String, + pub max_clients_number: i64, } #[derive(Debug, serde::Deserialize)] @@ -16,7 +17,6 @@ pub struct DatabaseSettings { pub database_name: String, } - impl DatabaseSettings { // Connection string: postgresql://:@:/ pub fn connection_string(&self) -> String { diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 5fc12d1..979b2d8 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -1,9 +1,11 @@ +use crate::configuration::Settings; use crate::models::user::User; use crate::models::Client; use actix_web::{post, web, Responder, Result}; use rand::Rng; use serde::Serialize; use sqlx::PgPool; +use std::sync::Arc; use tracing::Instrument; #[derive(Serialize)] @@ -31,9 +33,52 @@ fn generate_secret(len: usize) -> String { #[post("")] pub async fn add_handler( user: web::ReqData, + settings: web::Data>, pool: web::Data, ) -> Result { - //todo how many clients can an user have? + let query_span = tracing::info_span!("Counting the user's clients"); + match sqlx::query!( + r#" + SELECT + count(*) as client_count + FROM client c + WHERE c.user_id = $1 + "#, + user.id.clone(), + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(result) => { + let client_count = result.client_count.unwrap(); + if client_count >= settings.max_clients_number { + tracing::error!( + "Too many clients. The user {} has {} clients", + user.id, + client_count + ); + + return Ok(web::Json(ClientAddResponse { + status: "error".to_string(), + code: 400, + message: "Too many clients already created".to_string(), + client: None, + })); + } + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + + return Ok(web::Json(ClientAddResponse { + status: "error".to_string(), + code: 500, + message: "Failed to insert".to_string(), + client: None, + })); + } + }; + let mut client = Client::default(); client.id = 1; client.user_id = user.id.clone(); diff --git a/src/startup.rs b/src/startup.rs index 1b9dbcb..b3d46f2 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -25,7 +25,7 @@ async fn bearer_guard( req: ServiceRequest, credentials: BearerAuth, ) -> Result { - let settings = req.app_data::>().unwrap(); + let settings = req.app_data::>>().unwrap(); let client = reqwest::Client::new(); let resp = client .get(&settings.auth_url) @@ -76,7 +76,7 @@ async fn bearer_guard( } pub async fn run(settings: Settings) -> Result { - let settings = Arc::new(settings); + let settings = web::Data::new(Arc::new(settings)); let db_pool = PgPool::connect(&settings.database.connection_string()) .await .expect("Failed to connect to database."); From b6fc7a8b0c7ad3d9d180df335f2083b2ae492128 Mon Sep 17 00:00:00 2001 From: vsilent Date: Tue, 31 Oct 2023 15:58:02 +0200 Subject: [PATCH 24/73] dockerfiles & build --- .env | 2 +- Cargo.lock | 5 +++ Cargo.toml | 3 +- Dockerfile => Dockerfile.local | 13 ++++--- configuration.yaml | 3 +- docker-compose.yml | 15 ++++---- docker/.env | 5 +++ docker/Dockerfile | 63 ++++++++++++++++++++++++++++++++++ docker/configuration.yaml | 9 +++++ src/configuration.rs | 3 +- src/startup.rs | 4 +-- 11 files changed, 108 insertions(+), 17 deletions(-) rename Dockerfile => Dockerfile.local (87%) create mode 100644 docker/.env create mode 100644 docker/Dockerfile create mode 100644 docker/configuration.yaml diff --git a/.env b/.env index dffc672..247a3fd 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/stacker +DATABASE_URL=postgres://postgres:postgres@172.17.0.2:5432/stacker POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker diff --git a/Cargo.lock b/Cargo.lock index e874895..f50cefa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,6 +587,9 @@ name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +dependencies = [ + "serde", +] [[package]] name = "encoding_rs" @@ -1999,9 +2002,11 @@ dependencies = [ "dotenvy", "either", "heck", + "hex", "once_cell", "proc-macro2", "quote", + "serde", "serde_json", "sha2", "sqlx-core", diff --git a/Cargo.toml b/Cargo.toml index 0608a4f..6a5b4b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,5 +42,6 @@ features = [ "uuid", "tls", "chrono", - "json" + "json", + "offline" ] diff --git a/Dockerfile b/Dockerfile.local similarity index 87% rename from Dockerfile rename to Dockerfile.local index b763c62..37c6158 100644 --- a/Dockerfile +++ b/Dockerfile.local @@ -7,25 +7,28 @@ RUN apt-get update; \ WORKDIR /app +RUN cargo install sqlx-cli # copy manifests COPY ./Cargo.toml . +COPY ./Cargo.lock . COPY ./rustfmt.toml . COPY ./Makefile . COPY .env . +COPY ./migrations ./migrations -RUN cargo install sqlx-cli # build this project to cache dependencies -#ENV DATABASE_URL=postgres://postgres:postgres@172.0.0.1:5432/stacker -RUN sqlx database create && sqlx migrate run +#RUN sqlx database create && sqlx migrate run RUN cargo build --release; \ rm src/*.rs +# add .env and secret.key for Docker env +#RUN touch .env; # copy project source and necessary files COPY ./src ./src -# add .env and secret.key for Docker env -#RUN touch .env; +#RUN sqlx migrate run +#RUN cargo sqlx prepare -- --bin stacker # rebuild app with project source RUN rm -rf ./target/release/deps/stacker*; \ diff --git a/configuration.yaml b/configuration.yaml index 06a1988..5a2f19d 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,4 +1,5 @@ -application_port: 8000 +app_host: 127.0.0.1 +app_port: 8000 #auth_url: https://65190108818c4e98ac6000e4.mockapi.io/user/1 auth_url: https://dev.try.direct/server/user/oauth_server/api/me database: diff --git a/docker-compose.yml b/docker-compose.yml index 0d23c86..c51c7ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,18 +7,21 @@ volumes: services: stacker: - image: trydirect/stacker:0.0.1 - build: . + image: trydirect/stacker:0.0.3 + build: ./docker container_name: stacker restart: always volumes: - ./stacker/files:/app/files + - ./docker/configuration.yaml:/app/configuration.yaml + - ./migrations:/app/migrations + - ./docker/.env:/app/.env ports: - "8000:8000" env_file: - - .env + - ./docker/.env environment: - - RUST_LOG="trace" + - RUST_LOG=debug depends_on: stackerdb: condition: service_healthy @@ -34,9 +37,9 @@ services: image: postgres:16.0 restart: always ports: - - 127.0.0.1:5432:5432 + - 5432 env_file: - - .env + - ./docker/.env volumes: - stackerdb:/var/lib/postgresql/data - ./docker/postgresql.conf:/etc/postgresql/postgresql.conf \ No newline at end of file diff --git a/docker/.env b/docker/.env new file mode 100644 index 0000000..6371a97 --- /dev/null +++ b/docker/.env @@ -0,0 +1,5 @@ +DATABASE_URL=postgres://postgres:postgres@stackerdb:5432/stacker +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=stacker +POSTGRES_PORT=5432 \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..31bb982 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,63 @@ +FROM rust:bookworm as builder + +RUN apt-get update; \ + #apt-get install --no-install-recommends -y libpq-dev libssl-dev pkg-config; \ + apt-get install --no-install-recommends -y libssl-dev; \ + rm -rf /var/lib/apt/lists/*; \ + USER=root cargo new --bin app; + +RUN cargo install sqlx-cli + +RUN ls -la /usr/local/cargo/bin/sqlx +WORKDIR /app +# copy manifests +RUN ls -la /usr/local/cargo/bin/ +COPY ../Cargo.toml . +COPY ../Cargo.lock . +COPY ../rustfmt.toml . +COPY ../Makefile . +COPY ../.env . +COPY ../configuration.yaml . + +# build this project to cache dependencies +#RUN sqlx database create && sqlx migrate run + +RUN cargo build --release; \ + rm src/*.rs + +# add .env and secret.key for Docker env +#RUN touch .env; +# copy project source and necessary files +COPY ../src ./src + +#RUN sqlx migrate run +#RUN cargo sqlx prepare -- --bin stacker + + +# rebuild app with project source +RUN rm -rf ./target/release/deps/stacker*; \ + cargo build --release + +# deploy stage +FROM debian:bookworm as production + +# create app directory +WORKDIR /app +RUN mkdir ./files && chmod 0777 ./files + +# install libpq +RUN apt-get update; \ + apt-get install --no-install-recommends -y libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +# copy binary and configuration files +#COPY --from=builder ~/.cargo/bin/sqlx-cli sqlx-cli +COPY --from=builder /app/target/release/stacker . +COPY --from=builder /app/.env . +COPY --from=builder /app/configuration.yaml . +COPY --from=builder /usr/local/cargo/bin/sqlx sqlx + +EXPOSE 8080 + +# run the binary +ENTRYPOINT ["/app/stacker"] diff --git a/docker/configuration.yaml b/docker/configuration.yaml new file mode 100644 index 0000000..5eef969 --- /dev/null +++ b/docker/configuration.yaml @@ -0,0 +1,9 @@ +app_host: 0.0.0.0 +app_port: 8000 +auth_url: https://dev.try.direct/server/user/oauth_server/api/me +database: + host: stackerdb + port: 5432 + username: postgres + password: postgres + database_name: stacker diff --git a/src/configuration.rs b/src/configuration.rs index 9787660..826b84f 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -3,7 +3,8 @@ use serde; #[derive(Debug, serde::Deserialize)] pub struct Settings { pub database: DatabaseSettings, - pub application_port: u16, + pub app_port: u16, + pub app_host: u16, pub auth_url: String, } diff --git a/src/startup.rs b/src/startup.rs index ba3f394..512cc42 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -96,10 +96,10 @@ pub async fn run(settings: Settings) -> Result { .expect("Failed to connect to database."); let db_pool = web::Data::new(db_pool); - let address = format!("127.0.0.1:{}", settings.application_port); + let address = format!("{}:{}", settings.app_host, settings.app_port); tracing::info!("Start server at {:?}", &address); let listener = std::net::TcpListener::bind(address) - .expect(&format!("failed to bind to {}", settings.application_port)); + .expect(&format!("failed to bind to {}", settings.app_port)); let server = HttpServer::new(move || { App::new() From 838bba29be7f5ef5b31a0433659538d01e192b31 Mon Sep 17 00:00:00 2001 From: vsilent Date: Tue, 31 Oct 2023 16:56:03 +0200 Subject: [PATCH 25/73] dockerfiles & build fix --- docker/Dockerfile => Dockerfile | 4 +--- docker-compose.yml | 5 +++-- src/configuration.rs | 2 +- src/startup.rs | 9 ++++----- 4 files changed, 9 insertions(+), 11 deletions(-) rename docker/Dockerfile => Dockerfile (94%) diff --git a/docker/Dockerfile b/Dockerfile similarity index 94% rename from docker/Dockerfile rename to Dockerfile index 31bb982..ec273b1 100644 --- a/docker/Dockerfile +++ b/Dockerfile @@ -8,10 +8,8 @@ RUN apt-get update; \ RUN cargo install sqlx-cli -RUN ls -la /usr/local/cargo/bin/sqlx WORKDIR /app # copy manifests -RUN ls -la /usr/local/cargo/bin/ COPY ../Cargo.toml . COPY ../Cargo.lock . COPY ../rustfmt.toml . @@ -57,7 +55,7 @@ COPY --from=builder /app/.env . COPY --from=builder /app/configuration.yaml . COPY --from=builder /usr/local/cargo/bin/sqlx sqlx -EXPOSE 8080 +EXPOSE 8000 # run the binary ENTRYPOINT ["/app/stacker"] diff --git a/docker-compose.yml b/docker-compose.yml index c51c7ea..3c58429 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: stacker: image: trydirect/stacker:0.0.3 - build: ./docker + build: . container_name: stacker restart: always volumes: @@ -22,6 +22,7 @@ services: - ./docker/.env environment: - RUST_LOG=debug + - RUST_BACKTRACE=1 depends_on: stackerdb: condition: service_healthy @@ -37,7 +38,7 @@ services: image: postgres:16.0 restart: always ports: - - 5432 + - 5432:5432 env_file: - ./docker/.env volumes: diff --git a/src/configuration.rs b/src/configuration.rs index 826b84f..6a78e7c 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -4,7 +4,7 @@ use serde; pub struct Settings { pub database: DatabaseSettings, pub app_port: u16, - pub app_host: u16, + pub app_host: String, pub auth_url: String, } diff --git a/src/startup.rs b/src/startup.rs index 512cc42..57b8d60 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -27,15 +27,14 @@ async fn bearer_guard( ) -> Result { let settings = req.app_data::>().unwrap(); - let url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); - // let data_url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); - tracing::debug!("URL :::: {:?}", url); + // let url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); + // // let data_url = Url::parse("https://dev.try.direct/server/user/oauth_server/api/me").unwrap(); + // tracing::debug!("URL :::: {:?}", url); let client = reqwest::Client::new(); let resp = client - // .get(&settings.auth_url) - .get(url) + .get(&settings.auth_url) .bearer_auth(credentials.token()) .header(CONTENT_TYPE, "application/json") .header(ACCEPT, "application/json") From 66d8cb9f38d9bdd2e27cd0ab9df85e31bf0f730f Mon Sep 17 00:00:00 2001 From: vsilent Date: Tue, 31 Oct 2023 17:15:21 +0200 Subject: [PATCH 26/73] dockerfiles for dev environment --- docker/dev/.env | 5 + docker/dev/configuration.yaml | 9 ++ docker/dev/docker-compose.yml | 46 +++++++++ postgresql.conf => docker/dev/postgresql.conf | 98 +++++++------------ 4 files changed, 97 insertions(+), 61 deletions(-) create mode 100644 docker/dev/.env create mode 100644 docker/dev/configuration.yaml create mode 100644 docker/dev/docker-compose.yml rename postgresql.conf => docker/dev/postgresql.conf (90%) diff --git a/docker/dev/.env b/docker/dev/.env new file mode 100644 index 0000000..6371a97 --- /dev/null +++ b/docker/dev/.env @@ -0,0 +1,5 @@ +DATABASE_URL=postgres://postgres:postgres@stackerdb:5432/stacker +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=stacker +POSTGRES_PORT=5432 \ No newline at end of file diff --git a/docker/dev/configuration.yaml b/docker/dev/configuration.yaml new file mode 100644 index 0000000..5eef969 --- /dev/null +++ b/docker/dev/configuration.yaml @@ -0,0 +1,9 @@ +app_host: 0.0.0.0 +app_port: 8000 +auth_url: https://dev.try.direct/server/user/oauth_server/api/me +database: + host: stackerdb + port: 5432 + username: postgres + password: postgres + database_name: stacker diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml new file mode 100644 index 0000000..57cb801 --- /dev/null +++ b/docker/dev/docker-compose.yml @@ -0,0 +1,46 @@ +version: "2.2" + +volumes: + stackerdb: + driver: local + +services: + + stacker: + image: trydirect/stacker:0.0.3 + build: . + container_name: stacker + restart: always + volumes: + - ./stacker/files:/app/files + - ./docker/dev/configuration.yaml:/app/configuration.yaml + - ./migrations:/app/migrations + - ./docker/dev/.env:/app/.env + ports: + - "8000:8000" + env_file: + - ./docker/dev/.env + environment: + - RUST_LOG=debug + - RUST_BACKTRACE=1 + depends_on: + stackerdb: + condition: service_healthy + + + stackerdb: + container_name: stackerdb + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + image: postgres:16.0 + restart: always + ports: + - 5432 + env_file: + - ./docker/.env + volumes: + - stackerdb:/var/lib/postgresql/data + - ./docker/dev/postgresql.conf:/etc/postgresql/postgresql.conf \ No newline at end of file diff --git a/postgresql.conf b/docker/dev/postgresql.conf similarity index 90% rename from postgresql.conf rename to docker/dev/postgresql.conf index e2acada..4e89674 100644 --- a/postgresql.conf +++ b/docker/dev/postgresql.conf @@ -62,10 +62,9 @@ listen_addresses = '*' # defaults to 'localhost'; use '*' for all # (change requires restart) #port = 5432 # (change requires restart) -max_connections = 100 # (change requires restart) -#reserved_connections = 0 # (change requires restart) +#max_connections = 100 # (change requires restart) #superuser_reserved_connections = 3 # (change requires restart) -#unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories +#unix_socket_directories = '/tmp' # comma-separated list of directories # (change requires restart) #unix_socket_group = '' # (change requires restart) #unix_socket_permissions = 0777 # begin with 0 to use octal notation @@ -95,13 +94,11 @@ max_connections = 100 # (change requires restart) #authentication_timeout = 1min # 1s-600s #password_encryption = scram-sha-256 # scram-sha-256 or md5 -#scram_iterations = 4096 #db_user_namespace = off # GSSAPI using Kerberos #krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab' #krb_caseins_users = off -#gss_accept_delegation = off # - SSL - @@ -127,7 +124,7 @@ max_connections = 100 # (change requires restart) # - Memory - -shared_buffers = 128MB # min 128kB +#shared_buffers = 32MB # min 128kB # (change requires restart) #huge_pages = try # on, off, or try # (change requires restart) @@ -139,7 +136,7 @@ shared_buffers = 128MB # min 128kB # Caution: it is not advisable to set max_prepared_transactions nonzero unless # you actively intend to use prepared transactions. #work_mem = 4MB # min 64kB -#hash_mem_multiplier = 2.0 # 1-1000.0 multiplier on hash table work_mem +#hash_mem_multiplier = 1.0 # 1-1000.0 multiplier on hash table work_mem #maintenance_work_mem = 64MB # min 1MB #autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem #logical_decoding_work_mem = 64MB # min 64kB @@ -150,7 +147,7 @@ shared_buffers = 128MB # min 128kB # sysv # windows # (change requires restart) -dynamic_shared_memory_type = posix # the default is usually the first option +#dynamic_shared_memory_type = posix # the default is the first option # supported by the operating system: # posix # sysv @@ -158,9 +155,6 @@ dynamic_shared_memory_type = posix # the default is usually the first option # mmap # (change requires restart) #min_dynamic_shared_memory = 0MB # (change requires restart) -#vacuum_buffer_usage_limit = 256kB # size of vacuum and analyze buffer access strategy ring; - # 0 to disable vacuum buffer access strategy; - # range 128kB to 16GB # - Disk - @@ -185,7 +179,7 @@ dynamic_shared_memory_type = posix # the default is usually the first option #bgwriter_delay = 200ms # 10-10000ms between rounds #bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables #bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round -#bgwriter_flush_after = 512kB # measured in pages, 0 disables +#bgwriter_flush_after = 0 # measured in pages, 0 disables # - Asynchronous Behavior - @@ -225,8 +219,7 @@ dynamic_shared_memory_type = posix # the default is usually the first option #full_page_writes = on # recover from partial page writes #wal_log_hints = off # also do full page writes of non-critical updates # (change requires restart) -#wal_compression = off # enables compression of full-page writes; - # off, pglz, lz4, zstd, or on +#wal_compression = off # enable compression of full-page writes #wal_init_zero = on # zero-fill new WAL files #wal_recycle = on # recycle WAL files #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers @@ -242,36 +235,27 @@ dynamic_shared_memory_type = posix # the default is usually the first option #checkpoint_timeout = 5min # range 30s-1d #checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 -#checkpoint_flush_after = 256kB # measured in pages, 0 disables +#checkpoint_flush_after = 0 # measured in pages, 0 disables #checkpoint_warning = 30s # 0 disables -max_wal_size = 1GB -min_wal_size = 80MB - -# - Prefetching during recovery - - -#recovery_prefetch = try # prefetch pages referenced in the WAL? -#wal_decode_buffer_size = 512kB # lookahead window used for prefetching - # (change requires restart) +#max_wal_size = 1GB +#min_wal_size = 80MB # - Archiving - #archive_mode = off # enables archiving; off, on, or always # (change requires restart) -#archive_library = '' # library to use to archive a WAL file - # (empty string indicates archive_command should - # be used) -#archive_command = '' # command to use to archive a WAL file +#archive_command = '' # command to use to archive a logfile segment # placeholders: %p = path of file to archive # %f = file name only # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' -#archive_timeout = 0 # force a WAL file switch after this +#archive_timeout = 0 # force a logfile segment switch after this # number of seconds; 0 disables # - Archive Recovery - # These are only used in recovery mode. -#restore_command = '' # command to use to restore an archived WAL file +#restore_command = '' # command to use to restore an archived logfile segment # placeholders: %p = path of file to restore # %f = file name only # e.g. 'cp /mnt/server/archivedir/%f %p' @@ -329,6 +313,7 @@ min_wal_size = 80MB # method to choose sync standbys, number of sync standbys, # and comma-separated list of application_name # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed # - Standby Servers - @@ -336,6 +321,7 @@ min_wal_size = 80MB #primary_conninfo = '' # connection string to sending server #primary_slot_name = '' # replication slot on sending server +#promote_trigger_file = '' # file name whose presence ends recovery #hot_standby = on # "off" disallows queries during recovery # (change requires restart) #max_standby_archive_delay = 30s # max delay before canceling queries @@ -364,7 +350,6 @@ min_wal_size = 80MB #max_logical_replication_workers = 4 # taken from max_worker_processes # (change requires restart) #max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers -#max_parallel_apply_workers_per_subscription = 2 # taken from max_logical_replication_workers #------------------------------------------------------------------------------ @@ -390,7 +375,6 @@ min_wal_size = 80MB #enable_partition_pruning = on #enable_partitionwise_join = off #enable_partitionwise_aggregate = off -#enable_presorted_aggregate = on #enable_seqscan = on #enable_sort = on #enable_tidscan = on @@ -438,7 +422,6 @@ min_wal_size = 80MB # JOIN clauses #plan_cache_mode = auto # auto, force_generic_plan or # force_custom_plan -#recursive_worktable_factor = 10.0 # range 0.001-1000000 #------------------------------------------------------------------------------ @@ -448,15 +431,14 @@ min_wal_size = 80MB # - Where to Log - #log_destination = 'stderr' # Valid values are combinations of - # stderr, csvlog, jsonlog, syslog, and - # eventlog, depending on platform. - # csvlog and jsonlog require - # logging_collector to be on. + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. # This is used when logging to stderr: -#logging_collector = off # Enable capturing of stderr, jsonlog, - # and csvlog into log files. Required - # to be on for csvlogs and jsonlogs. +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. # (change requires restart) # These are only used if logging_collector is on: @@ -540,23 +522,18 @@ min_wal_size = 80MB # are logged regardless of their duration; 1.0 logs all # statements from all transactions, 0.0 never logs -#log_startup_progress_interval = 10s # Time between progress updates for - # long-running startup operations. - # 0 disables the feature, > 0 indicates - # the interval in milliseconds. - # - What to Log - #debug_print_parse = off #debug_print_rewritten = off #debug_print_plan = off #debug_pretty_print = on -#log_autovacuum_min_duration = 10min # log autovacuum activity; +#log_autovacuum_min_duration = -1 # log autovacuum activity; # -1 disables, 0 logs all actions and # their durations, > 0 logs only # actions running at least this number # of milliseconds. -#log_checkpoints = on +#log_checkpoints = off #log_connections = off #log_disconnections = off #log_duration = off @@ -600,9 +577,12 @@ min_wal_size = 80MB #log_temp_files = -1 # log temporary files equal or larger # than the specified size in kilobytes; # -1 disables, 0 logs all temp files -log_timezone = 'Etc/UTC' +#log_timezone = 'GMT' -# - Process Title - + +#------------------------------------------------------------------------------ +# PROCESS TITLE +#------------------------------------------------------------------------------ #cluster_name = '' # added to process titles if nonempty # (change requires restart) @@ -613,7 +593,7 @@ log_timezone = 'Etc/UTC' # STATISTICS #------------------------------------------------------------------------------ -# - Cumulative Query and Index Statistics - +# - Query and Index Statistics Collector - #track_activities = on #track_activity_query_size = 1024 # (change requires restart) @@ -621,7 +601,7 @@ log_timezone = 'Etc/UTC' #track_io_timing = off #track_wal_io_timing = off #track_functions = none # none, pl, all -#stats_fetch_consistency = cache # cache, none, snapshot +#stats_temp_directory = 'pg_stat_tmp' # - Monitoring - @@ -708,13 +688,12 @@ log_timezone = 'Etc/UTC' #xmlbinary = 'base64' #xmloption = 'content' #gin_pending_list_limit = 4MB -#createrole_self_grant = '' # set and/or inherit # - Locale and Formatting - -datestyle = 'iso, mdy' +#datestyle = 'iso, mdy' #intervalstyle = 'postgres' -timezone = 'Etc/UTC' +#timezone = 'GMT' #timezone_abbreviations = 'Default' # Select the set of available time zone # abbreviations. Currently, there are # Default @@ -728,17 +707,14 @@ timezone = 'Etc/UTC' # encoding # These settings are initialized by initdb, but they can be changed. -lc_messages = 'en_US.utf8' # locale for system error message +#lc_messages = 'C' # locale for system error message # strings -lc_monetary = 'en_US.utf8' # locale for monetary formatting -lc_numeric = 'en_US.utf8' # locale for number formatting -lc_time = 'en_US.utf8' # locale for time formatting - -#icu_validation_level = warning # report ICU locale validation - # errors at the given level +#lc_monetary = 'C' # locale for monetary formatting +#lc_numeric = 'C' # locale for number formatting +#lc_time = 'C' # locale for time formatting # default configuration for text search -default_text_search_config = 'pg_catalog.english' +#default_text_search_config = 'pg_catalog.simple' # - Shared Library Preloading - From d8974b8607c7e309e40028570700b7621750f3d3 Mon Sep 17 00:00:00 2001 From: vsilent Date: Tue, 31 Oct 2023 17:17:02 +0200 Subject: [PATCH 27/73] dockerfiles for dev environment --- docker/dev/docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index 57cb801..1ba68f2 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -13,13 +13,13 @@ services: restart: always volumes: - ./stacker/files:/app/files - - ./docker/dev/configuration.yaml:/app/configuration.yaml + - ./configuration.yaml:/app/configuration.yaml - ./migrations:/app/migrations - - ./docker/dev/.env:/app/.env + - ./.env:/app/.env ports: - "8000:8000" env_file: - - ./docker/dev/.env + - ./.env environment: - RUST_LOG=debug - RUST_BACKTRACE=1 @@ -40,7 +40,7 @@ services: ports: - 5432 env_file: - - ./docker/.env + - ./.env volumes: - stackerdb:/var/lib/postgresql/data - - ./docker/dev/postgresql.conf:/etc/postgresql/postgresql.conf \ No newline at end of file + - ./postgresql.conf:/etc/postgresql/postgresql.conf \ No newline at end of file From 3cd8eddefa50b1c857a6c151e744d10b8b9af826 Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 31 Oct 2023 17:26:46 +0200 Subject: [PATCH 28/73] issue-auth rebase --- src/main.rs | 16 +++++++-- src/startup.rs | 17 ++++------ tests/health_check.rs | 78 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 50788f2..105939b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,25 @@ +use std::net::TcpListener; +use sqlx::PgPool; use stacker::configuration::get_configuration; use stacker::startup::run; use stacker::telemetry::{get_subscriber, init_subscriber}; + #[actix_web::main] async fn main() -> std::io::Result<()> { let subscriber = get_subscriber("stacker".into(), "info".into()); init_subscriber(subscriber); - let configuration = get_configuration().expect("Failed to read configuration."); + let settings = get_configuration().expect("Failed to read configuration."); + + let db_pool = PgPool::connect(&settings.database.connection_string()) + .await + .expect("Failed to connect to database."); + + let address = format!("127.0.0.1:{}", settings.application_port); + tracing::info!("Start server at {:?}", &address); + let listener = TcpListener::bind(address) + .expect(&format!("failed to bind to {}", settings.application_port)); - run(configuration).await?.await + run(listener, db_pool, settings).await?.await } diff --git a/src/startup.rs b/src/startup.rs index b3d46f2..1f9b0dc 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -14,7 +14,8 @@ use actix_web::{ use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; use reqwest::header::{ACCEPT, CONTENT_TYPE}; use reqwest::Url; -use sqlx::PgPool; +use sqlx::{Pool, Postgres}; +use std::net::TcpListener; use std::sync::Arc; use tracing_actix_web::TracingLogger; @@ -75,18 +76,14 @@ async fn bearer_guard( Ok(req) } -pub async fn run(settings: Settings) -> Result { +pub async fn run( + listener: TcpListener, + db_pool: Pool, + settings: Settings, +) -> Result { let settings = web::Data::new(Arc::new(settings)); - let db_pool = PgPool::connect(&settings.database.connection_string()) - .await - .expect("Failed to connect to database."); let db_pool = web::Data::new(db_pool); - let address = format!("127.0.0.1:{}", settings.application_port); - tracing::info!("Start server at {:?}", &address); - let listener = std::net::TcpListener::bind(address) - .expect(&format!("failed to bind to {}", settings.application_port)); - let server = HttpServer::new(move || { App::new() .wrap(TracingLogger::default()) diff --git a/tests/health_check.rs b/tests/health_check.rs index f2a8323..0b858bf 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,7 +1,31 @@ //#[actix_rt::test] +use std::net::TcpListener; +use actix_web::{App, HttpServer, web, Responder, get}; use sqlx::{Connection, Executor, PgConnection, PgPool}; use stacker::configuration::{get_configuration, DatabaseSettings}; +use stacker::forms; + + +#[get("")] +async fn mock_auth() -> actix_web::Result { + println!("Starting auth server in test mode ..."); + // 1. set user id + // 2. add token to header / hardcoded + Ok(web::Json(forms::user::UserForm::default())) +} + +async fn mock_auth_server(listener:TcpListener) -> actix_web::dev::Server { + + HttpServer::new(|| { + App::new() + .service(web::scope("/me") + .service(mock_auth)) + }) + .listen(listener) + .unwrap() + .run() +} #[tokio::test] async fn health_check_works() { @@ -61,29 +85,67 @@ pub async fn configure_database(config: &DatabaseSettings) -> PgPool { connection_pool } + // we have to run server in another thread async fn spawn_app() -> TestApp { // Future - let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); + + // let mut rt = tokio::runtime::Runtime::new().unwrap(); + // rt.spawn(mock_auth_server(listener)).expect("Could not spawn auth server"); + let mut configuration = get_configuration().expect("Failed to get configuration"); + + let listener = std::net::TcpListener::bind("127.0.0.1:0") + .expect("Failed to bind port for testing auth server"); + + configuration.auth_url = format!("http://127.0.0.1:{}/me", listener.local_addr().unwrap().port()); + println!("Auth Server is running on: {}", configuration.auth_url); + + let handle = tokio::spawn(mock_auth_server(listener)); + handle.await.expect("Auth Server can not be started"); + + let listener = std::net::TcpListener::bind("127.0.0.1:0") + .expect("Failed to bind random port"); let port = listener.local_addr().unwrap().port(); let address = format!("http://127.0.0.1:{}", port); - let mut configuration = get_configuration().expect("Failed to get configuration"); configuration.database.database_name = uuid::Uuid::new_v4().to_string(); let connection_pool = configure_database(&configuration.database).await; - //let connection_pool = PgPool::connect(&configuration.database.connection_string()) - //.await - //.expect("Failed to connect to database"); - let server = stacker::startup::run(listener, connection_pool.clone()) - .expect("Failed to bind address."); + let server = stacker::startup::run(listener, connection_pool.clone(), configuration) + .await.expect("Failed to bind address."); let _ = tokio::spawn(server); println!("Used Port: {}", port); - //format!("http://127.0.0.1:{}", port) + TestApp { address, db_pool: connection_pool, } } + +#[tokio::test] +async fn add_rating_returns_a_200_for_valid_form_data() { + // Arrange + let app = spawn_app().await; + let client = reqwest::Client::new(); + + // let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; // %20 - space, %40 - @ + // let response = client + // .post(&format!("{}/subscriptions", &app.address)) + // .header("Content-Type", "application/x-www-form-urlencoded") + // .body(body) + // .send() + // .await + // .expect("Failed to execute request."); + // + // assert_eq!(200, response.status().as_u16()); + // + // let saved = sqlx::query!("SELECT email, name FROM subscriptions",) + // .fetch_one(&app.db_pool) + // .await + // .expect("Failed to fetch saved subscription."); + // + // assert_eq!(saved.email, "ursula_le_guin@gmail.com"); + // assert_eq!(saved.name, "le guin"); +} \ No newline at end of file From 0c94eed91b036aaee05e9a9834b50ad3b9538227 Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 31 Oct 2023 17:38:01 +0200 Subject: [PATCH 29/73] issue-auth client.secret uniquee index --- migrations/20231028161917_client.up.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migrations/20231028161917_client.up.sql b/migrations/20231028161917_client.up.sql index ea03bba..589038a 100644 --- a/migrations/20231028161917_client.up.sql +++ b/migrations/20231028161917_client.up.sql @@ -5,5 +5,6 @@ CREATE TABLE public.client ( secret varchar(255) NOT NULL, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, - CONSTRAINT client_pkey PRIMARY KEY (id) + CONSTRAINT client_pkey PRIMARY KEY (id), + CONSTRAINT client_secret_unique UNIQUE (secret) ); From 7e216c523a6d65e0d8b52138baf1cb0d6e63c8bb Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 31 Oct 2023 17:50:47 +0200 Subject: [PATCH 30/73] issue-auth utils moved to helpers --- src/{utils => helpers}/json.rs | 2 +- src/helpers/mod.rs | 3 +++ src/lib.rs | 1 - src/routes/rating/add.rs | 2 +- src/routes/stack/add.rs | 2 +- src/utils/mod.rs | 4 ---- src/utils/validator/mod.rs | 0 7 files changed, 6 insertions(+), 8 deletions(-) rename src/{utils => helpers}/json.rs (84%) delete mode 100644 src/utils/mod.rs delete mode 100644 src/utils/validator/mod.rs diff --git a/src/utils/json.rs b/src/helpers/json.rs similarity index 84% rename from src/utils/json.rs rename to src/helpers/json.rs index 171a7c4..4eaa625 100644 --- a/src/utils/json.rs +++ b/src/helpers/json.rs @@ -4,5 +4,5 @@ pub(crate) struct JsonResponse { pub(crate) status: String, pub(crate) message: String, pub(crate) code: u32, - pub(crate) id: Option + pub(crate) id: Option, } diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 7932cbb..deec073 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1 +1,4 @@ +pub(crate) mod json; pub mod serialize_datetime; + +pub use json::*; diff --git a/src/lib.rs b/src/lib.rs index 1abe1ac..56bd1f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,4 +7,3 @@ pub mod routes; pub mod services; pub mod startup; pub mod telemetry; -pub mod utils; diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs index 96a8192..8fde8b1 100644 --- a/src/routes/rating/add.rs +++ b/src/routes/rating/add.rs @@ -1,8 +1,8 @@ use crate::forms; +use crate::helpers::JsonResponse; use crate::models; use crate::models::user::User; use crate::models::RateCategory; -use crate::utils::json::JsonResponse; use actix_web::post; use actix_web::{web, Responder, Result}; use sqlx::PgPool; diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 109397e..06a3686 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -6,9 +6,9 @@ use actix_web::{ use crate::forms::stack::StackForm; use crate::forms::user::UserForm; +use crate::helpers::JsonResponse; use crate::models::user::User; use crate::models::Stack; -use crate::utils::json::JsonResponse; use actix_web::post; use chrono::Utc; use serde_json::Value; diff --git a/src/utils/mod.rs b/src/utils/mod.rs deleted file mode 100644 index 1ca1b3c..0000000 --- a/src/utils/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod json; -mod validator; - -pub use json::*; diff --git a/src/utils/validator/mod.rs b/src/utils/validator/mod.rs deleted file mode 100644 index e69de29..0000000 From af17e7a94cf5a3783ea025f895dfee49ec8cfd15 Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 31 Oct 2023 18:04:12 +0200 Subject: [PATCH 31/73] issue-auth generate_secret moved into helpers --- src/helpers/client/generate_secret.rs | 14 ++++++++++++++ src/helpers/client/mod.rs | 3 +++ src/helpers/mod.rs | 1 + src/routes/client/add.rs | 17 ++--------------- 4 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 src/helpers/client/generate_secret.rs create mode 100644 src/helpers/client/mod.rs diff --git a/src/helpers/client/generate_secret.rs b/src/helpers/client/generate_secret.rs new file mode 100644 index 0000000..c89bac8 --- /dev/null +++ b/src/helpers/client/generate_secret.rs @@ -0,0 +1,14 @@ +use rand::Rng; + +pub fn generate_secret(len: usize) -> String { + const CHARSET: &[u8] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)(*&^%$#@!~"; + let mut rng = rand::thread_rng(); + + (0..len) + .map(|_| { + let idx = rng.gen_range(0..CHARSET.len()); + CHARSET[idx] as char + }) + .collect() +} diff --git a/src/helpers/client/mod.rs b/src/helpers/client/mod.rs new file mode 100644 index 0000000..e636305 --- /dev/null +++ b/src/helpers/client/mod.rs @@ -0,0 +1,3 @@ +mod generate_secret; + +pub use generate_secret::*; diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index deec073..3500bbd 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1,3 +1,4 @@ +pub mod client; pub(crate) mod json; pub mod serialize_datetime; diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 979b2d8..0f5435c 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -1,8 +1,8 @@ use crate::configuration::Settings; +use crate::helpers::client; use crate::models::user::User; use crate::models::Client; use actix_web::{post, web, Responder, Result}; -use rand::Rng; use serde::Serialize; use sqlx::PgPool; use std::sync::Arc; @@ -16,19 +16,6 @@ struct ClientAddResponse { client: Option, } -fn generate_secret(len: usize) -> String { - const CHARSET: &[u8] = - b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)(*&^%$#@!~"; - let mut rng = rand::thread_rng(); - - (0..len) - .map(|_| { - let idx = rng.gen_range(0..CHARSET.len()); - CHARSET[idx] as char - }) - .collect() -} - #[tracing::instrument(name = "Add client.")] #[post("")] pub async fn add_handler( @@ -82,7 +69,7 @@ pub async fn add_handler( let mut client = Client::default(); client.id = 1; client.user_id = user.id.clone(); - client.secret = generate_secret(255); + client.secret = client::generate_secret(255); let query_span = tracing::info_span!("Saving new client into the database"); match sqlx::query!( From 9a4a2a0aa20d5185ab2ca756d025fd5e7587c8c6 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 1 Nov 2023 12:35:38 +0200 Subject: [PATCH 32/73] db host change --- .env | 2 +- Dockerfile.local | 58 ------------------ configuration.yaml | 2 +- docker-compose.yml | 19 ++++-- docker/{ => local}/.env | 0 docker/{ => local}/configuration.yaml | 0 docker/{ => local}/postgresql.conf | 0 src/startup.rs | 84 +++++++++++++-------------- 8 files changed, 58 insertions(+), 107 deletions(-) delete mode 100644 Dockerfile.local rename docker/{ => local}/.env (100%) rename docker/{ => local}/configuration.yaml (100%) rename docker/{ => local}/postgresql.conf (100%) diff --git a/.env b/.env index 247a3fd..dffc672 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -DATABASE_URL=postgres://postgres:postgres@172.17.0.2:5432/stacker +DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/stacker POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker diff --git a/Dockerfile.local b/Dockerfile.local deleted file mode 100644 index 37c6158..0000000 --- a/Dockerfile.local +++ /dev/null @@ -1,58 +0,0 @@ -FROM rust:slim as build - -RUN apt-get update; \ - apt-get install --no-install-recommends -y libpq-dev libssl-dev pkg-config; \ - rm -rf /var/lib/apt/lists/*; \ - USER=root cargo new --bin app; - -WORKDIR /app - -RUN cargo install sqlx-cli -# copy manifests -COPY ./Cargo.toml . -COPY ./Cargo.lock . -COPY ./rustfmt.toml . -COPY ./Makefile . -COPY .env . -COPY ./migrations ./migrations - -# build this project to cache dependencies -#RUN sqlx database create && sqlx migrate run - -RUN cargo build --release; \ - rm src/*.rs - -# add .env and secret.key for Docker env -#RUN touch .env; -# copy project source and necessary files -COPY ./src ./src - -#RUN sqlx migrate run -#RUN cargo sqlx prepare -- --bin stacker - -# rebuild app with project source -RUN rm -rf ./target/release/deps/stacker*; \ - cargo build --release - - -# deploy stage -FROM debian:bullseye-slim as production - -# create app directory -WORKDIR /app -RUN mkdir ./files && chmod 0777 ./files - -# install libpq -#RUN apt-get update; \ -# apt-get install --no-install-recommends -y libpq-dev libssl-dev; \ -# rm -rf /var/lib/apt/lists/* - -# copy binary and configuration files -#COPY --from=builder ~/.cargo/bin/sqlx-cli sqlx-cli -COPY --from=build /app/target/release/stacker . -COPY --from=build /app/.env . - -EXPOSE 8080 - -# run the binary -ENTRYPOINT ["/app/stacker"] diff --git a/configuration.yaml b/configuration.yaml index 5a2f19d..ec0baf1 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -3,7 +3,7 @@ app_port: 8000 #auth_url: https://65190108818c4e98ac6000e4.mockapi.io/user/1 auth_url: https://dev.try.direct/server/user/oauth_server/api/me database: - host: localhost + host: 127.0.0.1 port: 5432 username: postgres password: postgres diff --git a/docker-compose.yml b/docker-compose.yml index 3c58429..a11f2a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,11 @@ volumes: stackerdb: driver: local +networks: + backend: + name: stacker-backend + external: true + services: stacker: @@ -13,19 +18,21 @@ services: restart: always volumes: - ./stacker/files:/app/files - - ./docker/configuration.yaml:/app/configuration.yaml + - ./docker/local/configuration.yaml:/app/configuration.yaml - ./migrations:/app/migrations - - ./docker/.env:/app/.env + - ./docker/local/.env:/app/.env ports: - "8000:8000" env_file: - - ./docker/.env + - ./docker/local/.env environment: - RUST_LOG=debug - RUST_BACKTRACE=1 depends_on: stackerdb: condition: service_healthy + networks: + - backend stackerdb: @@ -40,7 +47,9 @@ services: ports: - 5432:5432 env_file: - - ./docker/.env + - ./docker/local/.env volumes: - stackerdb:/var/lib/postgresql/data - - ./docker/postgresql.conf:/etc/postgresql/postgresql.conf \ No newline at end of file + - ./docker/local/postgresql.conf:/etc/postgresql/postgresql.conf + networks: + - backend \ No newline at end of file diff --git a/docker/.env b/docker/local/.env similarity index 100% rename from docker/.env rename to docker/local/.env diff --git a/docker/configuration.yaml b/docker/local/configuration.yaml similarity index 100% rename from docker/configuration.yaml rename to docker/local/configuration.yaml diff --git a/docker/postgresql.conf b/docker/local/postgresql.conf similarity index 100% rename from docker/postgresql.conf rename to docker/local/postgresql.conf diff --git a/src/startup.rs b/src/startup.rs index 57b8d60..e0e723f 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -42,49 +42,49 @@ async fn bearer_guard( .await; - // tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); - - let resp = match resp { - Ok(resp) => { - //if resp.status().is_success() - tracing::debug!("{:?}", resp); - resp - } - // Ok(resp) => { - // tracing::error!("Authentication service returned no success {:?}", resp); - // return Err((ErrorUnauthorized(""), req)); - // } - Err(err) => { - tracing::error!("error from reqwest {:?}", err); - return Err((ErrorInternalServerError(""), req)); - } - }; - - let user_form: UserForm = match resp.json().await { - Ok(user) => { - tracing::info!("unpacked user {user:?}"); - user - } - Err(err) => { - tracing::error!("can't parse the response body {:?}", err); - return Err((ErrorUnauthorized(""), req)); - } - }; - - let user:User = match user_form.try_into() // try to convert UserForm into User model - { - Ok(user) => { user } - Err(err) => { - tracing::error!("Could not create User from form data: {:?}", err); - return Err((ErrorUnauthorized(""), req)); - } - }; - let existent_user = req.extensions_mut().insert(user); - if existent_user.is_some() { - tracing::error!("already logged {existent_user:?}"); - return Err((ErrorInternalServerError(""), req)); - } + tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); + // let resp = match resp { + // Ok(resp) => { + // //if resp.status().is_success() + // tracing::debug!("{:?}", resp); + // resp + // } + // // Ok(resp) => { + // // tracing::error!("Authentication service returned no success {:?}", resp); + // // return Err((ErrorUnauthorized(""), req)); + // // } + // Err(err) => { + // tracing::error!("error from reqwest {:?}", err); + // return Err((ErrorInternalServerError(""), req)); + // } + // }; + // + // let user_form: UserForm = match resp.json().await { + // Ok(user) => { + // tracing::info!("unpacked user {user:?}"); + // user + // } + // Err(err) => { + // tracing::error!("can't parse the response body {:?}", err); + // return Err((ErrorUnauthorized(""), req)); + // } + // }; + // + // let user:User = match user_form.try_into() // try to convert UserForm into User model + // { + // Ok(user) => { user } + // Err(err) => { + // tracing::error!("Could not create User from form data: {:?}", err); + // return Err((ErrorUnauthorized(""), req)); + // } + // }; + // let existent_user = req.extensions_mut().insert(user); + // if existent_user.is_some() { + // tracing::error!("already logged {existent_user:?}"); + // return Err((ErrorInternalServerError(""), req)); + // } + // Ok(req) } From dd238eb45a98027a687928d86059a5aa75e8d8e6 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 1 Nov 2023 13:19:49 +0200 Subject: [PATCH 33/73] merge with remote issue-auth --- Cargo.lock | 2 +- Dockerfile | 4 +-- docker/local/.env | 2 +- docker/local/configuration.yaml | 2 +- src/main.rs | 4 +-- src/startup.rs | 49 +++------------------------------ 6 files changed, 11 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e13fa91..c59df1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -630,7 +630,7 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" dependencies = [ "serde", ] diff --git a/Dockerfile b/Dockerfile index ec273b1..df4af41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,8 @@ COPY ../Cargo.toml . COPY ../Cargo.lock . COPY ../rustfmt.toml . COPY ../Makefile . -COPY ../.env . -COPY ../configuration.yaml . +COPY ../docker/local/.env . +COPY ../docker/local/configuration.yaml . # build this project to cache dependencies #RUN sqlx database create && sqlx migrate run diff --git a/docker/local/.env b/docker/local/.env index 6371a97..b4d26d5 100644 --- a/docker/local/.env +++ b/docker/local/.env @@ -1,4 +1,4 @@ -DATABASE_URL=postgres://postgres:postgres@stackerdb:5432/stacker +DATABASE_URL=postgres://postgres:postgres@192.168.48.2:5432/stacker POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker diff --git a/docker/local/configuration.yaml b/docker/local/configuration.yaml index 5eef969..1160655 100644 --- a/docker/local/configuration.yaml +++ b/docker/local/configuration.yaml @@ -2,7 +2,7 @@ app_host: 0.0.0.0 app_port: 8000 auth_url: https://dev.try.direct/server/user/oauth_server/api/me database: - host: stackerdb + host: 192.168.48.2 port: 5432 username: postgres password: postgres diff --git a/src/main.rs b/src/main.rs index 105939b..8cb3070 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,10 +16,10 @@ async fn main() -> std::io::Result<()> { .await .expect("Failed to connect to database."); - let address = format!("127.0.0.1:{}", settings.application_port); + let address = format!("{}:{}", settings.app_host, settings.app_port); tracing::info!("Start server at {:?}", &address); let listener = TcpListener::bind(address) - .expect(&format!("failed to bind to {}", settings.application_port)); + .expect(&format!("failed to bind to {}", settings.app_port)); run(listener, db_pool, settings).await?.await } diff --git a/src/startup.rs b/src/startup.rs index 4a05b33..6c8ac46 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -39,53 +39,12 @@ async fn bearer_guard( tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); - let resp = match resp { - Ok(resp) if resp.status().is_success() => resp, - Ok(resp) => { - tracing::error!("Authentication service returned no success {:?}", resp); - return Err((ErrorUnauthorized(""), req)); - } - Err(err) => { - tracing::error!("error from reqwest {:?}", err); - return Err((ErrorInternalServerError(""), req)); - } - }; - - let user_form: UserForm = match resp.json().await { - Ok(user) => { - tracing::info!("unpacked user {user:?}"); - user - } - Err(err) => { - tracing::error!("can't parse the response body {:?}", err); - return Err((ErrorUnauthorized(""), req)); - } - }; - - let user: User = match user_form.try_into() // try to convert UserForm into User model - { - Ok(user) => { user } - Err(err) => { - tracing::error!("Could not create User from form data: {:?}", err); - return Err((ErrorUnauthorized(""), req)); - } - }; - let existent_user = req.extensions_mut().insert(user); - if existent_user.is_some() { - tracing::error!("already logged {existent_user:?}"); - return Err((ErrorInternalServerError(""), req)); - } - // let resp = match resp { + // Ok(resp) if resp.status().is_success() => resp, // Ok(resp) => { - // //if resp.status().is_success() - // tracing::debug!("{:?}", resp); - // resp + // tracing::error!("Authentication service returned no success {:?}", resp); + // return Err((ErrorUnauthorized(""), req)); // } - // // Ok(resp) => { - // // tracing::error!("Authentication service returned no success {:?}", resp); - // // return Err((ErrorUnauthorized(""), req)); - // // } // Err(err) => { // tracing::error!("error from reqwest {:?}", err); // return Err((ErrorInternalServerError(""), req)); @@ -103,7 +62,7 @@ async fn bearer_guard( // } // }; // - // let user:User = match user_form.try_into() // try to convert UserForm into User model + // let user: User = match user_form.try_into() // try to convert UserForm into User model // { // Ok(user) => { user } // Err(err) => { From c960a8fbd430965c9bf0450e16d88b23b2ee47ac Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 1 Nov 2023 13:39:27 +0200 Subject: [PATCH 34/73] temp commented client endpoints --- docker-compose.yml | 2 +- src/startup.rs | 86 ++++++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a11f2a6..cff178f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: container_name: stacker restart: always volumes: - - ./stacker/files:/app/files + - ./files:/app/files - ./docker/local/configuration.yaml:/app/configuration.yaml - ./migrations:/app/migrations - ./docker/local/.env:/app/.env diff --git a/src/startup.rs b/src/startup.rs index 6c8ac46..f7a3a32 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -36,46 +36,44 @@ async fn bearer_guard( .send() .await; + let resp = match resp { + Ok(resp) if resp.status().is_success() => resp, + Ok(resp) => { + tracing::error!("Authentication service returned no success {:?}", resp); + // tracing::debug!("{:?}", resp.text().await.unwrap()); + return Err((ErrorUnauthorized("401 Unauthorized"), req)); + } + Err(err) => { + tracing::error!("error from reqwest {:?}", err); + return Err((ErrorInternalServerError(err.to_string()), req)); + } + }; - tracing::debug!("{:?}", resp.unwrap().text().await.unwrap()); + let user_form: UserForm = match resp.json().await { + Ok(user) => { + tracing::info!("unpacked user {user:?}"); + user + } + Err(err) => { + tracing::error!("can't parse the response body {:?}", err); + return Err((ErrorUnauthorized(""), req)); + } + }; + + let user: User = match user_form.try_into() // try to convert UserForm into User model + { + Ok(user) => { user } + Err(err) => { + tracing::error!("Could not create User from form data: {:?}", err); + return Err((ErrorUnauthorized("Unauthorized"), req)); + } + }; + let existent_user = req.extensions_mut().insert(user); + if existent_user.is_some() { + tracing::error!("already logged {existent_user:?}"); + return Err((ErrorInternalServerError(""), req)); + } - // let resp = match resp { - // Ok(resp) if resp.status().is_success() => resp, - // Ok(resp) => { - // tracing::error!("Authentication service returned no success {:?}", resp); - // return Err((ErrorUnauthorized(""), req)); - // } - // Err(err) => { - // tracing::error!("error from reqwest {:?}", err); - // return Err((ErrorInternalServerError(""), req)); - // } - // }; - // - // let user_form: UserForm = match resp.json().await { - // Ok(user) => { - // tracing::info!("unpacked user {user:?}"); - // user - // } - // Err(err) => { - // tracing::error!("can't parse the response body {:?}", err); - // return Err((ErrorUnauthorized(""), req)); - // } - // }; - // - // let user: User = match user_form.try_into() // try to convert UserForm into User model - // { - // Ok(user) => { user } - // Err(err) => { - // tracing::error!("Could not create User from form data: {:?}", err); - // return Err((ErrorUnauthorized(""), req)); - // } - // }; - // let existent_user = req.extensions_mut().insert(user); - // if existent_user.is_some() { - // tracing::error!("already logged {existent_user:?}"); - // return Err((ErrorInternalServerError(""), req)); - // } - // Ok(req) } @@ -96,12 +94,12 @@ pub async fn run( App::new() .wrap(TracingLogger::default()) .service(web::scope("/health_check").service(crate::routes::health_check)) - .service( - web::scope("/client") - .wrap(HttpAuthentication::bearer(bearer_guard)) - .wrap(Cors::permissive()) - .service(crate::routes::client::add_handler), - ) + // .service( + // web::scope("/client") + // .wrap(HttpAuthentication::bearer(bearer_guard)) + // .wrap(Cors::permissive()) + // .service(crate::routes::client::add_handler), + // ) .service( //todo 1. add client_guard. it should fetch client_id and hash from headers. based on db's //client secret and input body valiates the input. the client is to be handed over From 79c54e292ae21b0a54ae69f384a33c25ca1fc0c2 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 1 Nov 2023 16:21:01 +0200 Subject: [PATCH 35/73] get all ratings --- docker-compose.yml | 3 +- docker/local/.env | 2 +- docker/local/configuration.yaml | 2 +- src/routes/rating/get.rs | 53 +++++++++++++++++++++++++++++++++ src/services/mod.rs | 3 +- src/services/rating.rs | 22 ++++++++++++++ src/startup.rs | 3 +- 7 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/services/rating.rs diff --git a/docker-compose.yml b/docker-compose.yml index cff178f..2d3b934 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,8 @@ volumes: networks: backend: - name: stacker-backend + driver: bridge + name: backend external: true services: diff --git a/docker/local/.env b/docker/local/.env index b4d26d5..247a3fd 100644 --- a/docker/local/.env +++ b/docker/local/.env @@ -1,4 +1,4 @@ -DATABASE_URL=postgres://postgres:postgres@192.168.48.2:5432/stacker +DATABASE_URL=postgres://postgres:postgres@172.17.0.2:5432/stacker POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker diff --git a/docker/local/configuration.yaml b/docker/local/configuration.yaml index 1160655..9c1848f 100644 --- a/docker/local/configuration.yaml +++ b/docker/local/configuration.yaml @@ -2,7 +2,7 @@ app_host: 0.0.0.0 app_port: 8000 auth_url: https://dev.try.direct/server/user/oauth_server/api/me database: - host: 192.168.48.2 + host: 172.17.0.2 port: 5432 username: postgres password: postgres diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs index d8e4216..011f943 100644 --- a/src/routes/rating/get.rs +++ b/src/routes/rating/get.rs @@ -15,6 +15,7 @@ struct JsonResponse { message: String, code: u32, rating: Option, + objects: Option>, } #[tracing::instrument(name = "Get rating.")] @@ -41,6 +42,7 @@ pub async fn get_handler( code: 200, message: "".to_string(), rating: Some(rating), + objects: None })); } Err(sqlx::Error::RowNotFound) => { @@ -49,6 +51,7 @@ pub async fn get_handler( code: 404, message: format!("Not Found"), rating: None, + objects: None })); } Err(e) => { @@ -58,6 +61,56 @@ pub async fn get_handler( code: 500, message: format!("Internal Server Error"), rating: None, + objects: None + })); + } + } +} + +#[tracing::instrument(name = "Get all ratings.")] +#[get("")] +pub async fn default( + path: web::Path<()>, + pool: web::Data, +) -> Result { + + let query_span = tracing::info_span!("Get all rates."); + // let category = path.0; + match sqlx::query_as!( + models::Rating, + r"SELECT * FROM rating" + ) + .fetch_all(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(rating) => { + tracing::info!("Ratings found: {:?}", rating.len()); + return Ok(web::Json(JsonResponse { + status: "Success".to_string(), + code: 200, + message: "".to_string(), + rating: None, + objects: Some(rating), + })); + } + Err(sqlx::Error::RowNotFound) => { + return Ok(web::Json(JsonResponse { + status: "Error".to_string(), + code: 404, + message: format!("Not Found"), + rating: None, + objects: None + })); + } + Err(e) => { + tracing::error!("Failed to fetch rating, error: {:?}", e); + return Ok(web::Json(JsonResponse { + status: "Error".to_string(), + code: 500, + message: format!("Internal Server Error"), + rating: None, + objects: None })); } } diff --git a/src/services/mod.rs b/src/services/mod.rs index 4d551f8..33c56f4 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1 +1,2 @@ -mod stack; \ No newline at end of file +mod stack; +mod rating; \ No newline at end of file diff --git a/src/services/rating.rs b/src/services/rating.rs new file mode 100644 index 0000000..22f4202 --- /dev/null +++ b/src/services/rating.rs @@ -0,0 +1,22 @@ +use crate::models::rating::Rating; +use sqlx::PgPool; +use reqwest::Url; +use tracing::Instrument; +use tracing_subscriber::fmt::format; + +// impl Rating { + // pub async fn filter_by(query_string: &str, pool: PgPool) -> Result<()> { + // + // let url = Url::parse(query_string)?; + // tracing::debug!("parsed url {:?}", url); + // + // let query_span = tracing::info_span!("Search for rate by {}.", filter); + // let r = match sqlx::query_as!( + // models::Rating, + // r"SELECT * FROM rating WHERE id=$1 LIMIT 1", + // filter) + // .fetch(pool.get_ref()) + // .instrument(query_span) + // .await; + // } +// } diff --git a/src/startup.rs b/src/startup.rs index f7a3a32..5dd4c6b 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -113,7 +113,8 @@ pub async fn run( .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) .service(crate::routes::rating::add_handler) - .service(crate::routes::rating::get_handler), + .service(crate::routes::rating::get_handler) + .service(crate::routes::rating::default), ) // .service( // web::resource("/stack/{id}") From 5abeeaba66a95eb606e58fb42704df11855962d0 Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 1 Nov 2023 17:43:14 +0200 Subject: [PATCH 36/73] issue-auth unique select --- src/helpers/client/mod.rs | 2 ++ src/routes/client/add.rs | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/helpers/client/mod.rs b/src/helpers/client/mod.rs index e636305..1b847c4 100644 --- a/src/helpers/client/mod.rs +++ b/src/helpers/client/mod.rs @@ -1,3 +1,5 @@ mod generate_secret; +mod is_secret_unique; pub use generate_secret::*; +pub use is_secret_unique::*; diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 0f5435c..f2fcd07 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -69,7 +69,28 @@ pub async fn add_handler( let mut client = Client::default(); client.id = 1; client.user_id = user.id.clone(); - client.secret = client::generate_secret(255); + client.secret = loop { + let secret = client::generate_secret(255); + match client::is_secret_unique(pool.get_ref(), &secret).await { + Ok(is_unique) if is_unique => { + break secret; + } + Ok(_) => { + tracing::info!("Generate secret once more."); + continue; + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + + return Ok(web::Json(ClientAddResponse { + status: "error".to_string(), + code: 500, + message: "Failed to insert".to_string(), + client: None, + })); + } + } + }; let query_span = tracing::info_span!("Saving new client into the database"); match sqlx::query!( From 765233b8058fe44d9b0b630fc7c1a61cec521fa0 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 1 Nov 2023 21:35:15 +0200 Subject: [PATCH 37/73] use only obj_id for requests payload and responses --- migrations/20230903063840_creating_rating_tables.down.sql | 2 +- migrations/20230903063840_creating_rating_tables.up.sql | 6 +++--- src/models/rating.rs | 8 ++++---- src/routes/rating/add.rs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/migrations/20230903063840_creating_rating_tables.down.sql b/migrations/20230903063840_creating_rating_tables.down.sql index 403a002..e12e4ab 100644 --- a/migrations/20230903063840_creating_rating_tables.down.sql +++ b/migrations/20230903063840_creating_rating_tables.down.sql @@ -2,7 +2,7 @@ DROP INDEX idx_category; DROP INDEX idx_user_id; -DROP INDEX idx_product_id_rating_id; +DROP INDEX idx_obj_id_rating_id; DROP table rating; DROP table product; diff --git a/migrations/20230903063840_creating_rating_tables.up.sql b/migrations/20230903063840_creating_rating_tables.up.sql index 450e365..579bef6 100644 --- a/migrations/20230903063840_creating_rating_tables.up.sql +++ b/migrations/20230903063840_creating_rating_tables.up.sql @@ -11,17 +11,17 @@ CREATE TABLE product ( CREATE TABLE rating ( id serial, user_id VARCHAR(50) NOT NULL, - product_id integer NOT NULL, + obj_id integer NOT NULL, category VARCHAR(255) NOT NULL, comment TEXT DEFAULT NULL, hidden BOOLEAN DEFAULT FALSE, rate INTEGER, created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, - CONSTRAINT fk_product FOREIGN KEY(product_id) REFERENCES product(id), + CONSTRAINT fk_product FOREIGN KEY(obj_id) REFERENCES product(id), CONSTRAINT rating_pk PRIMARY KEY (id) ); CREATE INDEX idx_category ON rating(category); CREATE INDEX idx_user_id ON rating(user_id); -CREATE INDEX idx_product_id_rating_id ON rating(product_id, rate); +CREATE INDEX idx_obj_id_rating_id ON rating(obj_id, rate); diff --git a/src/models/rating.rs b/src/models/rating.rs index 54a6a2f..9a3f00b 100644 --- a/src/models/rating.rs +++ b/src/models/rating.rs @@ -19,11 +19,11 @@ pub struct Product { #[derive(Debug, Serialize)] pub struct Rating { pub id: i32, - pub user_id: String, // external user_id, 100, taken using token (middleware?) - pub product_id: i32, //primary key, for better data management - pub category: String, // rating of product | rating of service etc + pub user_id: String, // external user_id, 100, taken using token (middleware?) + pub obj_id: i32, // id of the external object + pub category: String, // rating of product | rating of service etc pub comment: Option, // always linked to a product - pub hidden: Option, // rating can be hidden for non-adequate user behaviour + pub hidden: Option, // rating can be hidden for non-adequate user behaviour pub rate: Option, #[serde(with = "crate::helpers::serialize_datetime")] pub created_at: DateTime, diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs index 8fde8b1..cd7fa2b 100644 --- a/src/routes/rating/add.rs +++ b/src/routes/rating/add.rs @@ -46,7 +46,7 @@ pub async fn add_handler( let query_span = tracing::info_span!("Search for existing vote."); match sqlx::query!( - r"SELECT id FROM rating where user_id=$1 AND product_id=$2 AND category=$3 LIMIT 1", + r"SELECT id FROM rating where user_id=$1 AND obj_id=$2 AND category=$3 LIMIT 1", user.id, form.obj_id, form.category as RateCategory @@ -87,7 +87,7 @@ pub async fn add_handler( // Insert rating match sqlx::query!( r#" - INSERT INTO rating (user_id, product_id, category, comment, hidden,rate, + INSERT INTO rating (user_id, obj_id, category, comment, hidden,rate, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, NOW() at time zone 'utc', NOW() at time zone 'utc') From dc7de28bfe52a479395a7aacca2e00ab16c7a002 Mon Sep 17 00:00:00 2001 From: vsilent Date: Fri, 3 Nov 2023 13:09:13 +0200 Subject: [PATCH 38/73] validate ownership of the db record for get stack endpoint, fetch and store more user details, serialize fix for uuid and crono by adding features in cargo.toml --- Cargo.lock | 2 + Cargo.toml | 4 +- Dockerfile | 1 - src/forms/user.rs | 6 +- src/helpers/json.rs | 33 ++++++ src/models/stack.rs | 246 +--------------------------------------- src/models/user.rs | 12 +- src/routes/stack/add.rs | 2 +- src/routes/stack/get.rs | 72 +++++++++--- src/startup.rs | 5 +- 10 files changed, 114 insertions(+), 269 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c59df1b..21202d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -442,6 +442,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "time 0.1.45", "wasm-bindgen", "windows-targets", @@ -2574,6 +2575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b6fa54a..47cbb10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ name = "stacker" [dependencies] actix-web = "4.3.1" -chrono = { version = "0.4.26", features = ["time"] } +chrono = { version = "0.4.26", features = ["time", "serde"] } config = "0.13.3" reqwest = { version = "0.11.17", features = ["json"] } serde = { version = "1.0.162", features = ["derive"] } @@ -23,7 +23,7 @@ tracing = { version = "0.1.37", features = ["log"] } tracing-bunyan-formatter = "0.3.8" tracing-log = "0.1.3" tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter"] } -uuid = { version = "1.3.4", features = ["v4"] } +uuid = { version = "1.3.4", features = ["v4", "serde"] } thiserror = "1.0" serde_valid = "0.16.3" serde_json = { version = "1.0.105", features = [] } diff --git a/Dockerfile b/Dockerfile index df4af41..666567e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,6 @@ COPY ../src ./src #RUN sqlx migrate run #RUN cargo sqlx prepare -- --bin stacker - # rebuild app with project source RUN rm -rf ./target/release/deps/stacker*; \ cargo build --release diff --git a/src/forms/user.rs b/src/forms/user.rs index 21fba4a..2da0dcf 100644 --- a/src/forms/user.rs +++ b/src/forms/user.rs @@ -131,7 +131,11 @@ impl TryInto for UserForm { // |msg| { format!("{:?}", msg) } // )?; Ok(UserModel { - id: self.user.id + id: self.user.id, + first_name: self.user.first_name, + last_name: self.user.last_name, + email: self.user.email, + email_confirmed: self.user.email_confirmed, }) } diff --git a/src/helpers/json.rs b/src/helpers/json.rs index 4eaa625..c0a6545 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -1,4 +1,7 @@ +// use std::collections::HashMap; +// use actix_web::{HttpRequest, HttpResponse, Responder}; use serde_derive::Serialize; + #[derive(Serialize)] pub(crate) struct JsonResponse { pub(crate) status: String, @@ -6,3 +9,33 @@ pub(crate) struct JsonResponse { pub(crate) code: u32, pub(crate) id: Option, } + + +// #[derive(Serialize)] +// pub(crate) struct JsonResponse { +// pub(crate) status: String, +// pub(crate) message: String, +// pub(crate) code: u32, +// pub(crate) custom_fields: HashMap>, +// } +// +// impl JsonResponse { +// pub(crate) fn new(status: String, message: String, code: u32) -> Self { +// let custom_fields = HashMap::new(); +// JsonResponse { +// status, +// message, +// code, +// custom_fields +// } +// } +// } +// +// // Implement the Responder trait for GlobalResponse +// impl Responder for JsonResponse { +// type Body = (); +// +// fn respond_to(self, _req: &HttpRequest) -> HttpResponse { +// HttpResponse::Ok().json(self) +// } +// } diff --git a/src/models/stack.rs b/src/models/stack.rs index c3bf934..21432d3 100644 --- a/src/models/stack.rs +++ b/src/models/stack.rs @@ -1,11 +1,9 @@ use chrono::{DateTime, Utc}; -use serde_derive::{Deserialize, Serialize}; use serde_json::Value; use uuid::Uuid; +use serde::{Serialize,Deserialize}; -// #[derive(sqlx::Type, Debug, Clone, Copy)] -// #[sqlx(rename_all = "lowercase", type_name = "json")] -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Stack { pub id: i32, // id - is a unique identifier for the app stack pub stack_id: Uuid, // external stack ID @@ -16,243 +14,3 @@ pub struct Stack { pub created_at: DateTime, pub updated_at: DateTime, } - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct FormData { - pub common_domain: String, - pub domain_list: DomainList, - pub region: String, - pub zone: Value, - pub server: String, - pub os: String, - pub ssl: String, - pub vars: Vec, - #[serde(rename = "integrated_features")] - pub integrated_features: Vec, - #[serde(rename = "extended_features")] - pub extended_features: Vec, - pub subscriptions: Vec, - #[serde(rename = "save_token")] - pub save_token: bool, - #[serde(rename = "cloud_token")] - pub cloud_token: String, - pub provider: String, - #[serde(rename = "stack_code")] - pub stack_code: String, - #[serde(rename = "selected_plan")] - pub selected_plan: String, - pub custom: Custom, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DomainList {} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Custom { - pub web: Vec, - pub feature: Vec, - pub service: Vec, - #[serde(rename = "servers_count")] - pub servers_count: i64, - #[serde(rename = "custom_stack_name")] - pub custom_stack_name: String, - #[serde(rename = "custom_stack_code")] - pub custom_stack_code: String, - #[serde(rename = "custom_stack_git_url")] - pub custom_stack_git_url: String, - #[serde(rename = "custom_stack_category")] - pub custom_stack_category: Vec, - #[serde(rename = "custom_stack_short_description")] - pub custom_stack_short_description: String, - #[serde(rename = "custom_stack_description")] - pub custom_stack_description: String, - #[serde(rename = "project_name")] - pub project_name: String, - #[serde(rename = "project_overview")] - pub project_overview: String, - #[serde(rename = "project_description")] - pub project_description: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Web { - pub name: String, - pub code: String, - pub domain: String, - pub shared_ports: Vec, - pub versions: Vec, - pub custom: bool, - #[serde(rename = "type")] - pub type_field: String, - pub main: bool, - #[serde(rename = "_id")] - pub id: String, - #[serde(rename = "dockerhub_user")] - pub dockerhub_user: String, - #[serde(rename = "dockerhub_name")] - pub dockerhub_name: String, - #[serde(rename = "ram_size")] - pub ram_size: String, - pub cpu: i64, - #[serde(rename = "disk_size")] - pub disk_size: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Feature { - #[serde(rename = "_etag")] - pub etag: Value, - #[serde(rename = "_id")] - pub id: i64, - #[serde(rename = "_created")] - pub created: String, - #[serde(rename = "_updated")] - pub updated: String, - pub name: String, - pub code: String, - pub role: Vec, - #[serde(rename = "type")] - pub type_field: String, - pub default: Value, - pub popularity: Value, - pub descr: Value, - pub ports: Ports, - pub commercial: Value, - pub subscription: Value, - pub autodeploy: Value, - pub suggested: Value, - pub dependency: Value, - #[serde(rename = "avoid_render")] - pub avoid_render: Value, - pub price: Value, - pub icon: Icon, - #[serde(rename = "category_id")] - pub category_id: i64, - #[serde(rename = "parent_app_id")] - pub parent_app_id: Value, - #[serde(rename = "full_description")] - pub full_description: Value, - pub description: String, - #[serde(rename = "plan_type")] - pub plan_type: Value, - #[serde(rename = "ansible_var")] - pub ansible_var: Value, - #[serde(rename = "repo_dir")] - pub repo_dir: Value, - pub cpu: String, - #[serde(rename = "ram_size")] - pub ram_size: String, - #[serde(rename = "disk_size")] - pub disk_size: String, - #[serde(rename = "dockerhub_image")] - pub dockerhub_image: String, - pub versions: Vec, - pub domain: String, - pub shared_ports: Vec, - pub main: bool, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Ports { - pub public: Vec, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Icon { - pub light: IconLight, - pub dark: IconDark, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct IconLight { - pub width: i64, - pub height: i64, - pub image: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct IconDark {} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Version { - #[serde(rename = "_etag")] - pub etag: Option, - #[serde(rename = "_id")] - pub id: i64, - #[serde(rename = "_created")] - pub created: Option, - #[serde(rename = "_updated")] - pub updated: String, - #[serde(rename = "app_id")] - pub app_id: i64, - pub name: String, - pub version: String, - #[serde(rename = "update_status")] - pub update_status: String, - pub tag: String, -} - -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Service { - #[serde(rename = "_etag")] - pub etag: Value, - #[serde(rename = "_id")] - pub id: i64, - #[serde(rename = "_created")] - pub created: String, - #[serde(rename = "_updated")] - pub updated: String, - pub name: String, - pub code: String, - pub role: Vec, - #[serde(rename = "type")] - pub type_field: String, - pub default: Value, - pub popularity: Value, - pub descr: Value, - pub ports: Value, - pub commercial: Value, - pub subscription: Value, - pub autodeploy: Value, - pub suggested: Value, - pub dependency: Value, - #[serde(rename = "avoid_render")] - pub avoid_render: Value, - pub price: Value, - pub icon: Icon, - #[serde(rename = "category_id")] - pub category_id: Value, - #[serde(rename = "parent_app_id")] - pub parent_app_id: Value, - #[serde(rename = "full_description")] - pub full_description: Value, - pub description: Value, - #[serde(rename = "plan_type")] - pub plan_type: Value, - #[serde(rename = "ansible_var")] - pub ansible_var: Value, - #[serde(rename = "repo_dir")] - pub repo_dir: Value, - pub cpu: Value, - #[serde(rename = "ram_size")] - pub ram_size: Value, - #[serde(rename = "disk_size")] - pub disk_size: Value, - #[serde(rename = "dockerhub_image")] - pub dockerhub_image: String, - pub versions: Vec, - pub domain: String, - pub shared_ports: Vec, - pub main: bool, -} \ No newline at end of file diff --git a/src/models/user.rs b/src/models/user.rs index db2d87a..4e77598 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -3,12 +3,22 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct User { pub id: String, + pub first_name: String, + pub last_name: String, + pub email: String, + pub email_confirmed: bool, + // pub phone: Option, + // pub website: Option, } impl Clone for User { fn clone(&self) -> Self { User { - id: self.id.clone() + id: self.id.clone(), + first_name: self.first_name.clone(), + last_name: self.last_name.clone(), + email: self.email.clone(), + email_confirmed: self.email_confirmed.clone(), } } } \ No newline at end of file diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 06a3686..53eb389 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use actix_web::{ web, web::{Bytes, Data, Json}, @@ -5,7 +6,6 @@ use actix_web::{ }; use crate::forms::stack::StackForm; -use crate::forms::user::UserForm; use crate::helpers::JsonResponse; use crate::models::user::User; use crate::models::Stack; diff --git a/src/routes/stack/get.rs b/src/routes/stack/get.rs index c5cf6ec..17f9181 100644 --- a/src/routes/stack/get.rs +++ b/src/routes/stack/get.rs @@ -1,32 +1,72 @@ -use actix_web::{web, HttpResponse}; -// use chrono::Utc; +use actix_web::{web, get, Responder, Result}; +use serde_derive::Serialize; use sqlx::PgPool; -// use uuid::Uuid; +use crate::models; +use crate::models::user::User; +#[derive(Serialize)] +struct JsonResponse { + status: String, + message: String, + code: u32, + id: Option, + object: Option, + objects: Option>, +} + +#[tracing::instrument(name = "Get stack.")] +#[get("/{id}")] pub async fn get( - id: web::Path, + user: web::ReqData, + path: web::Path<(i32,)>, pool: web::Data, -) -> HttpResponse { - let id = id.into_inner(); - tracing::info!("Get stack by id {:?}", id); +) -> Result { - match sqlx::query!( + let (id,) = path.into_inner(); + + tracing::info!("User {:?} is getting stack by id {:?}", user, id); + match sqlx::query_as!( + models::Stack, r#" - SELECT id FROM user_stack - WHERE id=$1 + SELECT * FROM user_stack WHERE id=$1 AND user_id=$2 LIMIT 1 "#, - id.parse::().unwrap() + id, user.id ) .fetch_one(pool.get_ref()) .await { - Ok(_) => { - tracing::info!("Stack found by id {}", id); - HttpResponse::Ok().finish() + Ok(stack) => { + tracing::info!("stack found: {:?}", stack.id,); + let response = JsonResponse { + status: "Success".to_string(), + code: 200, + message: "".to_string(), + id: Some(stack.id), + object: Some(stack), + objects: None + }; + return Ok(web::Json(response)); + } + Err(sqlx::Error::RowNotFound) => { + return Ok(web::Json(JsonResponse { + status: "Error".to_string(), + code: 404, + message: format!("Not Found"), + id: None, + object: None, + objects: None + })); } Err(e) => { - tracing::error!("Failed to execute query: {:?}", e); - HttpResponse::NotFound().finish() + tracing::error!("Failed to fetch stack, error: {:?}", e); + return Ok(web::Json(JsonResponse { + status: "Error".to_string(), + code: 500, + message: format!("Internal Server Error"), + id: None, + object: None, + objects: None + })); } } } diff --git a/src/startup.rs b/src/startup.rs index 5dd4c6b..e6115cf 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -5,7 +5,6 @@ use actix_web::dev::{Server, ServiceRequest}; use actix_web::error::{ErrorInternalServerError, ErrorUnauthorized}; use actix_web::HttpMessage; use actix_web::{ - // http::header::HeaderName, web::{self}, App, Error, @@ -13,7 +12,6 @@ use actix_web::{ }; use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; use reqwest::header::{ACCEPT, CONTENT_TYPE}; -use reqwest::Url; use sqlx::{Pool, Postgres}; use std::sync::Arc; use std::net::TcpListener; @@ -129,7 +127,8 @@ pub async fn run( web::scope("/stack") .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) - .service(crate::routes::stack::add::add), //.service(crate::routes::stack::deploy), + .service(crate::routes::stack::add::add) + .service(crate::routes::stack::get::get) ) .app_data(db_pool.clone()) .app_data(settings.clone()) From 04313ce0ec046201b0e2aa6014cad8a763ccf960 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 4 Nov 2023 11:20:21 +0200 Subject: [PATCH 39/73] issue-auth working on update enpoint of client --- configuration.yaml | 3 +- src/models/client.rs | 1 + src/routes/client/mod.rs | 2 + src/routes/client/update.rs | 132 ++++++++++++++++++++++++++++++++++++ src/startup.rs | 3 +- 5 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/routes/client/update.rs diff --git a/configuration.yaml b/configuration.yaml index 2bba7e7..d7187c1 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,5 +1,6 @@ application_port: 8000 -auth_url: https://dev.try.direct/server/user/oauth_server/api/me + #auth_url: https://dev.try.direct/server/user/oauth_server/api/me +auth_url: http://127.0.0.1:8080/me max_clients_number: 2 database: host: localhost diff --git a/src/models/client.rs b/src/models/client.rs index d6676dd..c6f68dc 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -6,3 +6,4 @@ pub struct Client { pub user_id: String, pub secret: String, } +//todo add created_at AND updated_at fields diff --git a/src/routes/client/mod.rs b/src/routes/client/mod.rs index 0cc183a..01808e4 100644 --- a/src/routes/client/mod.rs +++ b/src/routes/client/mod.rs @@ -1,3 +1,5 @@ pub mod add; +pub mod update; pub use add::*; +pub use update::*; diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs new file mode 100644 index 0000000..5a51dc4 --- /dev/null +++ b/src/routes/client/update.rs @@ -0,0 +1,132 @@ +use crate::configuration::Settings; +use crate::helpers::client; +use crate::models::user::User; +use crate::models::Client; +use actix_web::{error::ErrorNotFound, put, web, HttpResponse, Responder, Result}; +use serde::Serialize; +use sqlx::PgPool; +use std::sync::Arc; +use tracing::Instrument; + +#[derive(Serialize)] +struct ClientUpdateResponse { + status: String, + message: String, + code: u32, + client: Option, +} + +#[tracing::instrument(name = "Update client.")] +#[put("/{id}")] +pub async fn update_handler( + user: web::ReqData, + settings: web::Data>, + pool: web::Data, + path: web::Path<(i32,)>, +) -> Result { + let client_id = path.0; + //todo 1. find the client + //todo 2. if client is disabled. I mean the client_secret is null no action is to be performed + //todo 3. if client is active. update the secret and the updated_at fields + let query_span = tracing::info_span!("Fetching the client by ID"); + let client: Client = match sqlx::query_as!( + Client, + r#" + SELECT + id, user_id, secret + FROM client c + WHERE c.id = $1 + "#, + client_id, + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(client) => Ok(client), //todo continue only if not null secret + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + + /* + return Ok(web::Json(ClientUpdateResponse { + status: "error".to_string(), + code: 500, + message: "Failed to insert".to_string(), + client: None, + })); + */ + Err(ErrorNotFound("the client is not found")) //todo add a correct message + } + }?; + + /* + let mut client = Client::default(); + client.id = 1; + client.user_id = user.id.clone(); + client.secret = loop { + let secret = client::generate_secret(255); + match client::is_secret_unique(pool.get_ref(), &secret).await { + Ok(is_unique) if is_unique => { + break secret; + } + Ok(_) => { + tracing::info!("Generate secret once more."); + continue; + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + + return Ok(web::Json(ClientAddResponse { + status: "error".to_string(), + code: 500, + message: "Failed to insert".to_string(), + client: None, + })); + } + } + }; + + let query_span = tracing::info_span!("Saving new client into the database"); + match sqlx::query!( + r#" + INSERT INTO client (user_id, secret, created_at, updated_at) + VALUES ($1, $2, NOW() at time zone 'utc', NOW() at time zone 'utc') + RETURNING id + "#, + client.user_id.clone(), + client.secret, + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(result) => { + tracing::info!("New client {} have been saved to database", result.id); + client.id = result.id; + Ok(web::Json(ClientAddResponse { + status: "success".to_string(), + message: "".to_string(), + code: 200, + client: Some(client), + })) + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + + return Ok(web::Json(ClientAddResponse { + status: "error".to_string(), + code: 500, + message: "Failed to insert".to_string(), + client: None, + })); + } + } + */ + + return Ok(web::Json(ClientUpdateResponse { + status: format!("client_id={}", path.0), + code: 200, + message: "Failed to update".to_string(), + client: Some(client), + })); +} diff --git a/src/startup.rs b/src/startup.rs index 1f9b0dc..c217a41 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -92,7 +92,8 @@ pub async fn run( web::scope("/client") .wrap(HttpAuthentication::bearer(bearer_guard)) .wrap(Cors::permissive()) - .service(crate::routes::client::add_handler), + .service(crate::routes::client::add_handler) + .service(crate::routes::client::update_handler), ) .service( //todo 1. add client_guard. it should fetch client_id and hash from headers. based on db's From 368de8e6fa7947c1236bee6c805ec9e3df6b4ae9 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 4 Nov 2023 23:48:58 +0200 Subject: [PATCH 40/73] issue-auth update logic --- migrations/20231028161917_client.up.sql | 2 +- src/models/client.rs | 2 +- src/routes/client/add.rs | 3 +- src/routes/client/update.rs | 79 ++++++++----------------- 4 files changed, 30 insertions(+), 56 deletions(-) diff --git a/migrations/20231028161917_client.up.sql b/migrations/20231028161917_client.up.sql index 589038a..fcb9065 100644 --- a/migrations/20231028161917_client.up.sql +++ b/migrations/20231028161917_client.up.sql @@ -2,7 +2,7 @@ CREATE TABLE public.client ( id serial4 NOT NULL, user_id varchar(50) NOT NULL, - secret varchar(255) NOT NULL, + secret varchar(255), created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, CONSTRAINT client_pkey PRIMARY KEY (id), diff --git a/src/models/client.rs b/src/models/client.rs index c6f68dc..47ae61d 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -4,6 +4,6 @@ use serde::Serialize; pub struct Client { pub id: i32, pub user_id: String, - pub secret: String, + pub secret: Option, } //todo add created_at AND updated_at fields diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index f2fcd07..979cd71 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -73,7 +73,7 @@ pub async fn add_handler( let secret = client::generate_secret(255); match client::is_secret_unique(pool.get_ref(), &secret).await { Ok(is_unique) if is_unique => { - break secret; + break Some(secret); } Ok(_) => { tracing::info!("Generate secret once more."); @@ -128,3 +128,4 @@ pub async fn add_handler( } } } +//todo error responses diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs index 5a51dc4..c901ad0 100644 --- a/src/routes/client/update.rs +++ b/src/routes/client/update.rs @@ -2,7 +2,10 @@ use crate::configuration::Settings; use crate::helpers::client; use crate::models::user::User; use crate::models::Client; -use actix_web::{error::ErrorNotFound, put, web, HttpResponse, Responder, Result}; +use actix_web::{ + error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, + put, web, Responder, Result, +}; use serde::Serialize; use sqlx::PgPool; use std::sync::Arc; @@ -25,11 +28,8 @@ pub async fn update_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - //todo 1. find the client - //todo 2. if client is disabled. I mean the client_secret is null no action is to be performed - //todo 3. if client is active. update the secret and the updated_at fields let query_span = tracing::info_span!("Fetching the client by ID"); - let client: Client = match sqlx::query_as!( + let mut client: Client = match sqlx::query_as!( Client, r#" SELECT @@ -43,67 +43,52 @@ pub async fn update_handler( .instrument(query_span) .await { - Ok(client) => Ok(client), //todo continue only if not null secret + Ok(client) if client.secret.is_some() => Ok(client), + Ok(client) => Err(ErrorForbidden("client is not active")), + Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), Err(e) => { - tracing::error!("Failed to execute query: {:?}", e); + tracing::error!("Failed to execute fetch query: {:?}", e); - /* - return Ok(web::Json(ClientUpdateResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - client: None, - })); - */ - Err(ErrorNotFound("the client is not found")) //todo add a correct message + Err(ErrorInternalServerError("")) } }?; - /* - let mut client = Client::default(); - client.id = 1; - client.user_id = user.id.clone(); client.secret = loop { let secret = client::generate_secret(255); match client::is_secret_unique(pool.get_ref(), &secret).await { Ok(is_unique) if is_unique => { - break secret; + break Some(secret); } Ok(_) => { tracing::info!("Generate secret once more."); continue; } Err(e) => { - tracing::error!("Failed to execute query: {:?}", e); + tracing::error!("Failed to check the uniqueness of the secret: {:?}", e); - return Ok(web::Json(ClientAddResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - client: None, - })); + return Err(ErrorInternalServerError("")); } } }; - let query_span = tracing::info_span!("Saving new client into the database"); + let query_span = tracing::info_span!("Updating client into the database"); match sqlx::query!( r#" - INSERT INTO client (user_id, secret, created_at, updated_at) - VALUES ($1, $2, NOW() at time zone 'utc', NOW() at time zone 'utc') - RETURNING id + UPDATE client SET + secret=$1, + updated_at=NOW() at time zone 'utc' + WHERE id = $2 "#, - client.user_id.clone(), client.secret, + client.id ) - .fetch_one(pool.get_ref()) + .execute(pool.get_ref()) .instrument(query_span) .await { - Ok(result) => { - tracing::info!("New client {} have been saved to database", result.id); - client.id = result.id; - Ok(web::Json(ClientAddResponse { + Ok(_) => { + tracing::info!("Client {} have been saved to database", client.id); + Ok(web::Json(ClientUpdateResponse { status: "success".to_string(), message: "".to_string(), code: 200, @@ -112,21 +97,9 @@ pub async fn update_handler( } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - - return Ok(web::Json(ClientAddResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - client: None, - })); + return Err(ErrorInternalServerError("")); } } - */ - - return Ok(web::Json(ClientUpdateResponse { - status: format!("client_id={}", path.0), - code: 200, - message: "Failed to update".to_string(), - client: Some(client), - })); } + +//todo secret is logged. it should be wrapped in a password From 8c3349c9862aace285926fd4371984f91cc63d2c Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 08:33:11 +0200 Subject: [PATCH 41/73] issue-auth isolate generate secret --- src/helpers/client/generate_secret.rs | 21 +++++++++++++++++++- src/routes/client/add.rs | 28 +++++---------------------- src/routes/client/update.rs | 23 +++++----------------- 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/src/helpers/client/generate_secret.rs b/src/helpers/client/generate_secret.rs index c89bac8..8d2b323 100644 --- a/src/helpers/client/generate_secret.rs +++ b/src/helpers/client/generate_secret.rs @@ -1,6 +1,8 @@ +use crate::helpers::client; use rand::Rng; +use sqlx::PgPool; -pub fn generate_secret(len: usize) -> String { +fn make_secret(len: usize) -> String { const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)(*&^%$#@!~"; let mut rng = rand::thread_rng(); @@ -12,3 +14,20 @@ pub fn generate_secret(len: usize) -> String { }) .collect() } + +pub async fn generate_secret(pool: &PgPool, len: usize) -> Result { + loop { + let secret = make_secret(len); + match client::is_secret_unique(pool, &secret).await { + Ok(is_unique) if is_unique => { + return Ok(secret); + } + Ok(_) => { + continue; + } + Err(e) => { + return Err(format!("Failed to execute query: {:?}", e)); + } + } + } +} diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 979cd71..c8c4ff5 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -2,7 +2,7 @@ use crate::configuration::Settings; use crate::helpers::client; use crate::models::user::User; use crate::models::Client; -use actix_web::{post, web, Responder, Result}; +use actix_web::{error::ErrorInternalServerError, post, web, Responder, Result}; use serde::Serialize; use sqlx::PgPool; use std::sync::Arc; @@ -69,28 +69,10 @@ pub async fn add_handler( let mut client = Client::default(); client.id = 1; client.user_id = user.id.clone(); - client.secret = loop { - let secret = client::generate_secret(255); - match client::is_secret_unique(pool.get_ref(), &secret).await { - Ok(is_unique) if is_unique => { - break Some(secret); - } - Ok(_) => { - tracing::info!("Generate secret once more."); - continue; - } - Err(e) => { - tracing::error!("Failed to execute query: {:?}", e); - - return Ok(web::Json(ClientAddResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - client: None, - })); - } - } - }; + client.secret = client::generate_secret(pool.get_ref(), 255) + .await + .map(|s| Some(s)) + .map_err(|s| ErrorInternalServerError(s))?; let query_span = tracing::info_span!("Saving new client into the database"); match sqlx::query!( diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs index c901ad0..83049c2 100644 --- a/src/routes/client/update.rs +++ b/src/routes/client/update.rs @@ -53,23 +53,10 @@ pub async fn update_handler( } }?; - client.secret = loop { - let secret = client::generate_secret(255); - match client::is_secret_unique(pool.get_ref(), &secret).await { - Ok(is_unique) if is_unique => { - break Some(secret); - } - Ok(_) => { - tracing::info!("Generate secret once more."); - continue; - } - Err(e) => { - tracing::error!("Failed to check the uniqueness of the secret: {:?}", e); - - return Err(ErrorInternalServerError("")); - } - } - }; + client.secret = client::generate_secret(pool.get_ref(), 255) + .await + .map(|s| Some(s)) + .map_err(|s| ErrorInternalServerError(s))?; let query_span = tracing::info_span!("Updating client into the database"); match sqlx::query!( @@ -102,4 +89,4 @@ pub async fn update_handler( } } -//todo secret is logged. it should be wrapped in a password +//todo secret is logged. it should be wrapped in a password. secrecy crate From 6ab89dc17f506c833279291597be8c856b175b00 Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 08:40:04 +0200 Subject: [PATCH 42/73] issue-auth error responses --- src/routes/client/add.rs | 17 ++--------------- src/routes/client/update.rs | 2 +- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index c8c4ff5..6803cbb 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -56,13 +56,7 @@ pub async fn add_handler( } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - - return Ok(web::Json(ClientAddResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - client: None, - })); + return Err(ErrorInternalServerError("")); } }; @@ -100,14 +94,7 @@ pub async fn add_handler( } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - - return Ok(web::Json(ClientAddResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - client: None, - })); + return Err(ErrorInternalServerError("")); } } } -//todo error responses diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs index 83049c2..6de7be8 100644 --- a/src/routes/client/update.rs +++ b/src/routes/client/update.rs @@ -44,7 +44,7 @@ pub async fn update_handler( .await { Ok(client) if client.secret.is_some() => Ok(client), - Ok(client) => Err(ErrorForbidden("client is not active")), + Ok(_client) => Err(ErrorForbidden("client is not active")), Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), Err(e) => { tracing::error!("Failed to execute fetch query: {:?}", e); From 34e888f1999e6e214f7537bace16a0483c7baa3b Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 09:05:15 +0200 Subject: [PATCH 43/73] issue-auth removed a todo --- src/models/client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/client.rs b/src/models/client.rs index 47ae61d..cc93c9f 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -6,4 +6,3 @@ pub struct Client { pub user_id: String, pub secret: Option, } -//todo add created_at AND updated_at fields From e9a1c07884e3fc8aa67f29c049214323c75c89da Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 09:06:39 +0200 Subject: [PATCH 44/73] issue-auth removed todo --- src/routes/client/update.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs index 6de7be8..5702a25 100644 --- a/src/routes/client/update.rs +++ b/src/routes/client/update.rs @@ -88,5 +88,3 @@ pub async fn update_handler( } } } - -//todo secret is logged. it should be wrapped in a password. secrecy crate From 9a7074c9cb136588259fe087a21d463218394cc1 Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 09:57:31 +0200 Subject: [PATCH 45/73] issue-auth is_secret_unique --- src/helpers/client/is_secret_unique.rs | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/helpers/client/is_secret_unique.rs diff --git a/src/helpers/client/is_secret_unique.rs b/src/helpers/client/is_secret_unique.rs new file mode 100644 index 0000000..163bcc7 --- /dev/null +++ b/src/helpers/client/is_secret_unique.rs @@ -0,0 +1,28 @@ +use sqlx::PgPool; +use tracing::Instrument; + +#[tracing::instrument(name = "Check if secret is unique.")] +pub async fn is_secret_unique(pool_ref: &PgPool, secret: &String) -> Result { + let query_span = tracing::info_span!("Looking for the secret in the client's table."); + match sqlx::query!( + r#" + SELECT + count(*) as found + FROM client c + WHERE c.secret = $1 + LIMIT 1 + "#, + secret, + ) + .fetch_one(pool_ref) + .instrument(query_span) + .await + { + Ok(result) => { + return Ok(result.found < Some(1)); + } + Err(e) => { + return Err(format!("{e:?}")); + } + }; +} From c351a44d68e82077397fbf25e6ff08a3176653f2 Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 13:31:27 +0200 Subject: [PATCH 46/73] issue-auth removed serialize_datetime.rs --- src/helpers/mod.rs | 1 - src/helpers/serialize_datetime.rs | 21 --------------------- src/models/rating.rs | 10 ++++------ 3 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 src/helpers/serialize_datetime.rs diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 3500bbd..203eb0d 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -1,5 +1,4 @@ pub mod client; pub(crate) mod json; -pub mod serialize_datetime; pub use json::*; diff --git a/src/helpers/serialize_datetime.rs b/src/helpers/serialize_datetime.rs deleted file mode 100644 index 5c0f3c1..0000000 --- a/src/helpers/serialize_datetime.rs +++ /dev/null @@ -1,21 +0,0 @@ -use chrono::{DateTime, TimeZone, Utc}; -use serde::{Deserialize, Deserializer, Serializer}; - -const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S"; - -pub fn serialize(date: &DateTime, serializer: S) -> Result -where - S: Serializer, -{ - let s = format!("{}", date.format(FORMAT)); - serializer.serialize_str(&s) -} - -pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - Utc.datetime_from_str(&s, FORMAT) - .map_err(serde::de::Error::custom) -} diff --git a/src/models/rating.rs b/src/models/rating.rs index 9a3f00b..9145d18 100644 --- a/src/models/rating.rs +++ b/src/models/rating.rs @@ -19,15 +19,13 @@ pub struct Product { #[derive(Debug, Serialize)] pub struct Rating { pub id: i32, - pub user_id: String, // external user_id, 100, taken using token (middleware?) - pub obj_id: i32, // id of the external object - pub category: String, // rating of product | rating of service etc + pub user_id: String, // external user_id, 100, taken using token (middleware?) + pub obj_id: i32, // id of the external object + pub category: String, // rating of product | rating of service etc pub comment: Option, // always linked to a product - pub hidden: Option, // rating can be hidden for non-adequate user behaviour + pub hidden: Option, // rating can be hidden for non-adequate user behaviour pub rate: Option, - #[serde(with = "crate::helpers::serialize_datetime")] pub created_at: DateTime, - #[serde(with = "crate::helpers::serialize_datetime")] pub updated_at: DateTime, } From 8e06d2ace1c1ae1912826a8afd3bc450707c6995 Mon Sep 17 00:00:00 2001 From: Petru Date: Sun, 5 Nov 2023 13:55:27 +0200 Subject: [PATCH 47/73] issue-auth move middleware --- src/middleware/auth.rs | 3 -- src/middleware/mod.rs | 2 +- src/middleware/trydirect.rs | 68 +++++++++++++++++++++++++++++++ src/startup.rs | 80 ++++++------------------------------- 4 files changed, 81 insertions(+), 72 deletions(-) delete mode 100644 src/middleware/auth.rs create mode 100644 src/middleware/trydirect.rs diff --git a/src/middleware/auth.rs b/src/middleware/auth.rs deleted file mode 100644 index 0336728..0000000 --- a/src/middleware/auth.rs +++ /dev/null @@ -1,3 +0,0 @@ -// 1. Validate access token -// 2. Get user data by token -// 3. Create user \ No newline at end of file diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index c209e9c..4780d31 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -1 +1 @@ -mod auth; \ No newline at end of file +pub mod trydirect; diff --git a/src/middleware/trydirect.rs b/src/middleware/trydirect.rs new file mode 100644 index 0000000..55d063e --- /dev/null +++ b/src/middleware/trydirect.rs @@ -0,0 +1,68 @@ +use crate::configuration::Settings; +use crate::forms::user::UserForm; +use actix_web::dev::ServiceRequest; +use actix_web::error::{ErrorInternalServerError, ErrorUnauthorized}; +use actix_web::web::{self}; +use actix_web::Error; +use actix_web::HttpMessage; +use actix_web_httpauth::extractors::bearer::BearerAuth; +use reqwest::header::{ACCEPT, CONTENT_TYPE}; +use std::sync::Arc; + +use crate::models::user::User; + +#[tracing::instrument(name = "Bearer guard.")] +pub async fn bearer_guard( + req: ServiceRequest, + credentials: BearerAuth, +) -> Result { + let settings = req.app_data::>>().unwrap(); + let client = reqwest::Client::new(); + let resp = client + .get(&settings.auth_url) + .bearer_auth(credentials.token()) + .header(CONTENT_TYPE, "application/json") + .header(ACCEPT, "application/json") + .send() + .await; + + let resp = match resp { + Ok(resp) if resp.status().is_success() => resp, + Ok(resp) => { + tracing::error!("Authentication service returned no success {:?}", resp); + // tracing::debug!("{:?}", resp.text().await.unwrap()); + return Err((ErrorUnauthorized("401 Unauthorized"), req)); + } + Err(err) => { + tracing::error!("error from reqwest {:?}", err); + return Err((ErrorInternalServerError(err.to_string()), req)); + } + }; + + let user_form: UserForm = match resp.json().await { + Ok(user) => { + tracing::info!("unpacked user {user:?}"); + user + } + Err(err) => { + tracing::error!("can't parse the response body {:?}", err); + return Err((ErrorUnauthorized(""), req)); + } + }; + + let user: User = match user_form.try_into() // try to convert UserForm into User model + { + Ok(user) => { user } + Err(err) => { + tracing::error!("Could not create User from form data: {:?}", err); + return Err((ErrorUnauthorized("Unauthorized"), req)); + } + }; + let existent_user = req.extensions_mut().insert(user); + if existent_user.is_some() { + tracing::error!("already logged {existent_user:?}"); + return Err((ErrorInternalServerError(""), req)); + } + + Ok(req) +} diff --git a/src/startup.rs b/src/startup.rs index 6fc9591..daab9d6 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -1,78 +1,16 @@ use crate::configuration::Settings; -use crate::forms::user::UserForm; use actix_cors::Cors; -use actix_web::dev::{Server, ServiceRequest}; -use actix_web::error::{ErrorInternalServerError, ErrorUnauthorized}; -use actix_web::HttpMessage; +use actix_web::dev::Server; use actix_web::{ web::{self}, - App, Error, HttpServer, + App, HttpServer, }; -use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; -use reqwest::header::{ACCEPT, CONTENT_TYPE}; +use actix_web_httpauth::middleware::HttpAuthentication; use sqlx::{Pool, Postgres}; use std::net::TcpListener; use std::sync::Arc; use tracing_actix_web::TracingLogger; -use crate::models::user::User; - -#[tracing::instrument(name = "Bearer guard.")] -async fn bearer_guard( - req: ServiceRequest, - credentials: BearerAuth, -) -> Result { - let settings = req.app_data::>>().unwrap(); - let client = reqwest::Client::new(); - let resp = client - .get(&settings.auth_url) - .bearer_auth(credentials.token()) - .header(CONTENT_TYPE, "application/json") - .header(ACCEPT, "application/json") - .send() - .await; - - let resp = match resp { - Ok(resp) if resp.status().is_success() => resp, - Ok(resp) => { - tracing::error!("Authentication service returned no success {:?}", resp); - // tracing::debug!("{:?}", resp.text().await.unwrap()); - return Err((ErrorUnauthorized("401 Unauthorized"), req)); - } - Err(err) => { - tracing::error!("error from reqwest {:?}", err); - return Err((ErrorInternalServerError(err.to_string()), req)); - } - }; - - let user_form: UserForm = match resp.json().await { - Ok(user) => { - tracing::info!("unpacked user {user:?}"); - user - } - Err(err) => { - tracing::error!("can't parse the response body {:?}", err); - return Err((ErrorUnauthorized(""), req)); - } - }; - - let user: User = match user_form.try_into() // try to convert UserForm into User model - { - Ok(user) => { user } - Err(err) => { - tracing::error!("Could not create User from form data: {:?}", err); - return Err((ErrorUnauthorized("Unauthorized"), req)); - } - }; - let existent_user = req.extensions_mut().insert(user); - if existent_user.is_some() { - tracing::error!("already logged {existent_user:?}"); - return Err((ErrorInternalServerError(""), req)); - } - - Ok(req) -} - pub async fn run( listener: TcpListener, db_pool: Pool, @@ -92,7 +30,9 @@ pub async fn run( .service(web::scope("/health_check").service(crate::routes::health_check)) .service( web::scope("/client") - .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(HttpAuthentication::bearer( + crate::middleware::trydirect::bearer_guard, + )) .wrap(Cors::permissive()) .service(crate::routes::client::add_handler) .service(crate::routes::client::update_handler), @@ -107,7 +47,9 @@ pub async fn run( ) .service( web::scope("/rating") - .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(HttpAuthentication::bearer( + crate::middleware::trydirect::bearer_guard, + )) .wrap(Cors::permissive()) .service(crate::routes::rating::add_handler) .service(crate::routes::rating::get_handler) @@ -124,7 +66,9 @@ pub async fn run( // ) .service( web::scope("/stack") - .wrap(HttpAuthentication::bearer(bearer_guard)) + .wrap(HttpAuthentication::bearer( + crate::middleware::trydirect::bearer_guard, + )) .wrap(Cors::permissive()) .service(crate::routes::stack::add::add) .service(crate::routes::stack::get::get), From 6f53663862d3d5464dafe8a86117122c6253afa3 Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 7 Nov 2023 17:44:51 +0200 Subject: [PATCH 48/73] issue-auth disable client --- configuration.yaml | 4 +- src/routes/client/disable.rs | 84 ++++++++++++++++++++++++++++++++++++ src/routes/client/mod.rs | 6 ++- src/startup.rs | 3 +- 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 src/routes/client/disable.rs diff --git a/configuration.yaml b/configuration.yaml index 5099d3d..b4839ac 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,7 +1,7 @@ -#auth_url: http://127.0.0.1:8080/me +auth_url: http://127.0.0.1:8080/me app_host: 127.0.0.1 app_port: 8000 -auth_url: https://dev.try.direct/server/user/oauth_server/api/me + #auth_url: https://dev.try.direct/server/user/oauth_server/api/me max_clients_number: 2 database: host: 127.0.0.1 diff --git a/src/routes/client/disable.rs b/src/routes/client/disable.rs new file mode 100644 index 0000000..2e7f646 --- /dev/null +++ b/src/routes/client/disable.rs @@ -0,0 +1,84 @@ +use crate::configuration::Settings; +use crate::models::user::User; +use crate::models::Client; +use actix_web::{ + error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, + put, web, Responder, Result, +}; +use serde::Serialize; +use sqlx::PgPool; +use std::sync::Arc; +use tracing::Instrument; + +#[derive(Serialize)] +struct ClientDisableResponse { + status: String, + message: String, + code: u32, + client: Option, +} + +#[tracing::instrument(name = "Disable client.")] +#[put("/{id}/disable")] +pub async fn disable_handler( + user: web::ReqData, + settings: web::Data>, + pool: web::Data, + path: web::Path<(i32,)>, +) -> Result { + let client_id = path.0; + let query_span = tracing::info_span!("Fetching the client by ID"); + let mut client: Client = match sqlx::query_as!( + Client, + r#" + SELECT + id, user_id, secret + FROM client c + WHERE c.id = $1 + "#, + client_id, + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(client) if client.secret.is_some() => Ok(client), + Ok(_client) => Err(ErrorForbidden("client is not active")), + Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), + Err(e) => { + tracing::error!("Failed to execute fetch query: {:?}", e); + + Err(ErrorInternalServerError("")) + } + }?; + + client.secret = None; + let query_span = tracing::info_span!("Updating client into the database"); + match sqlx::query!( + r#" + UPDATE client SET + secret=null, + updated_at=NOW() at time zone 'utc' + WHERE id = $1 + "#, + client.id + ) + .execute(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(_) => { + tracing::info!("Client {} have been saved to database", client.id); + Ok(web::Json(ClientDisableResponse { + status: "success".to_string(), + message: "".to_string(), + code: 200, + client: Some(client), + })) + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + return Err(ErrorInternalServerError("")); + } + } +} diff --git a/src/routes/client/mod.rs b/src/routes/client/mod.rs index 01808e4..8c57e23 100644 --- a/src/routes/client/mod.rs +++ b/src/routes/client/mod.rs @@ -1,5 +1,7 @@ -pub mod add; -pub mod update; +mod add; +mod disable; +mod update; pub use add::*; +pub use disable::*; pub use update::*; diff --git a/src/startup.rs b/src/startup.rs index daab9d6..5ba3817 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -35,7 +35,8 @@ pub async fn run( )) .wrap(Cors::permissive()) .service(crate::routes::client::add_handler) - .service(crate::routes::client::update_handler), + .service(crate::routes::client::update_handler) + .service(crate::routes::client::disable_handler), ) .service( //todo 1. add client_guard. it should fetch client_id and hash from headers. based on db's From 5f4759f765bf0fb9a925678e7b2ed9a1971889a7 Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 7 Nov 2023 17:50:39 +0200 Subject: [PATCH 49/73] issue-auth - enable client --- src/routes/client/enable.rs | 90 +++++++++++++++++++++++++++++++++++++ src/routes/client/mod.rs | 2 + src/startup.rs | 1 + 3 files changed, 93 insertions(+) create mode 100644 src/routes/client/enable.rs diff --git a/src/routes/client/enable.rs b/src/routes/client/enable.rs new file mode 100644 index 0000000..1c17dc4 --- /dev/null +++ b/src/routes/client/enable.rs @@ -0,0 +1,90 @@ +use crate::configuration::Settings; +use crate::helpers::client; +use crate::models::user::User; +use crate::models::Client; +use actix_web::{ + error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, + put, web, Responder, Result, +}; +use serde::Serialize; +use sqlx::PgPool; +use std::sync::Arc; +use tracing::Instrument; + +#[derive(Serialize)] +struct ClientEnableResponse { + status: String, + message: String, + code: u32, + client: Option, +} + +#[tracing::instrument(name = "Enable client.")] +#[put("/{id}/enable")] +pub async fn enable_handler( + user: web::ReqData, + settings: web::Data>, + pool: web::Data, + path: web::Path<(i32,)>, +) -> Result { + let client_id = path.0; + let query_span = tracing::info_span!("Fetching the client by ID"); + let mut client: Client = match sqlx::query_as!( + Client, + r#" + SELECT + id, user_id, secret + FROM client c + WHERE c.id = $1 + "#, + client_id, + ) + .fetch_one(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(client) if client.secret.is_none() => Ok(client), + Ok(_client) => Err(ErrorForbidden("client is already enabled")), + Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), + Err(e) => { + tracing::error!("Failed to execute fetch query: {:?}", e); + + Err(ErrorInternalServerError("")) + } + }?; + + client.secret = client::generate_secret(pool.get_ref(), 255) + .await + .map(|s| Some(s)) + .map_err(|s| ErrorInternalServerError(s))?; + + let query_span = tracing::info_span!("Updating client into the database"); + match sqlx::query!( + r#" + UPDATE client SET + secret=$1, + updated_at=NOW() at time zone 'utc' + WHERE id = $2 + "#, + client.secret, + client.id + ) + .execute(pool.get_ref()) + .instrument(query_span) + .await + { + Ok(_) => { + tracing::info!("Client {} have been saved to database", client.id); + Ok(web::Json(ClientEnableResponse { + status: "success".to_string(), + message: "".to_string(), + code: 200, + client: Some(client), + })) + } + Err(e) => { + tracing::error!("Failed to execute query: {:?}", e); + return Err(ErrorInternalServerError("")); + } + } +} diff --git a/src/routes/client/mod.rs b/src/routes/client/mod.rs index 8c57e23..0fc0fde 100644 --- a/src/routes/client/mod.rs +++ b/src/routes/client/mod.rs @@ -1,7 +1,9 @@ mod add; mod disable; +mod enable; mod update; pub use add::*; pub use disable::*; +pub use enable::*; pub use update::*; diff --git a/src/startup.rs b/src/startup.rs index 5ba3817..9b21021 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -36,6 +36,7 @@ pub async fn run( .wrap(Cors::permissive()) .service(crate::routes::client::add_handler) .service(crate::routes::client::update_handler) + .service(crate::routes::client::enable_handler) .service(crate::routes::client::disable_handler), ) .service( From 3a21418ae59a0026f6b8be3e508f48a4c20588f8 Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 8 Nov 2023 18:35:26 +0200 Subject: [PATCH 50/73] issue-auth client berear_guard --- src/middleware/client.rs | 39 +++++++++++++++++++++++++++++++++++++ src/middleware/mod.rs | 1 + src/middleware/trydirect.rs | 2 +- src/routes/test/deploy.rs | 2 +- src/startup.rs | 12 ++++++------ 5 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 src/middleware/client.rs diff --git a/src/middleware/client.rs b/src/middleware/client.rs new file mode 100644 index 0000000..c445496 --- /dev/null +++ b/src/middleware/client.rs @@ -0,0 +1,39 @@ +use crate::configuration::Settings; +use actix_web::dev::ServiceRequest; +use actix_web::web::{self}; +use actix_web::Error; +use actix_web_httpauth::extractors::bearer::BearerAuth; +use std::sync::Arc; + +#[tracing::instrument(name = "Client bearer guard.")] +pub async fn bearer_guard( + req: ServiceRequest, + credentials: BearerAuth, +) -> Result { + let settings = req.app_data::>>().unwrap(); + //todo get client id from headers + //todo get hash from headers + //todo get body + //todo get client from db by id + //todo check that client is enabled + //todo compute hash of the body based on the secret + //todo if equal inject client + //todo if not equal 401 + + /* + let resp = match resp { + Ok(resp) if resp.status().is_success() => resp, + Ok(resp) => { + tracing::error!("Authentication service returned no success {:?}", resp); + // tracing::debug!("{:?}", resp.text().await.unwrap()); + return Err((ErrorUnauthorized("401 Unauthorized"), req)); + } + Err(err) => { + tracing::error!("error from reqwest {:?}", err); + return Err((ErrorInternalServerError(err.to_string()), req)); + } + }; + */ + + Ok(req) +} diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 4780d31..20dda30 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -1 +1,2 @@ +pub mod client; pub mod trydirect; diff --git a/src/middleware/trydirect.rs b/src/middleware/trydirect.rs index 55d063e..0d09cc9 100644 --- a/src/middleware/trydirect.rs +++ b/src/middleware/trydirect.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use crate::models::user::User; -#[tracing::instrument(name = "Bearer guard.")] +#[tracing::instrument(name = "Trydirect bearer guard.")] pub async fn bearer_guard( req: ServiceRequest, credentials: BearerAuth, diff --git a/src/routes/test/deploy.rs b/src/routes/test/deploy.rs index 60895f6..dad59e7 100644 --- a/src/routes/test/deploy.rs +++ b/src/routes/test/deploy.rs @@ -7,7 +7,7 @@ struct DeployResponse { } //todo inject client through enpoint's inputs -#[tracing::instrument(name = "Add rating.")] +#[tracing::instrument(name = "Test deploy.")] #[post("/deploy")] pub async fn handler() -> Result { Ok(web::Json(DeployResponse { diff --git a/src/startup.rs b/src/startup.rs index 9b21021..65228e9 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -40,12 +40,12 @@ pub async fn run( .service(crate::routes::client::disable_handler), ) .service( - //todo 1. add client_guard. it should fetch client_id and hash from headers. based on db's - //client secret and input body valiates the input. the client is to be handed over - //to the http endpoint - //todo 2. the generation secret and the client bearer to be separated in a separate - //utils module - web::scope("/test").service(crate::routes::test::deploy::handler), + web::scope("/test") + .wrap(HttpAuthentication::bearer( + crate::middleware::client::bearer_guard, + )) + .wrap(Cors::permissive()) + .service(crate::routes::test::deploy::handler), ) .service( web::scope("/rating") From f1e5fe84fc17fcd828cb885d2cacec673b61be41 Mon Sep 17 00:00:00 2001 From: Petru Date: Thu, 9 Nov 2023 18:05:42 +0200 Subject: [PATCH 51/73] issue-auth client middleware. start --- Cargo.lock | 29 ++++++++++---- Cargo.toml | 1 + src/middleware/client.rs | 83 +++++++++++++++++++++++++++++++++++++++- src/startup.rs | 4 +- 4 files changed, 104 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21202d5..3da91dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -725,9 +725,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-intrusive" @@ -740,25 +740,37 @@ dependencies = [ "parking_lot 0.11.2", ] +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", + "futures-macro", "futures-sink", "futures-task", "pin-project-lite", @@ -2129,6 +2141,7 @@ dependencies = [ "actix-web-httpauth", "chrono", "config", + "futures-util", "rand", "regex", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 47cbb10..d3adc1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ actix-cors = "0.6.4" tracing-actix-web = "0.7.7" regex = "1.10.2" rand = "0.8.5" +futures-util = "0.3.29" [dependencies.sqlx] version = "0.6.3" diff --git a/src/middleware/client.rs b/src/middleware/client.rs index c445496..899e458 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,8 +1,76 @@ +use std::future::{ready, Ready}; + +use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}; +use actix_web::error::ErrorBadRequest; +use actix_web::http::header::HeaderName; +use actix_web::Error; +use futures_util::future::LocalBoxFuture; + +pub struct Guard {} + +impl Guard { + pub fn new() -> Self { + Self {} + } +} + +impl Transform for Guard +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = GuardMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(GuardMiddleware { service })) + } +} + +pub struct GuardMiddleware { + service: S, +} + +impl Service for GuardMiddleware +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + forward_ready!(service); + + fn call(&self, req: ServiceRequest) -> Self::Future { + println!("Hi from start. You requested: {}", req.path()); //todo remove it + //let fut = self.service.call(req); + let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { + Some(client_id) => client_id, + None => { + return Box::pin(async { Err(ErrorBadRequest("missing header stacker-id")) }); + } + }; + + //todo retrieve db + //todo check the client + + Box::pin(self.service.call(req)) + } +} + +/* use crate::configuration::Settings; use actix_web::dev::ServiceRequest; +use actix_web::error::ErrorBadRequest; +use actix_web::http::header::HeaderName; use actix_web::web::{self}; use actix_web::Error; -use actix_web_httpauth::extractors::bearer::BearerAuth; use std::sync::Arc; #[tracing::instrument(name = "Client bearer guard.")] @@ -10,7 +78,17 @@ pub async fn bearer_guard( req: ServiceRequest, credentials: BearerAuth, ) -> Result { - let settings = req.app_data::>>().unwrap(); + tracing::info!("try to get client_id"); + /* + let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { + Some(client_id) => client_id, + None => { + return Err((ErrorBadRequest("missing header stacker-id"), req)); + } + }; + tracing::info!("client_id={client_id:?}"); + */ + //let settings = req.app_data::>>().unwrap(); //todo get client id from headers //todo get hash from headers //todo get body @@ -37,3 +115,4 @@ pub async fn bearer_guard( Ok(req) } +*/ diff --git a/src/startup.rs b/src/startup.rs index 65228e9..3f5c390 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -41,9 +41,7 @@ pub async fn run( ) .service( web::scope("/test") - .wrap(HttpAuthentication::bearer( - crate::middleware::client::bearer_guard, - )) + .wrap(crate::middleware::client::Guard::new()) .wrap(Cors::permissive()) .service(crate::routes::test::deploy::handler), ) From ba0cb5ece0b166b51d9f6aedf94e3b91d1d690ec Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 11 Nov 2023 08:30:27 +0200 Subject: [PATCH 52/73] issue-auth stacker-hash --- configuration.yaml | 4 ++-- src/middleware/client.rs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/configuration.yaml b/configuration.yaml index b4839ac..5099d3d 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,7 +1,7 @@ -auth_url: http://127.0.0.1:8080/me +#auth_url: http://127.0.0.1:8080/me app_host: 127.0.0.1 app_port: 8000 - #auth_url: https://dev.try.direct/server/user/oauth_server/api/me +auth_url: https://dev.try.direct/server/user/oauth_server/api/me max_clients_number: 2 database: host: 127.0.0.1 diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 899e458..cd153d9 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -57,6 +57,13 @@ where } }; + let hash = match req.headers().get(HeaderName::from_static("stacker-hash")) { + Some(hash) => hash, + None => { + return Box::pin(async { Err(ErrorBadRequest("missing header stacker-hash")) }); + } + }; + //todo retrieve db //todo check the client From dca5184a7748488fa5274058976821a625d26342 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 11 Nov 2023 10:04:15 +0200 Subject: [PATCH 53/73] issue-auth stacker-id, stacker-hash headers --- src/middleware/client.rs | 69 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/src/middleware/client.rs b/src/middleware/client.rs index cd153d9..7842151 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,10 +1,15 @@ +use crate::models::Client; use std::future::{ready, Ready}; - -use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}; -use actix_web::error::ErrorBadRequest; -use actix_web::http::header::HeaderName; -use actix_web::Error; +use tracing::Instrument; + +use actix_web::{ + dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, + error::ErrorBadRequest, + http::header::HeaderName, + web, Error, +}; use futures_util::future::LocalBoxFuture; +use sqlx::{Pool, Postgres}; pub struct Guard {} @@ -56,6 +61,18 @@ where return Box::pin(async { Err(ErrorBadRequest("missing header stacker-id")) }); } }; + let client_id: &str = match client_id.to_str() { + Ok(v) => v, + Err(_) => { + return Box::pin(async { Err(ErrorBadRequest("header stacker-id is not valid")) }); + } + }; + let client_id: i32 = match client_id.parse() { + Ok(v) => v, + Err(_) => { + return Box::pin(async { Err(ErrorBadRequest("header stacker-id is not valid")) }); + } + }; let hash = match req.headers().get(HeaderName::from_static("stacker-hash")) { Some(hash) => hash, @@ -63,6 +80,48 @@ where return Box::pin(async { Err(ErrorBadRequest("missing header stacker-hash")) }); } }; + let hash: &str = match hash.to_str() { + Ok(v) => v, + Err(_) => { + return Box::pin(async { + Err(ErrorBadRequest("header stacker-hash is not valid")) + }); + } + }; + + /* + let query_span = tracing::info_span!("Fetching the client by ID"); + let db_pool = req.app_data::>>().unwrap(); + //let mut client: Client = match sqlx::query_as!( + match sqlx::query_as!( + Client, + r#" + SELECT + id, user_id, secret + FROM client c + WHERE c.id = $1 + "#, + client_id, + ) + .fetch_one(db_pool.get_ref()) + .instrument(query_span) + { + _ => {} + }; + */ + /* + .await + { + Ok(client) if client.secret.is_some() => Ok(client), + Ok(_client) => Err(ErrorForbidden("client is not active")), + Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), + Err(e) => { + tracing::error!("Failed to execute fetch query: {:?}", e); + + Err(ErrorInternalServerError("")) + } + }?; + */ //todo retrieve db //todo check the client From fb84fefcd08933ea75a89127f16359ab2566c15e Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 11 Nov 2023 13:06:47 +0200 Subject: [PATCH 54/73] issue-auth client::GuardMiddleware fetches the client --- Cargo.lock | 40 ++++++- Cargo.toml | 1 + src/middleware/client.rs | 232 ++++++++++++++++----------------------- 3 files changed, 134 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3da91dd..4e2ed58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,11 +713,26 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -729,6 +744,17 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-intrusive" version = "0.4.2" @@ -740,6 +766,12 @@ dependencies = [ "parking_lot 0.11.2", ] +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + [[package]] name = "futures-macro" version = "0.3.29" @@ -769,10 +801,13 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -2141,6 +2176,7 @@ dependencies = [ "actix-web-httpauth", "chrono", "config", + "futures", "futures-util", "rand", "regex", diff --git a/Cargo.toml b/Cargo.toml index d3adc1c..86c6fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ tracing-actix-web = "0.7.7" regex = "1.10.2" rand = "0.8.5" futures-util = "0.3.29" +futures = "0.3.29" [dependencies.sqlx] version = "0.6.3" diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 7842151..a314211 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,14 +1,18 @@ use crate::models::Client; +use actix_web::error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}; +use futures::future::{FutureExt, LocalBoxFuture}; +use futures::lock::Mutex; +use futures::task::{Context, Poll}; use std::future::{ready, Ready}; +use std::sync::Arc; use tracing::Instrument; use actix_web::{ - dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, + dev::{Service, ServiceRequest, ServiceResponse, Transform}, error::ErrorBadRequest, http::header::HeaderName, web, Error, }; -use futures_util::future::LocalBoxFuture; use sqlx::{Pool, Postgres}; pub struct Guard {} @@ -21,7 +25,7 @@ impl Guard { impl Transform for Guard where - S: Service, Error = Error>, + S: Service, Error = Error> + 'static, S::Future: 'static, B: 'static, { @@ -32,153 +36,107 @@ where type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { - ready(Ok(GuardMiddleware { service })) + ready(Ok(GuardMiddleware { + service: Arc::new(Mutex::new(service)), + })) } } pub struct GuardMiddleware { - service: S, + service: Arc>, } impl Service for GuardMiddleware where - S: Service, Error = Error>, + S: Service, Error = Error> + 'static, S::Future: 'static, B: 'static, { type Response = ServiceResponse; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; - - forward_ready!(service); - - fn call(&self, req: ServiceRequest) -> Self::Future { - println!("Hi from start. You requested: {}", req.path()); //todo remove it - //let fut = self.service.call(req); - let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { - Some(client_id) => client_id, - None => { - return Box::pin(async { Err(ErrorBadRequest("missing header stacker-id")) }); - } - }; - let client_id: &str = match client_id.to_str() { - Ok(v) => v, - Err(_) => { - return Box::pin(async { Err(ErrorBadRequest("header stacker-id is not valid")) }); - } - }; - let client_id: i32 = match client_id.parse() { - Ok(v) => v, - Err(_) => { - return Box::pin(async { Err(ErrorBadRequest("header stacker-id is not valid")) }); - } - }; - - let hash = match req.headers().get(HeaderName::from_static("stacker-hash")) { - Some(hash) => hash, - None => { - return Box::pin(async { Err(ErrorBadRequest("missing header stacker-hash")) }); - } - }; - let hash: &str = match hash.to_str() { - Ok(v) => v, - Err(_) => { - return Box::pin(async { - Err(ErrorBadRequest("header stacker-hash is not valid")) - }); - } - }; - - /* - let query_span = tracing::info_span!("Fetching the client by ID"); - let db_pool = req.app_data::>>().unwrap(); - //let mut client: Client = match sqlx::query_as!( - match sqlx::query_as!( - Client, - r#" - SELECT - id, user_id, secret - FROM client c - WHERE c.id = $1 - "#, - client_id, - ) - .fetch_one(db_pool.get_ref()) - .instrument(query_span) - { - _ => {} - }; - */ - /* - .await - { - Ok(client) if client.secret.is_some() => Ok(client), - Ok(_client) => Err(ErrorForbidden("client is not active")), - Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), - Err(e) => { - tracing::error!("Failed to execute fetch query: {:?}", e); - - Err(ErrorInternalServerError("")) - } - }?; - */ - - //todo retrieve db - //todo check the client - - Box::pin(self.service.call(req)) + type Error = S::Error; + type Future = LocalBoxFuture<'static, Result, Error>>; + + fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { + self.service + .try_lock() + .expect("GuardMiddleware was called allready") + .poll_ready(ctx) } -} -/* -use crate::configuration::Settings; -use actix_web::dev::ServiceRequest; -use actix_web::error::ErrorBadRequest; -use actix_web::http::header::HeaderName; -use actix_web::web::{self}; -use actix_web::Error; -use std::sync::Arc; - -#[tracing::instrument(name = "Client bearer guard.")] -pub async fn bearer_guard( - req: ServiceRequest, - credentials: BearerAuth, -) -> Result { - tracing::info!("try to get client_id"); - /* - let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { - Some(client_id) => client_id, - None => { - return Err((ErrorBadRequest("missing header stacker-id"), req)); - } - }; - tracing::info!("client_id={client_id:?}"); - */ - //let settings = req.app_data::>>().unwrap(); - //todo get client id from headers - //todo get hash from headers - //todo get body - //todo get client from db by id - //todo check that client is enabled - //todo compute hash of the body based on the secret - //todo if equal inject client - //todo if not equal 401 - - /* - let resp = match resp { - Ok(resp) if resp.status().is_success() => resp, - Ok(resp) => { - tracing::error!("Authentication service returned no success {:?}", resp); - // tracing::debug!("{:?}", resp.text().await.unwrap()); - return Err((ErrorUnauthorized("401 Unauthorized"), req)); - } - Err(err) => { - tracing::error!("error from reqwest {:?}", err); - return Err((ErrorInternalServerError(err.to_string()), req)); + fn call(&self, req: ServiceRequest) -> Self::Future { + let service = self.service.clone(); + async move { + let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { + Some(client_id) => client_id, + None => { + return Err(ErrorBadRequest("missing header stacker-id")); + } + }; + + let client_id: &str = match client_id.to_str() { + Ok(v) => v, + Err(_) => { + return Err(ErrorBadRequest("header stacker-id is not valid")); + } + }; + let client_id: i32 = match client_id.parse() { + Ok(v) => v, + Err(_) => { + return Err(ErrorBadRequest("header stacker-id is not valid")); + } + }; + + let hash = match req.headers().get(HeaderName::from_static("stacker-hash")) { + Some(hash) => hash, + None => { + return Err(ErrorBadRequest("missing header stacker-hash")); + } + }; + let hash: &str = match hash.to_str() { + Ok(v) => v, + Err(_) => { + return Err(ErrorBadRequest("header stacker-hash is not valid")); + } + }; + + let query_span = tracing::info_span!("Fetching the client by ID"); + let db_pool = req.app_data::>>().unwrap(); + + let mut client: Client = match sqlx::query_as!( + Client, + r#" + SELECT + id, user_id, secret + FROM client c + WHERE c.id = $1 + "#, + client_id, + ) + .fetch_one(db_pool.get_ref()) + .instrument(query_span) + .await + { + Ok(client) if client.secret.is_some() => client, + Ok(_client) => { + return Err(ErrorForbidden("client is not active")); + } + Err(sqlx::Error::RowNotFound) => { + return Err(ErrorNotFound("the client is not found")); + } + Err(e) => { + tracing::error!("Failed to execute fetch query: {:?}", e); + + return Err(ErrorInternalServerError("")); + } + }; + + //todo attach client to request + //todo compute hash of the request + //todo compare the has of the request + + let service = service.lock().await; + service.call(req).await } - }; - */ - - Ok(req) + .boxed_local() + } } -*/ From f128e40e4cfa2ec392d2fdb03ba4bc159889a15c Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 13 Nov 2023 21:26:19 +0200 Subject: [PATCH 55/73] issue-auth inject client --- src/middleware/client.rs | 10 +++++++++- src/middleware/trydirect.rs | 6 ++---- src/models/client.rs | 4 ++-- src/routes/test/deploy.rs | 6 +++++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/middleware/client.rs b/src/middleware/client.rs index a314211..0ca8a1e 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,5 +1,6 @@ use crate::models::Client; use actix_web::error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}; +use actix_web::HttpMessage; use futures::future::{FutureExt, LocalBoxFuture}; use futures::lock::Mutex; use futures::task::{Context, Poll}; @@ -130,7 +131,14 @@ where } }; - //todo attach client to request + match req.extensions_mut().insert(Arc::new(client)) { + Some(_) => { + tracing::error!("client middleware already called once"); + return Err(ErrorInternalServerError("")); + } + None => {} + } + //todo compute hash of the request //todo compare the has of the request diff --git a/src/middleware/trydirect.rs b/src/middleware/trydirect.rs index 0d09cc9..1d01c50 100644 --- a/src/middleware/trydirect.rs +++ b/src/middleware/trydirect.rs @@ -30,7 +30,6 @@ pub async fn bearer_guard( Ok(resp) if resp.status().is_success() => resp, Ok(resp) => { tracing::error!("Authentication service returned no success {:?}", resp); - // tracing::debug!("{:?}", resp.text().await.unwrap()); return Err((ErrorUnauthorized("401 Unauthorized"), req)); } Err(err) => { @@ -50,9 +49,8 @@ pub async fn bearer_guard( } }; - let user: User = match user_form.try_into() // try to convert UserForm into User model - { - Ok(user) => { user } + let user: User = match user_form.try_into() { + Ok(user) => user, Err(err) => { tracing::error!("Could not create User from form data: {:?}", err); return Err((ErrorUnauthorized("Unauthorized"), req)); diff --git a/src/models/client.rs b/src/models/client.rs index cc93c9f..8a5b951 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -1,8 +1,8 @@ use serde::Serialize; -#[derive(Default, Serialize)] +#[derive(Default, Serialize, Debug)] pub struct Client { pub id: i32, pub user_id: String, - pub secret: Option, + pub secret: Option, //todo hide secret } diff --git a/src/routes/test/deploy.rs b/src/routes/test/deploy.rs index dad59e7..945e293 100644 --- a/src/routes/test/deploy.rs +++ b/src/routes/test/deploy.rs @@ -1,16 +1,20 @@ +use crate::models::Client; use actix_web::{post, web, Responder, Result}; use serde::Serialize; +use std::sync::Arc; #[derive(Serialize)] struct DeployResponse { status: String, + client: Arc, } //todo inject client through enpoint's inputs #[tracing::instrument(name = "Test deploy.")] #[post("/deploy")] -pub async fn handler() -> Result { +pub async fn handler(client: web::ReqData>) -> Result { Ok(web::Json(DeployResponse { status: "success".to_string(), + client: client.into_inner(), })) } From d0215f2a72f4bd04f58767e6c515fc594783797e Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 14 Nov 2023 21:42:33 +0200 Subject: [PATCH 56/73] issue-auth pass body of request through middleware --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/middleware/client.rs | 22 ++++++++++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e2ed58..35219e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2172,6 +2172,7 @@ name = "stacker" version = "0.1.0" dependencies = [ "actix-cors", + "actix-http", "actix-web", "actix-web-httpauth", "chrono", @@ -2188,6 +2189,7 @@ dependencies = [ "sqlx", "thiserror", "tokio", + "tokio-stream", "tracing", "tracing-actix-web", "tracing-bunyan-formatter", diff --git a/Cargo.toml b/Cargo.toml index 86c6fc2..d4a08e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,8 @@ regex = "1.10.2" rand = "0.8.5" futures-util = "0.3.29" futures = "0.3.29" +tokio-stream = "0.1.14" +actix-http = "3.4.0" [dependencies.sqlx] version = "0.6.3" diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 0ca8a1e..be0d1c5 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,10 +1,14 @@ use crate::models::Client; -use actix_web::error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}; +use actix_web::dev::Payload; +use actix_web::error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound, PayloadError}; +use actix_web::web::BytesMut; use actix_web::HttpMessage; use futures::future::{FutureExt, LocalBoxFuture}; use futures::lock::Mutex; use futures::task::{Context, Poll}; +use futures::StreamExt; use std::future::{ready, Ready}; +use std::iter::Iterator; use std::sync::Arc; use tracing::Instrument; @@ -64,7 +68,7 @@ where .poll_ready(ctx) } - fn call(&self, req: ServiceRequest) -> Self::Future { + fn call(&self, mut req: ServiceRequest) -> Self::Future { let service = self.service.clone(); async move { let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { @@ -141,6 +145,20 @@ where //todo compute hash of the request //todo compare the has of the request + //todo creates BytesMut with beforehand allocated memory + let body = req + .take_payload() + .fold(BytesMut::new(), |mut body, chunk| { + let chunk = chunk.unwrap(); //todo process the potential error of unwrap + body.extend_from_slice(&chunk); //todo + + ready(body) + }) + .await; + + let (_, mut payload) = actix_http::h1::Payload::create(true); + payload.unread_data(body.into()); + req.set_payload(payload.into()); let service = service.lock().await; service.call(req).await From dc2fe5ad9229b7af1552f2b3389f8082387aa9a8 Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 15 Nov 2023 17:45:24 +0200 Subject: [PATCH 57/73] issue-auth compute hash inside client middleware --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/middleware/client.rs | 39 +++++++++++++++++++++++++++------------ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35219e9..a4b8f33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2179,6 +2179,7 @@ dependencies = [ "config", "futures", "futures-util", + "hmac", "rand", "regex", "reqwest", @@ -2186,6 +2187,7 @@ dependencies = [ "serde_derive", "serde_json", "serde_valid", + "sha2", "sqlx", "thiserror", "tokio", diff --git a/Cargo.toml b/Cargo.toml index d4a08e8..9153031 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,8 @@ futures-util = "0.3.29" futures = "0.3.29" tokio-stream = "0.1.14" actix-http = "3.4.0" +hmac = "0.12.1" +sha2 = "0.10.8" [dependencies.sqlx] version = "0.6.3" diff --git a/src/middleware/client.rs b/src/middleware/client.rs index be0d1c5..66cc96e 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,5 +1,4 @@ use crate::models::Client; -use actix_web::dev::Payload; use actix_web::error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound, PayloadError}; use actix_web::web::BytesMut; use actix_web::HttpMessage; @@ -7,8 +6,9 @@ use futures::future::{FutureExt, LocalBoxFuture}; use futures::lock::Mutex; use futures::task::{Context, Poll}; use futures::StreamExt; +use hmac::{Hmac, Mac}; +use sha2::Sha256; use std::future::{ready, Ready}; -use std::iter::Iterator; use std::sync::Arc; use tracing::Instrument; @@ -97,8 +97,8 @@ where return Err(ErrorBadRequest("missing header stacker-hash")); } }; - let hash: &str = match hash.to_str() { - Ok(v) => v, + let hash: String = match hash.to_str() { + Ok(v) => v.to_owned(), Err(_) => { return Err(ErrorBadRequest("header stacker-hash is not valid")); } @@ -135,14 +135,6 @@ where } }; - match req.extensions_mut().insert(Arc::new(client)) { - Some(_) => { - tracing::error!("client middleware already called once"); - return Err(ErrorInternalServerError("")); - } - None => {} - } - //todo compute hash of the request //todo compare the has of the request //todo creates BytesMut with beforehand allocated memory @@ -156,10 +148,33 @@ where }) .await; + let mut mac = + match Hmac::::new_from_slice(client.secret.as_ref().unwrap().as_bytes()) { + Ok(mac) => mac, + Err(err) => { + tracing::error!("error generating hmac {err:?}"); + + return Err(ErrorInternalServerError("")); + } + }; + mac.update(body.as_ref()); + let computed_hash = format!("{:x}", mac.finalize().into_bytes()); + if hash != computed_hash { + return Err(ErrorBadRequest("hash is wrong")); + } + let (_, mut payload) = actix_http::h1::Payload::create(true); payload.unread_data(body.into()); req.set_payload(payload.into()); + match req.extensions_mut().insert(Arc::new(client)) { + Some(_) => { + tracing::error!("client middleware already called once"); + return Err(ErrorInternalServerError("")); + } + None => {} + } + let service = service.lock().await; service.call(req).await } From b5d51693789a581be71186519a19c4005df728a8 Mon Sep 17 00:00:00 2001 From: Petru Date: Wed, 15 Nov 2023 17:47:04 +0200 Subject: [PATCH 58/73] issue-auth --- src/middleware/client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 66cc96e..18ff0b7 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -135,8 +135,6 @@ where } }; - //todo compute hash of the request - //todo compare the has of the request //todo creates BytesMut with beforehand allocated memory let body = req .take_payload() @@ -157,6 +155,7 @@ where return Err(ErrorInternalServerError("")); } }; + mac.update(body.as_ref()); let computed_hash = format!("{:x}", mac.finalize().into_bytes()); if hash != computed_hash { From a53d29f08a39fa0989962945535b6c3acea9b5a8 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 18 Nov 2023 00:42:36 +0200 Subject: [PATCH 59/73] issue-auth config serde_yaml --- Cargo.lock | 213 +++++++++---------------------------------- Cargo.toml | 2 +- src/configuration.rs | 15 +-- 3 files changed, 48 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4b8f33..d683568 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -294,17 +294,6 @@ dependencies = [ "libc", ] -[[package]] -name = "async-trait" -version = "0.1.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - [[package]] name = "atoi" version = "1.0.0" @@ -448,25 +437,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "config" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" -dependencies = [ - "async-trait", - "json5", - "lazy_static", - "nom", - "pathdiff", - "ron", - "rust-ini", - "serde", - "serde_json", - "toml", - "yaml-rust", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -615,12 +585,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dlv-list" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" - [[package]] name = "dotenvy" version = "0.15.7" @@ -645,6 +609,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.5" @@ -862,7 +832,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -874,9 +844,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -1051,6 +1018,16 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.1", +] + [[package]] name = "instant" version = "0.1.12" @@ -1108,17 +1085,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "language-tags" version = "0.3.2" @@ -1137,12 +1103,6 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.10" @@ -1356,16 +1316,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-multimap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" -dependencies = [ - "dlv-list", - "hashbrown 0.12.3", -] - [[package]] name = "overload" version = "0.1.1" @@ -1426,63 +1376,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" -[[package]] -name = "pest" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "pest_meta" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - [[package]] name = "pin-project" version = "1.1.3" @@ -1754,27 +1653,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "ron" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "serde", -] - -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1886,18 +1764,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", @@ -1933,7 +1811,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0adc7a19d45e581abc6d169c865a0b14b84bb43a9e966d1cca4d733e70f7f35a" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itertools 0.10.5", "num-traits", "once_cell", @@ -1971,6 +1849,19 @@ dependencies = [ "regex", ] +[[package]] +name = "serde_yaml" +version = "0.9.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +dependencies = [ + "indexmap 2.1.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2106,7 +1997,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "indexmap", + "indexmap 1.9.3", "itoa", "libc", "log", @@ -2176,7 +2067,6 @@ dependencies = [ "actix-web", "actix-web-httpauth", "chrono", - "config", "futures", "futures-util", "hmac", @@ -2187,6 +2077,7 @@ dependencies = [ "serde_derive", "serde_json", "serde_valid", + "serde_yaml", "sha2", "sqlx", "thiserror", @@ -2440,15 +2331,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "tower-service" version = "0.3.2" @@ -2559,12 +2441,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2598,6 +2474,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + [[package]] name = "untrusted" version = "0.7.1" @@ -2882,15 +2764,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zstd" version = "0.12.4" diff --git a/Cargo.toml b/Cargo.toml index 9153031..1857b41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ name = "stacker" [dependencies] actix-web = "4.3.1" chrono = { version = "0.4.26", features = ["time", "serde"] } -config = "0.13.3" reqwest = { version = "0.11.17", features = ["json"] } serde = { version = "1.0.162", features = ["derive"] } tokio = { version = "1.28.1", features = ["full"] } @@ -39,6 +38,7 @@ tokio-stream = "0.1.14" actix-http = "3.4.0" hmac = "0.12.1" sha2 = "0.10.8" +serde_yaml = "0.9.27" [dependencies.sqlx] version = "0.6.3" diff --git a/src/configuration.rs b/src/configuration.rs index a3beeaf..976bd0b 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -35,15 +35,8 @@ impl DatabaseSettings { } } -pub fn get_configuration() -> Result { - // Initialize our configuration reader - let mut settings = config::Config::default(); - - // Add configuration values from a file named `configuration` - // with the .yaml extension - settings.merge(config::File::with_name("configuration"))?; // .json, .toml, .yaml, .yml - - // Try to convert the configuration values it read into - // our Settings type - settings.try_deserialize() +pub fn get_configuration() -> Result> { + Ok(serde_yaml::from_reader(std::fs::File::open( + "configuration.yaml", + )?)?) } From ebc36d5086c459ef96761be9b750f23f8522f7f0 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 18 Nov 2023 00:48:39 +0200 Subject: [PATCH 60/73] Revert "issue-auth config serde_yaml" This reverts commit a53d29f08a39fa0989962945535b6c3acea9b5a8. --- Cargo.lock | 213 ++++++++++++++++++++++++++++++++++--------- Cargo.toml | 2 +- src/configuration.rs | 15 ++- 3 files changed, 182 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d683568..a4b8f33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -294,6 +294,17 @@ dependencies = [ "libc", ] +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "atoi" version = "1.0.0" @@ -437,6 +448,25 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "config" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +dependencies = [ + "async-trait", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -585,6 +615,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "dotenvy" version = "0.15.7" @@ -609,12 +645,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "errno" version = "0.3.5" @@ -832,7 +862,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap", "slab", "tokio", "tokio-util", @@ -844,6 +874,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] [[package]] name = "hashbrown" @@ -1018,16 +1051,6 @@ dependencies = [ "serde", ] -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.1", -] - [[package]] name = "instant" version = "0.1.12" @@ -1085,6 +1108,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -1103,6 +1137,12 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.10" @@ -1316,6 +1356,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + [[package]] name = "overload" version = "0.1.1" @@ -1376,12 +1426,63 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pest" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "pest_meta" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project" version = "1.1.3" @@ -1653,6 +1754,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "serde", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1764,18 +1886,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", @@ -1811,7 +1933,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0adc7a19d45e581abc6d169c865a0b14b84bb43a9e966d1cca4d733e70f7f35a" dependencies = [ - "indexmap 1.9.3", + "indexmap", "itertools 0.10.5", "num-traits", "once_cell", @@ -1849,19 +1971,6 @@ dependencies = [ "regex", ] -[[package]] -name = "serde_yaml" -version = "0.9.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" -dependencies = [ - "indexmap 2.1.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - [[package]] name = "sha1" version = "0.10.6" @@ -1997,7 +2106,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "indexmap 1.9.3", + "indexmap", "itoa", "libc", "log", @@ -2067,6 +2176,7 @@ dependencies = [ "actix-web", "actix-web-httpauth", "chrono", + "config", "futures", "futures-util", "hmac", @@ -2077,7 +2187,6 @@ dependencies = [ "serde_derive", "serde_json", "serde_valid", - "serde_yaml", "sha2", "sqlx", "thiserror", @@ -2331,6 +2440,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -2441,6 +2559,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2474,12 +2598,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "unsafe-libyaml" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" - [[package]] name = "untrusted" version = "0.7.1" @@ -2764,6 +2882,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zstd" version = "0.12.4" diff --git a/Cargo.toml b/Cargo.toml index 1857b41..9153031 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ name = "stacker" [dependencies] actix-web = "4.3.1" chrono = { version = "0.4.26", features = ["time", "serde"] } +config = "0.13.3" reqwest = { version = "0.11.17", features = ["json"] } serde = { version = "1.0.162", features = ["derive"] } tokio = { version = "1.28.1", features = ["full"] } @@ -38,7 +39,6 @@ tokio-stream = "0.1.14" actix-http = "3.4.0" hmac = "0.12.1" sha2 = "0.10.8" -serde_yaml = "0.9.27" [dependencies.sqlx] version = "0.6.3" diff --git a/src/configuration.rs b/src/configuration.rs index 976bd0b..a3beeaf 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -35,8 +35,15 @@ impl DatabaseSettings { } } -pub fn get_configuration() -> Result> { - Ok(serde_yaml::from_reader(std::fs::File::open( - "configuration.yaml", - )?)?) +pub fn get_configuration() -> Result { + // Initialize our configuration reader + let mut settings = config::Config::default(); + + // Add configuration values from a file named `configuration` + // with the .yaml extension + settings.merge(config::File::with_name("configuration"))?; // .json, .toml, .yaml, .yml + + // Try to convert the configuration values it read into + // our Settings type + settings.try_deserialize() } From 5fe6d9d0f0ff161555d49b898f78b8636a8edfd0 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 18 Nov 2023 21:04:29 +0200 Subject: [PATCH 61/73] issue-auth get_header. first version --- src/middleware/client.rs | 38 +++++++++++++++++++------------------- src/models/client.rs | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 18ff0b7..94d13f1 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -71,25 +71,10 @@ where fn call(&self, mut req: ServiceRequest) -> Self::Future { let service = self.service.clone(); async move { - let client_id = match req.headers().get(HeaderName::from_static("stacker-id")) { - Some(client_id) => client_id, - None => { - return Err(ErrorBadRequest("missing header stacker-id")); - } - }; - - let client_id: &str = match client_id.to_str() { - Ok(v) => v, - Err(_) => { - return Err(ErrorBadRequest("header stacker-id is not valid")); - } - }; - let client_id: i32 = match client_id.parse() { - Ok(v) => v, - Err(_) => { - return Err(ErrorBadRequest("header stacker-id is not valid")); - } - }; + let client_id: i32 = get_header(&req, "stacker-id").map_err(|m| { + tracing::error!("stacker-id header. error {}", m); + ErrorBadRequest(format!("header stacker-id. {}", m)) + })?; let hash = match req.headers().get(HeaderName::from_static("stacker-hash")) { Some(hash) => hash, @@ -180,3 +165,18 @@ where .boxed_local() } } + +fn get_header(req: &ServiceRequest, header_name: &'static str) -> Result { + let header_value = req + .headers() + .get(HeaderName::from_static(header_name)) + .ok_or(format!("header {header_name} not found"))?; + + let header_value: &str = header_value + .to_str() + .map_err(|_| format!("header {header_name} can't be converted to string"))?; //map_err + // + header_value + .parse() + .map_err(|_| format!("header {header_name} is not integer")) +} diff --git a/src/models/client.rs b/src/models/client.rs index 8a5b951..1ff7b7c 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -4,5 +4,5 @@ use serde::Serialize; pub struct Client { pub id: i32, pub user_id: String, - pub secret: Option, //todo hide secret + pub secret: Option, } From 8b4542cc46a5d00483c12b4fe55279a755365016 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 18 Nov 2023 21:29:54 +0200 Subject: [PATCH 62/73] issue-auth general get_header --- src/middleware/client.rs | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 94d13f1..5fba662 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -9,6 +9,7 @@ use futures::StreamExt; use hmac::{Hmac, Mac}; use sha2::Sha256; use std::future::{ready, Ready}; +use std::str::FromStr; use std::sync::Arc; use tracing::Instrument; @@ -71,23 +72,8 @@ where fn call(&self, mut req: ServiceRequest) -> Self::Future { let service = self.service.clone(); async move { - let client_id: i32 = get_header(&req, "stacker-id").map_err(|m| { - tracing::error!("stacker-id header. error {}", m); - ErrorBadRequest(format!("header stacker-id. {}", m)) - })?; - - let hash = match req.headers().get(HeaderName::from_static("stacker-hash")) { - Some(hash) => hash, - None => { - return Err(ErrorBadRequest("missing header stacker-hash")); - } - }; - let hash: String = match hash.to_str() { - Ok(v) => v.to_owned(), - Err(_) => { - return Err(ErrorBadRequest("header stacker-hash is not valid")); - } - }; + let client_id: i32 = get_header(&req, "stacker-id").map_err(|m| ErrorBadRequest(m))?; + let hash: String = get_header(&req, "stacker-hash").map_err(|m| ErrorBadRequest(m))?; let query_span = tracing::info_span!("Fetching the client by ID"); let db_pool = req.app_data::>>().unwrap(); @@ -166,7 +152,10 @@ where } } -fn get_header(req: &ServiceRequest, header_name: &'static str) -> Result { +fn get_header(req: &ServiceRequest, header_name: &'static str) -> Result +where + T: FromStr, +{ let header_value = req .headers() .get(HeaderName::from_static(header_name)) @@ -177,6 +166,6 @@ fn get_header(req: &ServiceRequest, header_name: &'static str) -> Result() + .map_err(|_| format!("header {header_name} has wrong type")) } From 0fe235654b30f839e61b5ecd7489aa122d5b9780 Mon Sep 17 00:00:00 2001 From: Petru Date: Sat, 18 Nov 2023 21:42:21 +0200 Subject: [PATCH 63/73] issue-auth content_lenght. bytesMut --- src/middleware/client.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/middleware/client.rs b/src/middleware/client.rs index 5fba662..dc76c44 100644 --- a/src/middleware/client.rs +++ b/src/middleware/client.rs @@ -1,4 +1,5 @@ use crate::models::Client; +use actix_http::header::CONTENT_LENGTH; use actix_web::error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound, PayloadError}; use actix_web::web::BytesMut; use actix_web::HttpMessage; @@ -106,15 +107,19 @@ where } }; - //todo creates BytesMut with beforehand allocated memory + let content_length: usize = + get_header(&req, CONTENT_LENGTH.as_str()).map_err(|m| ErrorBadRequest(m))?; let body = req .take_payload() - .fold(BytesMut::new(), |mut body, chunk| { - let chunk = chunk.unwrap(); //todo process the potential error of unwrap - body.extend_from_slice(&chunk); //todo - - ready(body) - }) + .fold( + BytesMut::with_capacity(content_length), + |mut body, chunk| { + let chunk = chunk.unwrap(); //todo process the potential error of unwrap + body.extend_from_slice(&chunk); //todo + + ready(body) + }, + ) .await; let mut mac = From 41f13ac9b6cf3f7aea6219512dee18acf7cf9273 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 19 Nov 2023 11:43:58 +0200 Subject: [PATCH 64/73] optiomize error response --- README.md | 9 +- src/helpers/json.rs | 195 ++++++++++++++++++++++++++++++++------- src/models/rating.rs | 4 +- src/routes/rating/add.rs | 42 +++------ 4 files changed, 184 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index c95694e..53879ed 100644 --- a/README.md +++ b/README.md @@ -77,4 +77,11 @@ sqlx migrate revert #### Deploy ``` curl -X POST -H "Content-Type: application/json" -d @custom-stack-payload-2.json http://127.0.0.1:8000/stack -``` \ No newline at end of file +``` + + +#### Create API Client +curl -X POST http://localhost:8000/client --header 'Content-Type: application/json' -H "Authorization: Bearer $TD_BEARER" + +test client deploy +http://localhost:8000/test/deploy \ No newline at end of file diff --git a/src/helpers/json.rs b/src/helpers/json.rs index c0a6545..fdde72d 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -1,41 +1,170 @@ -// use std::collections::HashMap; -// use actix_web::{HttpRequest, HttpResponse, Responder}; +use actix_web::{Responder, Result}; +use actix_web::error::ErrorBadRequest; use serde_derive::Serialize; +use actix_web::web; +use actix_web::web::Json; +use actix_web::Error; #[derive(Serialize)] -pub(crate) struct JsonResponse { - pub(crate) status: String, +pub(crate) struct JsonResponse { + // pub(crate) status: String, pub(crate) message: String, - pub(crate) code: u32, + // pub(crate) code: u32, pub(crate) id: Option, + pub(crate) item: Option, + pub(crate) list: Option> } -// #[derive(Serialize)] -// pub(crate) struct JsonResponse { -// pub(crate) status: String, -// pub(crate) message: String, -// pub(crate) code: u32, -// pub(crate) custom_fields: HashMap>, -// } -// -// impl JsonResponse { -// pub(crate) fn new(status: String, message: String, code: u32) -> Self { -// let custom_fields = HashMap::new(); -// JsonResponse { -// status, -// message, -// code, -// custom_fields -// } -// } -// } -// -// // Implement the Responder trait for GlobalResponse -// impl Responder for JsonResponse { -// type Body = (); -// -// fn respond_to(self, _req: &HttpRequest) -> HttpResponse { -// HttpResponse::Ok().json(self) -// } -// } +#[derive(Serialize, Default)] +pub struct JsonResponseBuilder + where T: serde::Serialize + Default +{ + id: Option, + item: Option, + list: Option> +} + +impl JsonResponseBuilder + where T: serde::Serialize + Default +{ + pub(crate) fn new() -> Self { + Self::default() + } + + fn set_item(mut self, item:T) -> Self { + self.item = Some(item); + self + } + + pub(crate) fn set_id(mut self, id:i32) -> Self { + self.id = Some(id); + self + } + + pub(crate) fn set_list(mut self, list:Vec) -> Self { + self.list = Some(list); + self + } + + pub(crate) fn ok(self, msg: String) -> Result>, Error> { + + Ok(Json( + JsonResponse { + message: msg, + id: self.id, + item: self.item, + list: self.list, + } + )) + + } + + pub(crate) fn err(self, msg: String) -> Result>, Error> { + + let json_response = JsonResponse { + message: msg, + id: self.id, + item: self.item, + list: self.list + }; + + Err(ErrorBadRequest( + serde_json::to_string(&json_response).unwrap())) + } +} + +impl From for JsonResponseBuilder + where T: serde::Serialize + Default { + fn from(value: T) -> Self { + JsonResponseBuilder::default().set_item(value) + } +} + +impl From> for JsonResponseBuilder + where T: serde::Serialize + Default { + fn from(value: Vec) -> Self { + JsonResponseBuilder::default().set_list(value) + } +} + +impl JsonResponse +where T: serde::Serialize + Default +{ + pub fn build() -> JsonResponseBuilder + { + JsonResponseBuilder::default() + } + pub(crate) fn new(status: String, + message: String, + code: u32, + id: Option, + item:Option, + list: Option>) -> Self { + tracing::debug!("Executed.."); + JsonResponse { + message, + id, + item, + list, + } + } + + pub(crate) fn ok(id: i32, message: &str) -> JsonResponse { + + let msg = if !message.trim().is_empty() { + message.to_string() + } + else{ + String::from("Success") + }; + + JsonResponse { + message: msg, + id: Some(id), + item: None, + list: None, + } + } + + pub(crate) fn not_found() -> Self { + JsonResponse { + id: None, + item: None, + message: format!("Object not found"), + list: None, + } + } + + pub(crate) fn internal_error(message: &str) -> Self { + + let msg = if !message.trim().is_empty() { + message.to_string() + } + else{ + String::from("Internal error") + }; + JsonResponse { + id: None, + item: None, + message: msg, + list: None, + } + } + + pub(crate) fn not_valid(message: &str) -> Self { + + let msg = if !message.trim().is_empty() { + message.to_string() + } + else{ + String::from("Validation error") + }; + JsonResponse { + id: None, + item: None, + message: msg, + list: None, + } + } +} \ No newline at end of file diff --git a/src/models/rating.rs b/src/models/rating.rs index 9145d18..c9ba705 100644 --- a/src/models/rating.rs +++ b/src/models/rating.rs @@ -16,7 +16,7 @@ pub struct Product { pub updated_at: DateTime, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Default)] pub struct Rating { pub id: i32, pub user_id: String, // external user_id, 100, taken using token (middleware?) @@ -29,6 +29,8 @@ pub struct Rating { pub updated_at: DateTime, } + + #[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Copy)] #[sqlx(rename_all = "lowercase", type_name = "varchar")] pub enum RateCategory { diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs index cd7fa2b..fda8248 100644 --- a/src/routes/rating/add.rs +++ b/src/routes/rating/add.rs @@ -1,5 +1,5 @@ use crate::forms; -use crate::helpers::JsonResponse; +use crate::helpers::{JsonResponse, JsonResponseBuilder}; use crate::models; use crate::models::user::User; use crate::models::RateCategory; @@ -35,12 +35,8 @@ pub async fn add_handler( } Err(e) => { tracing::error!("Failed to fetch product: {:?}, error: {:?}", form.obj_id, e); - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 404, - message: format!("Object not found {}", form.obj_id), - id: None, - })); + return JsonResponse::::build() + .err(format!("Object not found {}", form.obj_id)); } }; @@ -64,22 +60,14 @@ pub async fn add_handler( form.category ); - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 409, - message: format!("Already Rated"), - id: Some(record.id), - })); + return JsonResponse::::build() + .set_id(record.id) + .ok(format!("Already Rated")); } Err(sqlx::Error::RowNotFound) => {} Err(e) => { tracing::error!("Failed to fetch rating, error: {:?}", e); - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 500, - message: format!("Internal Server Error"), - id: None, - })); + return JsonResponse::build().err(format!("Internal Server Error")); } } @@ -107,21 +95,13 @@ pub async fn add_handler( Ok(result) => { tracing::info!("New rating {} have been saved to database", result.id); - Ok(web::Json(JsonResponse { - status: "ok".to_string(), - code: 200, - message: "Saved".to_string(), - id: Some(result.id), - })) + return JsonResponse::build() + .set_id(result.id) + .ok("Saved".to_string()); } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - Ok(web::Json(JsonResponse { - status: "error".to_string(), - code: 500, - message: "Failed to insert".to_string(), - id: None, - })) + return JsonResponse::build().err("Failed to insert".to_string()); } } } From c7e0bdbb038df1e57c09c20181e8b9eb8a6891bf Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 20 Nov 2023 09:16:02 +0200 Subject: [PATCH 65/73] improved json response --- src/helpers/json.rs | 2 +- src/main.rs | 7 ++-- src/routes/rating/get.rs | 70 ++++++---------------------------------- src/routes/stack/add.rs | 31 +++--------------- 4 files changed, 19 insertions(+), 91 deletions(-) diff --git a/src/helpers/json.rs b/src/helpers/json.rs index fdde72d..7c193d6 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -32,7 +32,7 @@ impl JsonResponseBuilder Self::default() } - fn set_item(mut self, item:T) -> Self { + pub(crate) fn set_item(mut self, item:T) -> Self { self.item = Some(item); self } diff --git a/src/main.rs b/src/main.rs index 8cb3070..ebf4987 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ -use std::net::TcpListener; use sqlx::PgPool; use stacker::configuration::get_configuration; use stacker::startup::run; use stacker::telemetry::{get_subscriber, init_subscriber}; - +use std::net::TcpListener; #[actix_web::main] async fn main() -> std::io::Result<()> { @@ -18,8 +17,8 @@ async fn main() -> std::io::Result<()> { let address = format!("{}:{}", settings.app_host, settings.app_port); tracing::info!("Start server at {:?}", &address); - let listener = TcpListener::bind(address) - .expect(&format!("failed to bind to {}", settings.app_port)); + let listener = + TcpListener::bind(address).expect(&format!("failed to bind to {}", settings.app_port)); run(listener, db_pool, settings).await?.await } diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs index 011f943..01e847f 100644 --- a/src/routes/rating/get.rs +++ b/src/routes/rating/get.rs @@ -3,20 +3,13 @@ use actix_web::{get, web, Responder, Result}; use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; +use crate::helpers::JsonResponse; // workflow // add, update, list, get(user_id), ACL, // ACL - access to func for a user // ACL - access to objects for a user -#[derive(Serialize)] -struct JsonResponse { - status: String, - message: String, - code: u32, - rating: Option, - objects: Option>, -} #[tracing::instrument(name = "Get rating.")] #[get("/{id}")] @@ -36,82 +29,39 @@ pub async fn get_handler( .await { Ok(rating) => { - tracing::info!("rating found: {:?}", rating.id,); - return Ok(web::Json(JsonResponse { - status: "Success".to_string(), - code: 200, - message: "".to_string(), - rating: Some(rating), - objects: None - })); + tracing::info!("rating found: {:?}", rating.id); + return JsonResponse::build().set_item(Some(rating)).ok("OK".to_string()); } Err(sqlx::Error::RowNotFound) => { - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 404, - message: format!("Not Found"), - rating: None, - objects: None - })); + return JsonResponse::build().err("Not Found".to_string()); } Err(e) => { tracing::error!("Failed to fetch rating, error: {:?}", e); - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 500, - message: format!("Internal Server Error"), - rating: None, - objects: None - })); + return JsonResponse::build().err("Internal Server Error".to_string()); } } } #[tracing::instrument(name = "Get all ratings.")] #[get("")] -pub async fn default( - path: web::Path<()>, - pool: web::Data, -) -> Result { - +pub async fn default(path: web::Path<()>, pool: web::Data) -> Result { let query_span = tracing::info_span!("Get all rates."); // let category = path.0; - match sqlx::query_as!( - models::Rating, - r"SELECT * FROM rating" - ) + match sqlx::query_as!(models::Rating, r"SELECT * FROM rating") .fetch_all(pool.get_ref()) .instrument(query_span) .await { Ok(rating) => { tracing::info!("Ratings found: {:?}", rating.len()); - return Ok(web::Json(JsonResponse { - status: "Success".to_string(), - code: 200, - message: "".to_string(), - rating: None, - objects: Some(rating), - })); + return JsonResponse::build().set_list(rating).ok("OK".to_string()); } Err(sqlx::Error::RowNotFound) => { - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 404, - message: format!("Not Found"), - rating: None, - objects: None - })); + return JsonResponse::build().err("Not Found".to_string()); } Err(e) => { tracing::error!("Failed to fetch rating, error: {:?}", e); - return Ok(web::Json(JsonResponse { - status: "Error".to_string(), - code: 500, - message: format!("Internal Server Error"), - rating: None, - objects: None - })); + return JsonResponse::build().err("Internal Server Error".to_string()); } } } diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs index 53eb389..bbe2e8a 100644 --- a/src/routes/stack/add.rs +++ b/src/routes/stack/add.rs @@ -1,10 +1,8 @@ -use std::collections::HashMap; use actix_web::{ web, web::{Bytes, Data, Json}, Responder, Result, }; - use crate::forms::stack::StackForm; use crate::helpers::JsonResponse; use crate::models::user::User; @@ -16,6 +14,7 @@ use sqlx::PgPool; use std::str; use tracing::Instrument; use uuid::Uuid; +use crate::models; #[tracing::instrument(name = "Add stack.")] #[post("")] @@ -24,27 +23,17 @@ pub async fn add( user: web::ReqData, pool: Data, ) -> Result { - // None::.expect("my error"); - // return Err(JsonPayloadError::Payload(PayloadError::Overflow).into()); - // let content_type = req.headers().get("content-type"); - // println!("Request Content-Type: {:?}", content_type); let body_bytes = actix_web::body::to_bytes(body).await.unwrap(); let body_str = str::from_utf8(&body_bytes).unwrap(); - // method 1 let app_state: AppState = serde_json::from_str(body_str).unwrap(); - // method 2 let app_state = serde_json::from_str::(body_str).unwrap(); let form = match serde_json::from_str::(body_str) { Ok(f) => { println!("fine"); f } Err(err) => { - return Ok(Json(JsonResponse { - status: "Error".to_string(), - code: 400, - message: err.to_string(), - id: None, - })); + let err = format!("Error: {}", err); + return JsonResponse::::build().err(err); } }; // println!("app: {:?}", form); @@ -115,21 +104,11 @@ pub async fn add( "req_id: {} New stack details have been saved to database", request_id ); - Ok(Json(JsonResponse { - status: "OK".to_string(), - code: 200, - message: format!("Object saved"), - id: Some(record.id), - })) + return JsonResponse::build().set_id(record.id).ok("OK".to_string()); } Err(e) => { tracing::error!("req_id: {} Failed to execute query: {:?}", request_id, e); - Ok(Json(JsonResponse { - status: "Error".to_string(), - code: 400, - message: e.to_string(), - id: None, - })) + return JsonResponse::build().err("Internal Server Error".to_string()); } }; } From 138b147ed44b6cff5cbee99e0776f1836fa56c1c Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 17:07:53 +0200 Subject: [PATCH 66/73] issue-auth removed redundant code from json.rs --- src/helpers/json.rs | 154 +++++++++----------------------------------- 1 file changed, 29 insertions(+), 125 deletions(-) diff --git a/src/helpers/json.rs b/src/helpers/json.rs index 7c193d6..2c5e0b0 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -1,170 +1,74 @@ -use actix_web::{Responder, Result}; use actix_web::error::ErrorBadRequest; -use serde_derive::Serialize; -use actix_web::web; use actix_web::web::Json; use actix_web::Error; +use actix_web::Result; +use serde_derive::Serialize; #[derive(Serialize)] pub(crate) struct JsonResponse { - // pub(crate) status: String, - pub(crate) message: String, - // pub(crate) code: u32, - pub(crate) id: Option, - pub(crate) item: Option, - pub(crate) list: Option> + message: String, + id: Option, + item: Option, + list: Option>, } - #[derive(Serialize, Default)] pub struct JsonResponseBuilder - where T: serde::Serialize + Default +where + T: serde::Serialize + Default, { id: Option, item: Option, - list: Option> + list: Option>, } impl JsonResponseBuilder - where T: serde::Serialize + Default +where + T: serde::Serialize + Default, { - pub(crate) fn new() -> Self { - Self::default() - } - - pub(crate) fn set_item(mut self, item:T) -> Self { + pub(crate) fn set_item(mut self, item: T) -> Self { self.item = Some(item); self } - pub(crate) fn set_id(mut self, id:i32) -> Self { + pub(crate) fn set_id(mut self, id: i32) -> Self { self.id = Some(id); self } - pub(crate) fn set_list(mut self, list:Vec) -> Self { + pub(crate) fn set_list(mut self, list: Vec) -> Self { self.list = Some(list); self } - pub(crate) fn ok(self, msg: String) -> Result>, Error> { - - Ok(Json( - JsonResponse { - message: msg, - id: self.id, - item: self.item, - list: self.list, - } - )) - + pub(crate) fn ok(self, msg: String) -> Result>, Error> { + Ok(Json(JsonResponse { + message: msg, + id: self.id, + item: self.item, + list: self.list, + })) } - pub(crate) fn err(self, msg: String) -> Result>, Error> { - + pub(crate) fn err(self, msg: String) -> Result>, Error> { let json_response = JsonResponse { message: msg, id: self.id, item: self.item, - list: self.list + list: self.list, }; Err(ErrorBadRequest( - serde_json::to_string(&json_response).unwrap())) - } -} - -impl From for JsonResponseBuilder - where T: serde::Serialize + Default { - fn from(value: T) -> Self { - JsonResponseBuilder::default().set_item(value) - } -} - -impl From> for JsonResponseBuilder - where T: serde::Serialize + Default { - fn from(value: Vec) -> Self { - JsonResponseBuilder::default().set_list(value) + serde_json::to_string(&json_response).unwrap(), + )) } } impl JsonResponse -where T: serde::Serialize + Default +where + T: serde::Serialize + Default, { - pub fn build() -> JsonResponseBuilder - { + pub fn build() -> JsonResponseBuilder { JsonResponseBuilder::default() } - pub(crate) fn new(status: String, - message: String, - code: u32, - id: Option, - item:Option, - list: Option>) -> Self { - tracing::debug!("Executed.."); - JsonResponse { - message, - id, - item, - list, - } - } - - pub(crate) fn ok(id: i32, message: &str) -> JsonResponse { - - let msg = if !message.trim().is_empty() { - message.to_string() - } - else{ - String::from("Success") - }; - - JsonResponse { - message: msg, - id: Some(id), - item: None, - list: None, - } - } - - pub(crate) fn not_found() -> Self { - JsonResponse { - id: None, - item: None, - message: format!("Object not found"), - list: None, - } - } - - pub(crate) fn internal_error(message: &str) -> Self { - - let msg = if !message.trim().is_empty() { - message.to_string() - } - else{ - String::from("Internal error") - }; - JsonResponse { - id: None, - item: None, - message: msg, - list: None, - } - } - - pub(crate) fn not_valid(message: &str) -> Self { - - let msg = if !message.trim().is_empty() { - message.to_string() - } - else{ - String::from("Validation error") - }; - JsonResponse { - id: None, - item: None, - message: msg, - list: None, - } - } -} \ No newline at end of file +} From 81d8f6fe1c9b1608c868af9c05661f2d2f9e70e8 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 17:41:15 +0200 Subject: [PATCH 67/73] issue-auth removed ClientAddResponse --- src/helpers/json.rs | 32 +++++++++++++++++++++----------- src/routes/client/add.rs | 33 ++++++++------------------------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/helpers/json.rs b/src/helpers/json.rs index 2c5e0b0..80a21b3 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -1,4 +1,4 @@ -use actix_web::error::ErrorBadRequest; +use actix_web::error::{ErrorBadRequest, ErrorInternalServerError}; use actix_web::web::Json; use actix_web::Error; use actix_web::Result; @@ -41,27 +41,37 @@ where self } - pub(crate) fn ok(self, msg: String) -> Result>, Error> { - Ok(Json(JsonResponse { + fn to_json_response(self, msg: String) -> JsonResponse { + JsonResponse { message: msg, id: self.id, item: self.item, list: self.list, - })) + } } - pub(crate) fn err(self, msg: String) -> Result>, Error> { - let json_response = JsonResponse { - message: msg, - id: self.id, - item: self.item, - list: self.list, - }; + pub(crate) fn ok>(self, msg: I) -> Result>, Error> { + Ok(Json(self.to_json_response(msg.into()))) + } + + pub(crate) fn err>(self, msg: I) -> Result>, Error> { + let json_response = self.to_json_response(msg.into()); Err(ErrorBadRequest( serde_json::to_string(&json_response).unwrap(), )) } + + pub(crate) fn err_internal_server_error>( + self, + msg: I, + ) -> Result>, Error> { + let json_response = self.to_json_response(msg.into()); + + Err(ErrorInternalServerError( + serde_json::to_string(&json_response).unwrap(), + )) + } } impl JsonResponse diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs index 6803cbb..8e3e51f 100644 --- a/src/routes/client/add.rs +++ b/src/routes/client/add.rs @@ -1,21 +1,14 @@ use crate::configuration::Settings; use crate::helpers::client; +use crate::helpers::JsonResponse; use crate::models::user::User; use crate::models::Client; -use actix_web::{error::ErrorInternalServerError, post, web, Responder, Result}; -use serde::Serialize; +use actix_web::error::ErrorInternalServerError; +use actix_web::{post, web, Responder, Result}; use sqlx::PgPool; use std::sync::Arc; use tracing::Instrument; -#[derive(Serialize)] -struct ClientAddResponse { - status: String, - message: String, - code: u32, - client: Option, -} - #[tracing::instrument(name = "Add client.")] #[post("")] pub async fn add_handler( @@ -46,17 +39,12 @@ pub async fn add_handler( client_count ); - return Ok(web::Json(ClientAddResponse { - status: "error".to_string(), - code: 400, - message: "Too many clients already created".to_string(), - client: None, - })); + return JsonResponse::build().err("Too many clients already created"); } } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - return Err(ErrorInternalServerError("")); + return JsonResponse::build().err_internal_server_error(""); } }; @@ -66,7 +54,7 @@ pub async fn add_handler( client.secret = client::generate_secret(pool.get_ref(), 255) .await .map(|s| Some(s)) - .map_err(|s| ErrorInternalServerError(s))?; + .map_err(|s| ErrorInternalServerError(s))?; //todo move to helpers::JsonResponse let query_span = tracing::info_span!("Saving new client into the database"); match sqlx::query!( @@ -85,16 +73,11 @@ pub async fn add_handler( Ok(result) => { tracing::info!("New client {} have been saved to database", result.id); client.id = result.id; - Ok(web::Json(ClientAddResponse { - status: "success".to_string(), - message: "".to_string(), - code: 200, - client: Some(client), - })) + JsonResponse::build().set_item(client).ok("success") } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - return Err(ErrorInternalServerError("")); + JsonResponse::build().err_internal_server_error("") } } } From 6c9df49630068f50ed092b114ee0a4f4cac9ba43 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 17:50:46 +0200 Subject: [PATCH 68/73] issue-auth - DisableResponse --- src/routes/client/disable.rs | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/routes/client/disable.rs b/src/routes/client/disable.rs index 2e7f646..383130b 100644 --- a/src/routes/client/disable.rs +++ b/src/routes/client/disable.rs @@ -1,23 +1,12 @@ use crate::configuration::Settings; +use crate::helpers::JsonResponse; use crate::models::user::User; use crate::models::Client; -use actix_web::{ - error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, - put, web, Responder, Result, -}; -use serde::Serialize; +use actix_web::{error::ErrorInternalServerError, put, web, Responder, Result}; use sqlx::PgPool; use std::sync::Arc; use tracing::Instrument; -#[derive(Serialize)] -struct ClientDisableResponse { - status: String, - message: String, - code: u32, - client: Option, -} - #[tracing::instrument(name = "Disable client.")] #[put("/{id}/disable")] pub async fn disable_handler( @@ -43,14 +32,14 @@ pub async fn disable_handler( .await { Ok(client) if client.secret.is_some() => Ok(client), - Ok(_client) => Err(ErrorForbidden("client is not active")), - Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), + Ok(_client) => Err("client is not active"), + Err(sqlx::Error::RowNotFound) => Err("client not found"), Err(e) => { tracing::error!("Failed to execute fetch query: {:?}", e); - - Err(ErrorInternalServerError("")) + Err("") } - }?; + } + .map_err(|s| ErrorInternalServerError(s))?; //todo client.secret = None; let query_span = tracing::info_span!("Updating client into the database"); @@ -69,16 +58,11 @@ pub async fn disable_handler( { Ok(_) => { tracing::info!("Client {} have been saved to database", client.id); - Ok(web::Json(ClientDisableResponse { - status: "success".to_string(), - message: "".to_string(), - code: 200, - client: Some(client), - })) + JsonResponse::build().set_item(client).ok("success") } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - return Err(ErrorInternalServerError("")); + JsonResponse::build().err("") } } } From d3fba4b16abf7883824f1d6a853e75bd6e575ef5 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 17:56:03 +0200 Subject: [PATCH 69/73] issue-auth enable response --- src/routes/client/enable.rs | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/routes/client/enable.rs b/src/routes/client/enable.rs index 1c17dc4..a870c65 100644 --- a/src/routes/client/enable.rs +++ b/src/routes/client/enable.rs @@ -1,24 +1,13 @@ use crate::configuration::Settings; use crate::helpers::client; +use crate::helpers::JsonResponse; use crate::models::user::User; use crate::models::Client; -use actix_web::{ - error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, - put, web, Responder, Result, -}; -use serde::Serialize; +use actix_web::{error::ErrorBadRequest, put, web, Responder, Result}; use sqlx::PgPool; use std::sync::Arc; use tracing::Instrument; -#[derive(Serialize)] -struct ClientEnableResponse { - status: String, - message: String, - code: u32, - client: Option, -} - #[tracing::instrument(name = "Enable client.")] #[put("/{id}/enable")] pub async fn enable_handler( @@ -44,19 +33,20 @@ pub async fn enable_handler( .await { Ok(client) if client.secret.is_none() => Ok(client), - Ok(_client) => Err(ErrorForbidden("client is already enabled")), - Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), + Ok(_client) => Err("client is already enabled"), + Err(sqlx::Error::RowNotFound) => Err("the client is not found"), Err(e) => { tracing::error!("Failed to execute fetch query: {:?}", e); - Err(ErrorInternalServerError("")) + Err("") } - }?; + } + .map_err(|s| ErrorBadRequest(s))?; //todo client.secret = client::generate_secret(pool.get_ref(), 255) .await .map(|s| Some(s)) - .map_err(|s| ErrorInternalServerError(s))?; + .map_err(|s| ErrorBadRequest(s))?; let query_span = tracing::info_span!("Updating client into the database"); match sqlx::query!( @@ -75,16 +65,11 @@ pub async fn enable_handler( { Ok(_) => { tracing::info!("Client {} have been saved to database", client.id); - Ok(web::Json(ClientEnableResponse { - status: "success".to_string(), - message: "".to_string(), - code: 200, - client: Some(client), - })) + JsonResponse::build().set_item(client).ok("success") } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - return Err(ErrorInternalServerError("")); + JsonResponse::build().err_internal_server_error("") } } } From 62ee7bfb0eb6bf5775b135f3afdf0bc6de3d9ac0 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 17:59:54 +0200 Subject: [PATCH 70/73] issue-auth update response --- src/routes/client/update.rs | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs index 5702a25..f50b4ed 100644 --- a/src/routes/client/update.rs +++ b/src/routes/client/update.rs @@ -1,24 +1,12 @@ -use crate::configuration::Settings; use crate::helpers::client; use crate::models::user::User; use crate::models::Client; -use actix_web::{ - error::{ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, - put, web, Responder, Result, -}; -use serde::Serialize; +use crate::{configuration::Settings, helpers::JsonResponse}; +use actix_web::{error::ErrorBadRequest, put, web, Responder, Result}; use sqlx::PgPool; use std::sync::Arc; use tracing::Instrument; -#[derive(Serialize)] -struct ClientUpdateResponse { - status: String, - message: String, - code: u32, - client: Option, -} - #[tracing::instrument(name = "Update client.")] #[put("/{id}")] pub async fn update_handler( @@ -44,19 +32,20 @@ pub async fn update_handler( .await { Ok(client) if client.secret.is_some() => Ok(client), - Ok(_client) => Err(ErrorForbidden("client is not active")), - Err(sqlx::Error::RowNotFound) => Err(ErrorNotFound("the client is not found")), + Ok(_client) => Err("client is not active"), + Err(sqlx::Error::RowNotFound) => Err("the client is not found"), Err(e) => { tracing::error!("Failed to execute fetch query: {:?}", e); - Err(ErrorInternalServerError("")) + Err("") } - }?; + } + .map_err(|s| ErrorBadRequest(s))?; //todo client.secret = client::generate_secret(pool.get_ref(), 255) .await .map(|s| Some(s)) - .map_err(|s| ErrorInternalServerError(s))?; + .map_err(|s| ErrorBadRequest(s))?; //todo let query_span = tracing::info_span!("Updating client into the database"); match sqlx::query!( @@ -75,16 +64,11 @@ pub async fn update_handler( { Ok(_) => { tracing::info!("Client {} have been saved to database", client.id); - Ok(web::Json(ClientUpdateResponse { - status: "success".to_string(), - message: "".to_string(), - code: 200, - client: Some(client), - })) + JsonResponse::build().set_item(client).ok("success") } Err(e) => { tracing::error!("Failed to execute query: {:?}", e); - return Err(ErrorInternalServerError("")); + JsonResponse::build().err_internal_server_error("") } } } From df25e63a127ccad5232f7516e8a1580bfe4667e9 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 18:03:47 +0200 Subject: [PATCH 71/73] issue-auth rating add --- src/routes/rating/add.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs index fda8248..ba23a9d 100644 --- a/src/routes/rating/add.rs +++ b/src/routes/rating/add.rs @@ -1,5 +1,5 @@ use crate::forms; -use crate::helpers::{JsonResponse, JsonResponseBuilder}; +use crate::helpers::JsonResponse; use crate::models; use crate::models::user::User; use crate::models::RateCategory; @@ -60,7 +60,7 @@ pub async fn add_handler( form.category ); - return JsonResponse::::build() + return JsonResponse::build() .set_id(record.id) .ok(format!("Already Rated")); } From a42d40a9c05b703c68ba742244d5e4b5b3532276 Mon Sep 17 00:00:00 2001 From: Petru Date: Mon, 20 Nov 2023 18:08:37 +0200 Subject: [PATCH 72/73] issue-auth rating/get.rs --- src/routes/rating/get.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs index 01e847f..1603287 100644 --- a/src/routes/rating/get.rs +++ b/src/routes/rating/get.rs @@ -1,16 +1,14 @@ +use crate::helpers::JsonResponse; use crate::models; use actix_web::{get, web, Responder, Result}; -use serde_derive::Serialize; use sqlx::PgPool; use tracing::Instrument; -use crate::helpers::JsonResponse; // workflow // add, update, list, get(user_id), ACL, // ACL - access to func for a user // ACL - access to objects for a user - #[tracing::instrument(name = "Get rating.")] #[get("/{id}")] pub async fn get_handler( @@ -30,14 +28,14 @@ pub async fn get_handler( { Ok(rating) => { tracing::info!("rating found: {:?}", rating.id); - return JsonResponse::build().set_item(Some(rating)).ok("OK".to_string()); + return JsonResponse::build().set_item(Some(rating)).ok("OK"); } Err(sqlx::Error::RowNotFound) => { - return JsonResponse::build().err("Not Found".to_string()); + return JsonResponse::build().err("Not Found"); } Err(e) => { tracing::error!("Failed to fetch rating, error: {:?}", e); - return JsonResponse::build().err("Internal Server Error".to_string()); + return JsonResponse::build().err("Internal Server Error"); } } } @@ -57,11 +55,11 @@ pub async fn default(path: web::Path<()>, pool: web::Data) -> Result { - return JsonResponse::build().err("Not Found".to_string()); + return JsonResponse::build().err("Not Found"); } Err(e) => { tracing::error!("Failed to fetch rating, error: {:?}", e); - return JsonResponse::build().err("Internal Server Error".to_string()); + return JsonResponse::build().err("Internal Server Error"); } } } From 2a51b7bda23f333b6ed702d5f69101fb69099a0a Mon Sep 17 00:00:00 2001 From: Petru Date: Tue, 21 Nov 2023 17:59:03 +0200 Subject: [PATCH 73/73] issue-auth debug for models::client --- src/models/client.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/models/client.rs b/src/models/client.rs index 1ff7b7c..afef0c1 100644 --- a/src/models/client.rs +++ b/src/models/client.rs @@ -1,8 +1,18 @@ use serde::Serialize; -#[derive(Default, Serialize, Debug)] +#[derive(Default, Serialize)] pub struct Client { pub id: i32, pub user_id: String, pub secret: Option, } + +impl std::fmt::Debug for Client { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Client {{id: {:?}, user_id: {:?}}}", + self.id, self.user_id + ) + } +}