revealjs-hosting/src/main.rs

167 lines
5.3 KiB
Rust
Raw Normal View History

extern crate git2;
extern crate iron;
extern crate mount;
extern crate serde_json;
extern crate staticfile;
// This example serves the docs from target/doc/staticfile at /doc/
//
// Run `cargo doc && cargo run --example doc_server`, then
// point your browser to http://127.0.0.1:3000/doc/
use std::path::{Path, PathBuf};
use std::error::Error;
use iron::prelude::{Chain, Request, Response};
use iron::{status, Iron, IronResult};
use mount::Mount;
use staticfile::Static;
use git2::Repository;
struct WebHookConfig {
secret: String,
repo_root: PathBuf,
repo_prefixes: Vec<String>,
}
fn redeploy_repo(url: &str, path: &Path, name: &str) -> Result<(), git2::Error> {
let mut new_path = path.to_path_buf();
new_path.push(name);
eprintln!("Cloning {} into {:?} ({:?} / {})", url, new_path, path, name);
let repo = match Repository::open(&new_path) {
Ok(repo) => repo,
Err(e) => {
eprintln!("can't open repo, going to try cloning it: {}", e.description());
match Repository::clone(url, &new_path) {
Ok(repo) => repo,
Err(e) => panic!("failed to clone: {}", e),
}
}
};
repo.set_head("FETCH_HEAD")?;
// repo.checkout_tree(&branch.into_reference(), Some(git2::build::CheckoutBuilder::new().force().use_theirs(true)))?;
repo.checkout_head(Some(git2::build::CheckoutBuilder::new().force().use_theirs(true)))?;
Ok(())
}
fn webhook(req: &mut Request, config: &WebHookConfig) -> IronResult<Response> {
eprintln!("Webhook Request: {:?}", req);
let wh: serde_json::Value = serde_json::from_reader(&mut req.body).unwrap();
// Ensure we have a "Secret" parameter, and that it matches the config.
let found_secret = wh.get("secret");
if found_secret.is_none() {
return Ok(Response::with(status::Unauthorized));
}
let found_secret = found_secret.unwrap();
if found_secret != &config.secret {
return Ok(Response::with(status::Unauthorized));
}
// Ensure the repo is valid.
let repository = wh.get("repository");
if repository.is_none() {
eprintln!("No 'repository' key found");
return Ok(Response::with(status::PreconditionRequired));
}
let repository = repository.unwrap();
// Ensure there's an HTML URL that's a valid string
let html_url = match repository.get("html_url") {
None => {
eprintln!("No HTML URL found");
return Ok(Response::with(status::PreconditionRequired));
}
Some(s) => match s.as_str() {
None => {
eprintln!("HTML URL isn't a string");
return Ok(Response::with(status::PreconditionRequired));
}
Some(s) => s,
},
};
// Check to make sure the repo prefix is one we recognize
let mut found_match = false;
for prefix in &config.repo_prefixes {
if html_url.starts_with(prefix) {
found_match = true;
break;
}
}
if !found_match {
eprintln!("URL doesn't start with match");
return Ok(Response::with(status::PreconditionRequired));
}
// And ensure the URL is a string
let website_url = match repository.get("website") {
None => {
eprintln!("The website isn't configured for this repo");
return Ok(Response::with(status::PreconditionRequired));
}
Some(s) => match s.as_str() {
None => {
eprintln!("Website URL isn't a string");
return Ok(Response::with(status::PreconditionRequired));
}
Some(s) => match iron::Url::parse(s) {
Ok(u) => u,
Err(e) => {
eprintln!("Unable to parse URL: {:?}", e);
return Ok(Response::with(status::PreconditionFailed));
}
},
},
};
let target_path = match website_url.path().last() {
Some(s) => {
if s.is_empty() {
eprintln!("No website URL path was found -- defaulting to \"current\"");
"current".to_owned()
} else {
s.to_string()
}
}
None => {
eprintln!("No website URL path was found");
return Ok(Response::with(status::PreconditionFailed));
}
};
eprintln!("Final path: {:?} / {:?}", config.repo_root, target_path);
if let Err(e) = redeploy_repo(html_url, &config.repo_root, &target_path) {
eprintln!("unable to clone/update repo: {}", e.description());
return Ok(Response::with(status::NotModified));
}
Ok(Response::with(status::Ok))
}
fn main() {
let mut mount = Mount::new();
let hostaddr = "0.0.0.0:9119".to_owned();
let config = WebHookConfig {
secret: "1234".to_owned(),
repo_root: Path::new("D:\\Code\\talkserved").to_path_buf(),
repo_prefixes: vec!["https://git.xobs.io/xobs".to_owned()],
};
// Serve the shared JS/CSS at /
mount.mount("/", Static::new(config.repo_root.clone()));
// Listen for calls to "/webhook" and process accordingly
mount.mount(
"/webhook",
Chain::new(move |req: &mut Request| webhook(req, &config)),
);
println!("Doc server running on http://{}", hostaddr);
Iron::new(mount).http(hostaddr).unwrap();
}