diff --git a/config/default.toml b/config/default.toml index 9e632f6..9d2e540 100644 --- a/config/default.toml +++ b/config/default.toml @@ -4,15 +4,6 @@ allow_registration = true source_code = "https://github.com/realaravinth/pages" support_email = "support@librepages.example.org" -# To deploy a website from a Git repository, please provide the following details: -# 1. branch: the branch in the Git repository which contains the website files -# 2. repo: the public readonly/clonable URL of the website repository -# 3. path: the directory where you'd like Pages to clone the specified repository -# 3. secret: a unique secret which is used to authenticate webhook calls -pages = [ - { branch = "gh-pages", domain="local.mcaptcha.org", repo = "https://github.com/mCaptcha/website/", path ="/tmp/pages/mcaptcha/website", secret = "faee1b650ac586068a54cb160bd6353c5e16be7c64b49113fe57726e5393" }, -] - [server] # The port at which you want Pages to listen to port = 7000 diff --git a/src/deploy.rs b/src/deploy.rs index d217a37..fb493fe 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -44,30 +44,12 @@ pub struct DeployEvent { pub branch: String, } -pub fn find_page<'a>(secret: &str, ctx: &'a AppCtx) -> Option<&'a Page> { - for page in ctx.settings.pages.iter() { - if page.secret == secret { - return Some(page); - } - } - None -} - #[actix_web_codegen_const_routes::post(path = "crate::V1_API_ROUTES.deploy.update")] async fn update(payload: web::Json, ctx: AppCtx) -> ServiceResult { - if let Some(page) = find_page(&payload.secret, &ctx) { - let (tx, rx) = oneshot::channel(); - let page = page.clone(); - web::block(move || { - tx.send(page.update(&payload.branch)).unwrap(); - }) - .await - .unwrap(); - rx.await.unwrap()?; - Ok(HttpResponse::Ok()) - } else { - Err(ServiceError::WebsiteNotFound) - } + let payload = payload.into_inner(); + ctx.update_site(&payload.secret, Some(payload.branch)) + .await?; + Ok(HttpResponse::Ok()) } #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] @@ -103,8 +85,9 @@ async fn deploy_info( payload: web::Json, ctx: AppCtx, ) -> ServiceResult { - if let Some(page) = find_page(&payload.secret, &ctx) { - let resp = DeployInfo::from_page(page)?; + if let Ok(page) = ctx.db.get_site_from_secret(&payload.secret).await { + // if let Some(page) = find_page(&payload.secret, &ctx) { + let resp = DeployInfo::from_page(&Page::from_site(&ctx.settings, page))?; Ok(HttpResponse::Ok().json(resp)) } else { Err(ServiceError::WebsiteNotFound) @@ -127,14 +110,21 @@ mod tests { #[actix_rt::test] async fn deploy_update_works() { + const NAME: &str = "dplyupdwrkuser"; + const PASSWORD: &str = "longpasswordasdfa2"; + const EMAIL: &str = "dplyupdwrkuser@a.com"; + let (_dir, ctx) = tests::get_ctx().await; - println!("[log] test configuration {:#?}", ctx.settings); + let _ = ctx.delete_user(NAME, PASSWORD).await; + let (_, _signin_resp) = ctx.register_and_signin(NAME, EMAIL, PASSWORD).await; + let hostname = ctx.get_test_hostname(NAME); + ctx.add_test_site(NAME.into(), hostname.clone()).await; let app = get_app!(ctx).await; - let page = ctx.settings.pages.get(0); - let page = page.unwrap(); + + let page = ctx.db.get_site(NAME, &hostname).await.unwrap(); let mut payload = DeployEvent { - secret: page.secret.clone(), + secret: page.site_secret.clone(), branch: page.branch.clone(), }; @@ -157,14 +147,20 @@ mod tests { #[actix_rt::test] async fn deploy_info_works() { + const NAME: &str = "dplyinfwrkuser"; + const PASSWORD: &str = "longpasswordasdfa2"; + const EMAIL: &str = "dplyinfwrkuser@a.com"; + let (_dir, ctx) = tests::get_ctx().await; - println!("[log] test configuration {:#?}", ctx.settings); - let page = ctx.settings.pages.get(0); - let page = page.unwrap(); + let _ = ctx.delete_user(NAME, PASSWORD).await; + let (_, _signin_resp) = ctx.register_and_signin(NAME, EMAIL, PASSWORD).await; + let hostname = ctx.get_test_hostname(NAME); + ctx.add_test_site(NAME.into(), hostname.clone()).await; let app = get_app!(ctx).await; + let page = ctx.db.get_site(NAME, &hostname).await.unwrap(); let mut payload = DeploySecret { - secret: page.secret.clone(), + secret: page.site_secret.clone(), }; let resp = test::call_service( @@ -177,7 +173,7 @@ mod tests { let response: DeployInfo = actix_web::test::read_body_json(resp).await; assert_eq!(response.head, page.branch); - assert_eq!(response.remote, page.repo); + assert_eq!(response.remote, page.repo_url); payload.secret = page.branch.clone(); diff --git a/src/git.rs b/src/git.rs index e38c50f..9416ba4 100644 --- a/src/git.rs +++ b/src/git.rs @@ -15,6 +15,7 @@ * along with this program. If not, see . */ use std::path::Path; +use std::path::PathBuf; use git2::*; use mime_guess::MimeGuess; @@ -113,7 +114,7 @@ impl ContentType { /// For example, a read request for "foo bar.md" will fail even if that file is present /// in the repository. However, it will succeed if the output of [escape_spaces] is /// used in the request. -pub fn read_file(repo_path: &str, path: &str) -> ServiceResult { +pub fn read_file(repo_path: &PathBuf, path: &str) -> ServiceResult { let repo = git2::Repository::open(repo_path).unwrap(); let head = repo.head().unwrap(); let tree = head.peel_to_tree().unwrap(); @@ -121,7 +122,7 @@ pub fn read_file(repo_path: &str, path: &str) -> ServiceResult { } pub fn read_preview_file( - repo_path: &str, + repo_path: &PathBuf, preview_name: &str, path: &str, ) -> ServiceResult { @@ -279,12 +280,12 @@ pub mod tests { const PATH: &str = "/tmp/librepges/test_git_write_read_works"; write_file_util(PATH); - let resp = read_file(PATH, "README.txt").unwrap(); + let resp = read_file(&Path::new(PATH).into(), "README.txt").unwrap(); assert_eq!(resp.filename, "README.txt"); assert_eq!(resp.content.bytes(), FILE_CONTENT.as_bytes()); assert_eq!(resp.mime.first().unwrap(), "text/plain"); - let resp = read_preview_file(PATH, "master", "README.txt").unwrap(); + let resp = read_preview_file(&Path::new(PATH).into(), "master", "README.txt").unwrap(); assert_eq!(resp.filename, "README.txt"); assert_eq!(resp.content.bytes(), FILE_CONTENT.as_bytes()); assert_eq!(resp.mime.first().unwrap(), "text/plain"); diff --git a/src/page.rs b/src/page.rs index afc6c1f..1685bea 100644 --- a/src/page.rs +++ b/src/page.rs @@ -25,7 +25,10 @@ use std::println as info; use serde::Deserialize; use serde::Serialize; +use crate::db::Site; use crate::errors::*; +use crate::settings::Settings; +use crate::utils::get_website_path; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Page { @@ -37,6 +40,18 @@ pub struct Page { } impl Page { + pub fn from_site(settings: &Settings, s: Site) -> Self { + Self { + secret: s.site_secret, + repo: s.repo_url, + path: get_website_path(settings, &s.hostname) + .to_str() + .unwrap() + .to_owned(), + domain: s.hostname, + branch: s.branch, + } + } pub fn open_repo(&self) -> ServiceResult { Ok(Repository::open(&self.path)?) } diff --git a/src/serve.rs b/src/serve.rs index 07d03ef..dc5fd76 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -17,7 +17,6 @@ use actix_web::{http::header::ContentType, web, HttpRequest, HttpResponse, Responder}; use crate::errors::*; -use crate::page::Page; use crate::AppCtx; pub mod routes { @@ -34,19 +33,6 @@ pub mod routes { } } -pub fn find_page<'a>(domain: &str, ctx: &'a AppCtx) -> Option<&'a Page> { - log::info!("looking for {domain}"); - for page in ctx.settings.pages.iter() { - log::debug!("configured domains: {}", page.domain); - log::debug!("{}", page.domain.trim() == domain.trim()); - if page.domain.trim() == domain.trim() { - log::debug!("found configured domains: {}", page.domain); - return Some(page); - } - } - None -} - #[actix_web_codegen_const_routes::get(path = "crate::V1_API_ROUTES.serve.catch_all")] async fn index(req: HttpRequest, ctx: AppCtx) -> ServiceResult { let c = req.connection_info(); @@ -67,47 +53,40 @@ async fn index(req: HttpRequest, ctx: AppCtx) -> ServiceResult { unimplemented!( "map a local subdomain on settings.server.domain and use it to fetch page" ); - let res = match find_page(host, &ctx) { - Some(page) => { - log::debug!("Page found"); - let content = crate::git::read_preview_file( - &page.path, - preview_branch, - req.uri().path(), - )?; - let mime = if let Some(mime) = content.mime.first_raw() { - mime - } else { - "text/html; charset=utf-8" - }; - Ok(HttpResponse::Ok() - //.content_type(ContentType::html()) - .content_type(mime) - .body(content.content.bytes())) - } - None => Err(ServiceError::WebsiteNotFound), + let res = if ctx.db.hostname_exists(&host).await? { + let path = crate::utils::get_website_path(&ctx.settings, &host); + let content = + crate::git::read_preview_file(&path, preview_branch, req.uri().path())?; + let mime = if let Some(mime) = content.mime.first_raw() { + mime + } else { + "text/html; charset=utf-8" + }; + + Ok(HttpResponse::Ok() + .content_type(mime) + .body(content.content.bytes())) + } else { + Err(ServiceError::WebsiteNotFound) }; - return res; } } - match find_page(host, &ctx) { - Some(page) => { - log::debug!("Page found"); - let content = crate::git::read_file(&page.path, req.uri().path())?; - let mime = if let Some(mime) = content.mime.first_raw() { - mime - } else { - "text/html; charset=utf-8" - }; + if ctx.db.hostname_exists(host).await? { + let path = crate::utils::get_website_path(&ctx.settings, &host); + let content = crate::git::read_file(&path, req.uri().path())?; + let mime = if let Some(mime) = content.mime.first_raw() { + mime + } else { + "text/html; charset=utf-8" + }; - Ok(HttpResponse::Ok() - //.content_type(ContentType::html()) - .content_type(mime) - .body(content.content.bytes())) - } - None => Err(ServiceError::WebsiteNotFound), + Ok(HttpResponse::Ok() + .content_type(mime) + .body(content.content.bytes())) + } else { + Err(ServiceError::WebsiteNotFound) } } diff --git a/src/settings.rs b/src/settings.rs index 1c5c5b0..d817fd9 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -82,7 +82,6 @@ pub struct Settings { pub debug: bool, pub server: Server, pub source_code: String, - pub pages: Vec>, pub database: Database, pub page: PageConfig, } @@ -126,14 +125,11 @@ impl Settings { match env::var("PORT") { Ok(val) => { s = s.set_override("server.port", val).unwrap(); - //settings.server.port = val.parse().unwrap(); } Err(e) => warn!("couldn't interpret PORT: {}", e), } if let Ok(val) = env::var("DATABASE_URL") { - // match env::var("DATABASE_URL") { - // Ok(val) => { let url = Url::parse(&val).expect("couldn't parse Database URL"); s = s.set_override("database.url", url.to_string()).unwrap(); let database_type = DBType::from_url(&url).unwrap(); @@ -142,9 +138,6 @@ impl Settings { .unwrap(); } - // Err(_e) => { - // } - let intermediate_config = s.build_cloned().unwrap(); s = s @@ -178,41 +171,41 @@ impl Settings { } pub fn init(&self) { - for (index, page) in self.pages.iter().enumerate() { - Url::parse(&page.repo).unwrap(); - - fn create_dir_util(path: &Path) { - if path.exists() && path.is_file() { - panic!("Path is a file, should be a directory: {:?}", path); - } - - if !path.exists() { - std::fs::create_dir_all(&path).unwrap(); - } + fn create_dir_util(path: &Path) { + if path.exists() && path.is_file() { + panic!("Path is a file, should be a directory: {:?}", path); } - create_dir_util(Path::new(&page.path)); - create_dir_util(Path::new(&self.page.base_path)); - - for (index2, page2) in self.pages.iter().enumerate() { - if index2 == index { - continue; - } - if page.secret == page2.secret { - error!("{}", ServiceError::SecretTaken(page.clone(), page2.clone())); - } else if page.repo == page2.repo { - error!( - "{}", - ServiceError::DuplicateRepositoryURL(page.clone(), page2.clone(),) - ); - } else if page.path == page2.path { - error!("{}", ServiceError::PathTaken(page.clone(), page2.clone())); - } - } - if let Err(e) = page.update(&page.branch) { - error!("{e}"); + if !path.exists() { + std::fs::create_dir_all(&path).unwrap(); } } + + // create_dir_util(Path::new(&page.path)); + create_dir_util(Path::new(&self.page.base_path)); + + // for (index, page) in self.pages.iter().enumerate() { + // Url::parse(&page.repo).unwrap(); + // + // for (index2, page2) in self.pages.iter().enumerate() { + // if index2 == index { + // continue; + // } + // if page.secret == page2.secret { + // error!("{}", ServiceError::SecretTaken(page.clone(), page2.clone())); + // } else if page.repo == page2.repo { + // error!( + // "{}", + // ServiceError::DuplicateRepositoryURL(page.clone(), page2.clone(),) + // ); + // } else if page.path == page2.path { + // error!("{}", ServiceError::PathTaken(page.clone(), page2.clone())); + // } + // } + // if let Err(e) = page.update(&page.branch) { + // error!("{e}"); + // } + // } } #[cfg(not(tarpaulin_include))]