-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
88 changed files
with
6,313 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use serde::{Serialize, Deserialize}; | ||
use tardis::{ | ||
chrono::{DateTime, Utc}, | ||
web::poem_openapi, | ||
}; | ||
#[cfg(feature = "default")] | ||
use tardis::db::sea_orm; | ||
#[derive(Debug, Clone, Default, Serialize)] | ||
#[cfg_attr(feature = "default", derive(poem_openapi::Object, sea_orm::FromQueryResult))] | ||
pub struct RbumSafeSummaryResp { | ||
pub id: String, | ||
pub own_paths: String, | ||
pub owner: String, | ||
pub create_time: DateTime<Utc>, | ||
pub update_time: DateTime<Utc>, | ||
} | ||
|
||
impl RbumSafeSummaryResp { | ||
pub fn extends_to_detail_resp(self, owner_name: impl Into<String>) -> RbumSafeDetailResp { | ||
RbumSafeDetailResp { | ||
id: self.id, | ||
own_paths: self.own_paths, | ||
owner: self.owner, | ||
owner_name: owner_name.into(), | ||
create_time: self.create_time, | ||
update_time: self.update_time, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize)] | ||
#[cfg_attr(feature = "default", derive(poem_openapi::Object, sea_orm::FromQueryResult))] | ||
pub struct RbumSafeDetailResp { | ||
pub id: String, | ||
pub own_paths: String, | ||
pub owner: String, | ||
pub owner_name: String, | ||
pub create_time: DateTime<Utc>, | ||
pub update_time: DateTime<Utc>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "bios-client-hwsms" | ||
version.workspace = true | ||
authors.workspace = true | ||
homepage.workspace = true | ||
documentation.workspace = true | ||
repository.workspace = true | ||
license.workspace = true | ||
edition.workspace = true | ||
readme.workspace = true | ||
publish.workspace = true | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[features] | ||
default = ["reach"] | ||
reach = ["bios-reach"] | ||
[dependencies] | ||
serde.workspace = true | ||
tardis = { workspace = true } | ||
bios-reach = { optional = true, path = "../../support/reach" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
mod send_sms; | ||
pub use send_sms::*; | ||
mod send_diff_sms; | ||
pub use send_diff_sms::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use serde::Serialize; | ||
use tardis::{ | ||
basic::result::TardisResult, | ||
url::Url, | ||
web::reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}, | ||
}; | ||
|
||
use crate::{model::*, SmsClient}; | ||
|
||
#[derive(Debug, Serialize, Default)] | ||
#[serde(rename_all = "camelCase")] | ||
/// Referance: https://support.huaweicloud.com/api-msgsms/sms_05_0002.html | ||
pub struct SmsClientBatchDiffSendRequest<'r> { | ||
pub from: &'r str, | ||
pub status_callback: Option<&'r str>, | ||
pub sms_content: &'r [SmsContent<'r>], | ||
pub extend: Option<&'r str>, | ||
} | ||
|
||
impl<'r> SmsClientBatchDiffSendRequest<'r> { | ||
pub fn new(from: &'r str) -> Self { | ||
Self { from, ..Default::default() } | ||
} | ||
} | ||
|
||
impl SmsClient { | ||
pub async fn send_diff_sms(&self, mut request: SmsClientBatchDiffSendRequest<'_>) -> TardisResult<SmsResponse<Vec<SmsId>>> { | ||
const PATH: &str = "sms/batchSendDiffSms/v1"; | ||
let mut headers = HeaderMap::new(); | ||
request.status_callback = request.status_callback.or(self.status_callback.as_ref().map(Url::as_str)); | ||
headers.insert(AUTHORIZATION, HeaderValue::from_static(Self::AUTH_WSSE_HEADER_VALUE)); | ||
self.add_wsse_headers_to(&mut headers)?; | ||
let mut url = self.base_url.clone(); | ||
url.set_path(PATH); | ||
let resp = self.inner.post(url).headers(headers).json(&request).send().await?.json().await?; | ||
Ok(resp) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use serde::Serialize; | ||
use tardis::{basic::result::TardisResult, serde_json, url::Url, web::reqwest::header::HeaderMap}; | ||
|
||
use crate::{model::*, SmsClient}; | ||
|
||
#[derive(Debug, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct SendSmsRequest<'r> { | ||
pub from: &'r str, | ||
pub status_callback: Option<&'r str>, | ||
pub extend: Option<&'r str>, | ||
pub to: &'r str, | ||
pub template_id: &'r str, | ||
pub template_paras: Option<String>, | ||
pub signature: Option<&'r str>, | ||
} | ||
|
||
impl<'r> SendSmsRequest<'r> { | ||
pub fn new(from: &'r str, sms_content: SmsContent<'r>) -> Self { | ||
let template_paras = if !sms_content.template_paras.is_empty() { | ||
Some(serde_json::to_string(&sms_content.template_paras).expect("string[] to rust String shouldn't fail")) | ||
} else { | ||
None | ||
}; | ||
Self { | ||
from, | ||
status_callback: None, | ||
extend: None, | ||
to: sms_content.to, | ||
template_id: sms_content.template_id, | ||
template_paras, | ||
signature: sms_content.signature, | ||
} | ||
} | ||
} | ||
|
||
impl SmsClient { | ||
pub async fn send_sms(&self, mut request: SendSmsRequest<'_>) -> TardisResult<SmsResponse<Vec<SmsId>>> { | ||
tardis::log::trace!("send sms request: {:?}", request); | ||
const PATH: &str = "sms/batchSendSms/v1"; | ||
request.status_callback = request.status_callback.or(self.status_callback.as_ref().map(Url::as_str)); | ||
let mut headers = HeaderMap::new(); | ||
self.add_wsse_headers_to(&mut headers)?; | ||
let url = self.get_url(PATH); | ||
let builder = self.inner.post(url).headers(headers).form(&request); | ||
let resp = builder.send().await?.json().await?; | ||
tardis::log::trace!("send sms response: {:?}", resp); | ||
Ok(resp) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
//! Extented features | ||
#[cfg(feature = "reach")] | ||
mod reach; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use std::{collections::HashSet, sync::{Arc, OnceLock}}; | ||
|
||
use bios_reach::{ | ||
client::{GenericTemplate, SendChannel}, | ||
dto::{ContentReplace, ReachChannelKind}, config::ReachConfig, consts::MODULE_CODE, | ||
}; | ||
use tardis::{ | ||
async_trait::async_trait, | ||
basic::{error::TardisError, result::TardisResult}, TardisFuns, | ||
}; | ||
|
||
use crate::{SendSmsRequest, SmsContent, SmsClient}; | ||
|
||
#[async_trait] | ||
impl SendChannel for crate::SmsClient { | ||
fn kind(&self) -> ReachChannelKind { | ||
ReachChannelKind::Sms | ||
} | ||
async fn send(&self, template: GenericTemplate<'_>, content: &ContentReplace, to: &HashSet<&str>) -> TardisResult<()> { | ||
let content = content.render_final_content::<20>(template.content); | ||
tardis::log::trace!("send sms {content}"); | ||
let sms_content = SmsContent { | ||
to: &to.iter().fold( | ||
// 11 digits + 1 comma, it's an estimate | ||
String::with_capacity(to.len() * 12), | ||
|mut acc, x| { | ||
if !acc.is_empty() { | ||
acc.push(','); | ||
} | ||
acc.push_str(x); | ||
acc | ||
}, | ||
), | ||
template_id: template.sms_template_id.ok_or_else(|| TardisError::conflict("template missing field template_id", "409-reach-bad-template"))?, | ||
template_paras: vec![&content], | ||
signature: template.sms_signature, | ||
}; | ||
let from = template.sms_from.ok_or_else(|| TardisError::conflict("template missing field sms_from", "409-reach-bad-template"))?; | ||
let request = SendSmsRequest::new(from, sms_content); | ||
let resp = self.send_sms(request).await?; | ||
if resp.is_error() { | ||
use std::fmt::Write; | ||
let mut error_buffer = String::new(); | ||
writeln!(&mut error_buffer, "send sms error [{code}]: {desc}.", code = resp.code, desc = resp.description).expect("write to string shouldn't fail"); | ||
if let Some(ids) = resp.result { | ||
writeln!(&mut error_buffer, "Detail: ").expect("write to string shouldn't fail"); | ||
for detail in ids { | ||
writeln!(&mut error_buffer, "{:?}", detail).expect("write to string shouldn't fail"); | ||
} | ||
} | ||
return Err(TardisError::conflict(&error_buffer, "409-reach-sms-error")); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl crate::SmsClient { | ||
pub fn from_reach_config() -> Arc<Self> { | ||
static SMS_CLIENT: OnceLock<Arc<SmsClient>> = OnceLock::new(); | ||
SMS_CLIENT | ||
.get_or_init(|| { | ||
// this would block thread but it's ok | ||
let config = TardisFuns::cs_config::<ReachConfig>(MODULE_CODE); | ||
let sms_config = &config.sms; | ||
let base_url = sms_config.base_url.parse().expect("invalid sms base url"); | ||
let callback_url = sms_config.status_call_back.as_ref().map(|x| x.parse().expect("invalid sms status_call_back url")); | ||
SmsClient::new(base_url, &sms_config.app_key, &sms_config.app_secret, callback_url).into() | ||
}) | ||
.clone() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
//! # Huawei Cloud Platform Sms Client | ||
//! reference: https://support.huaweicloud.com/msgsms/index.html | ||
use tardis::{ | ||
basic::result::TardisResult, | ||
chrono::{Utc, SecondsFormat}, | ||
rand::random, | ||
url::Url, | ||
web::reqwest::{ | ||
header::{HeaderMap, HeaderValue, AUTHORIZATION}, | ||
Client, | ||
}, crypto::rust_crypto::sha2::Sha256, | ||
}; | ||
mod ext; | ||
mod api; | ||
pub use api::*; | ||
mod model; | ||
pub use model::*; | ||
#[derive(Clone, Debug)] | ||
pub struct SmsClient { | ||
pub(crate) inner: Client, | ||
pub app_key: String, | ||
pub app_secret: String, | ||
pub base_url: Url, | ||
pub status_callback: Option<Url>, | ||
} | ||
|
||
impl SmsClient { | ||
const AUTH_WSSE_HEADER_VALUE: &'static str = r#"WSSE realm="SDP",profile="UsernameToken",type="Appkey""#; | ||
fn add_wsse_headers_to(&self, headers: &mut HeaderMap) -> TardisResult<()> { | ||
use tardis::crypto::{crypto_base64::TardisCryptoBase64, crypto_digest::TardisCryptoDigest}; | ||
const WSSE_HEADER_NAME: &str = "X-WSSE"; | ||
const BASE64: TardisCryptoBase64 = TardisCryptoBase64; | ||
const DIGEST: TardisCryptoDigest = TardisCryptoDigest; | ||
let username = &self.app_key; | ||
// actually iso-8601 | ||
let created = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true); | ||
// and random 1~128bit number | ||
let nonce = format!("{:X}", random::<u128>()); | ||
let digest_raw = format!("{}{}{}", nonce, created, &self.app_secret); | ||
let password_digest = BASE64.encode_raw(DIGEST.digest_raw(digest_raw.as_bytes(), Sha256::new())?); | ||
let wsse_header = format!(r#"UsernameToken Username="{username}",PasswordDigest="{password_digest}",Nonce="{nonce}",Created="{created}""#); | ||
let wsse_header = HeaderValue::from_str(&wsse_header).expect("Fail to build sms header, maybe there are unexpected char in app_key."); | ||
headers.insert(AUTHORIZATION, HeaderValue::from_static(Self::AUTH_WSSE_HEADER_VALUE)); | ||
headers.insert(WSSE_HEADER_NAME, wsse_header); | ||
Ok(()) | ||
} | ||
fn get_url(&self, path: &str) -> Url { | ||
let mut new_url = self.base_url.clone(); | ||
let origin_path = new_url.path(); | ||
let new_path = [origin_path.trim_end_matches('/'), path.trim_start_matches('/')].join("/"); | ||
new_url.set_path(&new_path); | ||
new_url | ||
} | ||
pub fn new(base_url: Url, app_key: impl Into<String>, app_secret: impl Into<String>, status_callback: Option<Url>) -> Self { | ||
let app_key: String = app_key.into(); | ||
let app_secret: String = app_secret.into(); | ||
|
||
SmsClient { | ||
inner: Default::default(), | ||
base_url, | ||
app_key, | ||
app_secret, | ||
status_callback, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct SmsResponse<T> { | ||
pub code: String, | ||
pub description: String, | ||
pub result: Option<T>, | ||
} | ||
|
||
impl<T> SmsResponse<T> { | ||
pub fn is_error(&self) -> bool { | ||
self.code.starts_with('E') | ||
} | ||
pub fn is_ok(&self) -> bool { | ||
self.code == "000000" | ||
} | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct SmsId { | ||
pub sms_msg_id: String, | ||
pub from: String, | ||
pub origin_to: String, | ||
pub status: String, | ||
pub create_time: String, | ||
} | ||
|
||
#[derive(Debug, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
/// see: [SmsClientBatchDiffSendRequest] | ||
/// | ||
/// reference: https://support.huaweicloud.com/api-msgsms/sms_05_0002.html#ZH-CN_TOPIC_0000001430352905__table4039578 | ||
pub struct SmsContent<'r> { | ||
pub to: &'r str, | ||
pub template_id: &'r str, | ||
pub template_paras: Vec<&'r str>, | ||
pub signature: Option<&'r str>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.