[Add] Team section & teams post

This commit is contained in:
Alex Emmet 2026-04-22 13:52:22 +02:00
commit c2d03efc57
15 changed files with 1063 additions and 53 deletions

View file

@ -18,6 +18,46 @@ struct AppState {
website_root: PathBuf,
}
async fn git_pull(State(state): State<AppState>) -> impl IntoResponse {
let output = tokio::process::Command::new("git")
.arg("pull")
.current_dir(&state.website_root)
.output()
.await;
match output {
Ok(o) => {
let stdout = String::from_utf8_lossy(&o.stdout);
let stderr = String::from_utf8_lossy(&o.stderr);
let status = if o.status.success() { "ok" } else { "error" };
info!("git pull stdout: {}", stdout.trim());
if !stderr.is_empty() {
warn!("git pull stderr: {}", stderr.trim());
}
(
if o.status.success() {
StatusCode::OK
} else {
StatusCode::INTERNAL_SERVER_ERROR
},
format!(
"status: {}\n\nstdout:\n{}\nstderr:\n{}",
status, stdout, stderr
),
)
}
Err(e) => {
warn!("failed to spawn git pull: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("failed to run git pull: {}", e),
)
}
}
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
@ -31,9 +71,12 @@ async fn main() {
.route("/", get(serve_index))
.route("/post/{file}", get(serve_post_page))
.route("/api/backdrops.json", get(serve_backdrops))
.route("/api/team.json", get(serve_team))
.route("/api/staff.json", get(serve_staff))
.route("/api/teams.json", get(serve_teams))
.route("/api/posts.json", get(serve_posts))
.route("/api/post/{file}", get(serve_post_md))
.route("/api/update", get(git_pull))
.route("/directs/{name}", get(serve_direct))
.nest_service("/assets", ServeDir::new(website_root.join("assets")))
.nest_service("/posts", ServeDir::new(website_root.join("posts")))
.fallback(get(serve_static))
@ -90,8 +133,12 @@ async fn serve_backdrops(State(state): State<AppState>) -> impl IntoResponse {
serve_file(state.website_root.join("api/backdrops.json")).await
}
async fn serve_team(State(state): State<AppState>) -> impl IntoResponse {
serve_file(state.website_root.join("api/team.json")).await
async fn serve_staff(State(state): State<AppState>) -> impl IntoResponse {
serve_file(state.website_root.join("api/staff.json")).await
}
async fn serve_teams(State(state): State<AppState>) -> impl IntoResponse {
serve_file(state.website_root.join("api/teams.json")).await
}
async fn serve_posts(State(state): State<AppState>) -> impl IntoResponse {
@ -152,3 +199,40 @@ async fn serve_file(path: PathBuf) -> Response {
.body(Body::from(content))
.unwrap()
}
use axum::http::HeaderValue;
async fn serve_direct(
State(state): State<AppState>,
Path(name): Path<String>,
) -> impl IntoResponse {
if name.contains('/') || name.contains('\\') || name.starts_with('.') {
return StatusCode::BAD_REQUEST.into_response();
}
let path = state.website_root.join("directs").join(&name);
if !path.exists() || !path.is_file() {
return StatusCode::NOT_FOUND.into_response();
}
let content = match fs::read_to_string(&path).await {
Ok(c) => c.trim().to_string(),
Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
};
if content.is_empty() {
return StatusCode::NO_CONTENT.into_response();
}
if !content.starts_with("http://") && !content.starts_with("https://") {
warn!("invalid redirect target in {:?}: {}", path, content);
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
Response::builder()
.status(StatusCode::FOUND) // 302 redirect
.header(header::LOCATION, HeaderValue::from_str(&content).unwrap())
.body(Body::empty())
.unwrap()
}