use std::sync::Arc;
use std::time::SystemTime;
use actix_web::dev::Service;
use futures_util::FutureExt;
use colored::Colorize;
use futures_util::future;
use serde_json::{Value as JsonValue};
use teo_result::{Error, Result};
use teo_runtime::config::server::Server;
use teo_runtime::namespace::Namespace;
use actix_http::body::MessageBody;
use actix_http::{HttpMessage, Method as HttpMethod};
use actix_web::{App, FromRequest, HttpRequest, HttpResponse, HttpServer, web};
use actix_web::dev::{ServiceFactory, ServiceRequest, ServiceResponse};
use actix_web::middleware::DefaultHeaders;
use key_path::path;
use teo_parser::ast::handler::HandlerInputFormat;
use teo_runtime::action::Action;
use teo_runtime::handler::action::builtin_action_handler_from_name;
use teo_runtime::handler::Handler;
use teo_runtime::handler::handler::Method;
use teo_runtime::{connection, request};
use teo_runtime::connection::transaction;
use teo_runtime::handler::default::{create, find_first, find_many, find_unique, update, upsert, copy, create_many, update_many, copy_many, delete_many, count, aggregate, group_by, delete};
use teo_runtime::model::Model;
use teo_runtime::response::Response;
use teo_teon::Value;
use crate::cli::entrance::Entrance;
use crate::cli::runtime_version::RuntimeVersion;
use crate::server::parse::{parse_form_body, parse_json_body};
use teo_runtime::handler::input::{validate_and_transform_json_input_for_handler, validate_and_transform_json_input_for_builtin_action};
use teo_runtime::handler::r#match::HandlerMatch;
use crate::message::{info_message, request_message, unhandled_request_message};
use crate::server::error::WrapError;
use crate::server::request::RequestImpl;
use crate::server::responder::IntoHttpResponse;
fn make_server_app(
main_namespace: &'static Namespace,
conf: &'static Server,
) -> App<impl ServiceFactory<
ServiceRequest,
Response = ServiceResponse<impl MessageBody>,
Config = (),
InitError = (),
Error = actix_web::Error,
> + 'static> {
let app = App::new()
.wrap(DefaultHeaders::new()
.add(("Access-Control-Allow-Origin", "*"))
.add(("Access-Control-Allow-Methods", "OPTIONS, POST, GET"))
.add(("Access-Control-Allow-Headers", "*"))
.add(("Access-Control-Max-Age", "86400")))
.wrap_fn(|req, srv| {
let start = SystemTime::now();
let fut = srv.call(req);
async move {
let res = fut.await?;
{
let binding = res.request().extensions();
let handler_found_info = binding.get::<HandlerMatch>().clone();
let time_elapsed = SystemTime::now().duration_since(start).unwrap();
let path = res.request().path();
let method = res.request().method().as_str();
if let Some(handler_found_info) = handler_found_info {
request_message(time_elapsed, method, path, &handler_found_info.path, handler_found_info.name.as_str(), res.response().status().as_u16());
} else {
unhandled_request_message(time_elapsed, method, path, res.response().status().as_u16());
}
}
Ok(res)
}
})
.default_service(web::route().to(move |http_request: HttpRequest, payload: web::Payload| async move {
let path = main_namespace.handler_map.remove_path_prefix(http_request.path(), conf.path_prefix.as_ref().map(|s| s.as_str()));
let method = method_from(http_request.method())?;
let match_result = if let Some(m_result) = main_namespace.handler_map.r#match(method, path) {
m_result
} else if let Some(m_result) = main_namespace.handler_map.default_match(method, path) {
m_result
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
};
let mut group = false;
let dest_namespace = if let Some(d) = main_namespace.namespace_at_path(&match_result.path()) {
d
} else if match_result.path().len() > 0 {
if let Some(d) = main_namespace.namespace_at_path(&match_result.path_without_last()) {
group = true;
d
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
}
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
};
let handler_resolved = if group {
if let Some(model) = dest_namespace.models.get(match_result.group_name()) {
if let Some(group) = dest_namespace.model_handler_groups.get(match_result.group_name()) {
if let Some(handler) = group.handlers.get(match_result.handler_name()) {
(dest_namespace, HandlerResolved::Custom(handler))
} else {
if let Some(action) = builtin_action_handler_from_name(match_result.handler_name()) {
(dest_namespace, HandlerResolved::Builtin(model, action))
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
}
}
} else {
if let Some(action) = builtin_action_handler_from_name(match_result.handler_name()) {
(dest_namespace, HandlerResolved::Builtin(model, action))
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
}
}
} else if let Some(group) = dest_namespace.handler_groups.get(match_result.group_name()) {
if let Some(handler) = group.handlers.get(match_result.handler_name()) {
(dest_namespace, HandlerResolved::Custom(handler))
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
}
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
}
} else {
if let Some(handler) = dest_namespace.handlers.get(match_result.handler_name()) {
(dest_namespace, HandlerResolved::Custom(handler))
} else {
Err(teo_runtime::path::Error::not_found_message_only())?
}
};
let dest_namespace = handler_resolved.0;
let handler_resolved = handler_resolved.1;
if method == Method::Options {
let conn_ctx = connection::Ctx::from_namespace(main_namespace);
let transaction_ctx = transaction::Ctx::new(conn_ctx);
let ctx = request::Ctx::new(
request::Request::new(Arc::new(RequestImpl::new(http_request.clone()))),
Arc::new(Value::Null),
transaction_ctx,
match_result
);
return Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async {
Ok(Response::empty())
}).await?.into_http_response(http_request.clone()));
}
http_request.extensions_mut().insert(match_result.clone());
let mut format = HandlerInputFormat::Json;
match handler_resolved {
HandlerResolved::Custom(handler) => {
format = handler.format;
}
_ => (),
}
let json_body = match format {
HandlerInputFormat::Json => if method == Method::Get || method == Method::Delete {
JsonValue::Null
} else {
parse_json_body(payload).await?
},
HandlerInputFormat::Form => parse_form_body(http_request.clone(), payload).await?,
};
return match handler_resolved {
HandlerResolved::Builtin(model, action) => {
let body = validate_and_transform_json_input_for_builtin_action(model, action, &json_body, main_namespace)?;
let conn_ctx = connection::Ctx::from_namespace(main_namespace);
let transaction_ctx = transaction::Ctx::new(conn_ctx);
let ctx = request::Ctx::new(
request::Request::new(Arc::new(RequestImpl::new(http_request.clone()))),
Arc::new(body),
transaction_ctx,
match_result.clone(),
);
match match_result.handler_name() {
"findMany" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
find_many(&ctx).await
}).await?.into_http_response(http_request.clone())),
"findFirst" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
find_first(&ctx).await
}).await?.into_http_response(http_request.clone())),
"findUnique" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
find_unique(&ctx).await
}).await?.into_http_response(http_request.clone())),
"create" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
create(&ctx).await
}).await?.into_http_response(http_request.clone())),
"delete" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
delete(&ctx).await
}).await?.into_http_response(http_request.clone())),
"update" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
update(&ctx).await
}).await?.into_http_response(http_request.clone())),
"upsert" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
upsert(&ctx).await
}).await?.into_http_response(http_request.clone())),
"copy" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
copy(&ctx).await
}).await?.into_http_response(http_request.clone())),
"createMany" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
create_many(&ctx).await
}).await?.into_http_response(http_request.clone())),
"updateMany" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
update_many(&ctx).await
}).await?.into_http_response(http_request.clone())),
"copyMany" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
copy_many(&ctx).await
}).await?.into_http_response(http_request.clone())),
"deleteMany" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
delete_many(&ctx).await
}).await?.into_http_response(http_request.clone())),
"count" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
count(&ctx).await
}).await?.into_http_response(http_request.clone())),
"aggregate" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
aggregate(&ctx).await
}).await?.into_http_response(http_request.clone())),
"groupBy" => Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, &|ctx: request::Ctx| async move {
group_by(&ctx).await
}).await?.into_http_response(http_request.clone())),
_ => Err(teo_runtime::path::Error::not_found_message_only())?,
}
},
HandlerResolved::Custom(handler) => {
let body = validate_and_transform_json_input_for_handler(handler, &json_body, main_namespace)?;
let conn_ctx = connection::Ctx::from_namespace(main_namespace);
let transaction_ctx = transaction::Ctx::new(conn_ctx);
let ctx = request::Ctx::new(
request::Request::new(Arc::new(RequestImpl::new(http_request.clone()))),
Arc::new(body),
transaction_ctx,
match_result
);
Ok::<HttpResponse, WrapError>(dest_namespace.middleware_stack.call(ctx, handler.call).await?.into_http_response(http_request.clone()))
}
}
}));
app
}
pub(crate) async fn serve(
namespace: &'static Namespace,
conf: &'static Server,
runtime_version: &'static RuntimeVersion,
entrance: &'static Entrance,
silent: bool,
) -> Result<()> {
let bind = conf.bind.clone();
let port = bind.1;
let server = HttpServer::new(move || {
make_server_app(namespace, conf)
})
.bind((bind.0, bind.1 as u16))
.unwrap()
.run();
let result = future::join(server, server_start_message(port as u16, runtime_version, entrance, silent)).await;
result.1
}
async fn server_start_message(port: u16, runtime_version: &'static RuntimeVersion, entrance: &'static Entrance, silent: bool) -> Result<()> {
if silent { return Ok(()) }
let teo_version = env!("CARGO_PKG_VERSION");
let teo = format!("Teo {}", teo_version);
info_message(format!("{} ({}, {})", teo, runtime_version.to_string(), entrance.to_str()));
let port_str = format!("{port}").bold();
info_message(format!("listening on port {}", port_str));
Ok(())
}
fn method_from(m: &HttpMethod) -> Result<Method> {
Ok(match m.as_str() {
"GET" => Method::Get,
"POST" => Method::Post,
"PATCH" => Method::Patch,
"PUT" => Method::Put,
"DELETE" => Method::Delete,
"OPTIONS" => Method::Options,
_ => Err(Error::new(format!("unknown http method {}", m.as_str())))?
})
}
enum HandlerResolved<'a> {
Custom(&'a Handler),
Builtin(&'a Model, Action),
}