implement not found for ui
All checks were successful
Cargo / build (push) Successful in 34s
Cargo / ui (push) Successful in 38s
Cargo / check (push) Successful in 37s
Cargo / test (push) Successful in 38s

This commit is contained in:
2025-07-19 01:29:32 +01:00
parent dfdcad7b1f
commit be6c1b5701
10 changed files with 86 additions and 6 deletions

1
Cargo.lock generated
View File

@ -492,6 +492,7 @@ version = "0.1.0"
dependencies = [
"axum",
"clap",
"http-body",
"http-body-util",
"serde",
"serde_json",

View File

@ -6,6 +6,7 @@ edition = "2024"
[dependencies]
axum = "0.8.4"
clap = { version = "4.5.41", features = ["derive"] }
http-body = "1.0.1"
http-body-util = "0.1.3"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"

View File

@ -1,5 +1,6 @@
dist: ui
cd ui && make dist
cp -r ui/dist dist
target/debug/nuchat: src dist
cargo build

View File

@ -32,6 +32,7 @@
pnpm
just
openssl
watchexec
];
};

12
src/routes/app.rs Normal file
View File

@ -0,0 +1,12 @@
use axum::{
body::Body,
extract::Request,
http::{Request, Response, StatusCode},
response::IntoResponse,
routing::MethodRouter,
};
use http_body_util::combinators::BoxBody;
use tower::{BoxError, Service, service_fn};
use tower_http::services::{ServeDir, ServeFile};
}

View File

@ -1,6 +1,11 @@
use std::sync::mpsc;
use axum::Router;
use axum::body::Body;
use axum::extract::Request;
use axum::http::StatusCode;
use axum::middleware::{Next, from_fn};
use axum::response::Response;
use axum::routing::{get, post};
use tower_http::services::{ServeDir, ServeFile};
@ -11,13 +16,33 @@ use healthcheck::healthcheck;
use shutdown::shutdown;
pub fn app() -> (Router, mpsc::Receiver<bool>) {
let serve_dir = ServeDir::new("dist").not_found_service(ServeFile::new("dist/index.html"));
let serve_dir = || ServeDir::new("dist").not_found_service(ServeFile::new("dist/index.html"));
let (tx, rx) = mpsc::channel();
(
Router::new()
.route("/api/healthcheck", get(healthcheck))
.route("/api/shutdown", post(move || shutdown(tx.clone())))
.fallback_service(serve_dir),
.fallback_service(serve_dir())
.layer(from_fn(not_found_layer)),
rx,
)
}
async fn not_found_layer(req: Request, next: Next) -> Response {
let path: String = req.uri().path().to_owned();
let resp = next.run(req).await;
if resp.status() != StatusCode::NOT_FOUND {
resp
} else if path.starts_with("/api/") {
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from(String::from("Not Found")))
.unwrap()
} else {
let (mut parts, body) = resp.into_parts();
parts.status = StatusCode::OK;
Response::from_parts(parts, body)
}
}

View File

@ -42,13 +42,30 @@ async fn root_returns_spa() {
}
#[tokio::test]
async fn unkown_fallsback_to_spa() {
async fn unkown_url_fallback_to_spa() {
let (app, _) = nuchat::app();
let response = app
.clone()
.oneshot(
Request::builder()
.uri("/asdfasdfa") // unknown url
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = response.into_body().collect().await.unwrap().to_bytes();
let body = String::from_utf8(body.into_iter().collect()).unwrap();
assert!(body.starts_with("<!DOCTYPE html>"));
let response = app
.oneshot(
Request::builder()
.uri("/asdfasdfa") // unknown url
.uri("/api/asdfasdfa") // unknown url
.body(Body::empty())
.unwrap(),
)
@ -59,5 +76,5 @@ async fn unkown_fallsback_to_spa() {
let body = response.into_body().collect().await.unwrap().to_bytes();
let body = String::from_utf8(body.into_iter().collect()).unwrap();
assert!(body.starts_with("<!DOCTYPE html>"));
assert!(body.starts_with("Not Found"));
}

View File

@ -13,7 +13,7 @@ import HelloWorld from './components/HelloWorld.vue'
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
<RouterLink to="/test">Test</RouterLink>
<RouterLink to="/asdf">asdfasdf</RouterLink>
</nav>
</div>
</header>

View File

@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import NotFoundView from '../views/NotFoundView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@ -17,6 +18,11 @@ const router = createRouter({
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
{
path: '/:path(.*)*',
name: 'not-found',
component: NotFoundView,
},
],
})

View File

@ -0,0 +1,16 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
let path = "/"
for( let part of route.params.path ){
path += part + "/"
}
path = path.slice(0,-1)
</script>
<template>
<main>
Could not find page at {{ path }}
</main>
</template>