From 874162b8272890cbc378379cfd06fcefd6ff045a Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Tue, 26 Dec 2023 18:21:47 +0200
Subject: [PATCH 01/25] issue-auth stack/deploy

---
 src/routes/stack/deploy.rs | 117 +++++++++++++++----------------------
 1 file changed, 47 insertions(+), 70 deletions(-)

diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 1d52caa7..3b0bc018 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -1,9 +1,9 @@
 use crate::configuration::Settings;
+use crate::db;
 use crate::forms::StackPayload;
 use crate::helpers::stack::builder::DcBuilder;
 use crate::helpers::JsonResponse;
-use crate::models::stack::Stack;
-use crate::models::user::User;
+use crate::models;
 use actix_web::{post, web, web::Data, Responder, Result};
 use futures_lite::stream::StreamExt;
 use lapin::{
@@ -12,89 +12,66 @@ use lapin::{
 use sqlx::PgPool;
 use std::sync::Arc;
 
-
 #[tracing::instrument(name = "Deploy for every user. Admin endpoint")]
 #[post("/{id}/deploy")]
 pub async fn add(
-    user: web::ReqData<Arc<User>>,
+    user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
     pool: Data<PgPool>,
     sets: Data<Settings>,
 ) -> Result<impl Responder> {
     let id = path.0;
-    tracing::debug!("Received id: {}", id);
-
-    let stack = match sqlx::query_as!(
-        Stack,
-        r#"
-        SELECT * FROM user_stack WHERE id=$1 LIMIT 1
-        "#,
-        id
-    )
-    .fetch_one(pool.get_ref())
-    .await
-    {
-        Ok(stack) => {
-            tracing::info!("Stack found: {:?}", stack.id,);
-            Some(stack)
-        }
-        Err(sqlx::Error::RowNotFound) => {
-            tracing::error!("Row not found 404");
-            None
-        }
-        Err(e) => {
-            tracing::error!("Failed to fetch stack, error: {:?}", e);
-            None
-        }
-    };
+    let stack = db::stack::fetch(pool.get_ref(), id)
+        .await
+        .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
+        .and_then(|stack| match stack {
+            Some(stack) => Ok(stack),
+            None => Err(JsonResponse::<models::Stack>::build().not_found("not found")),
+        })?;
 
-    return match stack {
-        Some(stack) => {
-            let id = stack.id.clone();
-            let dc = DcBuilder::new(stack);
-            dc.build();
+    let id = stack.id.clone();
+    let dc = DcBuilder::new(stack);
+    dc.build().ok_or_else(|| {
+        tracing::error!("Error. Compose builder returned an empty string");
+        JsonResponse::<models::Stack>::build().internal_server_error("troubles at building")
+    })?;
 
-            let addr = sets.amqp.connection_string();
-            let routing_key = "install.start.tfa.all.all".to_string();
-            tracing::debug!("Sending message to {:?}", routing_key);
+    let addr = sets.amqp.connection_string();
+    let routing_key = "install.start.tfa.all.all".to_string();
+    tracing::debug!("Sending message to {:?}", routing_key);
 
-            let conn = Connection::connect(&addr, ConnectionProperties::default())
-                .await
-                .expect("Could not connect RabbitMQ");
+    let conn = Connection::connect(&addr, ConnectionProperties::default())
+        .await
+        .expect("Could not connect RabbitMQ");
 
-            tracing::info!("RABBITMQ CONNECTED");
+    tracing::info!("RABBITMQ CONNECTED");
 
-            let channel = conn.create_channel().await.unwrap();
-            let mut stack_data =
-                serde_json::from_value::<StackPayload>(dc.stack.body.clone()).unwrap();
+    let channel = conn.create_channel().await.unwrap();
+    let mut stack_data = serde_json::from_value::<StackPayload>(dc.stack.body.clone()).unwrap();
 
-            stack_data.id = Some(id);
-            stack_data.user_token = Some(user.id.clone());
-            stack_data.user_email = Some(user.email.clone());
-            stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
+    stack_data.id = Some(id);
+    stack_data.user_token = Some(user.id.clone());
+    stack_data.user_email = Some(user.email.clone());
+    stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
 
-            let payload = serde_json::to_string::<StackPayload>(&stack_data).unwrap();
-            let _payload = payload.as_bytes();
+    let payload = serde_json::to_string::<StackPayload>(&stack_data).unwrap();
+    let _payload = payload.as_bytes();
 
-            let confirm = channel
-                .basic_publish(
-                    "install",
-                    routing_key.as_str(),
-                    BasicPublishOptions::default(),
-                    _payload,
-                    BasicProperties::default(),
-                )
-                .await
-                .unwrap()
-                .await
-                .unwrap();
+    let confirm = channel
+        .basic_publish(
+            "install",
+            routing_key.as_str(),
+            BasicPublishOptions::default(),
+            _payload,
+            BasicProperties::default(),
+        )
+        .await
+        .unwrap()
+        .await
+        .unwrap();
 
-            assert_eq!(confirm, Confirmation::NotRequested);
-            tracing::debug!("Message sent to rabbitmq");
-            return Ok(JsonResponse::<Stack>::build().set_id(id).ok("Success"));
-        }
-        None => {
-            Err(JsonResponse::<Stack>::build().internal_server_error("Deployment failed"))
-        }
-    }
+    assert_eq!(confirm, Confirmation::NotRequested);
+    return Ok(JsonResponse::<models::Stack>::build()
+        .set_id(id)
+        .ok("Success"));
 }

From 7ae0f1f13656cd9f5286f827d3cdef371f707754 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Tue, 26 Dec 2023 18:36:32 +0200
Subject: [PATCH 02/25] issue-auth stack/deploy

---
 src/routes/stack/deploy.rs | 37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 3b0bc018..928d6efe 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -42,13 +42,24 @@ pub async fn add(
 
     let conn = Connection::connect(&addr, ConnectionProperties::default())
         .await
-        .expect("Could not connect RabbitMQ");
-
-    tracing::info!("RABBITMQ CONNECTED");
+        .map_err(|err| {
+            JsonResponse::<models::Stack>::build()
+                .internal_server_error("Could not connect RabbitMQ")
+        })?;
 
-    let channel = conn.create_channel().await.unwrap();
-    let mut stack_data = serde_json::from_value::<StackPayload>(dc.stack.body.clone()).unwrap();
+    let channel = conn.create_channel().await.map_err(|err| {
+        JsonResponse::<models::Stack>::build()
+            .internal_server_error("Can't create rabbitMQ channel")
+    })?;
+    let mut stack_data = serde_json::from_value::<StackPayload>(dc.stack.body.clone())
+        .map_err(|err| JsonResponse::<models::Stack>::build().bad_request("can't deserialize"))?; //todo
+                                                                                                  //add
+                                                                                                  //json
+                                                                                                  //error
+                                                                                                  //path
 
+    Ok(JsonResponse::<models::Stack>::build().ok("bdc"))
+    /*
     stack_data.id = Some(id);
     stack_data.user_token = Some(user.id.clone());
     stack_data.user_email = Some(user.email.clone());
@@ -66,12 +77,16 @@ pub async fn add(
             BasicProperties::default(),
         )
         .await
-        .unwrap()
+        .unwrap() //todo
         .await
-        .unwrap();
-
-    assert_eq!(confirm, Confirmation::NotRequested);
-    return Ok(JsonResponse::<models::Stack>::build()
+        .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
+        .and_then(|confirm| {
+            match confirm {
+                Confirmation::NotRequested => Err(JsonResponse::<models::Stack>::build().bad_request("confirmation is NotRequested")),
+                _ => Ok(JsonResponse::<models::Stack>::build()
         .set_id(id)
-        .ok("Success"));
+        .ok("Success"))
+            }
+        })
+    */
 }

From 36a88207c8ed97afc9ba2fb1e5d46a61129a4e5e Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Wed, 27 Dec 2023 17:29:47 +0200
Subject: [PATCH 03/25] issue-auth src/routes/stack/deploy.rs

---
 src/routes/stack/deploy.rs | 39 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 20 deletions(-)

diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 928d6efe..03c52649 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -52,41 +52,40 @@ pub async fn add(
             .internal_server_error("Can't create rabbitMQ channel")
     })?;
     let mut stack_data = serde_json::from_value::<StackPayload>(dc.stack.body.clone())
-        .map_err(|err| JsonResponse::<models::Stack>::build().bad_request("can't deserialize"))?; //todo
-                                                                                                  //add
-                                                                                                  //json
-                                                                                                  //error
-                                                                                                  //path
+        .map_err(|err| JsonResponse::<models::Stack>::build().bad_request("can't deserialize"))?;
 
-    Ok(JsonResponse::<models::Stack>::build().ok("bdc"))
-    /*
     stack_data.id = Some(id);
     stack_data.user_token = Some(user.id.clone());
     stack_data.user_email = Some(user.email.clone());
     stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
 
-    let payload = serde_json::to_string::<StackPayload>(&stack_data).unwrap();
-    let _payload = payload.as_bytes();
+    let payload = serde_json::to_string::<StackPayload>(&stack_data).map_err(|err| {
+        JsonResponse::<models::Stack>::build().internal_server_error(format!("{}", err))
+    })?;
 
-    let confirm = channel
+    channel
         .basic_publish(
             "install",
             routing_key.as_str(),
             BasicPublishOptions::default(),
-            _payload,
+            payload.as_bytes(),
             BasicProperties::default(),
         )
         .await
-        .unwrap() //todo
+        .map_err(|_| {
+            JsonResponse::<models::Stack>::build().internal_server_error("internal server error")
+        })? //todo the correct err
         .await
-        .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
-        .and_then(|confirm| {
-            match confirm {
-                Confirmation::NotRequested => Err(JsonResponse::<models::Stack>::build().bad_request("confirmation is NotRequested")),
-                _ => Ok(JsonResponse::<models::Stack>::build()
-        .set_id(id)
-        .ok("Success"))
+        .map_err(|_| {
+            JsonResponse::<models::Stack>::build().internal_server_error("internal server error")
+        })
+        .and_then(|confirm| match confirm {
+            Confirmation::NotRequested => {
+                Err(JsonResponse::<models::Stack>::build()
+                    .bad_request("confirmation is NotRequested"))
             }
+            _ => Ok(JsonResponse::<models::Stack>::build()
+                .set_id(id)
+                .ok("Success")),
         })
-    */
 }

From d38f220031e279000f61c8543d09b0a3e6dc1909 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Wed, 27 Dec 2023 17:41:24 +0200
Subject: [PATCH 04/25] issue-auth src/routes/stack/deploy.rs

