1use std::{process::Command, sync::Arc};
2
3use axum::{
4 Json, Router,
5 extract::{Path, State},
6 routing::get,
7};
8use serde::{Deserialize, Serialize};
9use tokio::net::TcpListener;
10
11#[derive(Debug, Deserialize, Serialize)]
12pub struct Config {
13 addr: String,
14 hooks: Vec<Entry>,
15}
16
17#[derive(Debug, Deserialize, Serialize)]
18pub struct Entry {
19 path: String,
20 command: String,
21 args: Vec<String>,
22}
23
24pub async fn run_server(cfg: Config) {
25 let listener = TcpListener::bind(cfg.addr.clone()).await.unwrap();
26 let state = Arc::new(cfg);
27 let app: Router = Router::new()
28 .route("/{*key}", get(handle_url))
29 .with_state(state);
30 axum::serve(listener, app).await.unwrap();
31}
32
33#[derive(Debug, Deserialize, Serialize)]
34#[serde(rename_all = "snake_case")]
35enum Response {
36 Success,
37 Failed,
38}
39
40#[axum::debug_handler]
41async fn handle_url(Path(key): Path<String>, State(cfg): State<Arc<Config>>) -> Json<Response> {
42 if let Some(entry) = cfg.hooks.iter().find(|it| it.path == key) {
43 println!("Running entry {}", key);
44 let mut command = Command::new(&entry.command);
45 command.args(entry.args.iter());
46 let res = command.status();
47
48 match res {
49 Ok(o) if o.success() => Json(Response::Success),
50 _ => Json(Response::Failed),
51 }
52 } else {
53 Json(Response::Failed)
54 }
55}