use crate::ApiInfo;
use crate::server::ops::{
LogFilesParams, LogMetadataParams, log_files_helper, log_files_metadata_helper,
};
use crate::server::{OrdinaryApiServerState, ROOT};
use axum::Json;
use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use hyper::HeaderMap;
use ordinary_monitor::LogLine;
use serde::Deserialize;
use std::fmt::{Display, Formatter};
use std::sync::Arc;
use tracing::Instrument;
use utoipa::IntoParams;
#[utoipa::path(
get,
path = "/logs/metadata",
tag = ROOT,
params(LogMetadataParams),
responses(
(status = 401, description = "unauthorized for operation"),
(status = 200, description = "log files metadata", body = [LogLine]),
),
security(
("access" = []),
),
)]
pub async fn log_files_metadata(
State(state): State<Arc<OrdinaryApiServerState>>,
Query(query): Query<LogMetadataParams>,
headers: HeaderMap,
) -> impl IntoResponse {
let span = tracing::info_span!("root");
let span = span.in_scope(|| tracing::info_span!("logs"));
async {
match crate::server::check_ordinary_auth(&state, &headers, 1, "root") {
Ok(account) => account,
Err(code) => return code.into_response(),
};
log_files_metadata_helper(&state, &query)
.await
.into_response()
}
.instrument(span)
.await
}
#[utoipa::path(
get,
path = "/logs/files/{file}",
tags = [ROOT],
params(LogFilesParams),
responses(
(status = 401, description = "unauthorized for operation"),
(status = 200, description = "jsonl log lines compressed with Zstd", body = [LogLine]),
),
security(
("access" = []),
),
)]
pub async fn log_files(
State(state): State<Arc<OrdinaryApiServerState>>,
Query(query): Query<LogFilesParams>,
Path(file_name): Path<String>,
headers: HeaderMap,
) -> impl IntoResponse {
let span = tracing::info_span!("root", domain = %query.d);
let span = span.in_scope(|| tracing::info_span!("logs"));
async move {
match crate::server::check_ordinary_auth(&state, &headers, 1, "root") {
Ok(account) => account,
Err(code) => return code.into_response(),
};
log_files_helper(&state, &query, file_name.as_str())
.await
.into_response()
}
.instrument(span)
.await
}
struct CpuTimeDisplay(u64);
impl Display for CpuTimeDisplay {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}ms", self.0)
}
}
#[allow(dead_code)]
struct CpuPercentageDisplay(f32);
impl Display for CpuPercentageDisplay {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:.2}%", self.0)
}
}
#[utoipa::path(
get,
path = "/info",
tags = [ROOT],
responses(
(status = 401, description = "unauthorized for operation"),
(status = 200, description = "API system info", body = [ApiInfo]),
),
security(
("access" = []),
),
)]
pub async fn info(
State(state): State<Arc<OrdinaryApiServerState>>,
headers: HeaderMap,
) -> impl IntoResponse {
let span = tracing::info_span!("root");
let span = span.in_scope(|| tracing::info_span!("info"));
span.in_scope(|| {
match crate::server::check_ordinary_auth(&state, &headers, 1, "root") {
Ok(account) => account,
Err(code) => return code.into_response(),
};
let api_info = {
let mut sys = state.system.lock();
sys.refresh_all();
if let Some(process) = sys.process(state.pid) {
let cpu_ct = sys.cpus().len();
let du = process.disk_usage();
Some(ApiInfo {
system_cpu_count: cpu_ct,
system_cpu_usage: CpuPercentageDisplay(sys.global_cpu_usage()).to_string(),
system_memory_total: bytesize::ByteSize(sys.total_memory())
.display()
.si_short()
.to_string(),
system_memory_used: bytesize::ByteSize(sys.used_memory())
.display()
.si_short()
.to_string(),
system_memory_free: bytesize::ByteSize(sys.free_memory())
.display()
.si_short()
.to_string(),
system_memory_available: bytesize::ByteSize(sys.available_memory())
.display()
.si_short()
.to_string(),
process_cpu_usage: CpuPercentageDisplay(process.cpu_usage() / cpu_ct as f32)
.to_string(),
process_cpu_time: CpuTimeDisplay(process.accumulated_cpu_time()).to_string(),
process_threads: num_threads::num_threads().map(usize::from),
process_memory_real: bytesize::ByteSize(process.memory())
.display()
.si_short()
.to_string(),
process_memory_virtual: bytesize::ByteSize(process.virtual_memory())
.display()
.si_short()
.to_string(),
process_disk_read: bytesize::ByteSize(du.total_read_bytes)
.display()
.si_short()
.to_string(),
process_disk_write: bytesize::ByteSize(du.total_written_bytes)
.display()
.si_short()
.to_string(),
})
} else {
None
}
};
if let Some(api_info) = api_info {
Json(api_info).into_response()
} else {
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
})
}
#[derive(Deserialize, IntoParams)]
pub struct LockParams {
a: String,
}
#[utoipa::path(
post,
path = "/lock",
tags = [ROOT],
params(LockParams),
responses(
(status = 401, description = "unauthorized for operation"),
(status = 200, description = "successfully locked"),
),
security(
("access" = []),
),
)]
pub async fn lock(
State(state): State<Arc<OrdinaryApiServerState>>,
Query(query): Query<LockParams>,
headers: HeaderMap,
) -> impl IntoResponse {
let span = tracing::info_span!("root");
let span = span.in_scope(|| tracing::info_span!("lock"));
span.in_scope(|| {
match crate::server::check_ordinary_auth(&state, &headers, 1, "root") {
Ok(account) => account,
Err(code) => return code.into_response(),
};
match state.account_lock_manager.lock_account(query.a.as_str()) {
Ok(()) => StatusCode::OK.into_response(),
Err(err) => {
tracing::error!(%err);
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
})
}
#[utoipa::path(
post,
path = "/unlock",
tags = [ROOT],
params(LockParams),
responses(
(status = 401, description = "unauthorized for operation"),
(status = 200, description = "successfully unlocked"),
),
security(
("access" = []),
),
)]
pub async fn unlock(
State(state): State<Arc<OrdinaryApiServerState>>,
Query(query): Query<LockParams>,
headers: HeaderMap,
) -> impl IntoResponse {
let span = tracing::info_span!("root");
let span = span.in_scope(|| tracing::info_span!("lock"));
span.in_scope(|| {
match crate::server::check_ordinary_auth(&state, &headers, 1, "root") {
Ok(account) => account,
Err(code) => return code.into_response(),
};
match state.account_lock_manager.unlock_account(query.a.as_str()) {
Ok(()) => StatusCode::OK.into_response(),
Err(err) => {
tracing::error!(%err);
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
})
}