diff --git a/apps/documentation/Cargo.toml b/apps/documentation/Cargo.toml
index 3c7f3d4d..00b56059 100644
--- a/apps/documentation/Cargo.toml
+++ b/apps/documentation/Cargo.toml
@@ -9,5 +9,7 @@ path = ".tuono/main.rs"
[dependencies]
tuono_lib = "0.10.0"
+glob = "0.3.1"
+time = { version = "0.3", features = ["macros"] }
serde = { version = "1.0.202", features = ["derive"] }
diff --git a/apps/documentation/src/components/hero/hero.tsx b/apps/documentation/src/components/hero/hero.tsx
index 4aa07282..6ff60759 100644
--- a/apps/documentation/src/components/hero/hero.tsx
+++ b/apps/documentation/src/components/hero/hero.tsx
@@ -35,7 +35,7 @@ export default function Hero(): JSX.Element {
size="lg"
style={{ border: 'solid 1px var(--mantine-color-violet-1)' }}
color="gray"
- leftSection="cargo install tuono"
+ leftSection="$ cargo install tuono"
rightSection={
copied ? (
diff --git a/apps/documentation/src/routes/sitemap.xml.rs b/apps/documentation/src/routes/sitemap.xml.rs
new file mode 100644
index 00000000..5fa2f4b4
--- /dev/null
+++ b/apps/documentation/src/routes/sitemap.xml.rs
@@ -0,0 +1,61 @@
+use tuono_lib::reqwest::{Client, StatusCode};
+use tuono_lib::{Request, Response};
+use tuono_lib::axum::http::{header, HeaderMap};
+use glob::glob;
+use time::OffsetDateTime;
+
+const FILE_TO_EXCLUDE: [&str; 2] = ["sitemap.xml", "__root"];
+
+const SITEMAP: &str = r#"
+
+ [PLACEHOLDER]
+"#;
+
+fn load_routes() -> Vec {
+ let mut paths: Vec = vec![];
+
+ for entry in glob("./src/routes/**/*").expect("Failed to glob src/routes folder").flatten() {
+ if !entry.is_dir() {
+ let path = clean_path(format!("/{}", entry.to_string_lossy()));
+
+ if !FILE_TO_EXCLUDE.iter().any(|exclude| path.ends_with(exclude)) {
+ paths.push(path)
+ }
+ }
+ }
+ paths
+}
+
+fn clean_path(value: String) -> String {
+ value
+ .replace("src/routes/", "")
+ .replace(".mdx", "")
+ .replace(".tsx", "")
+ .replace(".rs", "")
+ .replace("index", "")
+}
+
+#[tuono_lib::handler]
+async fn generate_sitemap(_req: Request, _fetch: Client) -> Response {
+ let mut headers = HeaderMap::new();
+ headers.insert(header::CONTENT_TYPE, "text/xml".parse().unwrap());
+
+ let routes = load_routes();
+
+ let mut sitemaps = String::new();
+
+ for path in routes {
+ let mut url = format!("https://tuono.dev{}", path);
+
+ if url.ends_with('/') {
+ url.pop();
+ }
+
+ sitemaps.push_str(
+ &format!(r#"{}{}"#,url, OffsetDateTime::now_utc().date())
+ )
+ }
+
+ Response::Custom((StatusCode::OK, headers, SITEMAP.replace("[PLACEHOLDER]", &sitemaps)))
+}
+