feat: add Rust backend — workspace with section executables
Cargo workspace with four crates: - nixpanel-common: shared SectionRequest/SectionResponse types - nixpanel-api: main Axum HTTP server (JWT auth, DB, routing) - nixpanel-services: section binary for systemctl start/stop/restart - nixpanel-sysinfo: section binary for CPU/RAM/disk/OS/version reads nixpanel-api features: - POST /api/auth/login — Argon2id verification, HS256 JWT - GET /api/dashboard — real account counts + sysinfo + versions - GET /api/services — live systemctl status via section binary - POST /api/services/:name/:action — start/stop/restart - GET /api/accounts — list from MariaDB - POST /api/accounts — create account + system user Section binaries are UPX-compressed after build. DB schema auto-migrated on first start; admin user seeded from /opt/nixpanel/.admin_user + .admin_pass files. Frontend changes: - nixserver/src/api.ts + App.tsx wired to real API (JWT stored in localStorage, real data in dashboard/services/accounts) - nixclient/src/api.ts added - Both vite configs get /api proxy to localhost:4000 for dev - install.sh updated to copy + compress all section binaries
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AppError {
|
||||
#[error("Unauthorized")]
|
||||
Unauthorized,
|
||||
|
||||
#[error("Not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("Bad request: {0}")]
|
||||
BadRequest(String),
|
||||
|
||||
#[error("Internal error: {0}")]
|
||||
Internal(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> Response {
|
||||
let (status, message) = match &self {
|
||||
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
|
||||
AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
|
||||
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
|
||||
AppError::Internal(_) => {
|
||||
tracing::error!("{}", self);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".into())
|
||||
}
|
||||
};
|
||||
(status, Json(serde_json::json!({ "error": message }))).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for AppError {
|
||||
fn from(e: sqlx::Error) -> Self {
|
||||
AppError::Internal(anyhow::anyhow!(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<jsonwebtoken::errors::Error> for AppError {
|
||||
fn from(e: jsonwebtoken::errors::Error) -> Self {
|
||||
AppError::Unauthorized
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user