Skip to content

Commit 640b80f

Browse files
Matias SalinasMatias Salinas
authored andcommitted
ui first version
1 parent c05fea7 commit 640b80f

File tree

21 files changed

+5932
-8
lines changed

21 files changed

+5932
-8
lines changed

Cargo.lock

Lines changed: 90 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ metrics-exporter-prometheus = "0.17.0"
5858
futures = "0.3"
5959
rand = "0.8"
6060
tower = "0.5.2"
61-
61+
tower-http = { version = "0.4", features = ["cors"] }
62+
rust-embed = "8.2.0"
63+
mime_guess = "2.0"
6264

6365
[dev-dependencies]
6466
tokio = { version = "1", features = ["macros", "rt-multi-thread", "test-util"] }

src/admin/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
// limitations under the License.
1414

1515
pub mod clean;
16-
pub mod status_memory;
16+
pub mod status_memory;
17+
pub mod ui;

src/admin/ui.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use axum::{
2+
extract::Path,
3+
http::{header, Response, StatusCode},
4+
response::IntoResponse,
5+
};
6+
use mime_guess::from_path;
7+
use rust_embed::RustEmbed;
8+
9+
#[derive(RustEmbed)]
10+
#[folder = "ui/dist/cb-admin/"] // Ruta relativa al Cargo.toml
11+
pub struct EmbeddedAssets;
12+
13+
/// Servidor de archivos embebidos para `/cb-admin/*path`
14+
/// Soporta rutas como:
15+
/// - `/cb-admin`
16+
/// - `/cb-admin/`
17+
/// - `/cb-admin/index.html`
18+
/// - `/cb-admin/cache` -> `cache/index.html`
19+
/// - `/cb-admin/cache/` -> `cache/index.html`
20+
pub async fn embedded_ui_handler(Path(path): Path<String>) -> impl IntoResponse {
21+
tracing::info!("📦 UI embedded request for: {}", path);
22+
23+
// Elimina "/" inicial para estandarizar
24+
let clean_path = path.trim_start_matches('/');
25+
26+
// Lógica para resolver correctamente rutas tipo `/cb-admin/cache`
27+
let resolved_path = if clean_path.is_empty() {
28+
"index.html".to_string()
29+
} else if EmbeddedAssets::get(clean_path).is_some() {
30+
clean_path.to_string()
31+
} else {
32+
let with_index = format!("{}/index.html", clean_path);
33+
if EmbeddedAssets::get(&with_index).is_some() {
34+
with_index
35+
} else {
36+
clean_path.to_string() // Intento final (puede fallar)
37+
}
38+
};
39+
40+
// Buscar el archivo embebido
41+
match EmbeddedAssets::get(&resolved_path) {
42+
Some(content) => {
43+
let mime = from_path(&resolved_path).first_or_octet_stream();
44+
Response::builder()
45+
.header(header::CONTENT_TYPE, mime.as_ref())
46+
.body(axum::body::Body::from(content.data.into_owned()))
47+
.unwrap()
48+
}
49+
None => {
50+
// Fallback a index.html para SPA si existe
51+
if let Some(index) = EmbeddedAssets::get("index.html") {
52+
return Response::builder()
53+
.header(header::CONTENT_TYPE, "text/html")
54+
.body(axum::body::Body::from(index.data.into_owned()))
55+
.unwrap();
56+
}
57+
58+
// Si ni siquiera hay index, error 404
59+
Response::builder()
60+
.status(StatusCode::NOT_FOUND)
61+
.body(axum::body::Body::from("404 Not Found"))
62+
.unwrap()
63+
}
64+
}
65+
}
66+
67+
/// Sirve el archivo `index.html` directamente para rutas `/cb-admin` o `/cb-admin/`
68+
pub async fn embedded_ui_index() -> impl IntoResponse {
69+
match EmbeddedAssets::get("index.html") {
70+
Some(content) => Response::builder()
71+
.header(header::CONTENT_TYPE, "text/html")
72+
.body(axum::body::Body::from(content.data.into_owned()))
73+
.unwrap(),
74+
None => Response::builder()
75+
.status(StatusCode::NOT_FOUND)
76+
.body("404 Not Found".into())
77+
.unwrap(),
78+
}
79+
}

