serverman/src/route/container/log.rs

75 lines
2.0 KiB
Rust

use std::convert::Infallible;
use axum::{
extract::{Path, State},
response::{
sse::{Event, KeepAlive},
IntoResponse, Sse,
},
};
use bollard::container::LogsOptions;
use futures_util::StreamExt;
use serde_json::json;
use crate::{http::accept::ExtractAccept, AppState};
pub(super) async fn get(
Path(container_name): Path<String>,
State(state): State<AppState>,
ExtractAccept(accept): ExtractAccept,
) -> impl IntoResponse {
match accept.to_str() {
Ok("text/event-stream") => get_log_stream(container_name, state).await.into_response(),
_ => get_log_string(container_name, state).await.into_response(),
}
}
async fn get_log_string(container_name: String, state: AppState) -> impl IntoResponse {
state
.docker
.logs(
&container_name,
Some(LogsOptions::<String> {
follow: false,
stdout: true,
stderr: true,
timestamps: true,
..Default::default()
}),
)
.map(|ev| match ev {
Ok(output) => output.to_string(),
Err(error) => error.to_string(),
})
.collect::<Vec<_>>()
.await
.join("\n")
}
async fn get_log_stream(container_name: String, state: AppState) -> impl IntoResponse {
let stream = state
.docker
.logs(
&container_name,
Some(LogsOptions::<String> {
follow: true,
stdout: true,
stderr: true,
timestamps: true,
..Default::default()
}),
)
.map(|msg| match msg {
Ok(output) => Ok::<_, Infallible>(
Event::default()
.json_data(json!({ "lines": output.to_string() }))
.unwrap(),
),
Err(error) => Ok(Event::default()
.json_data(json!({ "error": error.to_string() }))
.unwrap()),
});
Sse::new(stream).keep_alive(KeepAlive::default())
}