diff --git a/backend/src/cdn.rs b/backend/src/cdn.rs new file mode 100644 index 0000000..01f2d68 --- /dev/null +++ b/backend/src/cdn.rs @@ -0,0 +1,60 @@ +use crate::statics::{self, CF_ZONE_IDENT}; +use actix_web::{http::header, Error}; +use awc::Client; +use futures::Future; + +pub(crate) struct Cloudflare; + +impl Cloudflare { + fn identifier() -> &'static str { + &CF_ZONE_IDENT + } + + pub(crate) fn purge_cache( + client: &Client, + file: &str, + ) -> impl Future { + client + .post(format!( + "https://api.cloudflare.com/client/v4/zones/{}/purge_cache", + Self::identifier() + )) + .header(header::USER_AGENT, statics::USER_AGENT.as_str()) + .header("X-Auth-Email", Self::auth_email()) + .header("X-Auth-Key", Self::auth_key()) + .content_type("application/json") + .send_json(&CfPurgeRequest::singleton(file)) + .from_err() + .and_then(|mut response| { + response + .json::() + .map(|resp| resp.success) + .from_err() + }) + } + + fn auth_key() -> &'static str { + &statics::CF_AUTH_KEY + } + + fn auth_email() -> &'static str { + &statics::CF_AUTH_USER + } +} + +#[derive(Serialize)] +struct CfPurgeRequest { + files: Vec, +} + +impl CfPurgeRequest { + fn singleton(file: &str) -> Self { + let url = format!("https://{}/{}", statics::HOSTNAME.as_str(), file); + Self { files: vec![url] } + } +} + +#[derive(Deserialize)] +struct CfPurgeResponse { + success: bool, +} diff --git a/backend/src/config.rs b/backend/src/config.rs index 3e37ae2..3810f74 100644 --- a/backend/src/config.rs +++ b/backend/src/config.rs @@ -17,4 +17,16 @@ pub(crate) struct Opt { #[structopt(long = "gh-secret")] /// GitHub OAuth client secret pub(crate) github_secret: Option, + #[structopt(long = "cf-zone")] + /// Cloudflare zone identifier + pub(crate) cf_zone: Option, + #[structopt(long = "cf-auth-key")] + /// Cloudflare auth key + pub(crate) cf_auth_key: Option, + #[structopt(long = "cf-auth-user")] + /// Cloudflare auth user + pub(crate) cf_auth_user: Option, + #[structopt(long = "hostname")] + /// Hostname + pub(crate) hostname: Option, } diff --git a/backend/src/main.rs b/backend/src/main.rs index da932e8..0fe6081 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -7,6 +7,7 @@ extern crate serde_derive; #[macro_use] extern crate structopt; +mod cdn; mod config; mod data; mod error; @@ -14,6 +15,7 @@ mod service; mod statics; use crate::{ + cdn::Cloudflare, data::FilePath, error::Result, service::{Bitbucket, GitLab, Github, Service}, @@ -94,6 +96,14 @@ fn favicon32() -> HttpResponse { .body(FAVICON) } +fn purge_cache( + client: web::Data, + data: web::Path, +) -> impl Future { + Cloudflare::purge_cache(&client, &data) + .map(|success| HttpResponse::Ok().body(success.to_string())) +} + fn main() -> Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=trace"); pretty_env_logger::init(); @@ -116,6 +126,7 @@ fn main() -> Result<()> { "/gitlab/{user}/{repo}/{commit}/{file:.*}", web::get().to_async(handle_request::), ) + .route("/purge/{path:.*}", web::delete().to_async(purge_cache)) .service(actix_files::Files::new("/", "public").index_file("index.html")) }) .workers(OPT.workers) diff --git a/backend/src/service.rs b/backend/src/service.rs index d6018fd..ddae8a8 100644 --- a/backend/src/service.rs +++ b/backend/src/service.rs @@ -204,7 +204,6 @@ impl Service for GitLab { where S: 'static + Stream, { - // "https://gitlab.com/api/v4/projects/{}/repository/branches/{}", Box::new(match response.status() { StatusCode::OK => Box::new( response diff --git a/backend/src/statics.rs b/backend/src/statics.rs index 0729595..a45d509 100644 --- a/backend/src/statics.rs +++ b/backend/src/statics.rs @@ -1,4 +1,5 @@ use crate::{config::Opt, service::Github}; +use std::env; use structopt::StructOpt; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -7,4 +8,24 @@ lazy_static! { pub(crate) static ref USER_AGENT: String = format!("gitache/{}", VERSION); pub(crate) static ref OPT: Opt = Opt::from_args(); pub(crate) static ref GITHUB_AUTH_QUERY: String = Github::auth_query().unwrap_or_default(); + pub(crate) static ref CF_ZONE_IDENT: String = OPT + .cf_zone + .clone() + .or_else(|| env::var("CF_ZONE_IDENT").ok()) + .expect("Cloudflare zone identifier not set"); + pub(crate) static ref CF_AUTH_KEY: String = OPT + .cf_auth_key + .clone() + .or_else(|| env::var("CF_AUTH_KEY").ok()) + .expect("Cloudflare auth key not set"); + pub(crate) static ref CF_AUTH_USER: String = OPT + .cf_auth_user + .clone() + .or_else(|| env::var("CF_AUTH_USER").ok()) + .expect("Cloudflare auth user not set"); + pub(crate) static ref HOSTNAME: String = OPT + .hostname + .clone() + .or_else(|| env::var("GITACHE_HOSTNAME").ok()) + .unwrap_or_else(|| "gitcdn.tk".to_string()); }