use axum::{extract::{Query, State}, response::Redirect}; use crate::{AppState, datasource::Datasource, log, LogLevel::*}; use serde::Deserialize; const APPID: &str = "wx664469117500d259"; const APPSECRET : &str = "11ec6e6d4b9062f00a4fc08c529293be"; // const APPSECRET : &str = "wx664469117500d259"; #[derive(serde::Deserialize)] pub struct QueryParams { code: String, state: String, } #[allow(dead_code)] #[derive(Deserialize)] pub struct WxGetToken{ access_token: String, expires_in: usize, refresh_token: String, openid: String, scope:String } /* https://open.weixin.qq.com/connect/oauth2/authorize?appid= &redirect_uri=http://192.168.24.101:3000/auth&response_type=code&scope=snsapi_base&state=1#wechat_redirect wx664469117500d259 wxdf57fde70a442828 */ #[allow(dead_code)] #[derive(Deserialize)] pub struct WxUserInfo{ openid: String, nickname: String, sex: u16, headimgurl: String, } pub async fn auth( // headers: HeaderMap, State(state): State, Query(q): Query ) -> Redirect { /* 从公众号跳转时会访问 http://{{host}}/api/auth?code={code}&state={state} code为获取token所需中间码 state为授权链接的state用于防止越权,用途不明 */ // 访问以获取微信token log(Debug, format!("authing code+:`{}` state:`{}`",q.code,q.state)); // reqwest::Client::builder().build()?.get(url).send().await; let webcli = match reqwest::Client::builder() .timeout(std::time::Duration::from_secs(3)) .build() { Ok(cli) => cli, Err(e) => { log(Warning, format!("rewest client builder failed:: {e}")); return Redirect::to(format!("{}/null",crate::WEBP).as_str()) } }; log(Debug, format!("web client init")); // webcli.get("url").send(); let a = match webcli.get(format!( "https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code", // "https://iotplatform.worldflying.cn/api/wx/auth?appid={}&secret={}&code={}&state={}&grant_type=authorization_code", APPID, APPSECRET, q.code, // q.state )).send().await { Ok(a) => { // log(Info, format!("dealing")); let v = a.text().await; log(Debug, format!("dealing\n{:?}",v)); let v = if let Ok(v)=v {v}else { return Redirect::to(format!("{}/null",crate::WEBP).as_str()); }; match serde_json::from_str::(v.as_str()) // a.json::().await { Ok(b) => b, Err(e) =>{ log(Error, format!("redirect 2 {e}, raw:{v}")); return Redirect::to(format!("{}/null",crate::WEBP).as_str()) } } }, Err(e) => { log(Debug, format!("redirect 0 {e}")); return Redirect::to(format!("{}/null",crate::WEBP).as_str()) } }; // let host = headers.get("host") // .and_then(|hv| hv.to_str().ok()) // .unwrap_or("localhost:3000"); log(Debuging, format!("in authing")); let token=super::token(32); let res = super::check_openid(&state, a.openid.clone()).await; if let Ok((id,mqid)) = res{ // 用户已经注册 函数末尾发生跳转到主页 if let Err(e) = state.db_lite.execute("update user set token=? where id=?", (token.clone(), id)).await{ println!("{e}"); }; // 已注册时绑定设备 if let Some(sn)= q.state.strip_prefix("did="){ log(Debuging, format!("got did: {}",sn)); if let Err(e) = state.db_lite.execute("update device set belongto=? where sn=? and belongto=0", (id,sn)).await{ println!("{e}"); }else if let Ok(did) = state.db_lite.query("select id from device where sn=?", [sn], |r|{r.get::(0)}).await{ // if let Some(did)=state.db_lite.last_insert_rowid().await.ok(){ // 错误位置:update不会给last_insert_rowid if let Err(e) = state.db_lite.execute("insert into map_user_device (uid, did)values(?,?)", (id,did)).await{ println!("{e}"); } else{ if let Some(mq) = state.mq_detail{ if let Err(e)=mq.db_mq_map.execute( "insert into pub (clientid,topic,deadline)values(?,?,9223372036854775807),(?,?,9223372036854775807)", (mqid.clone(),format!("{}/cmd",sn),mqid.clone(),format!("/wf/Iot/device/{sn}"))).await{ log(Warning, format!("on register with device: {e}")); }; if let Err(e)=mq.db_mq_map.execute( "insert into sub (clientid,topic,deadline)values(?,?,9223372036854775807),(?,?,9223372036854775807)", (mqid.clone(),format!("{}/state",sn),mqid.clone(),format!("/wf/Iot/client/{sn}"))).await{ log(Warning, format!("on register with device: {e}")); }; } } }; } } else if a.scope !="snsapi_userinfo"{ // 用户未注册,跳转获取用户详细信息 log(Debug, "send redirect for get uinfo".to_string()); return Redirect::to( format!("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx664469117500d259&redirect_uri=https%3A%2F%2Fwww.worldflying.cn%2Ftools%2Fwxredirect%2Fplc.html&response_type=code&scope=snsapi_userinfo&state={}#wechat_redirect",q.state).as_str() // format!("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx664469117500d259&redirect_uri=https%3A%2F%2Fwww.worldflying.cn%2Ftools%2Fwxredirect%2Fiot.html&response_type=code&scope=snsapi_userinfo&state={}#wechat_redirect",q.state).as_str() ) } else { // 带用户信息的重定向收取目标分支,保存用户数据并创建mqtt相关信息 let b:WxUserInfo = if let Ok(b) = {if let Ok(b) = reqwest::get(format!( "https://api.weixin.qq.com/sns/userinfo?access_token={}&openid={}&lang=zh_CN", a.access_token, a.openid.clone() )).await{b.json().await} else { log(Info, "redirect 4".to_string()); return Redirect::to(format!("{}/null",crate::WEBP).as_str())}}{b} else { log(Info, "redirect 5".to_string()); return Redirect::to(format!("{}/null",crate::WEBP).as_str())}; let mqid = format!("webu{}",super::token(5)); if let Err(e) = state.db_lite.execute( "insert into user (uname,nickname,passwd,headimgurl,openid,sex,token,mqid) values(?,?,?,?,?,?,?,?)", (b.nickname.clone(),b.nickname.clone(),"",b.headimgurl,b.openid,b.sex,token.clone(),mqid.clone())).await{ println!("{e}"); } let uid = match state.db_lite.last_insert_rowid().await{ Ok(id) => id, Err(e) => { println!("{e}"); 0 } }; if let Err(e) = state.db_lite.execute( "insert into area (name,sup,createby)values(?,0,?)", (format!("{}的家",b.nickname.clone()),uid) ).await{println!("{e}")}; let area_sup: i64; if let Err(e) = state.db_lite.execute( "insert into map_user_area (uid,aid)values(?,?)", [uid,{area_sup=match state.db_lite.last_insert_rowid().await{Ok(id)=>id,Err(e)=>{println!("{e}");0}};area_sup}] ).await{println!("{e}")}; if let Err(e) = state.db_lite.execute( "insert into area (name,sup,createby)values('客厅',?,?)", [area_sup,uid] ).await{println!("{e}")}; // 未注册时绑定设备 if let Some(sn)= q.state.strip_prefix("did="){ log(Debuging, format!("got did: {}",sn)); if let Err(e) = state.db_lite.execute("update device set belongto=? where sn=? and belongto=0", (uid,sn)).await{ println!("{e}"); }else if let Ok(did) = state.db_lite.query("select id from device where sn=?", [sn], |r|{r.get::(0)}).await{ // if let Some(did)=state.db_lite.last_insert_rowid().await.ok(){ // 错误位置:update不会给last_insert_rowid if let Err(e) = state.db_lite.execute("insert into map_user_device (uid, did)values(?,?)", (uid,did)).await{ println!("{e}"); } }; if let Some(mq) = state.mq_detail{ if let Err(e)=mq.db_mq_map.execute( "insert into pub (clientid,topic,deadline)values(?,?,9223372036854775807),(?,?,9223372036854775807)", (mqid.clone(),format!("{}/cmd",sn),mqid.clone(),format!("/wf/Iot/device/{sn}"))).await{ log(Warning, format!("on register with device: {e}")); }; if let Err(e)=mq.db_mq_map.execute( "insert into sub (clientid,topic,deadline)values(?,?,9223372036854775807),(?,?,9223372036854775807)", (mqid.clone(),format!("{}/state",sn),mqid.clone(),format!("/wf/Iot/client/{sn}"))).await{ log(Warning, format!("on register with device: {e}")); }; } } log(Debug, "register success".to_string()); // 注册成功 函数末尾发生跳转到主页 // return Redirect::to(format!("http://124.222.106.170/plc_ctrl?token={}#{}",token.clone(),q.state).as_str()); }; // request::Request::get("body"); // log(Info, format!("bad happend {:?}",res)); log(Debug, format!("redirect back with token, state: {}",q.state)); return Redirect::to(format!("{}?token={}{}",crate::WEBP,token.clone(),if q.state.starts_with("?"){format!("#{}",q.state)}else{"".to_string()}).as_str()); // return Redirect::to(format!("{}?token={}",crate::WEBP,token.clone()).as_str()); }