shitfuck
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#[warn(unused_imports)]
|
||||
use clap::{Parser, Subcommand};
|
||||
mod markdown;
|
||||
use markdown::{get_all_posts, get_post_by_slug, get_posts_by_tag, watch_posts};
|
||||
@@ -28,6 +29,8 @@ enum Commands {
|
||||
Watch,
|
||||
/// Show Rust parser statistics
|
||||
Rsparseinfo,
|
||||
/// Check backend health
|
||||
Checkhealth,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -73,5 +76,9 @@ fn main() {
|
||||
Commands::Rsparseinfo => {
|
||||
println!("{}", markdown::rsparseinfo());
|
||||
}
|
||||
Commands::Checkhealth => {
|
||||
let health = markdown::checkhealth();
|
||||
println!("{}", serde_json::to_string_pretty(&health).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ BLAZINGLY FAST!
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#[warn(unused_imports)]
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -29,6 +29,7 @@ use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
use serde_json;
|
||||
use sysinfo::{System, Pid, RefreshKind, CpuRefreshKind, ProcessRefreshKind};
|
||||
use serde::Serialize;
|
||||
|
||||
const POSTS_CACHE_PATH: &str = "./cache/posts_cache.json";
|
||||
const POST_STATS_PATH: &str = "./cache/post_stats.json";
|
||||
@@ -68,6 +69,19 @@ static POST_CACHE: Lazy<RwLock<HashMap<String, Post>>> = Lazy::new(|| RwLock::ne
|
||||
static ALL_POSTS_CACHE: Lazy<RwLock<Option<Vec<Post>>>> = Lazy::new(|| RwLock::new(None));
|
||||
static POST_STATS: Lazy<RwLock<HashMap<String, PostStats>>> = Lazy::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct HealthReport {
|
||||
pub posts_dir_exists: bool,
|
||||
pub posts_count: usize,
|
||||
pub cache_file_exists: bool,
|
||||
pub cache_stats_file_exists: bool,
|
||||
pub cache_readable: bool,
|
||||
pub cache_stats_readable: bool,
|
||||
pub cache_post_count: Option<usize>,
|
||||
pub cache_stats_count: Option<usize>,
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
fn get_posts_directory() -> PathBuf {
|
||||
let candidates = [
|
||||
"./posts",
|
||||
@@ -440,3 +454,65 @@ pub fn save_post_cache_to_disk() {
|
||||
let _ = fs::write(POST_STATS_PATH, map);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checkhealth() -> HealthReport {
|
||||
let mut errors = Vec::new();
|
||||
let posts_dir = get_posts_directory();
|
||||
let posts_dir_exists = posts_dir.exists() && posts_dir.is_dir();
|
||||
let mut posts_count = 0;
|
||||
if posts_dir_exists {
|
||||
match std::fs::read_dir(&posts_dir) {
|
||||
Ok(entries) => {
|
||||
posts_count = entries.filter_map(|e| e.ok())
|
||||
.filter(|e| e.path().extension().map(|ext| ext == "md").unwrap_or(false))
|
||||
.count();
|
||||
},
|
||||
Err(e) => errors.push(format!("Failed to read posts dir: {}", e)),
|
||||
}
|
||||
} else {
|
||||
errors.push("Posts directory does not exist".to_string());
|
||||
}
|
||||
let cache_file_exists = Path::new(POSTS_CACHE_PATH).exists();
|
||||
let cache_stats_file_exists = Path::new(POST_STATS_PATH).exists();
|
||||
let (mut cache_readable, mut cache_post_count) = (false, None);
|
||||
if cache_file_exists {
|
||||
match std::fs::read_to_string(POSTS_CACHE_PATH) {
|
||||
Ok(data) => {
|
||||
match serde_json::from_str::<HashMap<String, Post>>(&data) {
|
||||
Ok(map) => {
|
||||
cache_readable = true;
|
||||
cache_post_count = Some(map.len());
|
||||
},
|
||||
Err(e) => errors.push(format!("Cache file not valid JSON: {}", e)),
|
||||
}
|
||||
},
|
||||
Err(e) => errors.push(format!("Failed to read cache file: {}", e)),
|
||||
}
|
||||
}
|
||||
let (mut cache_stats_readable, mut cache_stats_count) = (false, None);
|
||||
if cache_stats_file_exists {
|
||||
match std::fs::read_to_string(POST_STATS_PATH) {
|
||||
Ok(data) => {
|
||||
match serde_json::from_str::<HashMap<String, PostStats>>(&data) {
|
||||
Ok(map) => {
|
||||
cache_stats_readable = true;
|
||||
cache_stats_count = Some(map.len());
|
||||
},
|
||||
Err(e) => errors.push(format!("Cache stats file not valid JSON: {}", e)),
|
||||
}
|
||||
},
|
||||
Err(e) => errors.push(format!("Failed to read cache stats file: {}", e)),
|
||||
}
|
||||
}
|
||||
HealthReport {
|
||||
posts_dir_exists,
|
||||
posts_count,
|
||||
cache_file_exists,
|
||||
cache_stats_file_exists,
|
||||
cache_readable,
|
||||
cache_stats_readable,
|
||||
cache_post_count,
|
||||
cache_stats_count,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,25 @@ interface PostStats {
|
||||
last_compile_time_ms: number;
|
||||
}
|
||||
|
||||
interface HealthReport {
|
||||
posts_dir_exists: boolean;
|
||||
posts_count: number;
|
||||
cache_file_exists: boolean;
|
||||
cache_stats_file_exists: boolean;
|
||||
cache_readable: boolean;
|
||||
cache_stats_readable: boolean;
|
||||
cache_post_count?: number;
|
||||
cache_stats_count?: number;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export default function RustStatusPage() {
|
||||
const [stats, setStats] = useState<PostStats[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [health, setHealth] = useState<HealthReport | null>(null);
|
||||
const [healthLoading, setHealthLoading] = useState(true);
|
||||
const [healthError, setHealthError] = useState<string | null>(null);
|
||||
|
||||
// Summary calculations
|
||||
const totalHits = stats.reduce((sum, s) => sum + s.cache_hits, 0);
|
||||
@@ -35,8 +50,24 @@ export default function RustStatusPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchHealth = async () => {
|
||||
setHealthLoading(true);
|
||||
setHealthError(null);
|
||||
try {
|
||||
const res = await fetch('/api/admin/posts?checkhealth=1');
|
||||
if (!res.ok) throw new Error('Fehler beim Laden des Health-Checks');
|
||||
const data = await res.json();
|
||||
setHealth(data);
|
||||
} catch (e: any) {
|
||||
setHealthError(e.message || 'Unbekannter Fehler');
|
||||
} finally {
|
||||
setHealthLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchStats();
|
||||
fetchHealth();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -88,7 +119,63 @@ export default function RustStatusPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rest of your component remains the same */}
|
||||
{/* Health Check Section */}
|
||||
<div className="mb-6">
|
||||
<h2 className="text-base sm:text-lg font-semibold mb-2">Health-Check</h2>
|
||||
{healthLoading && <div className="text-center py-4 text-base">Lade Health-Check...</div>}
|
||||
{healthError && <div className="text-red-500 text-center text-base">{healthError}</div>}
|
||||
{health && (
|
||||
<div className="bg-white rounded-lg shadow p-4 flex flex-col gap-2">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<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>
|
||||
<span className="text-xs text-gray-600">Posts-Verzeichnis</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-lg font-bold text-blue-700">{health.posts_count}</span>
|
||||
<span className="text-xs text-gray-600">Posts</span>
|
||||
</div>
|
||||
<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>
|
||||
<span className="text-xs text-gray-600">Cache-Datei</span>
|
||||
</div>
|
||||
<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>
|
||||
<span className="text-xs text-gray-600">Cache-Stats</span>
|
||||
</div>
|
||||
<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>
|
||||
<span className="text-xs text-gray-600">Cache lesbar</span>
|
||||
</div>
|
||||
<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>
|
||||
<span className="text-xs text-gray-600">Stats lesbar</span>
|
||||
</div>
|
||||
{typeof health.cache_post_count === 'number' && (
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-lg font-bold text-blue-700">{health.cache_post_count}</span>
|
||||
<span className="text-xs text-gray-600">Cache-Posts</span>
|
||||
</div>
|
||||
)}
|
||||
{typeof health.cache_stats_count === 'number' && (
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-lg font-bold text-blue-700">{health.cache_stats_count}</span>
|
||||
<span className="text-xs text-gray-600">Stats-Einträge</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{health.errors.length > 0 && (
|
||||
<div className="mt-2 text-red-600 text-xs">
|
||||
<b>Fehler:</b>
|
||||
<ul className="list-disc ml-5">
|
||||
{health.errors.map((err, i) => <li key={i}>{err}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4 mb-6">
|
||||
<div className="bg-green-100 rounded-lg p-4 flex flex-col items-center shadow">
|
||||
|
||||
@@ -70,6 +70,26 @@ export async function GET(request: Request) {
|
||||
});
|
||||
}
|
||||
}
|
||||
const checkhealth = searchParams.get('checkhealth');
|
||||
if (checkhealth === '1') {
|
||||
// Call the Rust backend for health check
|
||||
const rustResult = spawnSync(
|
||||
process.cwd() + '/markdown_backend/target/release/markdown_backend',
|
||||
['checkhealth'],
|
||||
{ encoding: 'utf-8' }
|
||||
);
|
||||
if (rustResult.status === 0 && rustResult.stdout) {
|
||||
return new Response(rustResult.stdout, {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} else {
|
||||
return new Response(JSON.stringify({ error: rustResult.stderr || rustResult.error }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
// Return the current pinned.json object
|
||||
try {
|
||||
const pinnedPath = path.join(process.cwd(), 'posts', 'pinned.json');
|
||||
|
||||
Reference in New Issue
Block a user