use crate::tina::core::service::token::TokenService;
use crate::tina::data::app_error::AppError;
use crate::tina::data::http::request_log_param::RequestLogParam;
use crate::tina::data::http::response_data::HttpResData;
use crate::tina::data::http_status::HttpStatus;
use crate::tina::data::i18n_string::I18nString;
use crate::tina::data::json::ToJson;
use crate::tina::data::no_data::NoData;
use crate::tina::data::throttle::ThrottleParam;
use crate::tina::data::AppResult;
use crate::tina::i18n::message::system_message::SystemMessage;
use crate::tina::server::application::Application;
use crate::tina::server::http::request_ext::RequestExt;
use crate::tina::server::http::response::HttpResponseExt;
use crate::tina::server::http::route::{HttpMethod, RouteBaseConfig};
use crate::tina::server::http::route_ext::ApiResponder;
use crate::tina::server::session::Session;
use crate::tina::util::not_empty::INotEmpty;
use crate::tina::util::string::AsStr;
use crate::{app_error, app_error_from, app_system_error, app_system_error_with_msg, i18n_string};
use chrono::Local;
use either::Either;
use futures_util::FutureExt;
use serde_json::Value;
use std::error::Error;
use std::future::Future;
use std::panic::AssertUnwindSafe;
use std::sync::Arc;
use tracing::Level;
pub async fn before_demo<Req: RequestExt>(req: &Req) -> Result<(), AppError> {
let application = match req.get_application() {
Ok(v) => v,
Err(err) => return Err(err),
};
let route = match req.get_route_config() {
Ok(v) => v,
Err(err) => return Err(err),
};
if application.get_server_config()?.demo_enabled {
match route.method {
HttpMethod::POST | HttpMethod::PUT | HttpMethod::DELETE | HttpMethod::PATCH => {
return Err(app_system_error_with_msg!(i18n_string!(SystemMessage::ERROR_CAN_NOT_OPERATE_WITHIN_DEMO_MODEL)));
}
_ => {}
}
}
Ok(())
}
pub async fn before_panic<Req: RequestExt>(req: &Req) -> Result<Option<(String, Arc<RequestLogParam>)>, AppError> {
match req.get_request_log_param() {
Ok(v) => match v {
None => Ok(None),
Some(v) => Ok(Some((req.get_locale(), v))),
},
Err(_) => Ok(None),
}
}
pub async fn after_panic<Res: HttpResponseExt, Err: Error + Send + Sync + 'static, Fut: Future<Output = Result<Res, Err>>>(
fut: Fut,
obj: Option<(String, Arc<RequestLogParam>)>,
) -> Result<Res, AppError> {
let c = AssertUnwindSafe(fut).catch_unwind();
let r = c.await;
match r {
Ok(res) => match res {
Ok(res) => Ok(res),
Err(err) => Err(app_error_from!(err)),
},
Err(err) => {
let err = match AppError::from_any_send(err) {
Either::Left(err) => err,
Either::Right(_) => {
let app_err = app_system_error!("panic error");
let err_msg = app_err.to_string();
tracing::error!("{:?}", err_msg);
app_err
}
};
if let Some((locale, param)) = obj {
let err2 =
AppError::new_error(err.get_return_code().clone(), err.get_return_msg().clone(), err.get_location(), err.get_line());
let vo: HttpResData<NoData> = HttpResData {
code: err2.get_return_code(),
msg: Some(err2.get_return_msg().get_string(&locale)),
data: None,
extension: Default::default(),
session: Session::default(),
};
param.set_result(Arc::new(vo.to_json_value()))
}
Err(err)
}
}
}
pub async fn before_permission<Req: RequestExt>(req: &Req) -> Result<(), AppError> {
let route_config = req.get_route_config()?;
if !route_config.enable_token {
return Ok(());
}
if let Some(permission) = route_config.require_permission.as_ref() {
let permission = permission.as_str().trim();
if permission.not_empty() {
let uri = req.get_request_path();
let uri = uri.as_ref();
let application2 = req.get_application()?;
let permission_service = application2.get_permission_service()?;
let session = Session::from_http_request(req).await?;
return match permission_service.has_permi(&session, permission) {
true => Ok(()),
false => Err(app_error!(HttpStatus::Unauthorized, i18n_string!(SystemMessage::ERROR_NO_PERM_ACCESS_API, ("name", uri)))),
};
}
}
Ok(())
}
pub async fn before_request_log_param<Req: RequestExt>(
req: &Req,
) -> Result<(Session, Application, Arc<RequestLogParam>, Arc<RouteBaseConfig>), AppError> {
let application = req.get_application()?;
let session = Session::from_http_request(req).await?;
let req_log_param = req.get_request_log_param()?.ok_or_else(|| app_system_error!("no RequestLogParam found in request"))?;
let route = req.get_route_config()?;
req_log_param.set_log_level(route.log_param_level);
req_log_param.set_handler_name(route.handler_name.to_string());
req_log_param.set_contains_sensitive_param(route.contains_sensitive_param);
req_log_param.set_http_method(req.get_method().to_string());
req_log_param.set_remote_ip_address(req.get_remote_ip_address().to_string());
req_log_param.set_request_uri(req.get_request_path().into_owned());
req_log_param.set_max_param_len(application.get_log_config()?.log_parameter_json_length as usize);
req_log_param.set_start_time_millis(Local::now().naive_local().timestamp_millis());
Ok((session, application, req_log_param, route))
}
pub async fn after_request_log_param<Res: HttpResponseExt, Err: Error + Send + Sync + 'static>(
r: Result<Res, Err>,
obj: (Session, Application, Arc<RequestLogParam>, Arc<RouteBaseConfig>),
) -> Result<Res, AppError> {
fn log_param(req_log_param: Arc<RequestLogParam>, _application: Option<Application>, err: Option<impl std::error::Error>) {
req_log_param.set_end_time_millis(Local::now().naive_local().timestamp_millis());
if let Some(err) = err {
req_log_param.set_err(err);
}
let level = req_log_param.get_log_level();
match level {
Level::TRACE => {
if tracing::enabled!(Level::TRACE) {
tracing::trace!("{}", req_log_param);
}
}
Level::DEBUG => {
if tracing::enabled!(Level::DEBUG) {
tracing::debug!("{}", req_log_param);
}
}
Level::INFO => {
if tracing::enabled!(Level::INFO) {
tracing::info!("{}", req_log_param);
}
}
Level::WARN => {
if tracing::enabled!(Level::WARN) {
tracing::warn!("{}", req_log_param);
}
}
Level::ERROR => {
if tracing::enabled!(Level::ERROR) {
tracing::error!("{}", req_log_param);
}
}
}
}
let (session, application, req_log_param, route) = obj;
if Ok(true) == session.is_disable_request_log_param() {
return r.map_err(app_error_from!());
}
req_log_param.set_user_id(session.get_user().map(|v| v.get_username().to_string()).unwrap_or_default());
let enable_log_param = match application.get_log_config()?.enable_request_parameter_log {
true => match route.enable_log_param {
None => matches!(route.method, HttpMethod::POST | HttpMethod::PUT | HttpMethod::DELETE),
Some(v) => v,
},
false => false,
};
match r {
Ok(res) => {
if enable_log_param {
let err = match res.success() {
true => None,
false => Some(AppError::new_error(
HttpStatus::Error,
I18nString::direct_from(res.get_error_message().as_str()),
file!(),
line!(),
)),
};
log_param(req_log_param, Some(application), err);
}
Ok(res)
}
Err(err) => {
if enable_log_param {
log_param(req_log_param, Some(application), Some(&err));
}
Err(app_error_from!(err))
}
}
}
pub async fn before_token<Req: RequestExt>(req: &Req) -> Result<(), AppError> {
let session = Session::from_http_request(req).await?;
if session.get_user().is_some() {
return Ok(());
}
let route_config = req.get_route_config()?;
let token_user_result = TokenService::get_login_user(req).await;
let enable_token = route_config.enable_token;
match token_user_result {
Ok(token_user) => match token_user {
None => match enable_token {
true => Err(app_error!(HttpStatus::Unauthorized, i18n_string!(SystemMessage::ERROR_USER_NOTFOUND))),
false => Ok(()),
},
Some(mut token_user) => {
match TokenService::verify_token(req, &mut token_user).await {
Ok(_) => {
let token_user = Arc::new(token_user);
session.set_user(token_user)?;
Ok(())
}
Err(err) => match enable_token {
true => {
tracing::error!("验证用户Token异常: {:?}", err);
Err(err)
}
false => Ok(()),
},
}
}
},
Err(err) => match enable_token {
true => {
tracing::error!("获取Token用户信息异常: {:?}", err);
Err(err)
}
false => Ok(()),
},
}
}
pub async fn before_transaction<Req: RequestExt>(req: &Req) -> Result<Option<(Session, Arc<RouteBaseConfig>)>, AppError> {
let route_config = match req.get_route_config() {
Ok(v) => v,
Err(err) => return Err(err),
};
static AUTO_TRANSACTION_METHOD: [HttpMethod; 3] = [HttpMethod::POST, HttpMethod::PUT, HttpMethod::DELETE];
match route_config.enable_auto_transaction {
true => {
let session = Session::from_http_request(req).await?;
if AUTO_TRANSACTION_METHOD.contains(&route_config.method) {
let service = session.get_transaction_service()?;
service.require_transaction().await?;
}
Ok(Some((session, route_config)))
}
false => Ok(None),
}
}
pub async fn after_transaction<Res: HttpResponseExt, Err: Error + Send + Sync + 'static>(
r: Result<Res, Err>,
obj: Option<(Session, Arc<RouteBaseConfig>)>,
) -> Result<Res, AppError> {
match obj {
Some((session, route_config)) => match route_config.enable_auto_transaction {
true => match r {
Ok(res) => {
let service = session.get_transaction_service()?;
let success = { res.success() };
match success {
true => {
if service.is_in_transaction().await? {
service.commit_transaction().await?;
}
}
false => {
if service.is_in_transaction().await? {
service.rollback_transaction().await?;
}
}
}
Ok(res)
}
Err(err) => {
let service = session.get_transaction_service()?;
if service.is_in_transaction().await? {
service.rollback_transaction().await?;
}
Err(app_error_from!(err))
}
},
false => r.map_err(app_error_from!()),
},
None => r.map_err(app_error_from!()),
}
}
pub async fn before_throttle(session: &Session, throttle_params: &[ThrottleParam]) -> AppResult<()> {
let application = session.get_application();
let service = application.get_throttle_service()?;
service.handle_throttle(session, throttle_params).await?;
Ok(())
}
pub async fn after_throttle<Res: ApiResponder<Inner>, Inner>(
session: &Session,
throttle_params: &[ThrottleParam],
res: &Res,
) -> AppResult<()> {
let application = session.get_application();
let service = application.get_throttle_service()?;
match res.is_success() {
true => {
service.after_handle_throttle_success(session, throttle_params).await?;
}
false => {
service.after_handle_throttle_error(session, throttle_params).await?;
}
}
Ok(())
}
#[allow(unused_variables)]
pub async fn before_delegate(
route_config: Arc<RouteBaseConfig>,
remote_ip_address: &str,
session: &Session,
param_value: &Value,
) -> AppResult<Vec<ThrottleParam>> {
let mut throttle_params = Vec::new();
if route_config.throttles.not_empty() {
for throttle in route_config.throttles.iter() {
let mut param = throttle.to_throttle_param(remote_ip_address, session, param_value)?;
if !param.is_empty() {
throttle_params.append(&mut param);
}
}
}
if throttle_params.not_empty() {
before_throttle(session, throttle_params.as_slice()).await?;
}
Ok(throttle_params)
}
#[allow(unused_variables)]
pub async fn after_delegate<Res: ApiResponder<Inner>, Inner>(
session: &Session,
param_value: &Value,
res: &Res,
r1: Vec<ThrottleParam>,
) -> AppResult<()> {
if r1.not_empty() {
after_throttle(session, r1.as_slice(), res).await?;
}
Ok(())
}