mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 09:10:32 +00:00
Add dashboard metrics, activity log, and dark theme
- Add DashboardMetrics for tracking downloads/uploads/cache hits per registry - Add ActivityLog for recent activity with bounded size (50 entries) - Instrument Docker, npm, Maven, and Cargo handlers with metrics - Add /api/ui/dashboard endpoint with global stats and activity - Implement dark theme dashboard with real-time polling (5s interval) - Add mount points table showing registry paths and proxy upstreams
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use crate::activity_log::{ActionType, ActivityEntry};
|
||||
use crate::validation::{validate_digest, validate_docker_name, validate_docker_reference};
|
||||
use crate::AppState;
|
||||
use axum::{
|
||||
@@ -75,12 +76,22 @@ async fn download_blob(
|
||||
|
||||
let key = format!("docker/{}/blobs/{}", name, digest);
|
||||
match state.storage.get(&key).await {
|
||||
Ok(data) => (
|
||||
StatusCode::OK,
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
data,
|
||||
)
|
||||
.into_response(),
|
||||
Ok(data) => {
|
||||
state.metrics.record_download("docker");
|
||||
state.metrics.record_cache_hit();
|
||||
state.activity.push(ActivityEntry::new(
|
||||
ActionType::Pull,
|
||||
format!("{}@{}", name, &digest[..19.min(digest.len())]),
|
||||
"docker",
|
||||
"LOCAL",
|
||||
));
|
||||
(
|
||||
StatusCode::OK,
|
||||
[(header::CONTENT_TYPE, "application/octet-stream")],
|
||||
data,
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
Err(_) => StatusCode::NOT_FOUND.into_response(),
|
||||
}
|
||||
}
|
||||
@@ -176,6 +187,13 @@ async fn upload_blob(
|
||||
let key = format!("docker/{}/blobs/{}", name, digest);
|
||||
match state.storage.put(&key, &data).await {
|
||||
Ok(()) => {
|
||||
state.metrics.record_upload("docker");
|
||||
state.activity.push(ActivityEntry::new(
|
||||
ActionType::Push,
|
||||
format!("{}@{}", name, &digest[..19.min(digest.len())]),
|
||||
"docker",
|
||||
"LOCAL",
|
||||
));
|
||||
let location = format!("/v2/{}/blobs/{}", name, digest);
|
||||
(StatusCode::CREATED, [(header::LOCATION, location)]).into_response()
|
||||
}
|
||||
@@ -197,6 +215,15 @@ async fn get_manifest(
|
||||
let key = format!("docker/{}/manifests/{}.json", name, reference);
|
||||
match state.storage.get(&key).await {
|
||||
Ok(data) => {
|
||||
state.metrics.record_download("docker");
|
||||
state.metrics.record_cache_hit();
|
||||
state.activity.push(ActivityEntry::new(
|
||||
ActionType::Pull,
|
||||
format!("{}:{}", name, reference),
|
||||
"docker",
|
||||
"LOCAL",
|
||||
));
|
||||
|
||||
// Calculate digest for Docker-Content-Digest header
|
||||
use sha2::Digest;
|
||||
let digest = format!("sha256:{:x}", sha2::Sha256::digest(&data));
|
||||
@@ -245,6 +272,14 @@ async fn put_manifest(
|
||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||
}
|
||||
|
||||
state.metrics.record_upload("docker");
|
||||
state.activity.push(ActivityEntry::new(
|
||||
ActionType::Push,
|
||||
format!("{}:{}", name, reference),
|
||||
"docker",
|
||||
"LOCAL",
|
||||
));
|
||||
|
||||
let location = format!("/v2/{}/manifests/{}", name, reference);
|
||||
(
|
||||
StatusCode::CREATED,
|
||||
|
||||
Reference in New Issue
Block a user