feat: rm loading pages from settings and rely on DB. Propagate changes
across codebase
This commit is contained in:
parent
4d7d2fd359
commit
79a8b6586c
6 changed files with 108 additions and 133 deletions
|
@ -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
|
||||
|
|
|
@ -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<DeployEvent>, ctx: AppCtx) -> ServiceResult<impl Responder> {
|
||||
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<DeploySecret>,
|
||||
ctx: AppCtx,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
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();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<FileInfo> {
|
||||
pub fn read_file(repo_path: &PathBuf, path: &str) -> ServiceResult<FileInfo> {
|
||||
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<FileInfo> {
|
|||
}
|
||||
|
||||
pub fn read_preview_file(
|
||||
repo_path: &str,
|
||||
repo_path: &PathBuf,
|
||||
preview_name: &str,
|
||||
path: &str,
|
||||
) -> ServiceResult<FileInfo> {
|
||||
|
@ -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");
|
||||
|
|
15
src/page.rs
15
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<Repository> {
|
||||
Ok(Repository::open(&self.path)?)
|
||||
}
|
||||
|
|
77
src/serve.rs
77
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<impl Responder> {
|
||||
let c = req.connection_info();
|
||||
|
@ -67,47 +53,40 @@ async fn index(req: HttpRequest, ctx: AppCtx) -> ServiceResult<impl Responder> {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ pub struct Settings {
|
|||
pub debug: bool,
|
||||
pub server: Server,
|
||||
pub source_code: String,
|
||||
pub pages: Vec<Arc<Page>>,
|
||||
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))]
|
||||
|
|
Loading…
Reference in a new issue