src/main.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@
1717
// ----------------------
1818
// These are internal modules for handling the proxy logic, caching layers,
1919
// configuration loading, and in-memory eviction based on memory pressure.
20+
mod admin;
2021
mod config;
2122
mod eviction;
2223
mod memory;
2324
mod proxy;
2425
mod rules;
2526
mod storage;
26-
mod admin;
2727

2828
// ----------------------
2929
// External dependencies
3030
// ----------------------
31-
use axum::{Router, routing::get, routing::delete}; // Axum: Web framework for routing and request handling
31+
use axum::{Router, routing::delete, routing::get}; // Axum: Web framework for routing and request handling
3232
use hyper::Server; // Hyper: High-performance HTTP server
3333
use std::{net::SocketAddr, process::exit}; // Network + system utilities
3434

@@ -38,6 +38,7 @@ use tracing_subscriber::EnvFilter; // Log filtering via LOG_LEVEL
3838

3939
use crate::admin::clean::invalidate_handler;
4040
use crate::admin::status_memory::get_memory_cache_status;
41+
use crate::admin::ui::{embedded_ui_handler, embedded_ui_index};
4142
// ----------------------
4243
// Internal dependencies
4344
// ----------------------
@@ -46,6 +47,9 @@ use crate::eviction::start_background_eviction_task; // Memory pressure eviction
4647
use crate::storage::{azure, gcs, s3}; // Persistent storage backends
4748
use metrics_exporter_prometheus::PrometheusBuilder;
4849

50+
use hyper::http::{HeaderValue, Method, header};
51+
use tower_http::cors::CorsLayer;
52+
4953
/// ----------------------------
5054
/// CLI ARGUMENT STRUCTURE
5155
/// ----------------------------
@@ -191,12 +195,21 @@ async fn main() {
191195
// 7. Define Axum router with a single wildcard route
192196
// All incoming GET requests will be handled by the proxy logic.
193197
// ------------------------------------------------------
198+
let cors = CorsLayer::new()
199+
.allow_origin("http://localhost:4321".parse::<HeaderValue>().unwrap()) // o use HeaderValue::from_static(...)
200+
.allow_methods([Method::GET, Method::POST, Method::DELETE])
201+
.allow_headers([header::CONTENT_TYPE]);
202+
194203
let app = Router::new()
204+
.route("/cb-admin/api/cache", delete(invalidate_handler))
205+
.route("/cb-admin/api/status", get(get_memory_cache_status))
206+
.route("/cb-admin", get(embedded_ui_index))
207+
.route("/cb-admin/", get(embedded_ui_index))
208+
.route("/cb-admin/*path", get(embedded_ui_handler))
195209
.route("/metrics", get(move || async move { handle.render() }))
196-
.route("/", get(proxy::proxy_handler))
210+
.route("/", get(proxy::proxy_handler))
197211
.route("/*path", get(proxy::proxy_handler))
198-
.route("/admin/cache", delete(invalidate_handler))
199-
.route("/admin/status-memory", get(get_memory_cache_status));
212+
.layer(cors);
200213

201214
// ------------------------------------------------------
202215
// 8. Bind the server to all interfaces on port 3000

ui/.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# build output
2+
dist/
3+
4+
# generated types
5+
.astro/
6+
7+
# dependencies
8+
node_modules/
9+
10+
# logs
11+
npm-debug.log*
12+
yarn-debug.log*
13+
yarn-error.log*
14+
pnpm-debug.log*
15+
16+
# environment variables
17+
.env
18+
.env.production
19+
20+
# macOS-specific files
21+
.DS_Store
22+
23+
# jetbrains setting folder
24+
.idea/

ui/.vscode/extensions.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"recommendations": ["astro-build.astro-vscode"],
3+
"unwantedRecommendations": []
4+
}

ui/.vscode/launch.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"command": "./node_modules/.bin/astro dev",
6+
"name": "Development server",
7+
"request": "launch",
8+
"type": "node-terminal"
9+
}
10+
]
11+
}

0 commit comments

Comments
 (0)