ResourceHub API. Not for production. Replacement Rocket crate (library) to Actix (Actix web), added endpoints for get, get_by_id, create for first tests, later i will add database connection and impliment saving sensor data into Database.,
This commit is contained in:
1398
ResourceHub/Cargo.lock
generated
1398
ResourceHub/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,8 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.50.0", features = ["full"] }
|
tokio = { version = "1.50.0", features = ["full"] }
|
||||||
redis = { version = "1.0.4", features = ["json"] }
|
redis = { version = "1.0.4", features = ["json"] }
|
||||||
rocket = { version = "0.5.1", features = ["json"] }
|
|
||||||
tinterm = "0.2.0"
|
tinterm = "0.2.0"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
|
deadpool-redis = "0.23.0"
|
||||||
|
actix-web = "4.13.0"
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
# ResourceHUB [API]
|
# ResourceHUB [API]
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## [API] Library
|
||||||
|
|
||||||
|
[ACTIX (actix-web)](https://actix.rs/docs/server)
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> **[DataCollector]** sends [**sensor's data] **like as *PH Level*, *Humidity*, *Temperature*, etc) as ++JSON++ to this **[API]**
|
> **[DataCollector]** sends [**sensor's data] **like as *PH Level*, *Humidity*, *Temperature*, etc) as ++JSON++ to this [API]**
|
||||||
> **RESOURCE HUB - means a Public Storage with Sensors Data,.****
|
> **RESOURCE HUB - means a Public Storage with Sensors Data,.**
|
||||||
> 
|
> 
|
||||||
> ```json
|
> ```json
|
||||||
> {
|
> {
|
||||||
@@ -53,7 +59,6 @@
|
|||||||
> - **S1** - Means: **Sector** **1**
|
> - **S1** - Means: **Sector** **1**
|
||||||
> - **C1** - Means: **Cell** **1**
|
> - **C1** - Means: **Cell** **1**
|
||||||
> - **:** - field splitter (for future. May be parsers can use this for getting fields: cityid, cords, sector, cell)
|
> - **:** - field splitter (for future. May be parsers can use this for getting fields: cityid, cords, sector, cell)
|
||||||
>
|
|
||||||
> ### In concept
|
> ### In concept
|
||||||
> > SECTOR-5-CELL-1 can collect data about tomatos
|
> > SECTOR-5-CELL-1 can collect data about tomatos
|
||||||
> > SECTOR-5-CELL-2 can collect data about potatos
|
> > SECTOR-5-CELL-2 can collect data about potatos
|
||||||
@@ -61,7 +66,7 @@
|
|||||||
> > **a. "how much" system make a 'tomatos', 'gabbages,.' : 10 tomatos/month, 40 gabbeges/month, etc,.)**
|
> > **a. "how much" system make a 'tomatos', 'gabbages,.' : 10 tomatos/month, 40 gabbeges/month, etc,.)**
|
||||||
> > **b. "n/year"**
|
> > **b. "n/year"**
|
||||||
> > **c. **
|
> > **c. **
|
||||||
> ## * Sensors Data Table * [**Example**]
|
> ## Sensors Data Table [**Example**]
|
||||||
>
|
>
|
||||||
> | id | city_codename_cords_sector_cell | sensor_name_and_model | value | unit | timestamp |
|
> | id | city_codename_cords_sector_cell | sensor_name_and_model | value | unit | timestamp |
|
||||||
> | --- | -------------------------------------------------------------------------------- | ---------------------------------------------------- | ----- | ------- | --------- |
|
> | --- | -------------------------------------------------------------------------------- | ---------------------------------------------------- | ----- | ------- | --------- |
|
||||||
|
|||||||
BIN
ResourceHub/images/SOFT.png
Normal file
BIN
ResourceHub/images/SOFT.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
@@ -1,43 +1,70 @@
|
|||||||
use rocket::serde::{json::Json};
|
use actix_web::{get, post};
|
||||||
use rocket::response::status;
|
use actix_web::{Error, HttpResponse};
|
||||||
use rocket::{get, post};
|
use actix_web::web::{Path, Json};
|
||||||
|
|
||||||
use crate::models::sensors::SensorData;
|
use crate::models::sensors::SensorData;
|
||||||
use crate::models::sensors::get_sensor_data;
|
use crate::models::sensors::get_sensor_data; // TODO - DELETE LATER, WHEN create data obj in Database WILL ADDED
|
||||||
|
|
||||||
use crate::repository::remote::redis;
|
// use crate::repository::remote::redis;
|
||||||
|
// use crate::repository::remote::sqlite;
|
||||||
|
// use crate::repository::remote::postgres;
|
||||||
|
|
||||||
// get data json obj
|
// get data json obj
|
||||||
// database.getSensorData()
|
// database.getSensorData()
|
||||||
// send 200 http response with json obj
|
// send 200 http response with json obj
|
||||||
// 500
|
// 500
|
||||||
|
|
||||||
// create data json obj
|
// create data json obj
|
||||||
// database.create(obj)
|
// database.create(obj)
|
||||||
// send 200 http response
|
// send 200 http response
|
||||||
// 500
|
// 500
|
||||||
|
|
||||||
#[post("/sensors/data", format="json", data="<json>")]
|
|
||||||
pub async fn create(json: Json<SensorData>) -> status::Accepted<Json<SensorData>> {
|
|
||||||
let received_data = json.into_inner();
|
|
||||||
|
|
||||||
let data = get_sensor_data();
|
// TODO - add json deserialization anwer to client, maybe
|
||||||
status::Accepted(Json(data))
|
|
||||||
|
|
||||||
// saved_data = database.create(data);
|
#[post("/sensors/data")]
|
||||||
// status::Accepted(Json(saved_data))
|
pub async fn create(data: Json<SensorData>) -> HttpResponse {
|
||||||
|
let _received_data = data.into_inner();
|
||||||
|
|
||||||
|
// match let database.create(data) = true {
|
||||||
|
// Ok(HttpResponse::Ok().json(received_data))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return data to client
|
||||||
|
HttpResponse::Ok().json(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/sensors/data")]
|
#[get("/sensors/data")]
|
||||||
pub async fn get() -> status::Accepted<Json<SensorData>> {
|
pub async fn get() -> Result<HttpResponse, Error> {
|
||||||
|
// get pool db
|
||||||
|
// let pool_db =
|
||||||
|
|
||||||
// get all data from DB
|
// get all data from DB
|
||||||
let data = get_sensor_data();
|
let data = get_sensor_data();
|
||||||
|
|
||||||
status::Accepted(Json(data))
|
// return data to client
|
||||||
|
Ok(HttpResponse::Ok().json(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/sensors/data/{id}")]
|
||||||
|
pub async fn get_by_id(id: Path<u32>) -> Result<HttpResponse, Error> {
|
||||||
|
println!("{}", id);
|
||||||
|
|
||||||
|
// Vulnirability: danger if ID in sql requsets
|
||||||
|
|
||||||
|
// get pool db
|
||||||
|
// let pool_db =
|
||||||
|
|
||||||
|
// get all data from DB
|
||||||
|
let data = get_sensor_data();
|
||||||
|
|
||||||
|
// return data to client
|
||||||
|
Ok(HttpResponse::Ok().json(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Vulnirability: danger if ID in sql requsets !!!
|
||||||
// #[get("/sensors/data/<id>")]
|
// #[get("/sensors/data/<id>")]
|
||||||
// pub async fn get_by_id(id: u32) -> status::Accepted<Json<SensorData>> {
|
// pub async fn get_by_id(id: u32) -> status::Accepted<Json<SensorData>> {
|
||||||
// // Vulnirability: danger if ID in sql requsets
|
|
||||||
// // http://127.0.0.1:8000/api/v1/sensors/data/?query=SELECT%20id%20FROM%20sensors
|
// // http://127.0.0.1:8000/api/v1/sensors/data/?query=SELECT%20id%20FROM%20sensors
|
||||||
|
|
||||||
// // get data from DB
|
// // get data from DB
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ mod endpoints;
|
|||||||
mod models;
|
mod models;
|
||||||
mod repository;
|
mod repository;
|
||||||
|
|
||||||
use rocket::{routes};
|
use actix_web::{HttpServer, App, web, middleware};
|
||||||
use util::constants;
|
use util::constants;
|
||||||
use util::debug;
|
use util::debug;
|
||||||
use repository::remote::redis;
|
// use repository::remote::redis;
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
#[rocket::main]
|
async fn main() -> std::io::Result<()> {
|
||||||
async fn main() -> Result<(), rocket::Error> {
|
|
||||||
util::colored_text::print_colored_text_brand("> SocioCybereeng ");
|
util::colored_text::print_colored_text_brand("> SocioCybereeng ");
|
||||||
util::colored_text::print_colored_text_project("> Project: Hydroponic Systems ");
|
util::colored_text::print_colored_text_project("> Project: Hydroponic Systems ");
|
||||||
util::colored_text::print_colored_text_info("- [Welcome to ResourceHUB API] ");
|
util::colored_text::print_colored_text_info("- [Welcome to ResourceHUB API] ");
|
||||||
@@ -19,33 +18,49 @@ use repository::remote::redis;
|
|||||||
debug::print_debug_info();
|
debug::print_debug_info();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get database pool
|
util::colored_text::print_colored_text_info(
|
||||||
// let pool = redis::get_pool(constants::DB_URI).await;
|
&format!("Starting HTTP server at http://{}:{}/api/v1/",
|
||||||
|
constants::SERVER_URL, constants::SERVER_PORT));
|
||||||
|
|
||||||
|
HttpServer::new(|| {
|
||||||
|
App::new()
|
||||||
|
// enable logger - always register Actix Web Logger middleware last
|
||||||
|
.wrap(middleware::Logger::default().log_target("@"))
|
||||||
|
|
||||||
// Attempt to connect to DB and check the result
|
.service(
|
||||||
// Ok(conection) => {
|
web::scope( &format!("/api/v{}", constants::API_VERSION) )
|
||||||
// If the connection is successful, store it or use it as needed
|
.service(endpoints::sensors::data::get)
|
||||||
// let mut _db = conection;
|
.service(endpoints::sensors::data::get_by_id)
|
||||||
// }
|
.service(endpoints::sensors::data::create)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service /api/v2
|
||||||
|
// Service /api/v3
|
||||||
|
// Service /api/v4
|
||||||
|
// ...
|
||||||
|
|
||||||
// Start server
|
})
|
||||||
let _rocket = rocket::build()
|
.bind((constants::SERVER_URL, constants::SERVER_PORT))?
|
||||||
.mount(format!("/api/v{}/", constants::API_VERSION), routes![
|
.run()
|
||||||
endpoints::sensors::data::create,
|
.await
|
||||||
endpoints::sensors::data::get,
|
|
||||||
// endpoints::sensors::data::get_by_id,
|
|
||||||
])
|
|
||||||
.launch()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
// },
|
|
||||||
// Err(err) => {
|
|
||||||
// // Handle connection error and stop execution
|
|
||||||
// std::process::exit(1);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - ADD TLS (HTTPS)
|
||||||
|
// .bind_rustls_0_23(("127.0.0.1", 443), config)? // HTTPS port
|
||||||
|
// https://github.com/actix/examples/blob/main/middleware/http-to-https/src/main.rs
|
||||||
|
|
||||||
|
// TODO - ADD CORS
|
||||||
|
// https://actix.rs/docs/cors
|
||||||
|
|
||||||
|
|
||||||
|
// // Get database pool
|
||||||
|
// // let pool = redis::get_pool(constants::DB_URI).await;
|
||||||
|
|
||||||
|
|
||||||
|
// // Attempt to connect to DB and check the result
|
||||||
|
|
||||||
|
// // Start server
|
||||||
|
// endpoints::sensors::data::create,
|
||||||
|
// endpoints::sensors::data::get,
|
||||||
|
// // endpoints::sensors::data::get_by_id,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
// use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
// #[derive(Deserialize, Serialize)]
|
// #[derive(Deserialize, Serialize)]
|
||||||
// #[serde(crate = "rocket::serde")]
|
// #[serde(crate = "rocket::serde")]
|
||||||
@@ -11,8 +11,9 @@ use rocket::serde::{Deserialize, Serialize};
|
|||||||
// pub unit: String,
|
// pub unit: String,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SensorData {
|
pub struct SensorData {
|
||||||
pub id: Option<u32>,
|
pub id: Option<u32>,
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
sqlite
|
// sqlite
|
||||||
@@ -1,17 +1,63 @@
|
|||||||
// use tokio::sync::{OnceCell, Mutex};
|
// // use tokio::sync::{OnceCell, Mutex};
|
||||||
// use redis::{Connection, ConnectionLike, JsonCommands, RedisResult};
|
// // use redis::{Connection, ConnectionLike, JsonCommands, RedisResult};
|
||||||
// use rocket::serde::{Deserialize, Serialize};
|
// // use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
||||||
// static DB_POOL: OnceCell<redis::Client> = OnceCell::const_new();
|
// // static DB_POOL: OnceCell<redis::Client> = OnceCell::const_new();
|
||||||
|
// // static DB_CONNECTOR: OnceCell<Mutex<Connection>> = OnceCell::const_new();
|
||||||
|
|
||||||
|
// // pub async fn get_pool(URI: &str) -> &'static redis::Client {
|
||||||
|
// // DB_POOL.get_or_init(|| async {
|
||||||
|
// // redis::Client::open(URI).expect("[ERR] Can't connect to database.")
|
||||||
|
// // }).await
|
||||||
|
// // }
|
||||||
|
|
||||||
|
|
||||||
|
// // // Async‑compatible accessor
|
||||||
|
// // pub async fn get_conn() -> tokio::sync::MutexGuard<'static, Connection> {
|
||||||
|
// // DB_CONNECTOR
|
||||||
|
// // .get()
|
||||||
|
// // .expect("Redis not initialised")
|
||||||
|
// // .lock()
|
||||||
|
// // .await
|
||||||
|
// // }
|
||||||
|
|
||||||
|
|
||||||
|
// // pub async fn get_data<T>(key: &str, con: &mut Connection) -> RedisResult<(T)>
|
||||||
|
// // where T: Deserialize<'static> {
|
||||||
|
// // let raw_data: String = con.json_get(&key, ".")?;
|
||||||
|
// // Ok( serde_json::from_str(&raw_data)? )
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // pub async fn set_data<T>(key: &str, data: &T, con: &mut Connection) -> RedisResult<()>
|
||||||
|
// // where T: Serialize,
|
||||||
|
// // {
|
||||||
|
// // Ok( con.json_set(&key, "$", &data)? )
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// use tokio::sync::OnceCell;
|
||||||
|
// use tokio::sync::Mutex;
|
||||||
|
// use redis::{Client, Connection};
|
||||||
|
|
||||||
|
// // TODO - REPLACE REDIS POOL to deadpool_redis
|
||||||
|
// // https://docs.rs/deadpool-redis/latest/deadpool_redis/
|
||||||
|
|
||||||
|
// static DB_POOL: OnceCell<Client> = OnceCell::const_new();
|
||||||
// static DB_CONNECTOR: OnceCell<Mutex<Connection>> = OnceCell::const_new();
|
// static DB_CONNECTOR: OnceCell<Mutex<Connection>> = OnceCell::const_new();
|
||||||
|
|
||||||
// pub async fn get_pool(URI: &str) -> &'static redis::Client {
|
// pub async fn get_pool(uri: &str) -> redis::RedisResult<()> {
|
||||||
// DB_POOL.get_or_init(|| async {
|
// let client = Client::open(uri)?;
|
||||||
// redis::Client::open(URI).expect("[ERR] Can't connect to database.")
|
// DB_POOL.set(client).ok();
|
||||||
// }).await
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
// let conn = DB_POOL
|
||||||
|
// .get()
|
||||||
|
// .unwrap()
|
||||||
|
// .get_connection()
|
||||||
|
// ?;
|
||||||
|
// DB_CONNECTOR.set(Mutex::new(conn)).ok();
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
// // Async‑compatible accessor
|
// // Async‑compatible accessor
|
||||||
// pub async fn get_conn() -> tokio::sync::MutexGuard<'static, Connection> {
|
// pub async fn get_conn() -> tokio::sync::MutexGuard<'static, Connection> {
|
||||||
@@ -22,64 +68,31 @@
|
|||||||
// .await
|
// .await
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// // TODO TOKIIO AND MUTEX
|
||||||
|
|
||||||
// pub async fn get_data<T>(key: &str, con: &mut Connection) -> RedisResult<(T)>
|
// // async fn test<RV>() -> RedisResult<()> {
|
||||||
// where T: Deserialize<'static> {
|
// // let _pool = redis::get_pool(constants::DB_URI).await;
|
||||||
// let raw_data: String = con.json_get(&key, ".")?;
|
// // let mut con = redis::get_conn();
|
||||||
// Ok( serde_json::from_str(&raw_data)? )
|
|
||||||
|
// // let data1 = SensorData {
|
||||||
|
// // id: Some(1),
|
||||||
|
// // city_codename_cords_sector_cell: String::from("CERCELL:[40.7128,-74.0060]::S1:C1"),
|
||||||
|
// // sensor_name_and_model: String::from("Humidity:[DHT22]"),
|
||||||
|
// // value: 20.7,
|
||||||
|
// // unit: String::from("RH"),
|
||||||
|
// // timestamp: 17000002
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // con.son_set::<&str, &str, SensorData, RV>("asd", "$", &data1);
|
||||||
|
// // Ok(())
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// use deadpool_redis::Pool;
|
||||||
|
|
||||||
|
// pub async fn get_pool(URL: &str) -> Result<bool, ()> {
|
||||||
|
// let mut config = Config::from_url(URL).unwrap();
|
||||||
|
|
||||||
|
// Ok( config.create_pool(Some(Runtime::Tokio1)).unwrap() )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// pub async fn set_data<T>(key: &str, data: &T, con: &mut Connection) -> RedisResult<()>
|
|
||||||
// where T: Serialize,
|
|
||||||
// {
|
|
||||||
// Ok( con.json_set(&key, "$", &data)? )
|
|
||||||
// }
|
|
||||||
|
|
||||||
use tokio::sync::OnceCell;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use redis::{Client, Connection};
|
|
||||||
|
|
||||||
static DB_POOL: OnceCell<Client> = OnceCell::const_new();
|
|
||||||
static DB_CONNECTOR: OnceCell<Mutex<Connection>> = OnceCell::const_new();
|
|
||||||
|
|
||||||
pub async fn get_pool(uri: &str) -> redis::RedisResult<()> {
|
|
||||||
let client = Client::open(uri)?;
|
|
||||||
DB_POOL.set(client).ok();
|
|
||||||
|
|
||||||
let conn = DB_POOL
|
|
||||||
.get()
|
|
||||||
.unwrap()
|
|
||||||
.get_connection()
|
|
||||||
?;
|
|
||||||
DB_CONNECTOR.set(Mutex::new(conn)).ok();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Async‑compatible accessor
|
|
||||||
pub async fn get_conn() -> tokio::sync::MutexGuard<'static, Connection> {
|
|
||||||
DB_CONNECTOR
|
|
||||||
.get()
|
|
||||||
.expect("Redis not initialised")
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO TOKIIO AND MUTEX
|
|
||||||
|
|
||||||
// async fn test<RV>() -> RedisResult<()> {
|
|
||||||
// let _pool = redis::get_pool(constants::DB_URI).await;
|
|
||||||
// let mut con = redis::get_conn();
|
|
||||||
|
|
||||||
// let data1 = SensorData {
|
|
||||||
// id: Some(1),
|
|
||||||
// city_codename_cords_sector_cell: String::from("CERCELL:[40.7128,-74.0060]::S1:C1"),
|
|
||||||
// sensor_name_and_model: String::from("Humidity:[DHT22]"),
|
|
||||||
// value: 20.7,
|
|
||||||
// unit: String::from("RH"),
|
|
||||||
// timestamp: 17000002
|
|
||||||
// };
|
|
||||||
|
|
||||||
// con.son_set::<&str, &str, SensorData, RV>("asd", "$", &data1);
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
pub const SERVER_URL: &str = "127.0.0.1";
|
||||||
|
pub const SERVER_PORT: u16 = 3000;
|
||||||
pub const IS_DEBUG: bool = true; // set to 'false' in production!
|
pub const IS_DEBUG: bool = true; // set to 'false' in production!
|
||||||
|
pub const API_LIB: &str = "Actix";
|
||||||
pub const API_VERSION: &str = "1";
|
pub const API_VERSION: &str = "1";
|
||||||
pub const DB_NAME: &str = "Redis";
|
pub const DB_NAME: &str = "Redis";
|
||||||
pub const DB_URI: &str = "redis://:fswerfds@127.0.0.1:7878/";
|
pub const DB_URI: &str = "redis://:fswerfds@127.0.0.1:7878/";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::util::colored_text::*;
|
|||||||
|
|
||||||
pub fn print_debug_info() {
|
pub fn print_debug_info() {
|
||||||
print_colored_text_info(&format!("- SERVER INFO:"));
|
print_colored_text_info(&format!("- SERVER INFO:"));
|
||||||
|
print_colored_text_info(&format!(" - API LIB: {} ", constants::API_LIB));
|
||||||
print_colored_text_info(&format!(" - API VERSION: {} ", constants::API_VERSION));
|
print_colored_text_info(&format!(" - API VERSION: {} ", constants::API_VERSION));
|
||||||
print_colored_text_info(&format!(" - DB NAME: {} ", constants::DB_NAME));
|
print_colored_text_info(&format!(" - DB NAME: {} ", constants::DB_NAME));
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user