---
 src/routes/stack/deploy.rs | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 03c52649..36fbc281 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -33,7 +33,7 @@ pub async fn add(
     let dc = DcBuilder::new(stack);
     dc.build().ok_or_else(|| {
         tracing::error!("Error. Compose builder returned an empty string");
-        JsonResponse::<models::Stack>::build().internal_server_error("troubles at building")
+        JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
 
     let addr = sets.amqp.connection_string();
@@ -43,16 +43,19 @@ pub async fn add(
     let conn = Connection::connect(&addr, ConnectionProperties::default())
         .await
         .map_err(|err| {
-            JsonResponse::<models::Stack>::build()
-                .internal_server_error("Could not connect RabbitMQ")
+            tracing::error!("connecting to RabbitMQ {:?}", err);
+            JsonResponse::<models::Stack>::build().internal_server_error("")
         })?;
 
     let channel = conn.create_channel().await.map_err(|err| {
-        JsonResponse::<models::Stack>::build()
-            .internal_server_error("Can't create rabbitMQ channel")
+        tracing::error!("creating RabbitMQ channel {:?}", err);
+        JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
-    let mut stack_data = serde_json::from_value::<StackPayload>(dc.stack.body.clone())
-        .map_err(|err| JsonResponse::<models::Stack>::build().bad_request("can't deserialize"))?;
+    let mut stack_data =
+        serde_json::from_value::<StackPayload>(dc.stack.body.clone()).map_err(|err| {
+            tracing::error!("transforming json Value into StackPayload {:?}", err);
+            JsonResponse::<models::Stack>::build().bad_request("")
+        })?;
 
     stack_data.id = Some(id);
     stack_data.user_token = Some(user.id.clone());
@@ -60,7 +63,8 @@ pub async fn add(
     stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
 
     let payload = serde_json::to_string::<StackPayload>(&stack_data).map_err(|err| {
-        JsonResponse::<models::Stack>::build().internal_server_error(format!("{}", err))
+        tracing::error!("serializing StackPayload {:?}", err);
+        JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
 
     channel
@@ -72,12 +76,14 @@ pub async fn add(
             BasicProperties::default(),
         )
         .await
-        .map_err(|_| {
-            JsonResponse::<models::Stack>::build().internal_server_error("internal server error")
-        })? //todo the correct err
+        .map_err(|err| {
+            tracing::error!("publishing the message {:?}", err);
+            JsonResponse::<models::Stack>::build().internal_server_error("")
+        })?
         .await
-        .map_err(|_| {
-            JsonResponse::<models::Stack>::build().internal_server_error("internal server error")
+        .map_err(|err| {
+            tracing::error!("confirming the publication {:?}", err);
+            JsonResponse::<models::Stack>::build().internal_server_error("")
         })
         .and_then(|confirm| match confirm {
             Confirmation::NotRequested => {

From 1852cb623a5d7222a4f269e31cec7544cdfb65cd Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Wed, 27 Dec 2023 17:47:36 +0200
Subject: [PATCH 05/25] issue-auth channel

---
 src/routes/stack/deploy.rs | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 36fbc281..9432f552 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -47,10 +47,6 @@ pub async fn add(
             JsonResponse::<models::Stack>::build().internal_server_error("")
         })?;
 
-    let channel = conn.create_channel().await.map_err(|err| {
-        tracing::error!("creating RabbitMQ channel {:?}", err);
-        JsonResponse::<models::Stack>::build().internal_server_error("")
-    })?;
     let mut stack_data =
         serde_json::from_value::<StackPayload>(dc.stack.body.clone()).map_err(|err| {
             tracing::error!("transforming json Value into StackPayload {:?}", err);
@@ -67,7 +63,12 @@ pub async fn add(
         JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
 
-    channel
+    conn.create_channel()
+        .await
+        .map_err(|err| {
+            tracing::error!("creating RabbitMQ channel {:?}", err);
+            JsonResponse::<models::Stack>::build().internal_server_error("")
+        })?
         .basic_publish(
             "install",
             routing_key.as_str(),

From 3383358c5afcf024f7e1217adf1113ef7864a9fe Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Wed, 27 Dec 2023 17:51:17 +0200
Subject: [PATCH 06/25] issue-auth RabbitMQ logic

---
 src/routes/stack/deploy.rs | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 9432f552..c92aa70a 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -36,17 +36,6 @@ pub async fn add(
         JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
 
-    let addr = sets.amqp.connection_string();
-    let routing_key = "install.start.tfa.all.all".to_string();
-    tracing::debug!("Sending message to {:?}", routing_key);
-
-    let conn = Connection::connect(&addr, ConnectionProperties::default())
-        .await
-        .map_err(|err| {
-            tracing::error!("connecting to RabbitMQ {:?}", err);
-            JsonResponse::<models::Stack>::build().internal_server_error("")
-        })?;
-
     let mut stack_data =
         serde_json::from_value::<StackPayload>(dc.stack.body.clone()).map_err(|err| {
             tracing::error!("transforming json Value into StackPayload {:?}", err);
@@ -63,7 +52,17 @@ pub async fn add(
         JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
 
-    conn.create_channel()
+    let addr = sets.amqp.connection_string();
+    let routing_key = "install.start.tfa.all.all".to_string();
+    tracing::debug!("Sending message to {:?}", routing_key);
+
+    Connection::connect(&addr, ConnectionProperties::default())
+        .await
+        .map_err(|err| {
+            tracing::error!("connecting to RabbitMQ {:?}", err);
+            JsonResponse::<models::Stack>::build().internal_server_error("")
+        })?
+        .create_channel()
         .await
         .map_err(|err| {
             tracing::error!("creating RabbitMQ channel {:?}", err);

From 055988c936b9c76d127751461f0f4fdb2a2c0dbf Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 09:33:15 +0200
Subject: [PATCH 07/25] issue-auth pg_pool

---
 src/routes/client/add.rs     | 22 +++++++++++-----------
 src/routes/client/disable.rs | 32 ++++++++++++++++----------------
 src/routes/client/enable.rs  | 15 +++++++--------
 src/routes/client/update.rs  | 20 ++++++++++++--------
 src/routes/rating/get.rs     | 13 ++++++++-----
 src/routes/stack/add.rs      | 10 +++++-----
 src/routes/stack/compose.rs  |  8 ++++----
 src/routes/stack/deploy.rs   |  4 ++--
 src/routes/stack/get.rs      |  8 ++++----
 src/routes/stack/update.rs   | 34 +++++++++++++---------------------
 10 files changed, 82 insertions(+), 84 deletions(-)

diff --git a/src/routes/client/add.rs b/src/routes/client/add.rs
index b3d90d94..0b526d10 100644
--- a/src/routes/client/add.rs
+++ b/src/routes/client/add.rs
@@ -1,21 +1,21 @@
 use crate::configuration::Settings;
+use crate::db;
 use crate::helpers::client;
 use crate::helpers::JsonResponse;
 use crate::models;
-use crate::db;
 use actix_web::{post, web, Responder, Result};
 use sqlx::PgPool;
-use tracing::Instrument;
 use std::sync::Arc;
+use tracing::Instrument;
 
 #[tracing::instrument(name = "Add client.")]
 #[post("")]
 pub async fn add_handler(
     user: web::ReqData<Arc<models::User>>,
     settings: web::Data<Settings>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
 ) -> Result<impl Responder> {
-    add_handler_inner(&user.id, settings, pool)
+    add_handler_inner(&user.id, settings, pg_pool)
         .await
         .map(|client| JsonResponse::build().set_item(client).ok("Ok"))
         .map_err(|err| JsonResponse::<models::Client>::build().bad_request(err))
@@ -24,21 +24,21 @@ pub async fn add_handler(
 pub async fn add_handler_inner(
     user_id: &String,
     settings: web::Data<Settings>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
 ) -> Result<models::Client, String> {
-    let client_count = db::client::count_by_user(pool.get_ref(), user_id).await?;
+    let client_count = db::client::count_by_user(pg_pool.get_ref(), user_id).await?;
     if client_count >= settings.max_clients_number {
         return Err("Too many clients created".to_string());
     }
 
-    let client = create_client(pool.get_ref(), user_id).await?;
-    db::client::insert(pool.get_ref(), client).await
+    let client = create_client(pg_pool.get_ref(), user_id).await?;
+    db::client::insert(pg_pool.get_ref(), client).await
 }
 
-async fn create_client(pool: &PgPool, user_id: &String) -> Result<models::Client, String> {
-    let mut client = models::Client::default(); 
+async fn create_client(pg_pool: &PgPool, user_id: &String) -> Result<models::Client, String> {
+    let mut client = models::Client::default();
     client.user_id = user_id.clone();
-    client.secret = client::generate_secret(pool, 255)
+    client.secret = client::generate_secret(pg_pool, 255)
         .await
         .map(|s| Some(s))?;
 
diff --git a/src/routes/client/disable.rs b/src/routes/client/disable.rs
index 057a183f..70d2a1cb 100644
--- a/src/routes/client/disable.rs
+++ b/src/routes/client/disable.rs
@@ -1,33 +1,33 @@
 use crate::configuration::Settings;
+use crate::db;
 use crate::helpers::JsonResponse;
 use crate::models;
-use crate::db;
 use actix_web::{put, web, Responder, Result};
 use sqlx::PgPool;
-use tracing::Instrument;
 use std::sync::Arc;
+use tracing::Instrument;
 
 #[tracing::instrument(name = "Disable client.")]
 #[put("/{id}/disable")]
 pub async fn disable_handler(
     user: web::ReqData<Arc<models::User>>,
     settings: web::Data<Settings>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
     path: web::Path<(i32,)>,
 ) -> Result<impl Responder> {
-        let client_id = path.0;
-        let mut client = db::client::fetch(pool.get_ref(), client_id)
-            .await
-            .map_err(|msg| JsonResponse::<models::Client>::build().internal_server_error(msg))?
-            .ok_or_else(|| JsonResponse::<models::Client>::build().not_found("not found"))?; 
+    let client_id = path.0;
+    let mut client = db::client::fetch(pg_pool.get_ref(), client_id)
+        .await
+        .map_err(|msg| JsonResponse::<models::Client>::build().internal_server_error(msg))?
+        .ok_or_else(|| JsonResponse::<models::Client>::build().not_found("not found"))?;
 
-        if client.secret.is_none() {
-            return Err(JsonResponse::<models::Client>::build().bad_request("client is not active"));
-        }
+    if client.secret.is_none() {
+        return Err(JsonResponse::<models::Client>::build().bad_request("client is not active"));
+    }
 
-        client.secret = None;
-        db::client::update(pool.get_ref(), client)
-            .await
-            .map(|client| JsonResponse::build().set_item(client).ok("success"))
-            .map_err(|msg| JsonResponse::<models::Client>::build().bad_request(msg))
+    client.secret = None;
+    db::client::update(pg_pool.get_ref(), client)
+        .await
+        .map(|client| JsonResponse::build().set_item(client).ok("success"))
+        .map_err(|msg| JsonResponse::<models::Client>::build().bad_request(msg))
 }
diff --git a/src/routes/client/enable.rs b/src/routes/client/enable.rs
index 5669accc..c87fc445 100644
--- a/src/routes/client/enable.rs
+++ b/src/routes/client/enable.rs
@@ -1,38 +1,37 @@
 use crate::configuration::Settings;
+use crate::db;
 use crate::helpers::client;
 use crate::helpers::JsonResponse;
 use crate::models;
-use crate::db;
 use actix_web::{put, web, Responder, Result};
-use tracing::Instrument;
 use sqlx::PgPool;
 use std::sync::Arc;
+use tracing::Instrument;
 
 #[tracing::instrument(name = "Enable client.")]
 #[put("/{id}/enable")]
 pub async fn enable_handler(
     user: web::ReqData<Arc<models::User>>,
     settings: web::Data<Settings>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
     path: web::Path<(i32,)>,
 ) -> Result<impl Responder> {
     let client_id = path.0;
-    let mut client = db::client::fetch(pool.get_ref(), client_id)
+    let mut client = db::client::fetch(pg_pool.get_ref(), client_id)
         .await
         .map_err(|msg| JsonResponse::<models::Client>::build().internal_server_error(msg))?
-        .ok_or_else(|| JsonResponse::<models::Client>::build().not_found("not found"))?
-        ; 
+        .ok_or_else(|| JsonResponse::<models::Client>::build().not_found("not found"))?;
 
     if client.secret.is_some() {
         return Err(JsonResponse::<models::Client>::build().bad_request("client is already active"));
     }
 
-    client.secret = client::generate_secret(pool.get_ref(), 255)
+    client.secret = client::generate_secret(pg_pool.get_ref(), 255)
         .await
         .map(|secret| Some(secret))
         .map_err(|err| JsonResponse::<models::Client>::build().bad_request(err))?;
 
-    db::client::update(pool.get_ref(), client)
+    db::client::update(pg_pool.get_ref(), client)
         .await
         .map(|client| JsonResponse::build().set_item(client).ok("success"))
         .map_err(|err| JsonResponse::<models::Client>::build().bad_request(err))
diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs
index b1003162..a52af9b0 100644
--- a/src/routes/client/update.rs
+++ b/src/routes/client/update.rs
@@ -1,39 +1,43 @@
+use crate::db;
 use crate::helpers::client;
 use crate::models;
-use crate::db;
 use crate::{configuration::Settings, helpers::JsonResponse};
 use actix_web::{put, web, Responder, Result};
+use futures::TryFutureExt;
 use sqlx::PgPool;
 use std::sync::Arc;
 use tracing::Instrument;
-use futures::TryFutureExt;
 
 #[tracing::instrument(name = "Update client.")]
 #[put("/{id}")]
 pub async fn update_handler(
     user: web::ReqData<Arc<models::User>>,
     settings: web::Data<Settings>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
     path: web::Path<(i32,)>,
 ) -> Result<impl Responder> {
     let client_id = path.0;
-    let mut client = db::client::fetch(pool.get_ref(), client_id)
+    let mut client = db::client::fetch(pg_pool.get_ref(), client_id)
         .await
         .map_err(|msg| JsonResponse::<models::Client>::build().internal_server_error(msg))?
-        .ok_or_else(|| JsonResponse::<models::Client>::build().not_found("not found"))?; 
+        .ok_or_else(|| JsonResponse::<models::Client>::build().not_found("not found"))?;
 
     if client.secret.is_none() {
         return Err(JsonResponse::<models::Client>::build().bad_request("client is not active"));
     }
 
-    client.secret = client::generate_secret(pool.get_ref(), 255)
+    client.secret = client::generate_secret(pg_pool.get_ref(), 255)
         .await
         .map(|s| Some(s))
         .map_err(|msg| JsonResponse::<models::Client>::build().bad_request(msg))?;
 
-    db::client::update(pool.get_ref(), client)
+    db::client::update(pg_pool.get_ref(), client)
         .await
-        .map(|client| JsonResponse::<models::Client>::build().set_item(client).ok("success"))
+        .map(|client| {
+            JsonResponse::<models::Client>::build()
+                .set_item(client)
+                .ok("success")
+        })
         .map_err(|err| {
             tracing::error!("Failed to execute query: {:?}", err);
             JsonResponse::<models::Client>::build().internal_server_error("")
diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs
index 4bc0c1ff..9081960c 100644
--- a/src/routes/rating/get.rs
+++ b/src/routes/rating/get.rs
@@ -1,6 +1,6 @@
+use crate::db;
 use crate::helpers::JsonResponse;
 use crate::models;
-use crate::db;
 use actix_web::{get, web, Responder, Result};
 use sqlx::PgPool;
 use tracing::Instrument;
@@ -14,10 +14,10 @@ use tracing::Instrument;
 #[get("/{id}")]
 pub async fn get_handler(
     path: web::Path<(i32,)>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
 ) -> Result<impl Responder> {
     let rate_id = path.0;
-    let rating = db::rating::fetch(pool.get_ref(), rate_id)
+    let rating = db::rating::fetch(pg_pool.get_ref(), rate_id)
         .await
         .map_err(|_err| JsonResponse::<models::Rating>::build().internal_server_error(""))?
         .ok_or_else(|| JsonResponse::<models::Rating>::build().not_found("not found"))?;
@@ -27,8 +27,11 @@ pub async fn get_handler(
 
 #[tracing::instrument(name = "Get all ratings.")]
 #[get("")]
-pub async fn list_handler(path: web::Path<()>, pool: web::Data<PgPool>) -> Result<impl Responder> {
-    db::rating::fetch_all(pool.get_ref())
+pub async fn list_handler(
+    path: web::Path<()>,
+    pg_pool: web::Data<PgPool>,
+) -> Result<impl Responder> {
+    db::rating::fetch_all(pg_pool.get_ref())
         .await
         .map(|ratings| JsonResponse::build().set_list(ratings).ok("OK"))
         .map_err(|err| JsonResponse::<models::Rating>::build().internal_server_error(""))
diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs
index ef38ac5f..7ed5a09b 100644
--- a/src/routes/stack/add.rs
+++ b/src/routes/stack/add.rs
@@ -19,20 +19,20 @@ use std::sync::Arc;
 pub async fn add(
     body: Bytes,
     user: web::ReqData<Arc<models::User>>,
-    pool: Data<PgPool>,
+    pg_pool: Data<PgPool>,
 ) -> Result<impl Responder> {
     // @todo ACL
     let form = body_into_form(body).await?;
     let stack_name = form.custom.custom_stack_code.clone();
 
-    check_if_stack_exists(pool.get_ref(), &stack_name).await?;
+    check_if_stack_exists(pg_pool.get_ref(), &stack_name).await?;
 
     let body: Value = serde_json::to_value::<StackForm>(form)
         .or(serde_json::to_value::<StackForm>(StackForm::default()))
         .unwrap();
 
     let stack = models::Stack::new(user.id.clone(), stack_name, body);
-    db::stack::insert(pool.get_ref(), stack)
+    db::stack::insert(pg_pool.get_ref(), stack)
         .await
         .map(|stack| JsonResponse::build().set_item(stack).ok("Ok"))
         .map_err(|_| {
@@ -40,8 +40,8 @@ pub async fn add(
         })
 }
 
-async fn check_if_stack_exists(pool: &PgPool, stack_name: &String) -> Result<(), Error> {
-    db::stack::fetch_one_by_name(pool, stack_name)
+async fn check_if_stack_exists(pg_pool: &PgPool, stack_name: &String) -> Result<(), Error> {
+    db::stack::fetch_one_by_name(pg_pool, stack_name)
         .await
         .map_err(|_| {
             JsonResponse::<models::Stack>::build().internal_server_error("Internal Server Error")
diff --git a/src/routes/stack/compose.rs b/src/routes/stack/compose.rs
index 28290902..4e58141e 100644
--- a/src/routes/stack/compose.rs
+++ b/src/routes/stack/compose.rs
@@ -11,10 +11,10 @@ use std::sync::Arc;
 pub async fn add(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
-    pool: Data<PgPool>,
+    pg_pool: Data<PgPool>,
 ) -> Result<impl Responder> {
     let id = path.0;
-    let stack = db::stack::fetch(pool.get_ref(), id)
+    let stack = db::stack::fetch(pg_pool.get_ref(), id)
         .await
         .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
         .and_then(|stack| match stack {
@@ -39,11 +39,11 @@ pub async fn add(
 pub async fn admin(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
-    pool: Data<PgPool>,
+    pg_pool: Data<PgPool>,
 ) -> Result<impl Responder> {
     ///  Admin function for generating compose file for specified user
     let id = path.0;
-    let stack = db::stack::fetch(pool.get_ref(), id)
+    let stack = db::stack::fetch(pg_pool.get_ref(), id)
         .await
         .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
         .and_then(|stack| match stack {
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index c92aa70a..f18c99d6 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -17,11 +17,11 @@ use std::sync::Arc;
 pub async fn add(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
-    pool: Data<PgPool>,
+    pg_pool: Data<PgPool>,
     sets: Data<Settings>,
 ) -> Result<impl Responder> {
     let id = path.0;
-    let stack = db::stack::fetch(pool.get_ref(), id)
+    let stack = db::stack::fetch(pg_pool.get_ref(), id)
         .await
         .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
         .and_then(|stack| match stack {
diff --git a/src/routes/stack/get.rs b/src/routes/stack/get.rs
index ac21f342..1e9492a5 100644
--- a/src/routes/stack/get.rs
+++ b/src/routes/stack/get.rs
@@ -12,12 +12,12 @@ use tracing::Instrument;
 pub async fn item(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
 ) -> Result<impl Responder> {
     /// Get stack apps of logged user only
     let (id,) = path.into_inner();
 
-    let stack = db::stack::fetch(pool.get_ref(), id)
+    let stack = db::stack::fetch(pg_pool.get_ref(), id)
         .await
         .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
         .and_then(|stack| match stack {
@@ -36,14 +36,14 @@ pub async fn item(
 pub async fn list(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(String,)>,
-    pool: web::Data<PgPool>,
+    pg_pool: web::Data<PgPool>,
 ) -> Result<impl Responder> {
     /// This is admin endpoint, used by a m2m app, client app is confidential
     /// it should return stacks by user id
     /// in order to pass validation at external deployment service
     let user_id = path.into_inner().0;
 
-    db::stack::fetch_by_user(pool.get_ref(), &user_id)
+    db::stack::fetch_by_user(pg_pool.get_ref(), &user_id)
         .await
         .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
         .map(|stacks| JsonResponse::build().set_list(stacks).ok("OK"))
diff --git a/src/routes/stack/update.rs b/src/routes/stack/update.rs
index a418b532..229553f7 100644
--- a/src/routes/stack/update.rs
+++ b/src/routes/stack/update.rs
@@ -1,21 +1,16 @@
-use chrono::Utc;
-use actix_web::{
-    web,
-    web::{Data},
-    Responder, Result,
-};
 use crate::forms::stack::StackForm;
 use crate::helpers::JsonResponse;
+use crate::models;
 use crate::models::user::User;
 use actix_web::post;
+use actix_web::{web, web::Data, Responder, Result};
+use chrono::Utc;
 use serde_json::Value;
-use sqlx::PgPool;
 use serde_valid::Validate;
+use sqlx::PgPool;
+use std::sync::Arc;
 use tracing::Instrument;
 use uuid::Uuid;
-use crate::models;
-use std::sync::Arc;
-
 
 #[tracing::instrument(name = "Update stack.")]
 #[post("/{id}")]
@@ -23,18 +18,14 @@ pub async fn update(
     path: web::Path<(i32,)>,
     form: web::Json<StackForm>,
     user: web::ReqData<Arc<User>>,
-    pool: Data<PgPool>,
+    pg_pool: Data<PgPool>,
 ) -> Result<impl Responder> {
     // @todo ACL
 
     let (id,) = path.into_inner();
     let query_span = tracing::info_span!("Check existence by id.");
-    match sqlx::query_as!(
-        models::Stack,
-        r"SELECT * FROM user_stack WHERE id = $1",
-        id
-    )
-        .fetch_one(pool.get_ref())
+    match sqlx::query_as!(models::Stack, r"SELECT * FROM user_stack WHERE id = $1", id)
+        .fetch_one(pg_pool.get_ref())
         .instrument(query_span)
         .await
     {
@@ -60,7 +51,8 @@ pub async fn update(
     );
     let _request_span_guard = request_span.enter(); // ->exit
 
-    tracing::info!("request_id {} Updating '{}' '{}'",
+    tracing::info!(
+        "request_id {} Updating '{}' '{}'",
         request_id,
         form.custom.project_name,
         form.region
@@ -97,9 +89,9 @@ pub async fn update(
         Utc::now(),
         Utc::now(),
     )
-        .execute(pool.get_ref())
-        .instrument(query_span)
-        .await
+    .execute(pg_pool.get_ref())
+    .instrument(query_span)
+    .await
     {
         Ok(record) => {
             tracing::info!(

From ace2c26cfb382913459864d2f02f5a9133c86935 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 09:39:20 +0200
Subject: [PATCH 08/25] issue-auth db_pool renamed to pg_pool

---
 src/startup.rs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/startup.rs b/src/startup.rs
index 8b966eef..b592a4f3 100644
--- a/src/startup.rs
+++ b/src/startup.rs
@@ -12,17 +12,16 @@ use tracing_actix_web::TracingLogger;
 
 pub async fn run(
     listener: TcpListener,
-    db_pool: Pool<Postgres>,
+    pg_pool: Pool<Postgres>,
     settings: Settings,
 ) -> Result<Server, std::io::Error> {
     let settings = web::Data::new(settings);
-    let db_pool = web::Data::new(db_pool);
+    let pg_pool = web::Data::new(pg_pool);
 
     let server = HttpServer::new(move || {
         App::new()
             .wrap(TracingLogger::default())
-            .service(web::scope("/health_check")
-                .service(crate::routes::health_check))
+            .service(web::scope("/health_check").service(crate::routes::health_check))
             .service(
                 web::scope("/client")
                     .wrap(HttpAuthentication::bearer(
@@ -64,7 +63,7 @@ pub async fn run(
                     .service(crate::routes::stack::add::add)
                     .service(crate::routes::stack::update::update),
             )
-            .app_data(db_pool.clone())
+            .app_data(pg_pool.clone())
             .app_data(settings.clone())
     })
     .listen(listener)?

From 1d4fc9688f5b0c715b752f660075fa68c056ecb3 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 10:21:13 +0200
Subject: [PATCH 09/25] issue-auth helpers::MqPool

---
 Cargo.lock                 | 40 +++++++++++++++++++++++++++++++++++++-
 Cargo.toml                 |  1 +
 src/helpers/mod.rs         |  2 ++
 src/helpers/mq_pool.rs     | 27 +++++++++++++++++++++++++
 src/main.rs                |  4 ++--
 src/routes/stack/deploy.rs |  3 ++-
 src/startup.rs             |  5 +++++
 7 files changed, 78 insertions(+), 4 deletions(-)
 create mode 100644 src/helpers/mq_pool.rs

diff --git a/Cargo.lock b/Cargo.lock
index 73e0b28a..e7d155c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -975,11 +975,37 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "deadpool"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490"
+dependencies = [
+ "async-trait",
+ "deadpool-runtime",
+ "num_cpus",
+ "tokio",
+]
+
+[[package]]
+name = "deadpool-lapin"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce11c0dc86703e59a8921bb9afee10b13c242e47624347bd3a3b545c41db556e"
+dependencies = [
+ "deadpool 0.10.0",
+ "lapin",
+ "tokio-executor-trait",
+]
+
 [[package]]
 name = "deadpool-runtime"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49"
+dependencies = [
+ "tokio",
+]
 
 [[package]]
 name = "deranged"
@@ -3103,6 +3129,7 @@ dependencies = [
  "chrono",
  "clap",
  "config",
+ "deadpool-lapin",
  "derive_builder",
  "futures",
  "futures-lite 1.13.0",
@@ -3329,6 +3356,17 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "tokio-executor-trait"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "802ccf58e108fe16561f35348fabe15ff38218968f033d587e399a84937533cc"
+dependencies = [
+ "async-trait",
+ "executor-trait",
+ "tokio",
+]
+
 [[package]]
 name = "tokio-macros"
 version = "2.2.0"
@@ -3940,7 +3978,7 @@ dependencies = [
  "assert-json-diff",
  "async-trait",
  "base64 0.21.5",
- "deadpool",
+ "deadpool 0.9.5",
  "futures",
  "futures-timer",
  "http-types",
diff --git a/Cargo.toml b/Cargo.toml
index 54ee40f6..4555de3b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,6 +53,7 @@ lapin = { version = "2.3.1", features = ["serde_json"] }
 futures-lite = "1.13.0"
 clap = { version = "4.4.8", features = ["derive"] }
 serde_path_to_error = "0.1.14"
+deadpool-lapin = "0.11.0"
 
 [dependencies.sqlx]
 version = "0.6.3"
diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs
index e5439f74..6d7d2a16 100644
--- a/src/helpers/mod.rs
+++ b/src/helpers/mod.rs
@@ -1,5 +1,7 @@
 pub mod client;
 pub(crate) mod json;
+mod mq_pool;
 pub(crate) mod stack;
 
 pub use json::*;
+pub use mq_pool::MqPool;
diff --git a/src/helpers/mq_pool.rs b/src/helpers/mq_pool.rs
new file mode 100644
index 00000000..4e5bcbd1
--- /dev/null
+++ b/src/helpers/mq_pool.rs
@@ -0,0 +1,27 @@
+use deadpool_lapin::{Config, CreatePoolError, Pool, Runtime};
+
+#[derive(Debug)]
+pub struct MqPool {
+    pool: Pool,
+}
+
+impl MqPool {
+    pub fn try_new(url: String) -> Result<Self, std::io::Error> {
+        let mut cfg = Config::default();
+        cfg.url = Some(url);
+        let pool = cfg.create_pool(Some(Runtime::Tokio1)).map_err(|err| {
+            tracing::error!("{:?}", err);
+
+            match err {
+                CreatePoolError::Config(_) => {
+                    std::io::Error::new(std::io::ErrorKind::Other, "config error")
+                }
+                CreatePoolError::Build(_) => {
+                    std::io::Error::new(std::io::ErrorKind::Other, "build error")
+                }
+            }
+        })?;
+
+        Ok(MqPool { pool })
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index ebf4987c..8132f582 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,7 +11,7 @@ async fn main() -> std::io::Result<()> {
 
     let settings = get_configuration().expect("Failed to read configuration.");
 
-    let db_pool = PgPool::connect(&settings.database.connection_string())
+    let pg_pool = PgPool::connect(&settings.database.connection_string())
         .await
         .expect("Failed to connect to database.");
 
@@ -20,5 +20,5 @@ async fn main() -> std::io::Result<()> {
     let listener =
         TcpListener::bind(address).expect(&format!("failed to bind to {}", settings.app_port));
 
-    run(listener, db_pool, settings).await?.await
+    run(listener, pg_pool, settings).await?.await
 }
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index f18c99d6..2593c46e 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -2,7 +2,7 @@ use crate::configuration::Settings;
 use crate::db;
 use crate::forms::StackPayload;
 use crate::helpers::stack::builder::DcBuilder;
-use crate::helpers::JsonResponse;
+use crate::helpers::{JsonResponse, MqPool};
 use crate::models;
 use actix_web::{post, web, web::Data, Responder, Result};
 use futures_lite::stream::StreamExt;
@@ -18,6 +18,7 @@ pub async fn add(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
     pg_pool: Data<PgPool>,
+    mq_pool: Data<MqPool>,
     sets: Data<Settings>,
 ) -> Result<impl Responder> {
     let id = path.0;
diff --git a/src/startup.rs b/src/startup.rs
index b592a4f3..88312023 100644
--- a/src/startup.rs
+++ b/src/startup.rs
@@ -1,4 +1,5 @@
 use crate::configuration::Settings;
+use crate::helpers;
 use actix_cors::Cors;
 use actix_web::dev::Server;
 use actix_web::{
@@ -18,6 +19,9 @@ pub async fn run(
     let settings = web::Data::new(settings);
     let pg_pool = web::Data::new(pg_pool);
 
+    let mq_pool = helpers::MqPool::try_new(settings.amqp.connection_string())?;
+    let mq_pool = web::Data::new(mq_pool);
+
     let server = HttpServer::new(move || {
         App::new()
             .wrap(TracingLogger::default())
@@ -64,6 +68,7 @@ pub async fn run(
                     .service(crate::routes::stack::update::update),
             )
             .app_data(pg_pool.clone())
+            .app_data(mq_pool.clone())
             .app_data(settings.clone())
     })
     .listen(listener)?

From c6bf4a2c222ab43d2550c85e6d34e7d6f192b403 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 10:31:31 +0200
Subject: [PATCH 10/25] issue-auth mq_manager

---
 src/helpers/mod.rs                        | 4 ++--
 src/helpers/{mq_pool.rs => mq_manager.rs} | 8 +++++---
 src/routes/stack/deploy.rs                | 4 ++--
 src/startup.rs                            | 6 +++---
 4 files changed, 12 insertions(+), 10 deletions(-)
 rename src/helpers/{mq_pool.rs => mq_manager.rs} (87%)

diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs
index 6d7d2a16..6d3b1699 100644
--- a/src/helpers/mod.rs
+++ b/src/helpers/mod.rs
@@ -1,7 +1,7 @@
 pub mod client;
 pub(crate) mod json;
-mod mq_pool;
+mod mq_manager;
 pub(crate) mod stack;
 
 pub use json::*;
-pub use mq_pool::MqPool;
+pub use mq_manager::MqManager;
diff --git a/src/helpers/mq_pool.rs b/src/helpers/mq_manager.rs
similarity index 87%
rename from src/helpers/mq_pool.rs
rename to src/helpers/mq_manager.rs
index 4e5bcbd1..00b95261 100644
--- a/src/helpers/mq_pool.rs
+++ b/src/helpers/mq_manager.rs
@@ -1,11 +1,11 @@
 use deadpool_lapin::{Config, CreatePoolError, Pool, Runtime};
 
 #[derive(Debug)]
-pub struct MqPool {
+pub struct MqManager {
     pool: Pool,
 }
 
-impl MqPool {
+impl MqManager {
     pub fn try_new(url: String) -> Result<Self, std::io::Error> {
         let mut cfg = Config::default();
         cfg.url = Some(url);
@@ -22,6 +22,8 @@ impl MqPool {
             }
         })?;
 
-        Ok(MqPool { pool })
+        Ok(Self { pool })
     }
+
+    pub async fn create_channel() {}
 }
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 2593c46e..dc00eafd 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -2,7 +2,7 @@ use crate::configuration::Settings;
 use crate::db;
 use crate::forms::StackPayload;
 use crate::helpers::stack::builder::DcBuilder;
-use crate::helpers::{JsonResponse, MqPool};
+use crate::helpers::{JsonResponse, MqManager};
 use crate::models;
 use actix_web::{post, web, web::Data, Responder, Result};
 use futures_lite::stream::StreamExt;
@@ -18,7 +18,7 @@ pub async fn add(
     user: web::ReqData<Arc<models::User>>,
     path: web::Path<(i32,)>,
     pg_pool: Data<PgPool>,
-    mq_pool: Data<MqPool>,
+    mq_manager: Data<MqManager>,
     sets: Data<Settings>,
 ) -> Result<impl Responder> {
     let id = path.0;
diff --git a/src/startup.rs b/src/startup.rs
index 88312023..b510af64 100644
--- a/src/startup.rs
+++ b/src/startup.rs
@@ -19,8 +19,8 @@ pub async fn run(
     let settings = web::Data::new(settings);
     let pg_pool = web::Data::new(pg_pool);
 
-    let mq_pool = helpers::MqPool::try_new(settings.amqp.connection_string())?;
-    let mq_pool = web::Data::new(mq_pool);
+    let mq_manager = helpers::MqManager::try_new(settings.amqp.connection_string())?;
+    let mq_manager = web::Data::new(mq_manager);
 
     let server = HttpServer::new(move || {
         App::new()
@@ -68,7 +68,7 @@ pub async fn run(
                     .service(crate::routes::stack::update::update),
             )
             .app_data(pg_pool.clone())
-            .app_data(mq_pool.clone())
+            .app_data(mq_manager.clone())
             .app_data(settings.clone())
     })
     .listen(listener)?

From d870fcac78a1ed21f0293b993e2bb5dde1542259 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 22:13:28 +0200
Subject: [PATCH 11/25] issue-auth mq_manager::publish_and_confirm

---
 src/helpers/mq_manager.rs  | 64 +++++++++++++++++++++++++++++++++++++-
 src/routes/stack/deploy.rs | 51 ++++++------------------------
 2 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/src/helpers/mq_manager.rs b/src/helpers/mq_manager.rs
index 00b95261..ed7a9b17 100644
--- a/src/helpers/mq_manager.rs
+++ b/src/helpers/mq_manager.rs
@@ -1,4 +1,9 @@
 use deadpool_lapin::{Config, CreatePoolError, Pool, Runtime};
+use lapin::{
+    options::*,
+    publisher_confirm::{Confirmation, PublisherConfirm},
+    BasicProperties, Connection, ConnectionProperties,
+};
 
 #[derive(Debug)]
 pub struct MqManager {
@@ -25,5 +30,62 @@ impl MqManager {
         Ok(Self { pool })
     }
 
-    pub async fn create_channel() {}
+    pub async fn publish(
+        &self,
+        exchange: String,
+        routing_key: String,
+        payload: &[u8],
+    ) -> Result<PublisherConfirm, String> {
+        let addr = String::new();
+        Connection::connect(&addr, ConnectionProperties::default())
+            .await
+            .map_err(|err| {
+                tracing::error!("connecting to RabbitMQ {:?}", err);
+                format!("connecting to RabbitMQ {:?}", err)
+            })?
+            .create_channel()
+            .await
+            .map_err(|err| {
+                tracing::error!("creating RabbitMQ channel {:?}", err);
+                format!("creating RabbitMQ channel {:?}", err)
+            })?
+            .basic_publish(
+                "install",
+                routing_key.as_str(),
+                BasicPublishOptions::default(),
+                payload,
+                BasicProperties::default(),
+            )
+            .await
+            .map_err(|err| {
+                tracing::error!("publishing message {:?}", err);
+                format!("publishing message {:?}", err)
+            })
+    }
+
+    pub async fn publish_and_confirm(
+        &self,
+        exchange: String,
+        routing_key: String,
+        payload: &[u8],
+    ) -> Result<(), String> {
+        self.publish(exchange, routing_key, payload)
+            .await
+            .map_err(|err| {
+                tracing::error!("publishing the message {:?}", err);
+                format!("publishing the message {:?}", err)
+            })?
+            .await
+            .map_err(|err| {
+                tracing::error!("confirming the publication {:?}", err);
+                format!("confirming the publication {:?}", err)
+            })
+            .and_then(|confirm| match confirm {
+                Confirmation::NotRequested => {
+                    tracing::error!("confirmation is NotRequested");
+                    Err(format!("confirmation is NotRequested"))
+                }
+                _ => Ok(()),
+            })
+    }
 }
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index dc00eafd..86531193 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -6,9 +6,7 @@ use crate::helpers::{JsonResponse, MqManager};
 use crate::models;
 use actix_web::{post, web, web::Data, Responder, Result};
 use futures_lite::stream::StreamExt;
-use lapin::{
-    options::*, publisher_confirm::Confirmation, BasicProperties, Connection, ConnectionProperties,
-};
+use lapin::publisher_confirm::Confirmation;
 use sqlx::PgPool;
 use std::sync::Arc;
 
@@ -51,48 +49,19 @@ pub async fn add(
     let payload = serde_json::to_string::<StackPayload>(&stack_data).map_err(|err| {
         tracing::error!("serializing StackPayload {:?}", err);
         JsonResponse::<models::Stack>::build().internal_server_error("")
-    })?;
-
-    let addr = sets.amqp.connection_string();
-    let routing_key = "install.start.tfa.all.all".to_string();
-    tracing::debug!("Sending message to {:?}", routing_key);
+    })?; //todo is it possible to use lapin serde
 
-    Connection::connect(&addr, ConnectionProperties::default())
-        .await
-        .map_err(|err| {
-            tracing::error!("connecting to RabbitMQ {:?}", err);
-            JsonResponse::<models::Stack>::build().internal_server_error("")
-        })?
-        .create_channel()
-        .await
-        .map_err(|err| {
-            tracing::error!("creating RabbitMQ channel {:?}", err);
-            JsonResponse::<models::Stack>::build().internal_server_error("")
-        })?
-        .basic_publish(
-            "install",
-            routing_key.as_str(),
-            BasicPublishOptions::default(),
+    mq_manager
+        .publish_and_confirm(
+            "install".to_string(),
+            "install.start.tfa.all.all".to_string(),
             payload.as_bytes(),
-            BasicProperties::default(),
         )
         .await
-        .map_err(|err| {
-            tracing::error!("publishing the message {:?}", err);
-            JsonResponse::<models::Stack>::build().internal_server_error("")
-        })?
-        .await
-        .map_err(|err| {
-            tracing::error!("confirming the publication {:?}", err);
-            JsonResponse::<models::Stack>::build().internal_server_error("")
-        })
-        .and_then(|confirm| match confirm {
-            Confirmation::NotRequested => {
-                Err(JsonResponse::<models::Stack>::build()
-                    .bad_request("confirmation is NotRequested"))
-            }
-            _ => Ok(JsonResponse::<models::Stack>::build()
+        .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))
+        .map(|_| {
+            JsonResponse::<models::Stack>::build()
                 .set_id(id)
-                .ok("Success")),
+                .ok("Success")
         })
 }

From bf8cadc53164f58bfd48912b313bd1ce6a79856c Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 22:31:13 +0200
Subject: [PATCH 12/25] issue-auth move to deadpool

---
 src/helpers/mq_manager.rs | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/helpers/mq_manager.rs b/src/helpers/mq_manager.rs
index ed7a9b17..898f5af1 100644
--- a/src/helpers/mq_manager.rs
+++ b/src/helpers/mq_manager.rs
@@ -36,13 +36,12 @@ impl MqManager {
         routing_key: String,
         payload: &[u8],
     ) -> Result<PublisherConfirm, String> {
-        let addr = String::new();
-        Connection::connect(&addr, ConnectionProperties::default())
-            .await
-            .map_err(|err| {
-                tracing::error!("connecting to RabbitMQ {:?}", err);
-                format!("connecting to RabbitMQ {:?}", err)
-            })?
+        let connection = self.pool.get().await.map_err(|err| {
+            tracing::error!("getting connection from pool {:?}", err);
+            format!("getting connection from pool {:?}", err)
+        })?;
+
+        connection
             .create_channel()
             .await
             .map_err(|err| {
@@ -50,7 +49,7 @@ impl MqManager {
                 format!("creating RabbitMQ channel {:?}", err)
             })?
             .basic_publish(
-                "install",
+                exchange.as_str(),
                 routing_key.as_str(),
                 BasicPublishOptions::default(),
                 payload,

From 67c2f592ef442150bde618ca8c2ae474ee68f5aa Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 22:40:08 +0200
Subject: [PATCH 13/25] issue_auth mq_manager.create_channel

---
 src/helpers/mq_manager.rs | 38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/src/helpers/mq_manager.rs b/src/helpers/mq_manager.rs
index 898f5af1..707d4d17 100644
--- a/src/helpers/mq_manager.rs
+++ b/src/helpers/mq_manager.rs
@@ -2,7 +2,7 @@ use deadpool_lapin::{Config, CreatePoolError, Pool, Runtime};
 use lapin::{
     options::*,
     publisher_confirm::{Confirmation, PublisherConfirm},
-    BasicProperties, Connection, ConnectionProperties,
+    BasicProperties, Channel, Connection, ConnectionProperties,
 };
 
 #[derive(Debug)]
@@ -30,24 +30,30 @@ impl MqManager {
         Ok(Self { pool })
     }
 
+    async fn create_channel(&self) -> Result<Channel, String> {
+        self.pool
+            .get()
+            .await
+            .map_err(|err| {
+                tracing::error!("getting connection from pool {:?}", err);
+                format!("getting connection from pool {:?}", err)
+            })?
+            .create_channel()
+            .await
+            .map_err(|err| {
+                tracing::error!("creating RabbitMQ channel {:?}", err);
+                format!("creating RabbitMQ channel {:?}", err)
+            })
+    }
+
     pub async fn publish(
         &self,
         exchange: String,
         routing_key: String,
         payload: &[u8],
     ) -> Result<PublisherConfirm, String> {
-        let connection = self.pool.get().await.map_err(|err| {
-            tracing::error!("getting connection from pool {:?}", err);
-            format!("getting connection from pool {:?}", err)
-        })?;
-
-        connection
-            .create_channel()
-            .await
-            .map_err(|err| {
-                tracing::error!("creating RabbitMQ channel {:?}", err);
-                format!("creating RabbitMQ channel {:?}", err)
-            })?
+        self.create_channel()
+            .await?
             .basic_publish(
                 exchange.as_str(),
                 routing_key.as_str(),
@@ -69,11 +75,7 @@ impl MqManager {
         payload: &[u8],
     ) -> Result<(), String> {
         self.publish(exchange, routing_key, payload)
-            .await
-            .map_err(|err| {
-                tracing::error!("publishing the message {:?}", err);
-                format!("publishing the message {:?}", err)
-            })?
+            .await?
             .await
             .map_err(|err| {
                 tracing::error!("confirming the publication {:?}", err);

From 9ad15f73b173c224b094dce934a9e5852f294de2 Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sat, 30 Dec 2023 22:47:50 +0200
Subject: [PATCH 14/25] issue-auth mq_manager get_connection

---
 src/helpers/mq_manager.rs | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/helpers/mq_manager.rs b/src/helpers/mq_manager.rs
index 707d4d17..4fde2617 100644
--- a/src/helpers/mq_manager.rs
+++ b/src/helpers/mq_manager.rs
@@ -1,8 +1,8 @@
-use deadpool_lapin::{Config, CreatePoolError, Pool, Runtime};
+use deadpool_lapin::{Config, CreatePoolError, Object, Pool, Runtime};
 use lapin::{
     options::*,
     publisher_confirm::{Confirmation, PublisherConfirm},
-    BasicProperties, Channel, Connection, ConnectionProperties,
+    BasicProperties, Channel,
 };
 
 #[derive(Debug)]
@@ -30,14 +30,16 @@ impl MqManager {
         Ok(Self { pool })
     }
 
+    async fn get_connection(&self) -> Result<Object, String> {
+        self.pool.get().await.map_err(|err| {
+            tracing::error!("getting connection from pool {:?}", err);
+            format!("getting connection from pool {:?}", err)
+        })
+    }
+
     async fn create_channel(&self) -> Result<Channel, String> {
-        self.pool
-            .get()
-            .await
-            .map_err(|err| {
-                tracing::error!("getting connection from pool {:?}", err);
-                format!("getting connection from pool {:?}", err)
-            })?
+        self.get_connection()
+            .await?
             .create_channel()
             .await
             .map_err(|err| {

From 8d7e9a0136cb25e877411424e0e4bc32350156bf Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sun, 31 Dec 2023 18:34:57 +0200
Subject: [PATCH 15/25] issue-auth tracing::instrument for DcBuilder::build

---
 src/helpers/stack/builder.rs | 130 ++++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 64 deletions(-)

diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 1db77c54..42df43fa 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -1,12 +1,12 @@
+use crate::forms::{stack, App, StackForm, Volume, Web};
+use crate::helpers::stack::dctypes::{
+    AdvancedVolumes, Compose, ComposeNetwork, ComposeNetworkSettingDetails, ComposeNetworks,
+    ComposeVolume, Entrypoint, Environment, MapOrEmpty, NetworkSettings, Networks, Port, Ports,
+    PublishedPort, Service, Services, SingleValue, TopLevelVolumes, Volumes,
+};
+use crate::models::stack::Stack;
 use indexmap::IndexMap;
-use crate::helpers::stack::dctypes::{Compose, Port, Ports, PublishedPort, Service, Services,
-                                     Volumes, Environment, Entrypoint, AdvancedVolumes, SingleValue,
-                                     Networks, TopLevelVolumes, ComposeVolume, ComposeNetwork,
-                                     ComposeNetworks, MapOrEmpty, ComposeNetworkSettingDetails,
-                                     NetworkSettings};
 use serde_yaml;
-use crate::forms::{StackForm, stack, App, Volume, Web};
-use crate::models::stack::Stack;
 #[derive(Clone, Debug)]
 struct Config {}
 
@@ -16,7 +16,7 @@ impl Default for Config {
     }
 }
 
-impl Default for Port{
+impl Default for Port {
     fn default() -> Self {
         Port {
             target: 80,
@@ -38,10 +38,13 @@ pub struct DcBuilder {
 impl TryInto<AdvancedVolumes> for Volume {
     type Error = String;
     fn try_into(self) -> Result<AdvancedVolumes, Self::Error> {
-
         let source = self.host_path.clone();
         let target = self.container_path.clone();
-        tracing::debug!("Volume conversion result: source: {:?} target: {:?}", source, target);
+        tracing::debug!(
+            "Volume conversion result: source: {:?} target: {:?}",
+            source,
+            target
+        );
         Ok(AdvancedVolumes {
             source: source,
             target: target.unwrap_or("".to_string()),
@@ -57,12 +60,18 @@ impl TryInto<AdvancedVolumes> for Volume {
 impl TryInto<Port> for stack::Port {
     type Error = String;
     fn try_into(self) -> Result<Port, Self::Error> {
-        let cp  = self.container_port.clone()
+        let cp = self
+            .container_port
+            .clone()
             .unwrap_or("".to_string())
-            .parse::<u16>().map_err(|err| "Could not parse port".to_string() )?;
-        let hp = self.host_port.clone()
+            .parse::<u16>()
+            .map_err(|err| "Could not parse port".to_string())?;
+        let hp = self
+            .host_port
+            .clone()
             .unwrap_or("".to_string())
-            .parse::<u16>().map_err(|err| "Could not parse port".to_string() )?;
+            .parse::<u16>()
+            .map_err(|err| "Could not parse port".to_string())?;
 
         tracing::debug!("Port conversion result: cp: {:?} hp: {:?}", cp, hp);
         Ok(Port {
@@ -92,8 +101,7 @@ fn convert_shared_ports(ports: Option<Vec<stack::Port>>) -> Result<Vec<Port>, St
     match ports {
         Some(ports) => {
             tracing::debug!("Ports >>>> {:?}", ports);
-            for port in ports {
-            }
+            for port in ports {}
         }
         None => {
             tracing::debug!("No ports defined by user");
@@ -126,25 +134,22 @@ impl TryIntoService for App {
             ..Default::default()
         };
 
-        let networks: Networks = self.network
-            .clone()
-            .try_into()
-            .unwrap_or_default();
+        let networks: Networks = self.network.clone().try_into().unwrap_or_default();
 
-        let ports: Vec<Port> = self.ports
+        let ports: Vec<Port> = self
+            .ports
             .clone()
             .unwrap_or_default()
             .into_iter()
             .map(|x| x.try_into().unwrap())
             .collect();
 
-        let volumes: Vec<AdvancedVolumes> = self.volumes
+        let volumes: Vec<AdvancedVolumes> = self
+            .volumes
             .clone()
             .unwrap_or_default()
             .into_iter()
-            .map(|x| {
-                x.try_into().unwrap()
-            })
+            .map(|x| x.try_into().unwrap())
             .collect();
 
         let mut envs = IndexMap::new();
@@ -152,7 +157,7 @@ impl TryIntoService for App {
             let items = item
                 .into_iter()
                 .map(|(k, v)| (k, Some(SingleValue::String(v.clone()))))
-                .collect::<IndexMap<_,_>>();
+                .collect::<IndexMap<_, _>>();
 
             envs.extend(items);
         }
@@ -169,7 +174,6 @@ impl TryIntoService for App {
 
 impl Into<IndexMap<String, MapOrEmpty<NetworkSettings>>> for stack::ComposeNetworks {
     fn into(self) -> IndexMap<String, MapOrEmpty<NetworkSettings>> {
-
         // tracing::debug!("networks found {:?}", self.networks);
         let mut networks = vec!["default_network".to_string()];
         if self.networks.is_some() {
@@ -177,59 +181,58 @@ impl Into<IndexMap<String, MapOrEmpty<NetworkSettings>>> for stack::ComposeNetwo
         }
         let networks = networks
             .into_iter()
-            .map(|net|
-                (net,
-                 MapOrEmpty::Map(
-                     NetworkSettings {
-                         attachable: false,
-                         driver: None,
-                         driver_opts: Default::default(),
-                         enable_ipv6: false,
-                         internal: false,
-                         // external: None,
-                         external: Some(ComposeNetwork::Bool(true)),
-                         ipam: None,
-                         labels: Default::default(),
-                         name: Some("default".to_string()),
-                     }
-                 ))
-            )
+            .map(|net| {
+                (
+                    net,
+                    MapOrEmpty::Map(NetworkSettings {
+                        attachable: false,
+                        driver: None,
+                        driver_opts: Default::default(),
+                        enable_ipv6: false,
+                        internal: false,
+                        // external: None,
+                        external: Some(ComposeNetwork::Bool(true)),
+                        ipam: None,
+                        labels: Default::default(),
+                        name: Some("default".to_string()),
+                    }),
+                )
+            })
             .collect::<IndexMap<String, _>>();
         networks
     }
 }
 
-
 pub fn extract_named_volumes(app: App) -> IndexMap<String, MapOrEmpty<ComposeVolume>> {
-
     let mut named_volumes = IndexMap::default();
 
-        let volumes = app.volumes
-            .unwrap()
-            .into_iter()
-            .filter(|volume| is_named_docker_volume(
-                volume.host_path.clone().unwrap().as_str())
-            )
-            .map(|volume| {
-                let k = volume.host_path.clone().unwrap();
-                (k.clone(), MapOrEmpty::Map(ComposeVolume {
+    let volumes = app
+        .volumes
+        .unwrap()
+        .into_iter()
+        .filter(|volume| is_named_docker_volume(volume.host_path.clone().unwrap().as_str()))
+        .map(|volume| {
+            let k = volume.host_path.clone().unwrap();
+            (
+                k.clone(),
+                MapOrEmpty::Map(ComposeVolume {
                     driver: None,
                     driver_opts: Default::default(),
                     external: None,
                     labels: Default::default(),
-                    name: Some(k.clone())
-                }))
-            })
-            .collect::<IndexMap<String, MapOrEmpty<ComposeVolume>>>();
+                    name: Some(k.clone()),
+                }),
+            )
+        })
+        .collect::<IndexMap<String, MapOrEmpty<ComposeVolume>>>();
 
-        named_volumes.extend(volumes);
+    named_volumes.extend(volumes);
     // tracing::debug!("Named volumes: {:?}", named_volumes);
 
     named_volumes
 }
 
 impl DcBuilder {
-
     pub fn new(stack: Stack) -> Self {
         DcBuilder {
             config: Config::default(),
@@ -237,6 +240,7 @@ impl DcBuilder {
         }
     }
 
+    #[tracing::instrument(name = "building stack")]
     pub fn build(&self) -> Option<String> {
         tracing::debug!("Start build docker compose from {:?}", &self.stack.body);
         let mut compose_content = Compose {
@@ -277,7 +281,6 @@ impl DcBuilder {
                 if !named_volumes.is_empty() {
                     compose_content.volumes = TopLevelVolumes(named_volumes);
                 }
-
             }
             Err(e) => {
                 tracing::debug!("Unpack stack form error {:?}", e);
@@ -286,8 +289,7 @@ impl DcBuilder {
         tracing::debug!("services {:?}", &services);
         compose_content.services = Services(services);
 
-
-        let fname= format!("./files/{}.yml", self.stack.stack_id);
+        let fname = format!("./files/{}.yml", self.stack.stack_id);
         tracing::debug!("Saving docker compose to file {:?}", fname);
         let target_file = std::path::Path::new(fname.as_str());
         // serialize to string

From ea374d7d054e81da977565812c1ee5d6b18631ef Mon Sep 17 00:00:00 2001
From: Petru <smart__petea@mail.ru>
Date: Sun, 31 Dec 2023 19:04:34 +0200
Subject: [PATCH 16/25] issue-auth DcBuilder build()

---
 src/helpers/stack/builder.rs | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 42df43fa..5a42b75b 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -206,8 +206,12 @@ impl Into<IndexMap<String, MapOrEmpty<NetworkSettings>>> for stack::ComposeNetwo
 pub fn extract_named_volumes(app: App) -> IndexMap<String, MapOrEmpty<ComposeVolume>> {
     let mut named_volumes = IndexMap::default();
 
-    let volumes = app
-        .volumes
+    let volumes = app.volumes;
+    if volumes.is_none() {
+        return named_volumes;
+    }
+
+    let volumes = volumes
         .unwrap()
         .into_iter()
         .filter(|volume| is_named_docker_volume(volume.host_path.clone().unwrap().as_str()))
@@ -293,12 +297,11 @@ impl DcBuilder {
         tracing::debug!("Saving docker compose to file {:?}", fname);
         let target_file = std::path::Path::new(fname.as_str());
         // serialize to string
-        let serialized = match serde_yaml::to_string(&compose_content) {
-            Ok(s) => s,
-            Err(e) => panic!("Failed to serialize docker-compose file: {}", e),
-        };
-        // serialize to file
-        std::fs::write(target_file, serialized.clone()).unwrap();
+        let serialized = serde_yaml::to_string(&compose_content)
+            .map_err(|err| panic!("Failed to serialize docker-compose file: {}", err))
+            .unwrap();
+
+        std::fs::write(target_file, serialized.clone()).map_err(|err| panic!("{}", err));
 
         Some(serialized)
     }

From fb7b13203b682efb19fe43185c23f8d2c22a1a46 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Sat, 6 Jan 2024 15:09:36 +0200
Subject: [PATCH 17/25] issue-auth mq_manager. publish. Serialize

---
 src/helpers/mq_manager.rs  | 17 +++++++++++------
 src/routes/stack/deploy.rs |  7 +------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/helpers/mq_manager.rs b/src/helpers/mq_manager.rs
index 4fde2617..5b058ea4 100644
--- a/src/helpers/mq_manager.rs
+++ b/src/helpers/mq_manager.rs
@@ -4,6 +4,7 @@ use lapin::{
     publisher_confirm::{Confirmation, PublisherConfirm},
     BasicProperties, Channel,
 };
+use serde::ser::Serialize;
 
 #[derive(Debug)]
 pub struct MqManager {
@@ -48,19 +49,23 @@ impl MqManager {
             })
     }
 
-    pub async fn publish(
+    pub async fn publish<T: ?Sized + Serialize>(
         &self,
         exchange: String,
         routing_key: String,
-        payload: &[u8],
+        msg: &T,
     ) -> Result<PublisherConfirm, String> {
+        let payload = serde_json::to_string::<T>(msg).map_err(|err| {
+            format!("{:?}", err)
+        })?;
+
         self.create_channel()
             .await?
             .basic_publish(
                 exchange.as_str(),
                 routing_key.as_str(),
                 BasicPublishOptions::default(),
-                payload,
+                payload.as_bytes(),
                 BasicProperties::default(),
             )
             .await
@@ -70,13 +75,13 @@ impl MqManager {
             })
     }
 
-    pub async fn publish_and_confirm(
+    pub async fn publish_and_confirm<T: ?Sized + Serialize>(
         &self,
         exchange: String,
         routing_key: String,
-        payload: &[u8],
+        msg: &T
     ) -> Result<(), String> {
-        self.publish(exchange, routing_key, payload)
+        self.publish(exchange, routing_key, msg)
             .await?
             .await
             .map_err(|err| {
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 86531193..f626cc07 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -46,16 +46,11 @@ pub async fn add(
     stack_data.user_email = Some(user.email.clone());
     stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
 
-    let payload = serde_json::to_string::<StackPayload>(&stack_data).map_err(|err| {
-        tracing::error!("serializing StackPayload {:?}", err);
-        JsonResponse::<models::Stack>::build().internal_server_error("")
-    })?; //todo is it possible to use lapin serde
-
     mq_manager
         .publish_and_confirm(
             "install".to_string(),
             "install.start.tfa.all.all".to_string(),
-            payload.as_bytes(),
+            &stack_data,
         )
         .await
         .map_err(|err| JsonResponse::<models::Stack>::build().internal_server_error(err))

From 6ddd62ccef1363159ae9d73fbab93d5ddb7ec7e7 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Sat, 6 Jan 2024 21:44:03 +0200
Subject: [PATCH 18/25] issue-auth DcBuilder stack. try from

---
 src/forms/mod.rs             |  4 +--
 src/forms/stack.rs           | 33 ----------------------
 src/forms/stack_payload.rs   | 54 ++++++++++++++++++++++++++++++++++++
 src/helpers/stack/builder.rs |  6 ++--
 src/routes/stack/deploy.rs   | 12 ++------
 5 files changed, 62 insertions(+), 47 deletions(-)
 create mode 100644 src/forms/stack_payload.rs

diff --git a/src/forms/mod.rs b/src/forms/mod.rs
index ae65e1c4..cee99fef 100644
--- a/src/forms/mod.rs
+++ b/src/forms/mod.rs
@@ -1,8 +1,8 @@
 mod rating;
-
 pub mod stack;
 pub mod user;
+mod stack_payload;
 
 pub use rating::*;
-
 pub use stack::*;
+pub use stack_payload::*;
diff --git a/src/forms/stack.rs b/src/forms/stack.rs
index cc322330..febd2af9 100644
--- a/src/forms/stack.rs
+++ b/src/forms/stack.rs
@@ -125,39 +125,6 @@ pub struct StackForm {
     pub custom: Custom,
 }
 
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
-#[serde(rename_all = "snake_case")]
-pub struct StackPayload {
-    pub(crate) id: Option<i32>,
-    pub(crate) user_token: Option<String>,
-    pub(crate) user_email: Option<String>,
-    #[serde(rename = "commonDomain")]
-    pub common_domain: String,
-    pub domain_list: Option<DomainList>,
-    pub region: String,
-    pub zone: Option<String>,
-    pub server: String,
-    pub os: String,
-    pub ssl: String,
-    pub vars: Option<Vec<Var>>,
-    #[serde(rename = "integrated_features")]
-    pub integrated_features: Option<Vec<Value>>,
-    #[serde(rename = "extended_features")]
-    pub extended_features: Option<Vec<Value>>,
-    pub subscriptions: Option<Vec<String>>,
-    pub form_app: Option<Vec<String>>,
-    pub disk_type: Option<String>,
-    #[serde(rename = "save_token")]
-    pub save_token: bool,
-    #[serde(rename = "cloud_token")]
-    pub cloud_token: String,
-    pub provider: String,
-    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 {}
diff --git a/src/forms/stack_payload.rs b/src/forms/stack_payload.rs
new file mode 100644
index 00000000..521642d5
--- /dev/null
+++ b/src/forms/stack_payload.rs
@@ -0,0 +1,54 @@
+use std::convert::TryFrom;
+use crate::models;
+use crate::forms;
+use serde_json::Value;
+use serde::{Deserialize, Serialize};
+use serde_valid::Validate;
+
+#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
+#[serde(rename_all = "snake_case")]
+pub struct StackPayload {
+    pub(crate) id: Option<i32>,
+    pub(crate) user_token: Option<String>,
+    pub(crate) user_email: Option<String>,
+    #[serde(rename = "commonDomain")]
+    pub common_domain: String,
+    pub domain_list: Option<forms::stack::DomainList>,
+    pub region: String,
+    pub zone: Option<String>,
+    pub server: String,
+    pub os: String,
+    pub ssl: String,
+    pub vars: Option<Vec<forms::stack::Var>>,
+    #[serde(rename = "integrated_features")]
+    pub integrated_features: Option<Vec<Value>>,
+    #[serde(rename = "extended_features")]
+    pub extended_features: Option<Vec<Value>>,
+    pub subscriptions: Option<Vec<String>>,
+    pub form_app: Option<Vec<String>>,
+    pub disk_type: Option<String>,
+    #[serde(rename = "save_token")]
+    pub save_token: bool,
+    #[serde(rename = "cloud_token")]
+    pub cloud_token: String,
+    pub provider: String,
+    pub stack_code: String,
+    #[serde(rename = "selected_plan")]
+    pub selected_plan: String,
+    pub custom: forms::stack::Custom,
+}
+
+impl TryFrom<&models::Stack> for StackPayload {
+    type Error = String;
+
+    fn try_from(stack: &models::Stack) -> Result<Self, Self::Error> {
+        let mut stack_data = serde_json::from_value::<StackPayload>(stack.body.clone()).map_err(|err| {
+            format!("{:?}", err)
+        })?;
+
+        stack_data.id = Some(stack.id.clone());
+        stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
+
+        Ok(stack_data)
+    }
+}
diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 5a42b75b..eeca4a2a 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -4,7 +4,7 @@ use crate::helpers::stack::dctypes::{
     ComposeVolume, Entrypoint, Environment, MapOrEmpty, NetworkSettings, Networks, Port, Ports,
     PublishedPort, Service, Services, SingleValue, TopLevelVolumes, Volumes,
 };
-use crate::models::stack::Stack;
+use crate::models;
 use indexmap::IndexMap;
 use serde_yaml;
 #[derive(Clone, Debug)]
@@ -32,7 +32,7 @@ impl Default for Port {
 #[derive(Clone, Debug)]
 pub struct DcBuilder {
     config: Config,
-    pub(crate) stack: Stack,
+    pub(crate) stack: models::Stack,
 }
 
 impl TryInto<AdvancedVolumes> for Volume {
@@ -237,7 +237,7 @@ pub fn extract_named_volumes(app: App) -> IndexMap<String, MapOrEmpty<ComposeVol
 }
 
 impl DcBuilder {
-    pub fn new(stack: Stack) -> Self {
+    pub fn new(stack: models::Stack) -> Self {
         DcBuilder {
             config: Config::default(),
             stack,
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index f626cc07..80f49858 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -1,6 +1,6 @@
 use crate::configuration::Settings;
 use crate::db;
-use crate::forms::StackPayload;
+use crate::forms;
 use crate::helpers::stack::builder::DcBuilder;
 use crate::helpers::{JsonResponse, MqManager};
 use crate::models;
@@ -35,16 +35,10 @@ pub async fn add(
         JsonResponse::<models::Stack>::build().internal_server_error("")
     })?;
 
-    let mut stack_data =
-        serde_json::from_value::<StackPayload>(dc.stack.body.clone()).map_err(|err| {
-            tracing::error!("transforming json Value into StackPayload {:?}", err);
-            JsonResponse::<models::Stack>::build().bad_request("")
-        })?;
-
-    stack_data.id = Some(id);
+    let mut stack_data = forms::StackPayload::try_from(&dc.stack)
+        .map_err(|err| JsonResponse::<models::Stack>::build().bad_request(err))?;
     stack_data.user_token = Some(user.id.clone());
     stack_data.user_email = Some(user.email.clone());
-    stack_data.stack_code = stack_data.custom.custom_stack_code.clone();
 
     mq_manager
         .publish_and_confirm(

From 0c8936690caf53e6e3e31ff7ca5039460be21c92 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Sun, 7 Jan 2024 08:51:14 +0200
Subject: [PATCH 19/25] issue-auth Option->Result for DcBuilder::build

---
 src/helpers/stack/builder.rs | 74 ++++++++++++++++--------------------
 src/routes/stack/compose.rs  | 18 +++++----
 src/routes/stack/deploy.rs   |  5 +--
 3 files changed, 45 insertions(+), 52 deletions(-)

diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index eeca4a2a..1b7c0a9e 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -124,7 +124,7 @@ fn is_named_docker_volume(volume: &str) -> bool {
 }
 
 trait TryIntoService {
-    fn try_into_service(&self) -> Service;
+    fn try_into_service(&self) -> Service; //todo transform into try_into
 }
 
 impl TryIntoService for App {
@@ -245,64 +245,56 @@ impl DcBuilder {
     }
 
     #[tracing::instrument(name = "building stack")]
-    pub fn build(&self) -> Option<String> {
-        tracing::debug!("Start build docker compose from {:?}", &self.stack.body);
+    pub fn build(&self) -> Result<String, String> {
         let mut compose_content = Compose {
             version: Some("3.8".to_string()),
             ..Default::default()
         };
-        let _stack = serde_json::from_value::<StackForm>(self.stack.body.clone());
+        let apps = serde_json::from_value::<StackForm>(self.stack.body.clone())
+            .map_err(|err| format!("{:?}", err))?;
         let mut services = IndexMap::new();
         let mut named_volumes = IndexMap::default();
 
-        match _stack {
-            Ok(apps) => {
-                for app_type in &apps.custom.web {
-                    let service = app_type.app.try_into_service();
-                    services.insert(app_type.app.code.clone().to_owned(), Some(service));
-                    named_volumes.extend(extract_named_volumes(app_type.app.clone()));
-                }
-
-                if let Some(srvs) = apps.custom.service {
-                    for app_type in srvs {
-                        let service = app_type.app.try_into_service();
-                        services.insert(app_type.app.code.clone().to_owned(), Some(service));
-                        named_volumes.extend(extract_named_volumes(app_type.app.clone()));
-                    }
-                }
-
-                if let Some(features) = apps.custom.feature {
-                    for app_type in features {
-                        let service = app_type.app.try_into_service();
-                        services.insert(app_type.app.code.clone().to_owned(), Some(service));
-                        named_volumes.extend(extract_named_volumes(app_type.app.clone()));
-                    }
-                }
-
-                let networks = apps.custom.networks.clone();
-                compose_content.networks = ComposeNetworks(networks.into());
-
-                if !named_volumes.is_empty() {
-                    compose_content.volumes = TopLevelVolumes(named_volumes);
-                }
+        for app_type in &apps.custom.web {
+            let service = app_type.app.try_into_service(); //todo
+            services.insert(app_type.app.code.clone().to_owned(), Some(service));
+            named_volumes.extend(extract_named_volumes(app_type.app.clone()));
+        }
+
+        if let Some(srvs) = apps.custom.service {
+            for app_type in srvs {
+                let service = app_type.app.try_into_service(); //todo
+                services.insert(app_type.app.code.clone().to_owned(), Some(service));
+                named_volumes.extend(extract_named_volumes(app_type.app.clone()));
             }
-            Err(e) => {
-                tracing::debug!("Unpack stack form error {:?}", e);
+        }
+
+        if let Some(features) = apps.custom.feature {
+            for app_type in features {
+                let service = app_type.app.try_into_service(); //todo
+                services.insert(app_type.app.code.clone().to_owned(), Some(service));
+                named_volumes.extend(extract_named_volumes(app_type.app.clone()));
             }
         }
+
+        let networks = apps.custom.networks.clone();
+        compose_content.networks = ComposeNetworks(networks.into());
+
+        if !named_volumes.is_empty() {
+            compose_content.volumes = TopLevelVolumes(named_volumes);
+        }
+
         tracing::debug!("services {:?}", &services);
         compose_content.services = Services(services);
 
         let fname = format!("./files/{}.yml", self.stack.stack_id);
         tracing::debug!("Saving docker compose to file {:?}", fname);
         let target_file = std::path::Path::new(fname.as_str());
-        // serialize to string
         let serialized = serde_yaml::to_string(&compose_content)
-            .map_err(|err| panic!("Failed to serialize docker-compose file: {}", err))
-            .unwrap();
+            .map_err(|err| format!("Failed to serialize docker-compose file: {}", err))?;
 
-        std::fs::write(target_file, serialized.clone()).map_err(|err| panic!("{}", err));
+        std::fs::write(target_file, serialized.clone()).map_err(|err| format!("{}", err))?;
 
-        Some(serialized)
+        Ok(serialized)
     }
 }
diff --git a/src/routes/stack/compose.rs b/src/routes/stack/compose.rs
index 4e58141e..b8b51233 100644
--- a/src/routes/stack/compose.rs
+++ b/src/routes/stack/compose.rs
@@ -26,10 +26,11 @@ pub async fn add(
         })?;
 
     let id = stack.id.clone();
-    let fc = DcBuilder::new(stack).build().ok_or_else(|| {
-        tracing::error!("Error. Compose builder returned an empty string");
-        JsonResponse::<models::Stack>::build().internal_server_error("troubles at building")
-    })?;
+    let fc = DcBuilder::new(stack)
+        .build()
+        .map_err(|err| {
+            JsonResponse::<models::Stack>::build().internal_server_error(err)
+        })?;
 
     Ok(JsonResponse::build().set_id(id).set_item(fc).ok("Success"))
 }
@@ -52,10 +53,11 @@ pub async fn admin(
         })?;
 
     let id = stack.id.clone();
-    let fc = DcBuilder::new(stack).build().ok_or_else(|| {
-        tracing::error!("Error. Compose builder returned an empty string");
-        JsonResponse::<models::Stack>::build().internal_server_error("troubles at building")
-    })?;
+    let fc = DcBuilder::new(stack)
+        .build()
+        .map_err(|err| {
+            JsonResponse::<models::Stack>::build().internal_server_error(err)
+        })?;
 
     Ok(JsonResponse::build().set_id(id).set_item(fc).ok("Success"))
 }
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 80f49858..005ac531 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -30,9 +30,8 @@ pub async fn add(
 
     let id = stack.id.clone();
     let dc = DcBuilder::new(stack);
-    dc.build().ok_or_else(|| {
-        tracing::error!("Error. Compose builder returned an empty string");
-        JsonResponse::<models::Stack>::build().internal_server_error("")
+    dc.build().map_err(|err| {
+        JsonResponse::<models::Stack>::build().internal_server_error(err)
     })?;
 
     let mut stack_data = forms::StackPayload::try_from(&dc.stack)

From 3bad20d8927ee81c9e2a8b1b05ca9667e79344e6 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Sun, 7 Jan 2024 09:48:53 +0200
Subject: [PATCH 20/25] issue-auth StackForm::try_from

---
 src/forms/mod.rs             |  2 ++
 src/forms/stack.rs           | 46 ---------------------------
 src/forms/stack_form.rs      | 61 ++++++++++++++++++++++++++++++++++++
 src/helpers/stack/builder.rs |  4 +--
 src/routes/stack/add.rs      | 14 ++++-----
 src/routes/stack/update.rs   |  8 ++---
 6 files changed, 76 insertions(+), 59 deletions(-)
 create mode 100644 src/forms/stack_form.rs

diff --git a/src/forms/mod.rs b/src/forms/mod.rs
index cee99fef..a1379dec 100644
--- a/src/forms/mod.rs
+++ b/src/forms/mod.rs
@@ -2,7 +2,9 @@ mod rating;
 pub mod stack;
 pub mod user;
 mod stack_payload;
+mod stack_form;
 
 pub use rating::*;
 pub use stack::*;
 pub use stack_payload::*;
+pub use stack_form::*;
diff --git a/src/forms/stack.rs b/src/forms/stack.rs
index febd2af9..a9fc3ccc 100644
--- a/src/forms/stack.rs
+++ b/src/forms/stack.rs
@@ -79,52 +79,6 @@ impl AsRef<DockerImage> for App {
     }
 }
 
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
-pub struct StackForm {
-    // #[validate(min_length=2)]
-    // #[validate(max_length=255)]
-    #[serde(rename = "commonDomain")]
-    pub common_domain: Option<String>,
-    pub domain_list: Option<DomainList>,
-    #[validate(min_length = 2)]
-    #[validate(max_length = 255)]
-    pub stack_code: Option<String>,
-    #[validate(min_length = 2)]
-    #[validate(max_length = 50)]
-    pub region: String,
-    #[validate(min_length = 2)]
-    #[validate(max_length = 50)]
-    pub zone: Option<String>,
-    #[validate(min_length = 2)]
-    #[validate(max_length = 50)]
-    pub server: String,
-    #[validate(min_length = 2)]
-    #[validate(max_length = 50)]
-    pub os: String,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    pub ssl: String,
-    pub vars: Option<Vec<Var>>,
-    pub integrated_features: Option<Vec<Value>>,
-    pub extended_features: Option<Vec<Value>>,
-    pub subscriptions: Option<Vec<String>>,
-    pub form_app: Option<Vec<String>>,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    pub disk_type: Option<String>,
-    pub save_token: bool,
-    #[validate(min_length = 10)]
-    #[validate(max_length = 255)]
-    pub cloud_token: String,
-    #[validate(min_length = 2)]
-    #[validate(max_length = 50)]
-    pub provider: String,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    pub selected_plan: String,
-    pub custom: Custom,
-}
-
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct DomainList {}
diff --git a/src/forms/stack_form.rs b/src/forms/stack_form.rs
new file mode 100644
index 00000000..989205af
--- /dev/null
+++ b/src/forms/stack_form.rs
@@ -0,0 +1,61 @@
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use serde_valid::Validate;
+use std::collections::HashMap;
+use std::fmt;
+use crate::models;
+use crate::forms;
+
+#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
+pub struct StackForm {
+    // #[validate(min_length=2)]
+    // #[validate(max_length=255)]
+    #[serde(rename = "commonDomain")]
+    pub common_domain: Option<String>,
+    pub domain_list: Option<forms::DomainList>,
+    #[validate(min_length = 2)]
+    #[validate(max_length = 255)]
+    pub stack_code: Option<String>,
+    #[validate(min_length = 2)]
+    #[validate(max_length = 50)]
+    pub region: String,
+    #[validate(min_length = 2)]
+    #[validate(max_length = 50)]
+    pub zone: Option<String>,
+    #[validate(min_length = 2)]
+    #[validate(max_length = 50)]
+    pub server: String,
+    #[validate(min_length = 2)]
+    #[validate(max_length = 50)]
+    pub os: String,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    pub ssl: String,
+    pub vars: Option<Vec<forms::Var>>,
+    pub integrated_features: Option<Vec<Value>>,
+    pub extended_features: Option<Vec<Value>>,
+    pub subscriptions: Option<Vec<String>>,
+    pub form_app: Option<Vec<String>>,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    pub disk_type: Option<String>,
+    pub save_token: bool,
+    #[validate(min_length = 10)]
+    #[validate(max_length = 255)]
+    pub cloud_token: String,
+    #[validate(min_length = 2)]
+    #[validate(max_length = 50)]
+    pub provider: String,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    pub selected_plan: String,
+    pub custom: forms::Custom,
+}
+
+impl TryFrom<&models::Stack> for StackForm {
+    type Error = String;
+
+    fn try_from(stack: &models::Stack) -> Result<Self, Self::Error> {
+        serde_json::from_value::<StackForm>(stack.body.clone()).map_err(|err| format!("{:?}", err))
+    }
+}
diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 1b7c0a9e..ea91da78 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -250,8 +250,8 @@ impl DcBuilder {
             version: Some("3.8".to_string()),
             ..Default::default()
         };
-        let apps = serde_json::from_value::<StackForm>(self.stack.body.clone())
-            .map_err(|err| format!("{:?}", err))?;
+
+        let apps = StackForm::try_from(&self.stack)?; 
         let mut services = IndexMap::new();
         let mut named_volumes = IndexMap::default();
 
diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs
index 7ed5a09b..bcf281c7 100644
--- a/src/routes/stack/add.rs
+++ b/src/routes/stack/add.rs
@@ -1,5 +1,5 @@
 use crate::db;
-use crate::forms::stack::StackForm;
+use crate::forms;
 use crate::helpers::JsonResponse;
 use crate::models;
 use actix_web::Error;
@@ -27,8 +27,8 @@ pub async fn add(
 
     check_if_stack_exists(pg_pool.get_ref(), &stack_name).await?;
 
-    let body: Value = serde_json::to_value::<StackForm>(form)
-        .or(serde_json::to_value::<StackForm>(StackForm::default()))
+    let body: Value = serde_json::to_value::<forms::StackForm>(form)
+        .or(serde_json::to_value::<forms::StackForm>(forms::StackForm::default()))
         .unwrap();
 
     let stack = models::Stack::new(user.id.clone(), stack_name, body);
@@ -53,17 +53,17 @@ async fn check_if_stack_exists(pg_pool: &PgPool, stack_name: &String) -> Result<
         })
 }
 
-async fn body_into_form(body: Bytes) -> Result<StackForm, Error> {
+async fn body_into_form(body: Bytes) -> Result<forms::StackForm, Error> {
     let body_bytes = actix_web::body::to_bytes(body).await.unwrap();
     let body_str = str::from_utf8(&body_bytes)
-        .map_err(|err| JsonResponse::<StackForm>::build().internal_server_error(err.to_string()))?;
+        .map_err(|err| JsonResponse::<forms::StackForm>::build().internal_server_error(err.to_string()))?;
     let deserializer = &mut serde_json::Deserializer::from_str(body_str);
     serde_path_to_error::deserialize(deserializer)
         .map_err(|err| {
             let msg = format!("{}:{:?}", err.path().to_string(), err);
-            JsonResponse::<StackForm>::build().bad_request(msg)
+            JsonResponse::<forms::StackForm>::build().bad_request(msg)
         })
-        .and_then(|form: StackForm| {
+        .and_then(|form: forms::StackForm| {
             if !form.validate().is_ok() {
                 let errors = form.validate().unwrap_err();
                 let err_msg = format!("Invalid data received {:?}", &errors.to_string());
diff --git a/src/routes/stack/update.rs b/src/routes/stack/update.rs
index 229553f7..a22be0c2 100644
--- a/src/routes/stack/update.rs
+++ b/src/routes/stack/update.rs
@@ -1,4 +1,4 @@
-use crate::forms::stack::StackForm;
+use crate::forms;
 use crate::helpers::JsonResponse;
 use crate::models;
 use crate::models::user::User;
@@ -16,7 +16,7 @@ use uuid::Uuid;
 #[post("/{id}")]
 pub async fn update(
     path: web::Path<(i32,)>,
-    form: web::Json<StackForm>,
+    form: web::Json<forms::StackForm>,
     user: web::ReqData<Arc<User>>,
     pg_pool: Data<PgPool>,
 ) -> Result<impl Responder> {
@@ -67,11 +67,11 @@ pub async fn update(
         return Err(JsonResponse::<models::Stack>::build().bad_request(errors.to_string()));
     }
 
-    let body: Value = match serde_json::to_value::<StackForm>(form.into_inner()) {
+    let body: Value = match serde_json::to_value::<forms::StackForm>(form.into_inner()) {
         Ok(body) => body,
         Err(err) => {
             tracing::error!("Request_id {} error unwrap body {:?}", request_id, err);
-            serde_json::to_value::<StackForm>(StackForm::default()).unwrap()
+            serde_json::to_value::<forms::StackForm>(forms::StackForm::default()).unwrap()
         }
     };
 

From 27e66b97acd49e65bbcf488d86bd2656edfb87ca Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Sun, 7 Jan 2024 22:58:15 +0200
Subject: [PATCH 21/25] issue-auth Networks::try_from

---
 src/helpers/stack/builder.rs | 44 +++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index ea91da78..4acf9631 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -84,14 +84,18 @@ impl TryInto<Port> for stack::Port {
     }
 }
 
-impl TryInto<Networks> for stack::ServiceNetworks {
+impl TryFrom<&stack::ServiceNetworks> for Networks {
     type Error = ();
-    fn try_into(self) -> Result<Networks, Self::Error> {
-        let mut networks = vec!["default_network".to_string()];
-        if self.network.is_some() {
-            networks.append(&mut self.network.unwrap());
-        }
-        Ok(Networks::Simple(networks))
+
+    fn try_from(serviceNetworks: &stack::ServiceNetworks) -> Result<Networks, Self::Error> {
+        let mut result = vec!["default_network".to_string()];
+        serviceNetworks.network.as_ref().map(|networks| {
+            for n in networks {
+                result.push(n.to_string());
+            }
+        });
+
+        Ok(Networks::Simple(result))
     }
 }
 
@@ -123,20 +127,18 @@ fn is_named_docker_volume(volume: &str) -> bool {
     is_alphanumeric && does_not_contain_slash
 }
 
-trait TryIntoService {
-    fn try_into_service(&self) -> Service; //todo transform into try_into
-}
+impl TryFrom<&App> for Service {
+    type Error = String;
 
-impl TryIntoService for App {
-    fn try_into_service(&self) -> Service {
+    fn try_from(app: &App) -> Result<Self, Self::Error> {
         let mut service = Service {
-            image: Some(self.docker_image.to_string()),
+            image: Some(app.docker_image.to_string()),
             ..Default::default()
         };
 
-        let networks: Networks = self.network.clone().try_into().unwrap_or_default();
+        let networks = Networks::try_from(&app.network).unwrap_or_default();
 
-        let ports: Vec<Port> = self
+        let ports: Vec<Port> = app //todo
             .ports
             .clone()
             .unwrap_or_default()
@@ -144,7 +146,7 @@ impl TryIntoService for App {
             .map(|x| x.try_into().unwrap())
             .collect();
 
-        let volumes: Vec<AdvancedVolumes> = self
+        let volumes: Vec<AdvancedVolumes> = app //todo
             .volumes
             .clone()
             .unwrap_or_default()
@@ -153,7 +155,7 @@ impl TryIntoService for App {
             .collect();
 
         let mut envs = IndexMap::new();
-        for item in self.environment.environment.clone().unwrap_or_default() {
+        for item in app.environment.environment.clone().unwrap_or_default() { //todo
             let items = item
                 .into_iter()
                 .map(|(k, v)| (k, Some(SingleValue::String(v.clone()))))
@@ -168,7 +170,7 @@ impl TryIntoService for App {
         service.volumes = Volumes::Advanced(volumes);
         service.environment = Environment::KvPair(envs);
 
-        service
+        Ok(service)
     }
 }
 
@@ -256,14 +258,14 @@ impl DcBuilder {
         let mut named_volumes = IndexMap::default();
 
         for app_type in &apps.custom.web {
-            let service = app_type.app.try_into_service(); //todo
+            let service = Service::try_from(&app_type.app)?;
             services.insert(app_type.app.code.clone().to_owned(), Some(service));
             named_volumes.extend(extract_named_volumes(app_type.app.clone()));
         }
 
         if let Some(srvs) = apps.custom.service {
             for app_type in srvs {
-                let service = app_type.app.try_into_service(); //todo
+                let service = Service::try_from(&app_type.app)?;
                 services.insert(app_type.app.code.clone().to_owned(), Some(service));
                 named_volumes.extend(extract_named_volumes(app_type.app.clone()));
             }
@@ -271,7 +273,7 @@ impl DcBuilder {
 
         if let Some(features) = apps.custom.feature {
             for app_type in features {
-                let service = app_type.app.try_into_service(); //todo
+                let service = Service::try_from(&app_type.app)?;
                 services.insert(app_type.app.code.clone().to_owned(), Some(service));
                 named_volumes.extend(extract_named_volumes(app_type.app.clone()));
             }

From 3e01e8741207c96b4c2963de66365cf3368b8493 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Sun, 7 Jan 2024 23:50:43 +0200
Subject: [PATCH 22/25] issue-auth TryInto<Port>

---
 src/helpers/stack/builder.rs | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 4acf9631..55314ce2 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -1,4 +1,5 @@
 use crate::forms::{stack, App, StackForm, Volume, Web};
+use crate::forms;
 use crate::helpers::stack::dctypes::{
     AdvancedVolumes, Compose, ComposeNetwork, ComposeNetworkSettingDetails, ComposeNetworks,
     ComposeVolume, Entrypoint, Environment, MapOrEmpty, NetworkSettings, Networks, Port, Ports,
@@ -57,23 +58,21 @@ impl TryInto<AdvancedVolumes> for Volume {
     }
 }
 
-impl TryInto<Port> for stack::Port {
+impl TryInto<Port> for &stack::Port {
     type Error = String;
     fn try_into(self) -> Result<Port, Self::Error> {
         let cp = self
             .container_port
-            .clone()
-            .unwrap_or("".to_string())
-            .parse::<u16>()
+            .as_ref()
+            .map_or(Ok(0u16), |s| s.parse::<u16>())
             .map_err(|err| "Could not parse port".to_string())?;
+
         let hp = self
             .host_port
-            .clone()
-            .unwrap_or("".to_string())
-            .parse::<u16>()
+            .as_ref()
+            .map_or(Ok(0u16), |s| s.parse::<u16>())
             .map_err(|err| "Could not parse port".to_string())?;
 
-        tracing::debug!("Port conversion result: cp: {:?} hp: {:?}", cp, hp);
         Ok(Port {
             target: cp,
             host_ip: None,
@@ -138,13 +137,16 @@ impl TryFrom<&App> for Service {
 
         let networks = Networks::try_from(&app.network).unwrap_or_default();
 
-        let ports: Vec<Port> = app //todo
-            .ports
-            .clone()
-            .unwrap_or_default()
-            .into_iter()
-            .map(|x| x.try_into().unwrap())
-            .collect();
+        let ports: Vec<Port> = match &app.ports {
+            Some(ports) => {
+                let mut collector = vec![];
+                for port in ports {
+                    collector.push(port.try_into()?);
+                }
+                collector
+            }
+            None => vec![]
+        };
 
         let volumes: Vec<AdvancedVolumes> = app //todo
             .volumes

From bddf56fc8e28e1f12964bfe8f82b78bb0bc61cc5 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Mon, 8 Jan 2024 21:47:11 +0200
Subject: [PATCH 23/25] issue_auth stack_app

---
 src/forms/mod.rs             |   4 ++
 src/forms/stack.rs           | 105 ++---------------------------
 src/forms/stack_app.rs       | 126 +++++++++++++++++++++++++++++++++++
 src/forms/stack_custom.rs    |  81 ++++++++++++++++++++++
 src/forms/stack_payload.rs   |   2 +-
 src/helpers/stack/builder.rs |  69 +------------------
 6 files changed, 218 insertions(+), 169 deletions(-)
 create mode 100644 src/forms/stack_app.rs
 create mode 100644 src/forms/stack_custom.rs

diff --git a/src/forms/mod.rs b/src/forms/mod.rs
index a1379dec..b7ef2dcf 100644
--- a/src/forms/mod.rs
+++ b/src/forms/mod.rs
@@ -3,8 +3,12 @@ pub mod stack;
 pub mod user;
 mod stack_payload;
 mod stack_form;
+mod stack_custom;
+mod stack_app;
 
 pub use rating::*;
 pub use stack::*;
 pub use stack_payload::*;
 pub use stack_form::*;
+pub use stack_custom::*;
+pub use stack_app::*;
diff --git a/src/forms/stack.rs b/src/forms/stack.rs
index a9fc3ccc..d62fa319 100644
--- a/src/forms/stack.rs
+++ b/src/forms/stack.rs
@@ -3,6 +3,7 @@ use serde_json::Value;
 use serde_valid::Validate;
 use std::collections::HashMap;
 use std::fmt;
+use crate::forms;
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
 pub struct Role {
@@ -73,12 +74,6 @@ impl fmt::Display for DockerImage {
     }
 }
 
-impl AsRef<DockerImage> for App {
-    fn as_ref(&self) -> &DockerImage {
-        &self.docker_image
-    }
-}
-
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct DomainList {}
@@ -91,103 +86,11 @@ pub struct Var {}
 pub struct Price {
     pub value: f64,
 }
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
-pub struct Custom {
-    pub web: Vec<Web>,
-    pub feature: Option<Vec<Feature>>,
-    pub service: Option<Vec<Service>>,
-    #[validate(minimum = 0)]
-    #[validate(maximum = 10)]
-    pub servers_count: u32,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    pub custom_stack_code: String,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 255)]
-    pub project_git_url: Option<String>,
-    pub custom_stack_category: Option<Vec<String>>,
-    pub custom_stack_short_description: Option<String>,
-    pub custom_stack_description: Option<String>,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 255)]
-    pub project_name: String,
-    pub project_overview: Option<String>,
-    pub project_description: Option<String>,
-    #[serde(flatten)]
-    pub networks: ComposeNetworks, // all networks
-}
-
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
 pub struct Network {
     name: String,
 }
 
-#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
-pub struct App {
-    #[serde(rename = "_etag")]
-    #[validate(min_length = 3)]
-    #[validate(max_length = 255)]
-    pub etag: Option<String>,
-    #[serde(rename = "_id")]
-    pub id: u32,
-    #[serde(rename = "_created")]
-    pub created: Option<String>,
-    #[serde(rename = "_updated")]
-    pub updated: Option<String>,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    pub name: String,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    pub code: String,
-    #[validate(min_length = 3)]
-    #[validate(max_length = 50)]
-    #[serde(rename = "type")]
-    pub type_field: String,
-    #[serde(flatten)]
-    pub role: Role,
-    pub default: Option<bool>,
-    pub versions: Option<Vec<Version>>,
-    #[serde(flatten)]
-    pub docker_image: DockerImage,
-    #[serde(flatten)]
-    pub requirements: Requirements,
-    #[validate(minimum = 1)]
-    pub popularity: Option<u32>,
-    pub commercial: Option<bool>,
-    pub subscription: Option<Value>,
-    pub autodeploy: Option<bool>,
-    pub suggested: Option<bool>,
-    pub dependency: Option<Value>,
-    pub avoid_render: Option<bool>,
-    pub price: Option<Price>,
-    pub icon: Option<Icon>,
-    pub domain: Option<String>,
-    pub category_id: Option<u32>,
-    pub parent_app_id: Option<u32>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub descr: Option<String>,
-    pub full_description: Option<String>,
-    pub description: Option<String>,
-    pub plan_type: Option<String>,
-    pub ansible_var: Option<String>,
-    pub repo_dir: Option<String>,
-    pub url_app: Option<String>,
-    pub url_git: Option<String>,
-    pub restart: Option<String>,
-    pub volumes: Option<Vec<Volume>>,
-    #[serde(flatten)]
-    pub environment: Environment,
-    #[serde(flatten)]
-    pub network: ServiceNetworks,
-    // #[serde(flatten)]
-    // pub ports: Ports,
-    #[serde(rename(deserialize = "sharedPorts"))]
-    #[serde(rename(serialize = "shared_ports"))]
-    #[serde(alias = "shared_ports")]
-    pub ports: Option<Vec<Port>>,
-}
-
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Environment {
     pub(crate) environment: Option<Vec<HashMap<String, String>>>,
@@ -217,7 +120,7 @@ pub struct ComposeNetworks {
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Web {
     #[serde(flatten)]
-    pub app: App,
+    pub app: forms::App,
     pub custom: Option<bool>,
     pub main: bool,
 }
@@ -229,7 +132,7 @@ pub struct Feature {
     // #[serde(alias = "shared_ports")]
     // pub shared_ports: Option<Vec<Port>>,
     #[serde(flatten)]
-    pub app: App,
+    pub app: forms::App,
 }
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
@@ -239,7 +142,7 @@ pub struct Service {
     // #[serde(alias = "shared_ports")]
     // pub shared_ports: Option<Vec<Port>>,
     #[serde(flatten)]
-    pub(crate) app: App,
+    pub(crate) app: forms::App,
 }
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
diff --git a/src/forms/stack_app.rs b/src/forms/stack_app.rs
new file mode 100644
index 00000000..8ebdec38
--- /dev/null
+++ b/src/forms/stack_app.rs
@@ -0,0 +1,126 @@
+use crate::forms;
+use crate::helpers::stack::dctypes;
+use indexmap::IndexMap;
+use serde_json::Value;
+use serde::{Deserialize, Serialize};
+use serde_valid::Validate;
+
+#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
+pub struct App {
+    #[serde(rename = "_etag")]
+    #[validate(min_length = 3)]
+    #[validate(max_length = 255)]
+    pub etag: Option<String>,
+    #[serde(rename = "_id")]
+    pub id: u32,
+    #[serde(rename = "_created")]
+    pub created: Option<String>,
+    #[serde(rename = "_updated")]
+    pub updated: Option<String>,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    pub name: String,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    pub code: String,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    #[serde(rename = "type")]
+    pub type_field: String,
+    #[serde(flatten)]
+    pub role: forms::Role,
+    pub default: Option<bool>,
+    pub versions: Option<Vec<forms::Version>>,
+    #[serde(flatten)]
+    pub docker_image: forms::DockerImage,
+    #[serde(flatten)]
+    pub requirements: forms::Requirements,
+    #[validate(minimum = 1)]
+    pub popularity: Option<u32>,
+    pub commercial: Option<bool>,
+    pub subscription: Option<Value>,
+    pub autodeploy: Option<bool>,
+    pub suggested: Option<bool>,
+    pub dependency: Option<Value>,
+    pub avoid_render: Option<bool>,
+    pub price: Option<forms::Price>,
+    pub icon: Option<forms::Icon>,
+    pub domain: Option<String>,
+    pub category_id: Option<u32>,
+    pub parent_app_id: Option<u32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub descr: Option<String>,
+    pub full_description: Option<String>,
+    pub description: Option<String>,
+    pub plan_type: Option<String>,
+    pub ansible_var: Option<String>,
+    pub repo_dir: Option<String>,
+    pub url_app: Option<String>,
+    pub url_git: Option<String>,
+    pub restart: Option<String>,
+    pub volumes: Option<Vec<forms::Volume>>,
+    #[serde(flatten)]
+    pub environment: forms::Environment,
+    #[serde(flatten)]
+    pub network: forms::ServiceNetworks,
+    // #[serde(flatten)]
+    // pub ports: Ports,
+    #[serde(rename(deserialize = "sharedPorts"))]
+    #[serde(rename(serialize = "shared_ports"))]
+    #[serde(alias = "shared_ports")]
+    pub ports: Option<Vec<forms::Port>>,
+}
+
+impl App {
+    pub fn named_volumes(&self) -> IndexMap<String, dctypes::MapOrEmpty<dctypes::ComposeVolume>> { //todo Result
+        let mut named_volumes = IndexMap::default();
+
+        let volumes = &self.volumes;
+        if volumes.is_none() {
+            return named_volumes;
+        }
+
+        let volumes = volumes
+            .clone() //todo remove it
+            .unwrap()
+            .into_iter()
+            .filter(|volume| is_named_docker_volume(volume.host_path.clone().unwrap().as_str()))
+            .map(|volume| {
+                let k = volume.host_path.clone().unwrap();
+                (
+                    k.clone(),
+                    dctypes::MapOrEmpty::Map(dctypes::ComposeVolume {
+                        driver: None,
+                        driver_opts: Default::default(),
+                        external: None,
+                        labels: Default::default(),
+                        name: Some(k.clone()),
+                    }),
+                    )
+            })
+        .collect::<IndexMap<String, dctypes::MapOrEmpty<dctypes::ComposeVolume>>>();
+
+        named_volumes.extend(volumes);
+        // tracing::debug!("Named volumes: {:?}", named_volumes);
+
+        named_volumes
+    }
+}
+
+impl AsRef<forms::DockerImage> for App {
+    fn as_ref(&self) -> &forms::DockerImage {
+        &self.docker_image
+    }
+}
+
+
+fn is_named_docker_volume(volume: &str) -> bool { //todo
+    // Docker named volumes typically don't contain special characters or slashes
+    // They are alphanumeric and may include underscores or hyphens
+    let is_alphanumeric = volume
+        .chars()
+        .all(|c| c.is_alphanumeric() || c == '_' || c == '-');
+    let does_not_contain_slash = !volume.contains('/');
+    is_alphanumeric && does_not_contain_slash
+}
+
diff --git a/src/forms/stack_custom.rs b/src/forms/stack_custom.rs
new file mode 100644
index 00000000..b6277a2f
--- /dev/null
+++ b/src/forms/stack_custom.rs
@@ -0,0 +1,81 @@
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use serde_valid::Validate;
+use crate::forms;
+use indexmap::IndexMap;
+use crate::helpers::stack::dctypes;
+
+#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
+pub struct Custom {
+    pub web: Vec<forms::Web>,
+    pub feature: Option<Vec<forms::Feature>>,
+    pub service: Option<Vec<forms::Service>>,
+    #[validate(minimum = 0)]
+    #[validate(maximum = 10)]
+    pub servers_count: u32,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 50)]
+    pub custom_stack_code: String,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 255)]
+    pub project_git_url: Option<String>,
+    pub custom_stack_category: Option<Vec<String>>,
+    pub custom_stack_short_description: Option<String>,
+    pub custom_stack_description: Option<String>,
+    #[validate(min_length = 3)]
+    #[validate(max_length = 255)]
+    pub project_name: String,
+    pub project_overview: Option<String>,
+    pub project_description: Option<String>,
+    #[serde(flatten)]
+    pub networks: forms::ComposeNetworks, // all networks
+}
+
+impl Custom {
+    pub fn services(&self) -> Result<IndexMap<String, Option<dctypes::Service>>, String> {
+        let mut services = IndexMap::new();
+
+        for app_type in &self.web {
+            let service = dctypes::Service::try_from(&app_type.app)?;
+            services.insert(app_type.app.code.clone().to_owned(), Some(service));
+        }
+
+        if let Some(srvs) = &self.service {
+            for app_type in srvs {
+                let service = dctypes::Service::try_from(&app_type.app)?;
+                services.insert(app_type.app.code.clone().to_owned(), Some(service));
+            }
+        }
+
+        if let Some(features) = &self.feature {
+            for app_type in features {
+                let service = dctypes::Service::try_from(&app_type.app)?;
+                services.insert(app_type.app.code.clone().to_owned(), Some(service));
+            }
+        }
+
+        Ok(services)
+    }
+
+    pub fn named_volumes(&self) -> Result<IndexMap<String, dctypes::MapOrEmpty<dctypes::ComposeVolume>>, String> {
+        let mut named_volumes = IndexMap::new();
+
+        for app_type in &self.web {
+            named_volumes.extend(app_type.app.named_volumes());
+        }
+
+        if let Some(srvs) = &self.service {
+            for app_type in srvs {
+                named_volumes.extend(app_type.app.named_volumes());
+            }
+        }
+
+        if let Some(features) = &self.feature {
+            for app_type in features {
+                named_volumes.extend(app_type.app.named_volumes());
+            }
+        }
+
+        Ok(named_volumes)
+    }
+}
diff --git a/src/forms/stack_payload.rs b/src/forms/stack_payload.rs
index 521642d5..e7a56cd0 100644
--- a/src/forms/stack_payload.rs
+++ b/src/forms/stack_payload.rs
@@ -35,7 +35,7 @@ pub struct StackPayload {
     pub stack_code: String,
     #[serde(rename = "selected_plan")]
     pub selected_plan: String,
-    pub custom: forms::stack::Custom,
+    pub custom: forms::Custom,
 }
 
 impl TryFrom<&models::Stack> for StackPayload {
diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 55314ce2..4457cd2c 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -116,16 +116,6 @@ fn convert_shared_ports(ports: Option<Vec<stack::Port>>) -> Result<Vec<Port>, St
     Ok(_ports)
 }
 
-fn is_named_docker_volume(volume: &str) -> bool {
-    // Docker named volumes typically don't contain special characters or slashes
-    // They are alphanumeric and may include underscores or hyphens
-    let is_alphanumeric = volume
-        .chars()
-        .all(|c| c.is_alphanumeric() || c == '_' || c == '-');
-    let does_not_contain_slash = !volume.contains('/');
-    is_alphanumeric && does_not_contain_slash
-}
-
 impl TryFrom<&App> for Service {
     type Error = String;
 
@@ -207,39 +197,6 @@ impl Into<IndexMap<String, MapOrEmpty<NetworkSettings>>> for stack::ComposeNetwo
     }
 }
 
-pub fn extract_named_volumes(app: App) -> IndexMap<String, MapOrEmpty<ComposeVolume>> {
-    let mut named_volumes = IndexMap::default();
-
-    let volumes = app.volumes;
-    if volumes.is_none() {
-        return named_volumes;
-    }
-
-    let volumes = volumes
-        .unwrap()
-        .into_iter()
-        .filter(|volume| is_named_docker_volume(volume.host_path.clone().unwrap().as_str()))
-        .map(|volume| {
-            let k = volume.host_path.clone().unwrap();
-            (
-                k.clone(),
-                MapOrEmpty::Map(ComposeVolume {
-                    driver: None,
-                    driver_opts: Default::default(),
-                    external: None,
-                    labels: Default::default(),
-                    name: Some(k.clone()),
-                }),
-            )
-        })
-        .collect::<IndexMap<String, MapOrEmpty<ComposeVolume>>>();
-
-    named_volumes.extend(volumes);
-    // tracing::debug!("Named volumes: {:?}", named_volumes);
-
-    named_volumes
-}
-
 impl DcBuilder {
     pub fn new(stack: models::Stack) -> Self {
         DcBuilder {
@@ -256,30 +213,8 @@ impl DcBuilder {
         };
 
         let apps = StackForm::try_from(&self.stack)?; 
-        let mut services = IndexMap::new();
-        let mut named_volumes = IndexMap::default();
-
-        for app_type in &apps.custom.web {
-            let service = Service::try_from(&app_type.app)?;
-            services.insert(app_type.app.code.clone().to_owned(), Some(service));
-            named_volumes.extend(extract_named_volumes(app_type.app.clone()));
-        }
-
-        if let Some(srvs) = apps.custom.service {
-            for app_type in srvs {
-                let service = Service::try_from(&app_type.app)?;
-                services.insert(app_type.app.code.clone().to_owned(), Some(service));
-                named_volumes.extend(extract_named_volumes(app_type.app.clone()));
-            }
-        }
-
-        if let Some(features) = apps.custom.feature {
-            for app_type in features {
-                let service = Service::try_from(&app_type.app)?;
-                services.insert(app_type.app.code.clone().to_owned(), Some(service));
-                named_volumes.extend(extract_named_volumes(app_type.app.clone()));
-            }
-        }
+        let  services = apps.custom.services()?;
+        let  named_volumes = apps.custom.named_volumes()?;
 
         let networks = apps.custom.networks.clone();
         compose_content.networks = ComposeNetworks(networks.into());

From b8334e4b6818dce890553ada7790746b10a9705d Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Wed, 10 Jan 2024 17:53:32 +0200
Subject: [PATCH 24/25] issue-auth Volume::is_named_docker

---
 src/forms/stack.rs     | 13 +++++++++++
 src/forms/stack_app.rs | 52 +++++++++++++-----------------------------
 2 files changed, 29 insertions(+), 36 deletions(-)

diff --git a/src/forms/stack.rs b/src/forms/stack.rs
index d62fa319..5a1296c1 100644
--- a/src/forms/stack.rs
+++ b/src/forms/stack.rs
@@ -101,6 +101,19 @@ pub struct Volume {
     pub(crate) container_path: Option<String>,
 }
 
+impl Volume {
+    pub fn is_named_docker(&self) -> bool {
+        // Docker named volumes typically don't contain special characters or slashes
+        // They are alphanumeric and may include underscores or hyphens
+        self
+            .host_path
+            .as_ref()
+            .unwrap()
+            .chars()
+            .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
+    }
+}
+
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Volumes {
     volumes: Vec<Volume>,
diff --git a/src/forms/stack_app.rs b/src/forms/stack_app.rs
index 8ebdec38..5608d768 100644
--- a/src/forms/stack_app.rs
+++ b/src/forms/stack_app.rs
@@ -72,36 +72,28 @@ pub struct App {
 }
 
 impl App {
-    pub fn named_volumes(&self) -> IndexMap<String, dctypes::MapOrEmpty<dctypes::ComposeVolume>> { //todo Result
+    pub fn named_volumes(&self) -> IndexMap<String, dctypes::MapOrEmpty<dctypes::ComposeVolume>> { 
         let mut named_volumes = IndexMap::default();
 
-        let volumes = &self.volumes;
-        if volumes.is_none() {
+        if self.volumes.is_none() {
             return named_volumes;
         }
 
-        let volumes = volumes
-            .clone() //todo remove it
-            .unwrap()
-            .into_iter()
-            .filter(|volume| is_named_docker_volume(volume.host_path.clone().unwrap().as_str()))
-            .map(|volume| {
-                let k = volume.host_path.clone().unwrap();
-                (
-                    k.clone(),
-                    dctypes::MapOrEmpty::Map(dctypes::ComposeVolume {
-                        driver: None,
-                        driver_opts: Default::default(),
-                        external: None,
-                        labels: Default::default(),
-                        name: Some(k.clone()),
-                    }),
-                    )
-            })
-        .collect::<IndexMap<String, dctypes::MapOrEmpty<dctypes::ComposeVolume>>>();
+        for volume in self.volumes.as_ref().unwrap() {
+            if !volume.is_named_docker() {
+                continue;
+            }
 
-        named_volumes.extend(volumes);
-        // tracing::debug!("Named volumes: {:?}", named_volumes);
+            let k = volume.host_path.as_ref().unwrap().clone();
+            let v = dctypes::MapOrEmpty::Map(dctypes::ComposeVolume {
+                driver: None,
+                driver_opts: Default::default(),
+                external: None,
+                labels: Default::default(),
+                name: Some(k.clone()),
+            });
+            named_volumes.insert(k, v);
+        }
 
         named_volumes
     }
@@ -112,15 +104,3 @@ impl AsRef<forms::DockerImage> for App {
         &self.docker_image
     }
 }
-
-
-fn is_named_docker_volume(volume: &str) -> bool { //todo
-    // Docker named volumes typically don't contain special characters or slashes
-    // They are alphanumeric and may include underscores or hyphens
-    let is_alphanumeric = volume
-        .chars()
-        .all(|c| c.is_alphanumeric() || c == '_' || c == '-');
-    let does_not_contain_slash = !volume.contains('/');
-    is_alphanumeric && does_not_contain_slash
-}
-

From ea055ccfcd320c615972b57c46ae9a96003b0dc4 Mon Sep 17 00:00:00 2001
From: petru <smart__petea@mail.ru>
Date: Thu, 11 Jan 2024 18:02:00 +0200
Subject: [PATCH 25/25] issue-auth forms::stack

---
 src/forms/mod.rs                              |  9 -------
 src/forms/{stack_app.rs => stack/app.rs}      | 24 ++++++++---------
 .../{stack_custom.rs => stack/custom.rs}      |  8 +++---
 src/forms/{stack_form.rs => stack/form.rs}    | 12 ++++-----
 src/forms/{stack.rs => stack/mod.rs}          | 16 ++++++++---
 .../{stack_payload.rs => stack/payload.rs}    |  8 +++---
 src/helpers/stack/builder.rs                  | 27 +++++++++----------
 src/helpers/stack/dctypes.rs                  |  8 +-----
 src/middleware/client.rs                      |  2 +-
 src/middleware/trydirect.rs                   |  4 +--
 src/routes/client/update.rs                   |  1 -
 src/routes/rating/add.rs                      |  3 +--
 src/routes/rating/get.rs                      |  2 +-
 src/routes/stack/add.rs                       | 12 ++++-----
 src/routes/stack/deploy.rs                    |  3 +--
 src/routes/stack/update.rs                    |  8 +++---
 src/services/rating.rs                        |  2 --
 src/telemetry.rs                              |  2 +-
 18 files changed, 70 insertions(+), 81 deletions(-)
 rename src/forms/{stack_app.rs => stack/app.rs} (83%)
 rename src/forms/{stack_custom.rs => stack/custom.rs} (92%)
 rename src/forms/{stack_form.rs => stack/form.rs} (84%)
 rename src/forms/{stack.rs => stack/mod.rs} (96%)
 rename src/forms/{stack_payload.rs => stack/payload.rs} (87%)

diff --git a/src/forms/mod.rs b/src/forms/mod.rs
index b7ef2dcf..9647ea6e 100644
--- a/src/forms/mod.rs
+++ b/src/forms/mod.rs
@@ -1,14 +1,5 @@
 mod rating;
 pub mod stack;
 pub mod user;
-mod stack_payload;
-mod stack_form;
-mod stack_custom;
-mod stack_app;
 
 pub use rating::*;
-pub use stack::*;
-pub use stack_payload::*;
-pub use stack_form::*;
-pub use stack_custom::*;
-pub use stack_app::*;
diff --git a/src/forms/stack_app.rs b/src/forms/stack/app.rs
similarity index 83%
rename from src/forms/stack_app.rs
rename to src/forms/stack/app.rs
index 5608d768..71509262 100644
--- a/src/forms/stack_app.rs
+++ b/src/forms/stack/app.rs
@@ -28,13 +28,13 @@ pub struct App {
     #[serde(rename = "type")]
     pub type_field: String,
     #[serde(flatten)]
-    pub role: forms::Role,
+    pub role: forms::stack::Role,
     pub default: Option<bool>,
-    pub versions: Option<Vec<forms::Version>>,
+    pub versions: Option<Vec<forms::stack::Version>>,
     #[serde(flatten)]
-    pub docker_image: forms::DockerImage,
+    pub docker_image: forms::stack::DockerImage,
     #[serde(flatten)]
-    pub requirements: forms::Requirements,
+    pub requirements: forms::stack::Requirements,
     #[validate(minimum = 1)]
     pub popularity: Option<u32>,
     pub commercial: Option<bool>,
@@ -43,8 +43,8 @@ pub struct App {
     pub suggested: Option<bool>,
     pub dependency: Option<Value>,
     pub avoid_render: Option<bool>,
-    pub price: Option<forms::Price>,
-    pub icon: Option<forms::Icon>,
+    pub price: Option<forms::stack::Price>,
+    pub icon: Option<forms::stack::Icon>,
     pub domain: Option<String>,
     pub category_id: Option<u32>,
     pub parent_app_id: Option<u32>,
@@ -58,17 +58,17 @@ pub struct App {
     pub url_app: Option<String>,
     pub url_git: Option<String>,
     pub restart: Option<String>,
-    pub volumes: Option<Vec<forms::Volume>>,
+    pub volumes: Option<Vec<forms::stack::Volume>>,
     #[serde(flatten)]
-    pub environment: forms::Environment,
+    pub environment: forms::stack::Environment,
     #[serde(flatten)]
-    pub network: forms::ServiceNetworks,
+    pub network: forms::stack::ServiceNetworks,
     // #[serde(flatten)]
     // pub ports: Ports,
     #[serde(rename(deserialize = "sharedPorts"))]
     #[serde(rename(serialize = "shared_ports"))]
     #[serde(alias = "shared_ports")]
-    pub ports: Option<Vec<forms::Port>>,
+    pub ports: Option<Vec<forms::stack::Port>>,
 }
 
 impl App {
@@ -99,8 +99,8 @@ impl App {
     }
 }
 
-impl AsRef<forms::DockerImage> for App {
-    fn as_ref(&self) -> &forms::DockerImage {
+impl AsRef<forms::stack::DockerImage> for App {
+    fn as_ref(&self) -> &forms::stack::DockerImage {
         &self.docker_image
     }
 }
diff --git a/src/forms/stack_custom.rs b/src/forms/stack/custom.rs
similarity index 92%
rename from src/forms/stack_custom.rs
rename to src/forms/stack/custom.rs
index b6277a2f..34f5437b 100644
--- a/src/forms/stack_custom.rs
+++ b/src/forms/stack/custom.rs
@@ -7,9 +7,9 @@ use crate::helpers::stack::dctypes;
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
 pub struct Custom {
-    pub web: Vec<forms::Web>,
-    pub feature: Option<Vec<forms::Feature>>,
-    pub service: Option<Vec<forms::Service>>,
+    pub web: Vec<forms::stack::Web>,
+    pub feature: Option<Vec<forms::stack::Feature>>,
+    pub service: Option<Vec<forms::stack::Service>>,
     #[validate(minimum = 0)]
     #[validate(maximum = 10)]
     pub servers_count: u32,
@@ -28,7 +28,7 @@ pub struct Custom {
     pub project_overview: Option<String>,
     pub project_description: Option<String>,
     #[serde(flatten)]
-    pub networks: forms::ComposeNetworks, // all networks
+    pub networks: forms::stack::ComposeNetworks, // all networks
 }
 
 impl Custom {
diff --git a/src/forms/stack_form.rs b/src/forms/stack/form.rs
similarity index 84%
rename from src/forms/stack_form.rs
rename to src/forms/stack/form.rs
index 989205af..4e11776f 100644
--- a/src/forms/stack_form.rs
+++ b/src/forms/stack/form.rs
@@ -7,12 +7,12 @@ use crate::models;
 use crate::forms;
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
-pub struct StackForm {
+pub struct Stack { 
     // #[validate(min_length=2)]
     // #[validate(max_length=255)]
     #[serde(rename = "commonDomain")]
     pub common_domain: Option<String>,
-    pub domain_list: Option<forms::DomainList>,
+    pub domain_list: Option<forms::stack::DomainList>,
     #[validate(min_length = 2)]
     #[validate(max_length = 255)]
     pub stack_code: Option<String>,
@@ -31,7 +31,7 @@ pub struct StackForm {
     #[validate(min_length = 3)]
     #[validate(max_length = 50)]
     pub ssl: String,
-    pub vars: Option<Vec<forms::Var>>,
+    pub vars: Option<Vec<forms::stack::Var>>,
     pub integrated_features: Option<Vec<Value>>,
     pub extended_features: Option<Vec<Value>>,
     pub subscriptions: Option<Vec<String>>,
@@ -49,13 +49,13 @@ pub struct StackForm {
     #[validate(min_length = 3)]
     #[validate(max_length = 50)]
     pub selected_plan: String,
-    pub custom: forms::Custom,
+    pub custom: forms::stack::Custom,
 }
 
-impl TryFrom<&models::Stack> for StackForm {
+impl TryFrom<&models::Stack> for Stack {
     type Error = String;
 
     fn try_from(stack: &models::Stack) -> Result<Self, Self::Error> {
-        serde_json::from_value::<StackForm>(stack.body.clone()).map_err(|err| format!("{:?}", err))
+        serde_json::from_value::<Stack>(stack.body.clone()).map_err(|err| format!("{:?}", err))
     }
 }
diff --git a/src/forms/stack.rs b/src/forms/stack/mod.rs
similarity index 96%
rename from src/forms/stack.rs
rename to src/forms/stack/mod.rs
index 5a1296c1..aadeddcc 100644
--- a/src/forms/stack.rs
+++ b/src/forms/stack/mod.rs
@@ -5,6 +5,16 @@ use std::collections::HashMap;
 use std::fmt;
 use crate::forms;
 
+mod app;
+mod custom;
+mod form;
+mod payload;
+
+pub use app::*;
+pub use custom::*;
+pub use form::*;
+pub use payload::*;
+
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
 pub struct Role {
     pub role: Option<Vec<String>>,
@@ -133,7 +143,7 @@ pub struct ComposeNetworks {
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Web {
     #[serde(flatten)]
-    pub app: forms::App,
+    pub app: forms::stack::App,
     pub custom: Option<bool>,
     pub main: bool,
 }
@@ -145,7 +155,7 @@ pub struct Feature {
     // #[serde(alias = "shared_ports")]
     // pub shared_ports: Option<Vec<Port>>,
     #[serde(flatten)]
-    pub app: forms::App,
+    pub app: forms::stack::App,
 }
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
@@ -155,7 +165,7 @@ pub struct Service {
     // #[serde(alias = "shared_ports")]
     // pub shared_ports: Option<Vec<Port>>,
     #[serde(flatten)]
-    pub(crate) app: forms::App,
+    pub(crate) app: forms::stack::App,
 }
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
diff --git a/src/forms/stack_payload.rs b/src/forms/stack/payload.rs
similarity index 87%
rename from src/forms/stack_payload.rs
rename to src/forms/stack/payload.rs
index e7a56cd0..70be8410 100644
--- a/src/forms/stack_payload.rs
+++ b/src/forms/stack/payload.rs
@@ -7,7 +7,7 @@ use serde_valid::Validate;
 
 #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)]
 #[serde(rename_all = "snake_case")]
-pub struct StackPayload {
+pub struct Payload {
     pub(crate) id: Option<i32>,
     pub(crate) user_token: Option<String>,
     pub(crate) user_email: Option<String>,
@@ -35,14 +35,14 @@ pub struct StackPayload {
     pub stack_code: String,
     #[serde(rename = "selected_plan")]
     pub selected_plan: String,
-    pub custom: forms::Custom,
+    pub custom: forms::stack::Custom,
 }
 
-impl TryFrom<&models::Stack> for StackPayload {
+impl TryFrom<&models::Stack> for Payload {
     type Error = String;
 
     fn try_from(stack: &models::Stack) -> Result<Self, Self::Error> {
-        let mut stack_data = serde_json::from_value::<StackPayload>(stack.body.clone()).map_err(|err| {
+        let mut stack_data = serde_json::from_value::<Payload>(stack.body.clone()).map_err(|err| {
             format!("{:?}", err)
         })?;
 
diff --git a/src/helpers/stack/builder.rs b/src/helpers/stack/builder.rs
index 4457cd2c..9068c9d0 100644
--- a/src/helpers/stack/builder.rs
+++ b/src/helpers/stack/builder.rs
@@ -1,4 +1,3 @@
-use crate::forms::{stack, App, StackForm, Volume, Web};
 use crate::forms;
 use crate::helpers::stack::dctypes::{
     AdvancedVolumes, Compose, ComposeNetwork, ComposeNetworkSettingDetails, ComposeNetworks,
@@ -36,7 +35,7 @@ pub struct DcBuilder {
     pub(crate) stack: models::Stack,
 }
 
-impl TryInto<AdvancedVolumes> for Volume {
+impl TryInto<AdvancedVolumes> for forms::stack::Volume {
     type Error = String;
     fn try_into(self) -> Result<AdvancedVolumes, Self::Error> {
         let source = self.host_path.clone();
@@ -58,20 +57,20 @@ impl TryInto<AdvancedVolumes> for Volume {
     }
 }
 
-impl TryInto<Port> for &stack::Port {
+impl TryInto<Port> for &forms::stack::Port {
     type Error = String;
     fn try_into(self) -> Result<Port, Self::Error> {
         let cp = self
             .container_port
             .as_ref()
             .map_or(Ok(0u16), |s| s.parse::<u16>())
-            .map_err(|err| "Could not parse port".to_string())?;
+            .map_err(|_| "Could not parse port".to_string())?;
 
         let hp = self
             .host_port
             .as_ref()
             .map_or(Ok(0u16), |s| s.parse::<u16>())
-            .map_err(|err| "Could not parse port".to_string())?;
+            .map_err(|_| "Could not parse port".to_string())?;
 
         Ok(Port {
             target: cp,
@@ -83,12 +82,12 @@ impl TryInto<Port> for &stack::Port {
     }
 }
 
-impl TryFrom<&stack::ServiceNetworks> for Networks {
+impl TryFrom<&forms::stack::ServiceNetworks> for Networks {
     type Error = ();
 
-    fn try_from(serviceNetworks: &stack::ServiceNetworks) -> Result<Networks, Self::Error> {
+    fn try_from(service_networks: &forms::stack::ServiceNetworks) -> Result<Networks, Self::Error> {
         let mut result = vec!["default_network".to_string()];
-        serviceNetworks.network.as_ref().map(|networks| {
+        service_networks.network.as_ref().map(|networks| {
             for n in networks {
                 result.push(n.to_string());
             }
@@ -98,13 +97,13 @@ impl TryFrom<&stack::ServiceNetworks> for Networks {
     }
 }
 
-fn convert_shared_ports(ports: Option<Vec<stack::Port>>) -> Result<Vec<Port>, String> {
+//todo
+fn convert_shared_ports(ports: Option<Vec<forms::stack::Port>>) -> Result<Vec<Port>, String> {
     tracing::debug!("convert shared ports {:?}", &ports);
     let mut _ports: Vec<Port> = vec![];
     match ports {
         Some(ports) => {
             tracing::debug!("Ports >>>> {:?}", ports);
-            for port in ports {}
         }
         None => {
             tracing::debug!("No ports defined by user");
@@ -116,10 +115,10 @@ fn convert_shared_ports(ports: Option<Vec<stack::Port>>) -> Result<Vec<Port>, St
     Ok(_ports)
 }
 
-impl TryFrom<&App> for Service {
+impl TryFrom<&forms::stack::App> for Service {
     type Error = String;
 
-    fn try_from(app: &App) -> Result<Self, Self::Error> {
+    fn try_from(app: &forms::stack::App) -> Result<Self, Self::Error> {
         let mut service = Service {
             image: Some(app.docker_image.to_string()),
             ..Default::default()
@@ -166,7 +165,7 @@ impl TryFrom<&App> for Service {
     }
 }
 
-impl Into<IndexMap<String, MapOrEmpty<NetworkSettings>>> for stack::ComposeNetworks {
+impl Into<IndexMap<String, MapOrEmpty<NetworkSettings>>> for forms::stack::ComposeNetworks {
     fn into(self) -> IndexMap<String, MapOrEmpty<NetworkSettings>> {
         // tracing::debug!("networks found {:?}", self.networks);
         let mut networks = vec!["default_network".to_string()];
@@ -212,7 +211,7 @@ impl DcBuilder {
             ..Default::default()
         };
 
-        let apps = StackForm::try_from(&self.stack)?; 
+        let apps = forms::stack::Stack::try_from(&self.stack)?; 
         let  services = apps.custom.services()?;
         let  named_volumes = apps.custom.named_volumes()?;
 
diff --git a/src/helpers/stack/dctypes.rs b/src/helpers/stack/dctypes.rs
index 931deab1..22383934 100644
--- a/src/helpers/stack/dctypes.rs
+++ b/src/helpers/stack/dctypes.rs
@@ -46,12 +46,6 @@ pub struct Compose {
     pub extensions: HashMap<Extension, Value>,
 }
 
-impl Compose {
-    pub fn new() -> Self {
-        Default::default()
-    }
-}
-
 #[derive(Builder, Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
 #[builder(setter(into), default)]
 pub struct Service {
@@ -839,4 +833,4 @@ impl<T> Serialize for MapOrEmpty<T>
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/middleware/client.rs b/src/middleware/client.rs
index 60bce270..a6915d55 100644
--- a/src/middleware/client.rs
+++ b/src/middleware/client.rs
@@ -77,7 +77,7 @@ where
             let header_hash: String = get_header(&req, "stacker-hash")?;
 
             let db_pool = req.app_data::<web::Data<Pool<Postgres>>>().unwrap().get_ref();
-            let mut client: Client = db_fetch_client(db_pool, client_id).await?;
+            let client: Client = db_fetch_client(db_pool, client_id).await?;
             if client.secret.is_none() {
                 return Err("client is not active".to_string());
             }
diff --git a/src/middleware/trydirect.rs b/src/middleware/trydirect.rs
index e40f89b1..62b47046 100644
--- a/src/middleware/trydirect.rs
+++ b/src/middleware/trydirect.rs
@@ -32,7 +32,7 @@ async fn fetch_user(auth_url: &str, token: &str) -> Result<models::User, String>
         .header(ACCEPT, "application/json")
         .send()
         .await
-        .map_err(|err| "no resp from auth server".to_string())?;
+        .map_err(|_err| "no resp from auth server".to_string())?;
 
     if !resp.status().is_success() {
         return Err("401 Unauthorized".to_string());
@@ -41,6 +41,6 @@ async fn fetch_user(auth_url: &str, token: &str) -> Result<models::User, String>
     resp
         .json::<UserForm>()
         .await
-        .map_err(|err| "can't parse the response body".to_string())?
+        .map_err(|_err| "can't parse the response body".to_string())?
         .try_into()
 }
diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs
index a52af9b0..5f19de51 100644
--- a/src/routes/client/update.rs
+++ b/src/routes/client/update.rs
@@ -3,7 +3,6 @@ use crate::helpers::client;
 use crate::models;
 use crate::{configuration::Settings, helpers::JsonResponse};
 use actix_web::{put, web, Responder, Result};
-use futures::TryFutureExt;
 use sqlx::PgPool;
 use std::sync::Arc;
 use tracing::Instrument;
diff --git a/src/routes/rating/add.rs b/src/routes/rating/add.rs
index 48f03309..ea9657b2 100644
--- a/src/routes/rating/add.rs
+++ b/src/routes/rating/add.rs
@@ -6,7 +6,6 @@ use actix_web::{post, web, Responder, Result};
 use sqlx::PgPool;
 use tracing::Instrument;
 use std::sync::Arc;
-use futures::TryFutureExt;
 
 // workflow
 // add, update, list, get(user_id), ACL,
@@ -39,5 +38,5 @@ pub async fn add_handler(
     db::rating::insert(pg_pool.get_ref(), rating)
         .await
         .map(|rating| JsonResponse::build().set_item(rating).ok("success"))
-        .map_err(|err| JsonResponse::<models::Rating>::build().internal_server_error("Failed to insert"))
+        .map_err(|_err| JsonResponse::<models::Rating>::build().internal_server_error("Failed to insert"))
 }
diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs
index 9081960c..c7591891 100644
--- a/src/routes/rating/get.rs
+++ b/src/routes/rating/get.rs
@@ -34,5 +34,5 @@ pub async fn list_handler(
     db::rating::fetch_all(pg_pool.get_ref())
         .await
         .map(|ratings| JsonResponse::build().set_list(ratings).ok("OK"))
-        .map_err(|err| JsonResponse::<models::Rating>::build().internal_server_error(""))
+        .map_err(|_err| JsonResponse::<models::Rating>::build().internal_server_error(""))
 }
diff --git a/src/routes/stack/add.rs b/src/routes/stack/add.rs
index bcf281c7..5a850c09 100644
--- a/src/routes/stack/add.rs
+++ b/src/routes/stack/add.rs
@@ -27,8 +27,8 @@ pub async fn add(
 
     check_if_stack_exists(pg_pool.get_ref(), &stack_name).await?;
 
-    let body: Value = serde_json::to_value::<forms::StackForm>(form)
-        .or(serde_json::to_value::<forms::StackForm>(forms::StackForm::default()))
+    let body: Value = serde_json::to_value::<forms::stack::Stack>(form)
+        .or(serde_json::to_value::<forms::stack::Stack>(forms::stack::Stack::default()))
         .unwrap();
 
     let stack = models::Stack::new(user.id.clone(), stack_name, body);
@@ -53,17 +53,17 @@ async fn check_if_stack_exists(pg_pool: &PgPool, stack_name: &String) -> Result<
         })
 }
 
-async fn body_into_form(body: Bytes) -> Result<forms::StackForm, Error> {
+async fn body_into_form(body: Bytes) -> Result<forms::stack::Stack, Error> {
     let body_bytes = actix_web::body::to_bytes(body).await.unwrap();
     let body_str = str::from_utf8(&body_bytes)
-        .map_err(|err| JsonResponse::<forms::StackForm>::build().internal_server_error(err.to_string()))?;
+        .map_err(|err| JsonResponse::<forms::stack::Stack>::build().internal_server_error(err.to_string()))?;
     let deserializer = &mut serde_json::Deserializer::from_str(body_str);
     serde_path_to_error::deserialize(deserializer)
         .map_err(|err| {
             let msg = format!("{}:{:?}", err.path().to_string(), err);
-            JsonResponse::<forms::StackForm>::build().bad_request(msg)
+            JsonResponse::<forms::stack::Stack>::build().bad_request(msg)
         })
-        .and_then(|form: forms::StackForm| {
+        .and_then(|form: forms::stack::Stack| {
             if !form.validate().is_ok() {
                 let errors = form.validate().unwrap_err();
                 let err_msg = format!("Invalid data received {:?}", &errors.to_string());
diff --git a/src/routes/stack/deploy.rs b/src/routes/stack/deploy.rs
index 005ac531..71d459ff 100644
--- a/src/routes/stack/deploy.rs
+++ b/src/routes/stack/deploy.rs
@@ -5,7 +5,6 @@ use crate::helpers::stack::builder::DcBuilder;
 use crate::helpers::{JsonResponse, MqManager};
 use crate::models;
 use actix_web::{post, web, web::Data, Responder, Result};
-use futures_lite::stream::StreamExt;
 use lapin::publisher_confirm::Confirmation;
 use sqlx::PgPool;
 use std::sync::Arc;
@@ -34,7 +33,7 @@ pub async fn add(
         JsonResponse::<models::Stack>::build().internal_server_error(err)
     })?;
 
-    let mut stack_data = forms::StackPayload::try_from(&dc.stack)
+    let mut stack_data = forms::stack::Payload::try_from(&dc.stack)
         .map_err(|err| JsonResponse::<models::Stack>::build().bad_request(err))?;
     stack_data.user_token = Some(user.id.clone());
     stack_data.user_email = Some(user.email.clone());
diff --git a/src/routes/stack/update.rs b/src/routes/stack/update.rs
index a22be0c2..316a1ba2 100644
--- a/src/routes/stack/update.rs
+++ b/src/routes/stack/update.rs
@@ -16,7 +16,7 @@ use uuid::Uuid;
 #[post("/{id}")]
 pub async fn update(
     path: web::Path<(i32,)>,
-    form: web::Json<forms::StackForm>,
+    form: web::Json<forms::stack::Stack>,
     user: web::ReqData<Arc<User>>,
     pg_pool: Data<PgPool>,
 ) -> Result<impl Responder> {
@@ -67,11 +67,11 @@ pub async fn update(
         return Err(JsonResponse::<models::Stack>::build().bad_request(errors.to_string()));
     }
 
-    let body: Value = match serde_json::to_value::<forms::StackForm>(form.into_inner()) {
+    let body: Value = match serde_json::to_value::<forms::stack::Stack>(form.into_inner()) {
         Ok(body) => body,
         Err(err) => {
             tracing::error!("Request_id {} error unwrap body {:?}", request_id, err);
-            serde_json::to_value::<forms::StackForm>(forms::StackForm::default()).unwrap()
+            serde_json::to_value::<forms::stack::Stack>(forms::stack::Stack::default()).unwrap()
         }
     };
 
@@ -93,7 +93,7 @@ pub async fn update(
     .instrument(query_span)
     .await
     {
-        Ok(record) => {
+        Ok(_record) => {
             tracing::info!(
                 "req_id: {} stack details have been saved to database",
                 request_id
diff --git a/src/services/rating.rs b/src/services/rating.rs
index 22f42024..82222211 100644
--- a/src/services/rating.rs
+++ b/src/services/rating.rs
@@ -1,6 +1,4 @@
 use crate::models::rating::Rating;
-use sqlx::PgPool;
-use reqwest::Url;
 use tracing::Instrument;
 use tracing_subscriber::fmt::format;
 
diff --git a/src/telemetry.rs b/src/telemetry.rs
index 724381a7..fb57df1e 100644
--- a/src/telemetry.rs
+++ b/src/telemetry.rs
@@ -1,4 +1,4 @@
-use tracing::subscriber::{self, set_global_default};
+use tracing::subscriber::set_global_default;
 use tracing::Subscriber;
 use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
 use tracing_log::LogTracer;
