|
|
@@ -1,33 +1,156 @@
|
|
|
mod datasource;
|
|
|
mod api;
|
|
|
+use std::{collections::HashMap, process::exit, str::FromStr};
|
|
|
+
|
|
|
+use axum::{extract::{Request, State}, middleware::Next, routing::any};
|
|
|
use datasource::sqlite;
|
|
|
+use tokio::sync::Mutex;
|
|
|
use tower_http::cors::{Any, CorsLayer}; // 添加Any和CorsLayer的引用
|
|
|
|
|
|
+const WEBP: &str =
|
|
|
+ "https://www.worldflying.cn/tools/4gplc/"
|
|
|
+// "http://124.222.106.170/plc_ctrl"
|
|
|
+;
|
|
|
+
|
|
|
+
|
|
|
+fn now() -> String{
|
|
|
+ chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#[repr(u8)]
|
|
|
+#[derive(PartialEq, PartialOrd,Debug)]
|
|
|
+#[allow(dead_code)]
|
|
|
+pub enum LogLevel{
|
|
|
+ Debug = 0,
|
|
|
+ Warning,
|
|
|
+ Info,
|
|
|
+ Error,
|
|
|
+ OFF = 0xfe,
|
|
|
+ Debuging,
|
|
|
+}
|
|
|
+
|
|
|
+use LogLevel::*;
|
|
|
+const LOG_LEVEL: LogLevel = Debug;
|
|
|
+
|
|
|
+pub fn log(level: LogLevel, msg: String) {
|
|
|
+ if level < LOG_LEVEL {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ println!("[{:?}] {} {}", level, now(), msg);
|
|
|
+}
|
|
|
+
|
|
|
#[derive(Clone)]
|
|
|
struct AppState{
|
|
|
db_lite: sqlite::SqlitePool
|
|
|
}
|
|
|
|
|
|
+#[derive(Clone)]
|
|
|
+struct Counter {
|
|
|
+ counts: std::sync::Arc<Mutex<HashMap<String, [u64;2]>>>,
|
|
|
+}
|
|
|
+
|
|
|
+impl Counter {
|
|
|
+ fn new() -> Self {
|
|
|
+ Counter {
|
|
|
+ counts: std::sync::Arc::new(Mutex::new(HashMap::new())),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ async fn increment(&self, path: &str, ok: bool) {
|
|
|
+ let mut counts = self.counts.lock().await;
|
|
|
+ (*counts.entry(path.to_string()).or_insert([0,0]))[if ok{0}else{1}] +=1;
|
|
|
+ }
|
|
|
+ async fn get_counts(&self) -> HashMap<String, [u64;2]> {
|
|
|
+ let counts = self.counts.lock().await;
|
|
|
+ HashMap::from(counts.clone())
|
|
|
+ }
|
|
|
+ // async fn clean(&self) {
|
|
|
+ // let mut counts = self.counts.lock().await;
|
|
|
+ // counts.clear();
|
|
|
+ // }
|
|
|
+}
|
|
|
+// cargo run --package iotplatform_lite --bin iotplatform_lite
|
|
|
+const CFGPATH: &'static str = "config.json";
|
|
|
+
|
|
|
#[tokio::main]
|
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
- let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
|
|
+#[derive(serde::Deserialize,serde::Serialize, Debug)]
|
|
|
+ pub struct Conf{
|
|
|
+ host: String,
|
|
|
+ ssl: Option<String>,
|
|
|
+ db: String,
|
|
|
+ ssl_cert: Option<String>,
|
|
|
+ ssl_key: Option<String>,
|
|
|
+ }
|
|
|
+ let conf:Conf =match serde_json::from_reader(match std::fs::File::open(CFGPATH){
|
|
|
+ Ok(f) => f,
|
|
|
+ Err(_) => {let f=std::fs::File::create_new(CFGPATH).unwrap();
|
|
|
+ serde_json::to_writer(&f, &Conf{
|
|
|
+ ssl: None,
|
|
|
+ host: "0.0.0.0:3000".to_string(),
|
|
|
+ db: "db.sqlite".to_string(),
|
|
|
+ ssl_cert: None,
|
|
|
+ ssl_key: None,
|
|
|
+ }).unwrap();
|
|
|
+ f}
|
|
|
+ }){
|
|
|
+ Ok(conf) => conf,
|
|
|
+ Err(_) => {log(Warning, "config file phrase fail, overwriting ...\n\t\tdone, running with default configuration".to_string());Conf { host: "0.0.0.0:3000".to_string(), db: "db.sqlite".to_string(),ssl_cert:None, ssl_key:None,ssl:None }},
|
|
|
+ };
|
|
|
|
|
|
- let appstat = AppState{db_lite: sqlite::init_sqlite_pool("./db.sqlite", 10).await?};
|
|
|
+ let appstat = AppState{db_lite: sqlite::init_sqlite_pool(&conf.db, 10).await?};
|
|
|
+
|
|
|
+ let monitor = Counter::new();
|
|
|
|
|
|
use axum::routing::{post,get};
|
|
|
let app = axum::Router::new()
|
|
|
+ .route("/api/wx/auth", get(api::auth::auth))
|
|
|
+ .with_state(appstat.clone())
|
|
|
// 应用完整的CORS配置
|
|
|
- .nest("/api", axum::Router::new()
|
|
|
+ .nest("/apilite", axum::Router::new()
|
|
|
+ // .nest("/api", axum::Router::new()
|
|
|
+ // .route("/wx/auth", get(api::auth::auth))
|
|
|
+
|
|
|
.route("/loggin", post(api::user::u_loggin))
|
|
|
+
|
|
|
+ .route("/device", post(api::device::d_all))
|
|
|
+
|
|
|
+ .route("/user/info", post(api::user::u_info))
|
|
|
.route("/user/edit", post(api::user::u_edit))
|
|
|
.route("/user/list", post(api::user::u_list))
|
|
|
- .route("/device", post(api::device::d_all))
|
|
|
|
|
|
+ // .route("/user/getjsconfig", post(api::user::getjsconfig))
|
|
|
+
|
|
|
+ .route("/area/new", post(api::area::a_new))
|
|
|
+ .route("/area/edit", post(api::area::a_edit))
|
|
|
+ .route("/area/delete", post(api::area::a_del))
|
|
|
+ .route("/area/remove", post(api::area::a_remove))
|
|
|
+
|
|
|
.route("/device/edit", post(api::device::d_edit))
|
|
|
+ // .route("/device/transform", post(api::device::d_transform))
|
|
|
+ .route("/device/new", post(api::device::d_new))
|
|
|
+ .route("/device/delete", post(api::device::d_del))
|
|
|
+ .route("/device/remove", post(api::device::d_remove))
|
|
|
+
|
|
|
+ .route("/device/burn", post(api::device::burn))
|
|
|
+ // .route("/device/save", post(todo!()))
|
|
|
+
|
|
|
.route("/flow/share/new", post(api::flow_task::new_flow_task_share_device))
|
|
|
.route("/flow/share/checkout", post(api::flow_task::checkout_flow_task_of_share_device))
|
|
|
|
|
|
- .route("/auth", get(api::auth::auth))
|
|
|
+ .route_layer(
|
|
|
+ axum::middleware::from_fn_with_state(
|
|
|
+ monitor.clone(),
|
|
|
+ |a:State<Counter>,req: Request,c:Next | async move{
|
|
|
+ log(Debuging, format!("in url {} {:?}",req.uri().path(),req.uri().query()));
|
|
|
+ let url = format!("{} {}",req.method().as_str(),req.uri().path());
|
|
|
+ let resp = c.run(req).await;
|
|
|
+ a.increment(url.as_str(),resp.status().is_success()).await;
|
|
|
+ resp
|
|
|
+ }
|
|
|
+ )
|
|
|
+ )
|
|
|
+ .with_state(appstat)
|
|
|
|
|
|
// layer需要在最后添加,添加视为入栈,生效顺序为出栈顺序
|
|
|
.layer(CorsLayer::new()
|
|
|
@@ -35,17 +158,59 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
.allow_methods(Any)
|
|
|
.allow_headers(Any))
|
|
|
)
|
|
|
- .route("/", get(|| async {"hello".to_string()}))
|
|
|
- .route("/", post(|| async {"hello".to_string()}))
|
|
|
+ // .route("/", get(|couter:State<Counter>| async move {"hello".to_string()}))
|
|
|
+ .route("/", any(|| async {"hello".to_string()}))
|
|
|
// .route("/hello", get(api::example))
|
|
|
-
|
|
|
- .with_state(appstat)
|
|
|
-
|
|
|
- .layer(CorsLayer::new()
|
|
|
- .allow_origin(Any)
|
|
|
- .allow_methods(Any)
|
|
|
- .allow_headers(Any))
|
|
|
+ .nest("/monitor", axum::Router::new()
|
|
|
+ .route("/show", get(|c:State<Counter>|async move{ axum::Json::from(c.get_counts().await) }))
|
|
|
+ .with_state(monitor)
|
|
|
+ )
|
|
|
;
|
|
|
- axum::serve(listener, app).await?;
|
|
|
+ if let (Some(cert),Some(key),Some(ssl)) = (conf.ssl_cert,conf.ssl_key,conf.ssl) {
|
|
|
+ use axum_server::tls_rustls::RustlsConfig;
|
|
|
+ use std::{path::PathBuf,net::SocketAddr};
|
|
|
+ log(Info, format!("rederect for http2https at {}==>{}",conf.host,ssl));
|
|
|
+ let (listener, app2) = (
|
|
|
+ tokio::net::TcpListener::bind(conf.host).await?,
|
|
|
+ axum::Router::new()
|
|
|
+ .route("/", any(redirect_http_to_https)));
|
|
|
+ tokio::spawn(async move{
|
|
|
+ log(Info, format!("http2https start"));
|
|
|
+ axum::serve(listener,app2).await
|
|
|
+ });
|
|
|
+
|
|
|
+ log(Info, format!("host https at {}",ssl));
|
|
|
+ log(Info, "starting with ssl-https".to_string());
|
|
|
+ axum_server::bind_rustls(
|
|
|
+ match SocketAddr::from_str(&ssl){
|
|
|
+ Ok(a) => {log(Debug, format!("ssl starting in {a}"));a},
|
|
|
+ Err(e) => {log(Error, format!("{e}"));exit(1)}
|
|
|
+ },
|
|
|
+ match RustlsConfig::from_pem_file(
|
|
|
+ PathBuf::from(cert),
|
|
|
+ PathBuf::from(key),
|
|
|
+ ).await{
|
|
|
+ Ok(k) => k,
|
|
|
+ Err(e) => {log(Error, format!("{e}"));exit(1)}
|
|
|
+ }
|
|
|
+ ).serve(app.into_make_service()).await?;
|
|
|
+ } else {
|
|
|
+ log(Info, format!("hosting at {}",conf.host));
|
|
|
+ let listener = tokio::net::TcpListener::bind(conf.host).await?;
|
|
|
+ axum::serve(listener, app).await?;
|
|
|
+ }
|
|
|
+ log(Info, format!("bye!"));
|
|
|
Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+use axum::{response::Redirect, http::{HeaderMap,Uri,StatusCode}};
|
|
|
+async fn redirect_http_to_https(host:HeaderMap, uri: Uri) -> Result<Redirect, StatusCode> {
|
|
|
+ if let Some(host) = host.get("Host").and_then(|t|{t.to_str().ok()}){
|
|
|
+ let https_uri = format!("https://{}{}", host, uri.path());
|
|
|
+ log(Debuging, format!("asdf WTF!! {https_uri}"));
|
|
|
+ Ok(Redirect::permanent(&https_uri))
|
|
|
+ } else {
|
|
|
+ Err(StatusCode::BAD_REQUEST)
|
|
|
+ }
|
|
|
}
|