updated graphic layout for rust
Some checks failed
Deploy / build-and-deploy (push) Failing after 2s
Some checks failed
Deploy / build-and-deploy (push) Failing after 2s
This commit is contained in:
@@ -1,13 +1,19 @@
|
|||||||
#[warn(unused_imports)]
|
#[warn(unused_imports)]
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
mod markdown;
|
mod markdown;
|
||||||
use markdown::{get_all_posts, get_post_by_slug, get_posts_by_tag, watch_posts, get_parser_logs, clear_parser_logs};
|
use markdown::{get_all_posts, get_post_by_slug, get_posts_by_tag, watch_posts, get_parser_logs, clear_parser_logs, load_parser_logs_from_disk};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read; // STD AYOOOOOOOOOOOOOO - Tsodin
|
use std::io::Read; // STD AYOOOOOOOOOOOOOO - Tsodin
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is the Parsers "Command Centeral"
|
||||||
|
// Commands for the CLI are Defined Here
|
||||||
|
// The Parser will provide appropriate Errors, if you care then modify.
|
||||||
|
// Hours wasted: 2.42h (Due to shitty error logging)
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "Markdown Backend")]
|
#[command(name = "Markdown Backend")]
|
||||||
#[command(about = "A CLI for managing markdown blog posts", long_about = None)]
|
#[command(about = "A CLI for managing markdown blog posts", long_about = None)]
|
||||||
@@ -51,6 +57,7 @@ enum Commands {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
markdown::load_post_cache_from_disk();
|
markdown::load_post_cache_from_disk();
|
||||||
|
load_parser_logs_from_disk();
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Commands::List => {
|
Commands::List => {
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ use regex::Regex;
|
|||||||
// Constants
|
// Constants
|
||||||
const POSTS_CACHE_PATH: &str = "./cache/posts_cache.json";
|
const POSTS_CACHE_PATH: &str = "./cache/posts_cache.json";
|
||||||
const POST_STATS_PATH: &str = "./cache/post_stats.json";
|
const POST_STATS_PATH: &str = "./cache/post_stats.json";
|
||||||
const MAX_FILE_SIZE: usize = 10 * 1024 * 1024; // 10MB
|
const MAX_FILE_SIZE: usize = 2 * 1024 * 1024; // 10MB
|
||||||
const PARSING_TIMEOUT_SECS: u64 = 30;
|
const PARSING_TIMEOUT_SECS: u64 = 6000;
|
||||||
const MAX_LOG_ENTRIES: usize = 1000;
|
const MAX_LOG_ENTRIES: usize = 1000;
|
||||||
|
const PARSER_LOGS_PATH: &str = "./cache/parser_logs.json";
|
||||||
|
|
||||||
// Data structures
|
// Data structures
|
||||||
#[derive(Debug, Deserialize, Clone, Serialize)]
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
@@ -41,7 +42,7 @@ pub struct PostFrontmatter {
|
|||||||
pub tags: Option<Vec<String>>,
|
pub tags: Option<Vec<String>>,
|
||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
}
|
}
|
||||||
|
// Post Data Structures
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
@@ -54,6 +55,7 @@ pub struct Post {
|
|||||||
pub author: String,
|
pub author: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data Structure for Posts Statistics
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct PostStats {
|
pub struct PostStats {
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
@@ -65,6 +67,7 @@ pub struct PostStats {
|
|||||||
pub last_cache_status: String, // "hit" or "miss"
|
pub last_cache_status: String, // "hit" or "miss"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data Structures for Health Reporting
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct HealthReport {
|
pub struct HealthReport {
|
||||||
pub posts_dir_exists: bool,
|
pub posts_dir_exists: bool,
|
||||||
@@ -78,6 +81,7 @@ pub struct HealthReport {
|
|||||||
pub errors: Vec<String>,
|
pub errors: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log Data Structure (frontend related)
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct LogEntry {
|
pub struct LogEntry {
|
||||||
pub timestamp: String,
|
pub timestamp: String,
|
||||||
@@ -210,6 +214,8 @@ fn get_posts_directory() -> PathBuf {
|
|||||||
PathBuf::from("./posts")
|
PathBuf::from("./posts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to find Markdown files.
|
||||||
|
// This will scan Directories recursively
|
||||||
fn find_markdown_files(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
|
fn find_markdown_files(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
|
||||||
let mut files = Vec::new();
|
let mut files = Vec::new();
|
||||||
if dir.is_dir() {
|
if dir.is_dir() {
|
||||||
@@ -227,6 +233,7 @@ fn find_markdown_files(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
|
|||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a SlugPath.
|
||||||
fn path_to_slug(file_path: &Path, posts_dir: &Path) -> String {
|
fn path_to_slug(file_path: &Path, posts_dir: &Path) -> String {
|
||||||
let relative_path = file_path.strip_prefix(posts_dir).unwrap_or(file_path);
|
let relative_path = file_path.strip_prefix(posts_dir).unwrap_or(file_path);
|
||||||
let without_ext = relative_path.with_extension("");
|
let without_ext = relative_path.with_extension("");
|
||||||
@@ -236,6 +243,7 @@ fn path_to_slug(file_path: &Path, posts_dir: &Path) -> String {
|
|||||||
.replace("\\", "::")
|
.replace("\\", "::")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Slugify the Path
|
||||||
fn slug_to_path(slug: &str, posts_dir: &Path) -> PathBuf {
|
fn slug_to_path(slug: &str, posts_dir: &Path) -> PathBuf {
|
||||||
let parts: Vec<&str> = slug.split("::").collect();
|
let parts: Vec<&str> = slug.split("::").collect();
|
||||||
if parts.len() == 1 {
|
if parts.len() == 1 {
|
||||||
@@ -253,6 +261,7 @@ fn slug_to_path(slug: &str, posts_dir: &Path) -> PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Look at the Markdown File and generate a Creation Date based upon gathered things.
|
||||||
fn get_file_creation_date(path: &Path) -> std::io::Result<DateTime<Utc>> {
|
fn get_file_creation_date(path: &Path) -> std::io::Result<DateTime<Utc>> {
|
||||||
let metadata = fs::metadata(path)?;
|
let metadata = fs::metadata(path)?;
|
||||||
match metadata.created() {
|
match metadata.created() {
|
||||||
@@ -264,6 +273,9 @@ fn get_file_creation_date(path: &Path) -> std::io::Result<DateTime<Utc>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Frontend expects a plain old string that will be used for the anchor
|
||||||
|
// something like this -> #i-am-a-heading
|
||||||
|
// This creates a crossreference for Links that scroll to said heading
|
||||||
fn process_anchor_links(content: &str) -> String {
|
fn process_anchor_links(content: &str) -> String {
|
||||||
let re = regex::Regex::new(r"\[([^\]]+)\]\(#([^)]+)\)").unwrap();
|
let re = regex::Regex::new(r"\[([^\]]+)\]\(#([^)]+)\)").unwrap();
|
||||||
re.replace_all(content, |caps: ®ex::Captures| {
|
re.replace_all(content, |caps: ®ex::Captures| {
|
||||||
@@ -274,6 +286,8 @@ fn process_anchor_links(content: &str) -> String {
|
|||||||
}).to_string()
|
}).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here we just remove the Emoji if it is in the heading.
|
||||||
|
// Example "🏳️🌈 Hi!" will turn into "#hi"
|
||||||
fn strip_emojis(s: &str) -> String {
|
fn strip_emojis(s: &str) -> String {
|
||||||
s.chars()
|
s.chars()
|
||||||
.filter(|c| {
|
.filter(|c| {
|
||||||
@@ -291,6 +305,8 @@ fn strip_emojis(s: &str) -> String {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a obsolete Function for Custom Tags for HTML
|
||||||
|
// Example usage in Text: <warning />
|
||||||
fn process_custom_tags(content: &str) -> String {
|
fn process_custom_tags(content: &str) -> String {
|
||||||
let mut processed = content.to_string();
|
let mut processed = content.to_string();
|
||||||
|
|
||||||
@@ -343,13 +359,35 @@ fn add_log(level: &str, message: &str, slug: Option<&str>, details: Option<&str>
|
|||||||
slug: slug.map(|s| s.to_string()),
|
slug: slug.map(|s| s.to_string()),
|
||||||
details: details.map(|s| s.to_string()),
|
details: details.map(|s| s.to_string()),
|
||||||
};
|
};
|
||||||
|
{
|
||||||
let mut logs = PARSER_LOGS.write().unwrap();
|
let mut logs = PARSER_LOGS.write().unwrap();
|
||||||
logs.push_back(log_entry);
|
logs.push_back(log_entry.clone());
|
||||||
|
// Keep only the last MAX_LOG_ENTRIES
|
||||||
// Keep only the last MAX_LOG_ENTRIES
|
while logs.len() > MAX_LOG_ENTRIES {
|
||||||
if logs.len() > MAX_LOG_ENTRIES {
|
logs.pop_front();
|
||||||
logs.pop_front();
|
}
|
||||||
|
// Write logs to disk
|
||||||
|
let _ = save_parser_logs_to_disk_inner(&logs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_parser_logs_to_disk_inner(logs: &VecDeque<LogEntry>) -> std::io::Result<()> {
|
||||||
|
let _ = std::fs::create_dir_all("./cache");
|
||||||
|
let logs_vec: Vec<_> = logs.iter().cloned().collect();
|
||||||
|
let json = serde_json::to_string(&logs_vec)?;
|
||||||
|
std::fs::write(PARSER_LOGS_PATH, json)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_parser_logs_from_disk() {
|
||||||
|
if let Ok(data) = std::fs::read_to_string(PARSER_LOGS_PATH) {
|
||||||
|
if let Ok(logs_vec) = serde_json::from_str::<Vec<LogEntry>>(&data) {
|
||||||
|
let mut logs = PARSER_LOGS.write().unwrap();
|
||||||
|
logs.clear();
|
||||||
|
for entry in logs_vec {
|
||||||
|
logs.push_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +403,8 @@ pub fn rsparseinfo() -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This Function gets the Post by its Slugified Version.
|
||||||
|
// This is basically only used for Caching (loading from it).
|
||||||
pub fn get_post_by_slug(slug: &str) -> Result<Post, Box<dyn std::error::Error>> {
|
pub fn get_post_by_slug(slug: &str) -> Result<Post, Box<dyn std::error::Error>> {
|
||||||
add_log("info", "Starting post parsing", Some(slug), None);
|
add_log("info", "Starting post parsing", Some(slug), None);
|
||||||
|
|
||||||
@@ -530,6 +570,7 @@ pub fn get_post_by_slug(slug: &str) -> Result<Post, Box<dyn std::error::Error>>
|
|||||||
let compile_time = compile_start.elapsed();
|
let compile_time = compile_start.elapsed();
|
||||||
|
|
||||||
// Insert into cache
|
// Insert into cache
|
||||||
|
// If this no worky , programm fucky wucky? - Check Logs
|
||||||
POST_CACHE.write().unwrap().insert(slug.to_string(), post.clone());
|
POST_CACHE.write().unwrap().insert(slug.to_string(), post.clone());
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
@@ -691,6 +732,8 @@ pub fn checkhealth() -> HealthReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_parser_logs() -> Vec<LogEntry> {
|
pub fn get_parser_logs() -> Vec<LogEntry> {
|
||||||
|
// Always reload from disk to ensure up-to-date logs
|
||||||
|
load_parser_logs_from_disk();
|
||||||
let logs = PARSER_LOGS.read().unwrap();
|
let logs = PARSER_LOGS.read().unwrap();
|
||||||
logs.iter().cloned().collect()
|
logs.iter().cloned().collect()
|
||||||
}
|
}
|
||||||
@@ -698,4 +741,5 @@ pub fn get_parser_logs() -> Vec<LogEntry> {
|
|||||||
pub fn clear_parser_logs() {
|
pub fn clear_parser_logs() {
|
||||||
let mut logs = PARSER_LOGS.write().unwrap();
|
let mut logs = PARSER_LOGS.write().unwrap();
|
||||||
logs.clear();
|
logs.clear();
|
||||||
|
let _ = std::fs::remove_file(PARSER_LOGS_PATH);
|
||||||
}
|
}
|
||||||
@@ -130,40 +130,60 @@ export default function RustStatusPage() {
|
|||||||
|
|
||||||
const getLevelIcon = (level: string) => {
|
const getLevelIcon = (level: string) => {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 'error': return '❌';
|
case 'error':
|
||||||
case 'warning': return '⚠️';
|
return (
|
||||||
case 'info': return 'ℹ️';
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
default: return '📝';
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'warning':
|
||||||
|
return (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'info':
|
||||||
|
return (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M4 4a2 2 0 00-2 2v8a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2H4zm0 2h12v8H4V6zm2 2a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm0 3a1 1 0 011-1h4a1 1 0 110 2H7a1 1 0 01-1-1z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-100 p-4 sm:p-6">
|
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4 sm:p-6">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="w-full max-w-6xl">
|
||||||
{/* Header with title and action buttons */}
|
{/* Header with title and action buttons */}
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-6">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 mb-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2">
|
||||||
<div className="bg-white rounded-lg shadow p-2 flex items-center justify-center">
|
<div className="bg-white rounded-lg shadow-sm p-1.5 flex items-center justify-center">
|
||||||
<img
|
<img
|
||||||
className="w-10 h-10 sm:w-12 sm:h-12"
|
className="w-8 h-8 sm:w-10 sm:h-10"
|
||||||
src="https://upload.wikimedia.org/wikipedia/commons/d/d5/Rust_programming_language_black_logo.svg"
|
src="https://upload.wikimedia.org/wikipedia/commons/d/d5/Rust_programming_language_black_logo.svg"
|
||||||
alt="Rust Logo"
|
alt="Rust Logo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-xl sm:text-2xl font-bold">Rust-Parser Statistiken</h1>
|
<h1 className="text-lg sm:text-xl font-bold">Rust-Parser Statistiken</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 w-full sm:w-auto justify-end">
|
<div className="flex items-center gap-2 w-full sm:w-auto justify-end">
|
||||||
{/* Back to Admin button */}
|
{/* Back to Admin button */}
|
||||||
<a
|
<a
|
||||||
href="/admin"
|
href="/admin"
|
||||||
className="p-2 sm:px-4 sm:py-2 bg-gray-200 hover:bg-gray-300 rounded-lg shadow flex items-center gap-1 transition-colors"
|
className="p-1.5 sm:px-3 sm:py-1.5 bg-gray-200 hover:bg-gray-300 rounded-lg shadow-sm flex items-center gap-1 transition-colors text-sm"
|
||||||
title="Zurück zur Admin-Panel"
|
title="Zurück zur Admin-Panel"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="hidden sm:inline">Zurück zur Admin-Panel</span>
|
<span className="hidden sm:inline">Zurück</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{/* Refresh button */}
|
{/* Refresh button */}
|
||||||
@@ -173,12 +193,12 @@ export default function RustStatusPage() {
|
|||||||
fetchHealth();
|
fetchHealth();
|
||||||
fetchLogs();
|
fetchLogs();
|
||||||
}}
|
}}
|
||||||
className="p-2 sm:px-4 sm:py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg shadow flex items-center gap-1 transition-colors"
|
className="p-1.5 sm:px-3 sm:py-1.5 bg-blue-500 hover:bg-blue-600 text-white rounded-lg shadow-sm flex items-center gap-1 transition-colors text-sm"
|
||||||
title="Aktualisieren"
|
title="Aktualisieren"
|
||||||
disabled={loading || healthLoading || logsLoading}
|
disabled={loading || healthLoading || logsLoading}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
className={`w-5 h-5 ${(loading || healthLoading || logsLoading) ? 'animate-spin' : ''}`}
|
className={`w-4 h-4 ${(loading || healthLoading || logsLoading) ? 'animate-spin' : ''}`}
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -191,52 +211,118 @@ export default function RustStatusPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Health Check Section */}
|
{/* Health Check Section */}
|
||||||
<div className="mb-6">
|
<div className="mb-4">
|
||||||
<h2 className="text-base sm:text-lg font-semibold mb-2 text-center">Health-Check</h2>
|
<h2 className="text-sm sm:text-base font-semibold mb-2 text-center">Health-Check</h2>
|
||||||
{healthLoading && <div className="text-center py-4 text-base">Lade Health-Check...</div>}
|
{healthLoading && <div className="text-center py-3 text-sm">Lade Health-Check...</div>}
|
||||||
{healthError && <div className="text-red-500 text-center text-base">{healthError}</div>}
|
{healthError && <div className="text-red-500 text-center text-sm">{healthError}</div>}
|
||||||
{health && (
|
{health && (
|
||||||
<div className="bg-white rounded-lg shadow p-4 flex flex-col gap-2 items-center">
|
<div className="bg-gradient-to-br from-gray-50 to-white rounded-lg shadow-sm border border-gray-200 p-4 flex flex-col gap-3 items-center">
|
||||||
<div className="flex flex-wrap gap-4 justify-center">
|
<div className="flex flex-wrap gap-4 justify-center">
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className={`text-lg font-bold ${health.posts_dir_exists ? 'text-green-700' : 'text-red-700'}`}>{health.posts_dir_exists ? '✔' : '✖'}</span>
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||||
<span className="text-xs text-gray-600">Posts-Verzeichnis</span>
|
health.posts_dir_exists ? 'bg-green-200 text-green-700' : 'bg-red-200 text-red-700'
|
||||||
|
}`}>
|
||||||
|
{health.posts_dir_exists ? (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Posts-Verzeichnis</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className="text-lg font-bold text-blue-700">{health.posts_count}</span>
|
<div className="w-8 h-8 rounded-full bg-blue-200 text-blue-700 flex items-center justify-center">
|
||||||
<span className="text-xs text-gray-600">Posts</span>
|
<span className="text-sm font-bold">{health.posts_count}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Posts</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className={`text-lg font-bold ${health.cache_file_exists ? 'text-green-700' : 'text-red-700'}`}>{health.cache_file_exists ? '✔' : '✖'}</span>
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||||
<span className="text-xs text-gray-600">Cache-Datei</span>
|
health.cache_file_exists ? 'bg-green-200 text-green-700' : 'bg-red-200 text-red-700'
|
||||||
|
}`}>
|
||||||
|
{health.cache_file_exists ? (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Cache-Datei</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className={`text-lg font-bold ${health.cache_stats_file_exists ? 'text-green-700' : 'text-red-700'}`}>{health.cache_stats_file_exists ? '✔' : '✖'}</span>
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||||
<span className="text-xs text-gray-600">Cache-Stats</span>
|
health.cache_stats_file_exists ? 'bg-green-200 text-green-700' : 'bg-red-200 text-red-700'
|
||||||
|
}`}>
|
||||||
|
{health.cache_stats_file_exists ? (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Cache-Stats</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className={`text-lg font-bold ${health.cache_readable ? 'text-green-700' : 'text-red-700'}`}>{health.cache_readable ? '✔' : '✖'}</span>
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||||
<span className="text-xs text-gray-600">Cache lesbar</span>
|
health.cache_readable ? 'bg-green-200 text-green-700' : 'bg-red-200 text-red-700'
|
||||||
|
}`}>
|
||||||
|
{health.cache_readable ? (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Cache lesbar</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className={`text-lg font-bold ${health.cache_stats_readable ? 'text-green-700' : 'text-red-700'}`}>{health.cache_stats_readable ? '✔' : '✖'}</span>
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
|
||||||
<span className="text-xs text-gray-600">Stats lesbar</span>
|
health.cache_stats_readable ? 'bg-green-200 text-green-700' : 'bg-red-200 text-red-700'
|
||||||
|
}`}>
|
||||||
|
{health.cache_stats_readable ? (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Stats lesbar</span>
|
||||||
</div>
|
</div>
|
||||||
{typeof health.cache_post_count === 'number' && (
|
{typeof health.cache_post_count === 'number' && (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className="text-lg font-bold text-blue-700">{health.cache_post_count}</span>
|
<div className="w-8 h-8 rounded-full bg-blue-200 text-blue-700 flex items-center justify-center">
|
||||||
<span className="text-xs text-gray-600">Cache-Posts</span>
|
<span className="text-sm font-bold">{health.cache_post_count}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Cache-Posts</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{typeof health.cache_stats_count === 'number' && (
|
{typeof health.cache_stats_count === 'number' && (
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<span className="text-lg font-bold text-blue-700">{health.cache_stats_count}</span>
|
<div className="w-8 h-8 rounded-full bg-blue-200 text-blue-700 flex items-center justify-center">
|
||||||
<span className="text-xs text-gray-600">Stats-Einträge</span>
|
<span className="text-sm font-bold">{health.cache_stats_count}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-gray-600 mt-1 font-medium">Stats-Einträge</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{health.errors.length > 0 && (
|
{health.errors.length > 0 && (
|
||||||
<div className="mt-2 text-red-600 text-xs text-center">
|
<div className="mt-3 text-red-600 text-xs text-center bg-red-50 p-3 rounded-md border border-red-200">
|
||||||
<b>Fehler:</b>
|
<b>Fehler:</b>
|
||||||
<ul className="list-disc ml-5 inline-block text-left">
|
<ul className="list-disc ml-5 inline-block text-left">
|
||||||
{health.errors.map((err, i) => <li key={i}>{err}</li>)}
|
{health.errors.map((err, i) => <li key={i}>{err}</li>)}
|
||||||
@@ -248,33 +334,33 @@ export default function RustStatusPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Summary Cards */}
|
{/* Summary Cards */}
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4 mb-6">
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4 mb-4">
|
||||||
<div className="bg-green-100 rounded-lg p-4 flex flex-col items-center shadow">
|
<div className="bg-gradient-to-br from-green-50 to-green-100 rounded-lg p-3 flex flex-col items-center shadow-sm border border-green-200">
|
||||||
<span className="text-xl sm:text-2xl font-bold text-green-700">{totalHits}</span>
|
<span className="text-lg sm:text-xl font-bold text-green-700">{totalHits}</span>
|
||||||
<span className="text-sm sm:text-base text-gray-700 mt-1 sm:mt-2 text-center">Cache-Treffer</span>
|
<span className="text-xs sm:text-sm text-gray-700 mt-1 text-center font-medium">Cache-Treffer</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-red-100 rounded-lg p-4 flex flex-col items-center shadow">
|
<div className="bg-gradient-to-br from-red-50 to-red-100 rounded-lg p-3 flex flex-col items-center shadow-sm border border-red-200">
|
||||||
<span className="text-xl sm:text-2xl font-bold text-red-700">{totalMisses}</span>
|
<span className="text-lg sm:text-xl font-bold text-red-700">{totalMisses}</span>
|
||||||
<span className="text-sm sm:text-base text-gray-700 mt-1 sm:mt-2 text-center">Cache-Fehlschläge</span>
|
<span className="text-xs sm:text-sm text-gray-700 mt-1 text-center font-medium">Cache-Fehlschläge</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-blue-100 rounded-lg p-4 flex flex-col items-center shadow">
|
<div className="bg-gradient-to-br from-blue-50 to-blue-100 rounded-lg p-3 flex flex-col items-center shadow-sm border border-blue-200">
|
||||||
<span className="text-xl sm:text-2xl font-bold text-blue-700">{avgInterpret} ms</span>
|
<span className="text-lg sm:text-xl font-bold text-blue-700">{avgInterpret} ms</span>
|
||||||
<span className="text-sm sm:text-base text-gray-700 mt-1 sm:mt-2 text-center">Ø Interpretationszeit</span>
|
<span className="text-xs sm:text-sm text-gray-700 mt-1 text-center font-medium">Ø Interpretationszeit</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-purple-100 rounded-lg p-4 flex flex-col items-center shadow">
|
<div className="bg-gradient-to-br from-purple-50 to-purple-100 rounded-lg p-3 flex flex-col items-center shadow-sm border border-purple-200">
|
||||||
<span className="text-xl sm:text-2xl font-bold text-purple-700">{avgCompile} ms</span>
|
<span className="text-lg sm:text-xl font-bold text-purple-700">{avgCompile} ms</span>
|
||||||
<span className="text-sm sm:text-base text-gray-700 mt-1 sm:mt-2 text-center">Ø Kompilierzeit</span>
|
<span className="text-xs sm:text-sm text-gray-700 mt-1 text-center font-medium">Ø Kompilierzeit</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Parser Logs Section */}
|
{/* Parser Logs Section */}
|
||||||
<div className="bg-white rounded-lg shadow p-4 mb-6">
|
<div className="bg-white rounded-lg shadow-sm p-4 mb-4">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-3">
|
||||||
<h2 className="text-lg font-semibold">Parser Logs</h2>
|
<h2 className="text-base font-semibold">Parser Logs</h2>
|
||||||
<div className="flex flex-col sm:flex-row gap-2">
|
<div className="flex flex-col sm:flex-row gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={clearLogs}
|
onClick={clearLogs}
|
||||||
className="px-3 py-2 bg-red-500 hover:bg-red-600 text-white rounded text-sm transition-colors"
|
className="px-2.5 py-1.5 bg-red-500 hover:bg-red-600 text-white rounded text-xs transition-colors"
|
||||||
title="Clear all logs"
|
title="Clear all logs"
|
||||||
>
|
>
|
||||||
Clear Logs
|
Clear Logs
|
||||||
@@ -283,21 +369,21 @@ export default function RustStatusPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Log Filters */}
|
{/* Log Filters */}
|
||||||
<div className="flex flex-col sm:flex-row gap-4 mb-4">
|
<div className="flex flex-col sm:flex-row gap-3 mb-3">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search logs..."
|
placeholder="Search logs..."
|
||||||
value={logSearch}
|
value={logSearch}
|
||||||
onChange={(e) => setLogSearch(e.target.value)}
|
onChange={(e) => setLogSearch(e.target.value)}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-1.5 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<select
|
<select
|
||||||
value={logFilter}
|
value={logFilter}
|
||||||
onChange={(e) => setLogFilter(e.target.value)}
|
onChange={(e) => setLogFilter(e.target.value)}
|
||||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="px-3 py-1.5 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
||||||
>
|
>
|
||||||
<option value="all">All Levels</option>
|
<option value="all">All Levels</option>
|
||||||
<option value="info">Info</option>
|
<option value="info">Info</option>
|
||||||
@@ -308,24 +394,42 @@ export default function RustStatusPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Logs Display */}
|
{/* Logs Display */}
|
||||||
<div className="max-h-96 overflow-y-auto">
|
<div className="max-h-80 overflow-y-auto">
|
||||||
{logsLoading && <div className="text-center py-4">Loading logs...</div>}
|
{logsLoading && <div className="text-center py-3 text-sm">Loading logs...</div>}
|
||||||
{logsError && <div className="text-red-500 text-center py-4">{logsError}</div>}
|
{logsError && <div className="text-red-500 text-center py-3 text-sm">{logsError}</div>}
|
||||||
{!logsLoading && !logsError && (
|
{!logsLoading && !logsError && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{filteredLogs.length === 0 ? (
|
{filteredLogs.length === 0 ? (
|
||||||
<div className="text-center py-4 text-gray-500">No logs found</div>
|
<div className="text-center py-3 text-gray-500 text-sm">No logs found</div>
|
||||||
) : (
|
) : (
|
||||||
filteredLogs.map((log, index) => (
|
filteredLogs.map((log, index) => (
|
||||||
<div key={index} className={`p-3 rounded-lg border ${getLevelColor(log.level)}`}>
|
<div key={index} className={`p-3 rounded-lg border-l-4 shadow-sm ${
|
||||||
|
log.level === 'error' ? 'bg-gradient-to-r from-red-50 to-red-100 border-red-400' :
|
||||||
|
log.level === 'warning' ? 'bg-gradient-to-r from-yellow-50 to-yellow-100 border-yellow-400' :
|
||||||
|
'bg-gradient-to-r from-blue-50 to-blue-100 border-blue-400'
|
||||||
|
}`}>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<span className="text-lg">{getLevelIcon(log.level)}</span>
|
<div className={`flex-shrink-0 p-1 rounded-full ${
|
||||||
|
log.level === 'error' ? 'bg-red-200 text-red-700' :
|
||||||
|
log.level === 'warning' ? 'bg-yellow-200 text-yellow-700' :
|
||||||
|
'bg-blue-200 text-blue-700'
|
||||||
|
}`}>
|
||||||
|
{getLevelIcon(log.level)}
|
||||||
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<span className="text-xs font-mono text-gray-500">
|
<span className="text-xs font-mono text-gray-600 tracking-wide">
|
||||||
{new Date(log.timestamp).toLocaleString()}
|
{new Date(log.timestamp).toLocaleString('en-US', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
<span className={`px-2 py-0.5 rounded-full text-xs font-semibold tracking-wide ${
|
||||||
log.level === 'error' ? 'bg-red-200 text-red-800' :
|
log.level === 'error' ? 'bg-red-200 text-red-800' :
|
||||||
log.level === 'warning' ? 'bg-yellow-200 text-yellow-800' :
|
log.level === 'warning' ? 'bg-yellow-200 text-yellow-800' :
|
||||||
'bg-blue-200 text-blue-800'
|
'bg-blue-200 text-blue-800'
|
||||||
@@ -333,14 +437,14 @@ export default function RustStatusPage() {
|
|||||||
{log.level.toUpperCase()}
|
{log.level.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
{log.slug && (
|
{log.slug && (
|
||||||
<span className="px-2 py-1 bg-gray-200 text-gray-700 rounded text-xs font-mono">
|
<span className="px-2 py-0.5 bg-gray-200 text-gray-700 rounded-full text-xs font-mono tracking-wide">
|
||||||
{log.slug}
|
{log.slug}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium">{log.message}</div>
|
<div className="text-sm font-medium text-gray-900 leading-relaxed">{log.message}</div>
|
||||||
{log.details && (
|
{log.details && (
|
||||||
<div className="text-xs text-gray-600 mt-1 font-mono bg-gray-100 p-2 rounded">
|
<div className="text-xs text-gray-600 mt-1 font-mono bg-gray-50 p-2 rounded-md border border-gray-200 leading-relaxed">
|
||||||
{log.details}
|
{log.details}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -355,33 +459,33 @@ export default function RustStatusPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<div className="bg-white rounded-lg shadow p-3 sm:p-4 overflow-x-auto">
|
<div className="bg-white rounded-lg shadow-sm p-3 sm:p-4 overflow-x-auto">
|
||||||
<h2 className="text-base sm:text-lg font-semibold mb-3">Rohdaten</h2>
|
<h2 className="text-sm sm:text-base font-semibold mb-2">Rohdaten</h2>
|
||||||
{loading && <div className="text-center py-6 text-base">Lade Statistiken...</div>}
|
{loading && <div className="text-center py-4 text-sm">Lade Statistiken...</div>}
|
||||||
{error && <div className="text-red-500 text-center text-base">{error}</div>}
|
{error && <div className="text-red-500 text-center text-sm">{error}</div>}
|
||||||
{!loading && !error && (
|
{!loading && !error && (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full border border-gray-200 bg-white rounded">
|
<table className="min-w-full border border-gray-200 bg-white rounded text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="bg-gray-100">
|
<tr className="bg-gray-100">
|
||||||
<th className="px-3 py-2 text-left text-sm">Slug</th>
|
<th className="px-2 py-1.5 text-left text-xs">Slug</th>
|
||||||
<th className="px-3 py-2 text-right text-sm">Cache-Treffer</th>
|
<th className="px-2 py-1.5 text-right text-xs">Cache-Treffer</th>
|
||||||
<th className="px-3 py-2 text-right text-sm">Cache-Fehlschläge</th>
|
<th className="px-2 py-1.5 text-right text-xs">Cache-Fehlschläge</th>
|
||||||
<th className="px-3 py-2 text-right text-sm">Interpret (ms)</th>
|
<th className="px-2 py-1.5 text-right text-xs">Interpret (ms)</th>
|
||||||
<th className="px-3 py-2 text-right text-sm">Kompilier (ms)</th>
|
<th className="px-2 py-1.5 text-right text-xs">Kompilier (ms)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{stats.length === 0 ? (
|
{stats.length === 0 ? (
|
||||||
<tr><td colSpan={5} className="text-center py-3 text-sm">Keine Statistiken verfügbar.</td></tr>
|
<tr><td colSpan={5} className="text-center py-2 text-xs">Keine Statistiken verfügbar.</td></tr>
|
||||||
) : (
|
) : (
|
||||||
stats.map(stat => (
|
stats.map(stat => (
|
||||||
<tr key={stat.slug} className="border-t border-gray-200">
|
<tr key={stat.slug} className="border-t border-gray-200">
|
||||||
<td className="px-3 py-2 font-mono text-sm">{stat.slug}</td>
|
<td className="px-2 py-1.5 font-mono text-xs">{stat.slug}</td>
|
||||||
<td className="px-3 py-2 text-right text-sm">{stat.cache_hits}</td>
|
<td className="px-2 py-1.5 text-right text-xs">{stat.cache_hits}</td>
|
||||||
<td className="px-3 py-2 text-right text-sm">{stat.cache_misses}</td>
|
<td className="px-2 py-1.5 text-right text-xs">{stat.cache_misses}</td>
|
||||||
<td className="px-3 py-2 text-right text-sm">{stat.last_interpret_time_ms}</td>
|
<td className="px-2 py-1.5 text-right text-xs">{stat.last_interpret_time_ms}</td>
|
||||||
<td className="px-3 py-2 text-right text-sm">{stat.last_compile_time_ms}</td>
|
<td className="px-2 py-1.5 text-right text-xs">{stat.last_compile_time_ms}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export async function DELETE(request: Request) {
|
|||||||
// Call the Rust backend to clear parser logs
|
// Call the Rust backend to clear parser logs
|
||||||
const rustResult = spawnSync(
|
const rustResult = spawnSync(
|
||||||
process.cwd() + '/markdown_backend/target/release/markdown_backend',
|
process.cwd() + '/markdown_backend/target/release/markdown_backend',
|
||||||
['clearLogs'],
|
['clear-logs'],
|
||||||
{ encoding: 'utf-8' }
|
{ encoding: 'utf-8' }
|
||||||
);
|
);
|
||||||
if (rustResult.status === 0 && rustResult.stdout) {
|
if (rustResult.status === 0 && rustResult.stdout) {
|
||||||
|
|||||||
Reference in New Issue
Block a user