mirror of
https://github.com/tuono-labs/tuono
synced 2026-07-25 12:52:47 -07:00
Create logger service (#101)
* feat: create logger service * feat: opt out __tuono/data routes from logger * feat: update version to v0.12.1
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
edition = "2021"
|
||||
authors = ["V. Ageno <valerioageno@yahoo.it>"]
|
||||
description = "The react/rust fullstack framework"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono_lib"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
edition = "2021"
|
||||
authors = ["V. Ageno <valerioageno@yahoo.it>"]
|
||||
description = "The react/rust fullstack framework"
|
||||
@@ -33,9 +33,12 @@ either = "1.13.0"
|
||||
tower-http = {version = "0.6.0", features = ["fs"]}
|
||||
colored = "2.1.0"
|
||||
|
||||
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.12.0"}
|
||||
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.12.1"}
|
||||
# Match the same version used by axum
|
||||
tokio-tungstenite = "0.24.0"
|
||||
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
||||
tungstenite = "0.24.0"
|
||||
http = "1.1.0"
|
||||
pin-project = "1.1.7"
|
||||
tower = "0.5.1"
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use crate::{ssr::Js, Payload};
|
||||
use axum::extract::{Path, Request};
|
||||
use axum::response::Html;
|
||||
use colored::*;
|
||||
use std::collections::HashMap;
|
||||
use tokio::time::Instant;
|
||||
|
||||
pub async fn catch_all(
|
||||
Path(params): Path<HashMap<String, String>>,
|
||||
request: Request,
|
||||
) -> Html<String> {
|
||||
let start = Instant::now();
|
||||
let pathname = request.uri();
|
||||
let headers = request.headers();
|
||||
|
||||
@@ -20,18 +17,6 @@ pub async fn catch_all(
|
||||
|
||||
let result = Js::render_to_string(Some(&payload));
|
||||
|
||||
let duration = start.elapsed();
|
||||
|
||||
// TODO: handle 404 error on catch_all route
|
||||
let http_code = "200";
|
||||
|
||||
println!(
|
||||
" GET {} {} in {}ms",
|
||||
req.uri.path(),
|
||||
http_code.green(),
|
||||
duration.as_millis()
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(html) => Html(html),
|
||||
_ => Html("500 internal server error".to_string()),
|
||||
|
||||
@@ -5,6 +5,7 @@ mod payload;
|
||||
mod request;
|
||||
mod response;
|
||||
mod server;
|
||||
mod services;
|
||||
mod ssr;
|
||||
mod vite_reverse_proxy;
|
||||
mod vite_websocket_proxy;
|
||||
|
||||
@@ -3,9 +3,7 @@ use crate::{ssr::Js, Payload};
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
use axum::response::{Html, IntoResponse, Redirect};
|
||||
use axum::Json;
|
||||
use colored::*;
|
||||
use erased_serde::Serialize;
|
||||
use tokio::time::Instant;
|
||||
|
||||
pub struct Props {
|
||||
data: Box<dyn Serialize>,
|
||||
@@ -74,25 +72,13 @@ impl Response {
|
||||
pub fn render_to_string(&self, req: Request) -> impl IntoResponse {
|
||||
match self {
|
||||
Self::Props(Props { data, http_code }) => {
|
||||
let start = Instant::now();
|
||||
let payload = Payload::new(&req, data).client_payload().unwrap();
|
||||
|
||||
let response = match Js::render_to_string(Some(&payload)) {
|
||||
match Js::render_to_string(Some(&payload)) {
|
||||
Ok(html) => (*http_code, Html(html)),
|
||||
Err(_) => (*http_code, Html("500 Internal server error".to_string())),
|
||||
}
|
||||
.into_response();
|
||||
|
||||
let duration = start.elapsed();
|
||||
|
||||
println!(
|
||||
" GET {} {} in {}ms",
|
||||
req.uri.path(),
|
||||
http_code.as_str().green(),
|
||||
duration.as_millis()
|
||||
);
|
||||
|
||||
response
|
||||
.into_response()
|
||||
}
|
||||
Self::Redirect(to) => Redirect::permanent(to).into_response(),
|
||||
Self::Custom(response) => response.clone().into_response(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::mode::{Mode, GLOBAL_MODE};
|
||||
|
||||
use crate::manifest::load_manifest;
|
||||
use crate::mode::{Mode, GLOBAL_MODE};
|
||||
use crate::services::logger::LoggerLayer;
|
||||
use axum::routing::{get, Router};
|
||||
use colored::Colorize;
|
||||
use ssr_rs::Ssr;
|
||||
@@ -42,9 +42,13 @@ impl Server {
|
||||
let router = self
|
||||
.router
|
||||
.to_owned()
|
||||
.layer(LoggerLayer::new())
|
||||
.route("/vite-server/", get(vite_websocket_proxy))
|
||||
.route("/vite-server/*path", get(vite_reverse_proxy))
|
||||
.fallback_service(ServeDir::new(DEV_PUBLIC_DIR).fallback(get(catch_all)))
|
||||
.fallback_service(
|
||||
ServeDir::new(DEV_PUBLIC_DIR)
|
||||
.fallback(get(catch_all).layer(LoggerLayer::new())),
|
||||
)
|
||||
.with_state(fetch);
|
||||
|
||||
axum::serve(listener, router)
|
||||
@@ -58,7 +62,11 @@ impl Server {
|
||||
let router = self
|
||||
.router
|
||||
.to_owned()
|
||||
.fallback_service(ServeDir::new(PROD_PUBLIC_DIR).fallback(get(catch_all)))
|
||||
.layer(LoggerLayer::new())
|
||||
.fallback_service(
|
||||
ServeDir::new(PROD_PUBLIC_DIR)
|
||||
.fallback(get(catch_all).layer(LoggerLayer::new())),
|
||||
)
|
||||
.with_state(fetch);
|
||||
|
||||
axum::serve(listener, router)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod logger;
|
||||
@@ -0,0 +1,106 @@
|
||||
use colored::Colorize;
|
||||
use http::{method::Method, Request, Response};
|
||||
use pin_project::pin_project;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::time::Instant;
|
||||
use tower::{Layer, Service};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LoggerLayer {}
|
||||
|
||||
impl LoggerLayer {
|
||||
pub fn new() -> Self {
|
||||
LoggerLayer {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Layer<S> for LoggerLayer {
|
||||
type Service = Logger<S>;
|
||||
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
Logger::new(inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Logger<S> {
|
||||
inner: S,
|
||||
}
|
||||
|
||||
impl<S> Logger<S> {
|
||||
pub fn new(inner: S) -> Self {
|
||||
Logger { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for Logger<S>
|
||||
where
|
||||
S: Service<Request<ReqBody>, Response = Response<ResBody>>,
|
||||
ResBody: Default,
|
||||
<S as Service<Request<ReqBody>>>::Error: Debug,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = LoggerFuture<S::Future>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
|
||||
let method = req.method().clone();
|
||||
let path = req.uri().path().to_string();
|
||||
|
||||
LoggerFuture {
|
||||
future: self.inner.call(req),
|
||||
method,
|
||||
path,
|
||||
start: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct LoggerFuture<F> {
|
||||
#[pin]
|
||||
future: F,
|
||||
method: Method,
|
||||
path: String,
|
||||
start: Instant,
|
||||
}
|
||||
|
||||
impl<F, B, E> Future for LoggerFuture<F>
|
||||
where
|
||||
F: Future<Output = Result<Response<B>, E>>,
|
||||
B: Default,
|
||||
E: Debug,
|
||||
{
|
||||
type Output = Result<Response<B>, E>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
let res: F::Output = match this.future.poll(cx) {
|
||||
Poll::Ready(res) => res,
|
||||
Poll::Pending => return Poll::Pending,
|
||||
};
|
||||
|
||||
if this.path.starts_with("/__tuono/data") {
|
||||
return Poll::Ready(res);
|
||||
}
|
||||
|
||||
let status_code = res.as_ref().unwrap().status();
|
||||
|
||||
println!(
|
||||
" {} {} {} in {}ms",
|
||||
this.method,
|
||||
this.path,
|
||||
status_code.as_str().green(),
|
||||
this.start.elapsed().as_millis()
|
||||
);
|
||||
|
||||
Poll::Ready(res)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono_lib_macros"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
edition = "2021"
|
||||
description = "The react/rust fullstack framework"
|
||||
keywords = [ "react", "typescript", "fullstack", "web", "ssr"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-fs-router-vite-plugin",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.1",
|
||||
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-lazy-fn-vite-plugin",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.1",
|
||||
"description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-router",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.1",
|
||||
"description": "React routing component for the framework tuono. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono",
|
||||
"version": "0.12.0",
|
||||
"version": "0.12.1",
|
||||
"description": "The react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
Reference in New Issue
Block a user