Compare commits

..

7 Commits

13 changed files with 272 additions and 161 deletions

View File

@ -62,6 +62,13 @@ jobs:
profile: minimal profile: minimal
override: true override: true
- uses: actions/setup-node@v2
with:
node-version: "16.x"
- name: download deps
run: make dev-env
- name: Apply migrations - name: Apply migrations
run: make migrate run: make migrate
env: env:

View File

@ -4,15 +4,6 @@ allow_registration = true
source_code = "https://github.com/realaravinth/pages" source_code = "https://github.com/realaravinth/pages"
support_email = "support@librepages.example.org" 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] [server]
# The port at which you want Pages to listen to # The port at which you want Pages to listen to
port = 7000 port = 7000

View File

@ -1,6 +1,6 @@
CREATE TABLE IF NOT EXISTS librepages_sites ( CREATE TABLE IF NOT EXISTS librepages_sites (
site_secret VARCHAR(32) NOT NULL UNIQUE, site_secret VARCHAR(32) NOT NULL UNIQUE,
repo_url VARCHAR(3000) NOT NULL UNIQUE, repo_url VARCHAR(3000) NOT NULL,
branch TEXT NOT NULL, branch TEXT NOT NULL,
hostname VARCHAR(3000) NOT NULL UNIQUE, hostname VARCHAR(3000) NOT NULL UNIQUE,
ID SERIAL PRIMARY KEY NOT NULL, ID SERIAL PRIMARY KEY NOT NULL,

View File

@ -77,6 +77,26 @@
}, },
"query": "UPDATE librepages_users set name = $1\n WHERE name = $2" "query": "UPDATE librepages_users set name = $1\n WHERE name = $2"
}, },
"3705b8869aab99d749c08d9c9633931f0b74216957b6a2881bd56a33c33a8c47": {
"describe": {
"columns": [
{
"name": "exists",
"ordinal": 0,
"type_info": "Bool"
}
],
"nullable": [
null
],
"parameters": {
"Left": [
"Text"
]
}
},
"query": "SELECT EXISTS (SELECT 1 from librepages_sites WHERE hostname = $1)"
},
"416b9f0412f0d7ee05d4a350839c5a6d1e06c1d7f8942744f6d806ddc47084c2": { "416b9f0412f0d7ee05d4a350839c5a6d1e06c1d7f8942744f6d806ddc47084c2": {
"describe": { "describe": {
"columns": [], "columns": [],

View File

@ -16,6 +16,7 @@
*/ */
pub mod account; pub mod account;
pub mod auth; pub mod auth;
pub mod pages;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

78
src/ctx/api/v1/pages.rs Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use actix_web::web;
use serde::{Deserialize, Serialize};
use tokio::sync::oneshot;
use crate::ctx::Ctx;
use crate::db::Site;
use crate::errors::*;
use crate::page::Page;
use crate::utils::get_random;
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
/// Data required to add site
pub struct AddSite {
pub repo_url: String,
pub branch: String,
pub hostname: String,
pub owner: String,
}
impl AddSite {
fn to_site(self) -> Site {
let site_secret = get_random(32);
Site {
site_secret,
repo_url: self.repo_url,
branch: self.branch,
hostname: self.hostname,
owner: self.owner,
}
}
}
impl Ctx {
pub async fn add_site(&self, site: AddSite) -> ServiceResult<()> {
let db_site = site.to_site();
self.db.add_site(&db_site).await?;
let page = Page::from_site(&self.settings, db_site);
page.update(&page.branch)?;
Ok(())
}
pub async fn update_site(&self, secret: &str, branch: Option<String>) -> ServiceResult<()> {
if let Ok(db_site) = self.db.get_site_from_secret(secret).await {
let page = Page::from_site(&self.settings, db_site);
let (tx, rx) = oneshot::channel();
let page = page.clone();
web::block(move || {
if let Some(branch) = branch {
tx.send(page.update(&branch)).unwrap();
} else {
tx.send(page.update(&page.branch)).unwrap();
}
})
.await
.unwrap();
rx.await.unwrap()?;
Ok(())
} else {
Err(ServiceError::WebsiteNotFound)
}
}
}

View File

@ -288,7 +288,7 @@ impl Database {
) )
.fetch_one(&self.pool) .fetch_one(&self.pool)
.await .await
.map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; .map_err(|e| map_row_not_found_err(e, ServiceError::WebsiteNotFound))?;
struct Owner { struct Owner {
name: String, name: String,
@ -300,7 +300,7 @@ impl Database {
) )
.fetch_one(&self.pool) .fetch_one(&self.pool)
.await .await
.map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; .map_err(|e| map_row_not_found_err(e, ServiceError::WebsiteNotFound))?;
let site = Site { let site = Site {
site_secret: site_secret.to_owned(), site_secret: site_secret.to_owned(),
@ -326,7 +326,7 @@ impl Database {
) )
.fetch_one(&self.pool) .fetch_one(&self.pool)
.await .await
.map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; .map_err(|e| map_row_not_found_err(e, ServiceError::WebsiteNotFound))?;
let res = site.to_site(owner.into()); let res = site.to_site(owner.into());
@ -362,9 +362,27 @@ impl Database {
) )
.execute(&self.pool) .execute(&self.pool)
.await .await
.map_err(|e| map_row_not_found_err(e, ServiceError::AccountNotFound))?; .map_err(|e| map_row_not_found_err(e, ServiceError::WebsiteNotFound))?;
Ok(()) Ok(())
} }
/// check if hostname exists
pub async fn hostname_exists(&self, hostname: &str) -> ServiceResult<bool> {
let res = sqlx::query!(
"SELECT EXISTS (SELECT 1 from librepages_sites WHERE hostname = $1)",
hostname,
)
.fetch_one(&self.pool)
.await
.map_err(map_register_err)?;
let mut resp = false;
if let Some(x) = res.exists {
resp = x;
}
Ok(resp)
}
} }
struct InnerSite { struct InnerSite {
site_secret: String, site_secret: String,
@ -635,7 +653,6 @@ mod tests {
db.register(&p).await.unwrap(); db.register(&p).await.unwrap();
// testing adding site
let site = Site { let site = Site {
site_secret: "foobar".into(), site_secret: "foobar".into(),
repo_url: "https://git.batsense.net/LibrePages/librepages.git".into(), repo_url: "https://git.batsense.net/LibrePages/librepages.git".into(),
@ -643,8 +660,16 @@ mod tests {
hostname: "db_works.tests.librepages.librepages.org".into(), hostname: "db_works.tests.librepages.librepages.org".into(),
owner: p.username.into(), owner: p.username.into(),
}; };
// test if hostname exists. Should be false
assert!(!db.hostname_exists(&site.hostname).await.unwrap());
// testing adding site
db.add_site(&site).await.unwrap(); db.add_site(&site).await.unwrap();
// test if hostname exists. Should be true
assert!(db.hostname_exists(&site.hostname).await.unwrap());
// get site // get site
let db_site = db.get_site(p.username, &site.hostname).await.unwrap(); let db_site = db.get_site(p.username, &site.hostname).await.unwrap();
assert_eq!(db_site, site); assert_eq!(db_site, site);
@ -662,6 +687,8 @@ mod tests {
// delete site // delete site
db.delete_site(p.username, &site.hostname).await.unwrap(); db.delete_site(p.username, &site.hostname).await.unwrap();
assert!(db.list_all_sites(p.username).await.unwrap().is_empty());
// test if hostname exists. Should be false
assert!(!db.hostname_exists(&site.hostname).await.unwrap());
} }
} }

View File

@ -44,30 +44,12 @@ pub struct DeployEvent {
pub branch: String, 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")] #[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> { async fn update(payload: web::Json<DeployEvent>, ctx: AppCtx) -> ServiceResult<impl Responder> {
if let Some(page) = find_page(&payload.secret, &ctx) { let payload = payload.into_inner();
let (tx, rx) = oneshot::channel(); ctx.update_site(&payload.secret, Some(payload.branch))
let page = page.clone(); .await?;
web::block(move || { Ok(HttpResponse::Ok())
tx.send(page.update(&payload.branch)).unwrap();
})
.await
.unwrap();
rx.await.unwrap()?;
Ok(HttpResponse::Ok())
} else {
Err(ServiceError::WebsiteNotFound)
}
} }
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
@ -103,8 +85,9 @@ async fn deploy_info(
payload: web::Json<DeploySecret>, payload: web::Json<DeploySecret>,
ctx: AppCtx, ctx: AppCtx,
) -> ServiceResult<impl Responder> { ) -> ServiceResult<impl Responder> {
if let Some(page) = find_page(&payload.secret, &ctx) { if let Ok(page) = ctx.db.get_site_from_secret(&payload.secret).await {
let resp = DeployInfo::from_page(page)?; // 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)) Ok(HttpResponse::Ok().json(resp))
} else { } else {
Err(ServiceError::WebsiteNotFound) Err(ServiceError::WebsiteNotFound)
@ -127,14 +110,21 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn deploy_update_works() { 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; 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 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 { let mut payload = DeployEvent {
secret: page.secret.clone(), secret: page.site_secret.clone(),
branch: page.branch.clone(), branch: page.branch.clone(),
}; };
@ -157,14 +147,20 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn deploy_info_works() { 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; let (_dir, ctx) = tests::get_ctx().await;
println!("[log] test configuration {:#?}", ctx.settings); let _ = ctx.delete_user(NAME, PASSWORD).await;
let page = ctx.settings.pages.get(0); let (_, _signin_resp) = ctx.register_and_signin(NAME, EMAIL, PASSWORD).await;
let page = page.unwrap(); let hostname = ctx.get_test_hostname(NAME);
ctx.add_test_site(NAME.into(), hostname.clone()).await;
let app = get_app!(ctx).await; let app = get_app!(ctx).await;
let page = ctx.db.get_site(NAME, &hostname).await.unwrap();
let mut payload = DeploySecret { let mut payload = DeploySecret {
secret: page.secret.clone(), secret: page.site_secret.clone(),
}; };
let resp = test::call_service( let resp = test::call_service(
@ -177,7 +173,7 @@ mod tests {
let response: DeployInfo = actix_web::test::read_body_json(resp).await; let response: DeployInfo = actix_web::test::read_body_json(resp).await;
assert_eq!(response.head, page.branch); assert_eq!(response.head, page.branch);
assert_eq!(response.remote, page.repo); assert_eq!(response.remote, page.repo_url);
payload.secret = page.branch.clone(); payload.secret = page.branch.clone();

View File

@ -15,6 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use git2::*; use git2::*;
use mime_guess::MimeGuess; 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 /// 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 /// in the repository. However, it will succeed if the output of [escape_spaces] is
/// used in the request. /// 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 repo = git2::Repository::open(repo_path).unwrap();
let head = repo.head().unwrap(); let head = repo.head().unwrap();
let tree = head.peel_to_tree().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( pub fn read_preview_file(
repo_path: &str, repo_path: &PathBuf,
preview_name: &str, preview_name: &str,
path: &str, path: &str,
) -> ServiceResult<FileInfo> { ) -> ServiceResult<FileInfo> {
@ -279,12 +280,12 @@ pub mod tests {
const PATH: &str = "/tmp/librepges/test_git_write_read_works"; const PATH: &str = "/tmp/librepges/test_git_write_read_works";
write_file_util(PATH); 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.filename, "README.txt");
assert_eq!(resp.content.bytes(), FILE_CONTENT.as_bytes()); assert_eq!(resp.content.bytes(), FILE_CONTENT.as_bytes());
assert_eq!(resp.mime.first().unwrap(), "text/plain"); 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.filename, "README.txt");
assert_eq!(resp.content.bytes(), FILE_CONTENT.as_bytes()); assert_eq!(resp.content.bytes(), FILE_CONTENT.as_bytes());
assert_eq!(resp.mime.first().unwrap(), "text/plain"); assert_eq!(resp.mime.first().unwrap(), "text/plain");

View File

@ -25,7 +25,10 @@ use std::println as info;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::db::Site;
use crate::errors::*; use crate::errors::*;
use crate::settings::Settings;
use crate::utils::get_website_path;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Page { pub struct Page {
@ -37,6 +40,18 @@ pub struct Page {
} }
impl 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> { pub fn open_repo(&self) -> ServiceResult<Repository> {
Ok(Repository::open(&self.path)?) Ok(Repository::open(&self.path)?)
} }

View File

@ -17,7 +17,6 @@
use actix_web::{http::header::ContentType, web, HttpRequest, HttpResponse, Responder}; use actix_web::{http::header::ContentType, web, HttpRequest, HttpResponse, Responder};
use crate::errors::*; use crate::errors::*;
use crate::page::Page;
use crate::AppCtx; use crate::AppCtx;
pub mod routes { 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")] #[actix_web_codegen_const_routes::get(path = "crate::V1_API_ROUTES.serve.catch_all")]
async fn index(req: HttpRequest, ctx: AppCtx) -> ServiceResult<impl Responder> { async fn index(req: HttpRequest, ctx: AppCtx) -> ServiceResult<impl Responder> {
let c = req.connection_info(); let c = req.connection_info();
@ -67,47 +53,40 @@ async fn index(req: HttpRequest, ctx: AppCtx) -> ServiceResult<impl Responder> {
unimplemented!( unimplemented!(
"map a local subdomain on settings.server.domain and use it to fetch page" "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() let res = if ctx.db.hostname_exists(&host).await? {
//.content_type(ContentType::html()) let path = crate::utils::get_website_path(&ctx.settings, &host);
.content_type(mime) let content =
.body(content.content.bytes())) crate::git::read_preview_file(&path, preview_branch, req.uri().path())?;
} let mime = if let Some(mime) = content.mime.first_raw() {
None => Err(ServiceError::WebsiteNotFound), 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) { if ctx.db.hostname_exists(host).await? {
Some(page) => { let path = crate::utils::get_website_path(&ctx.settings, &host);
log::debug!("Page found"); let content = crate::git::read_file(&path, req.uri().path())?;
let content = crate::git::read_file(&page.path, req.uri().path())?; let mime = if let Some(mime) = content.mime.first_raw() {
let mime = if let Some(mime) = content.mime.first_raw() { mime
mime } else {
} else { "text/html; charset=utf-8"
"text/html; charset=utf-8" };
};
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
//.content_type(ContentType::html()) .content_type(mime)
.content_type(mime) .body(content.content.bytes()))
.body(content.content.bytes())) } else {
} Err(ServiceError::WebsiteNotFound)
None => Err(ServiceError::WebsiteNotFound),
} }
} }

View File

@ -82,7 +82,6 @@ pub struct Settings {
pub debug: bool, pub debug: bool,
pub server: Server, pub server: Server,
pub source_code: String, pub source_code: String,
pub pages: Vec<Arc<Page>>,
pub database: Database, pub database: Database,
pub page: PageConfig, pub page: PageConfig,
} }
@ -126,14 +125,11 @@ impl Settings {
match env::var("PORT") { match env::var("PORT") {
Ok(val) => { Ok(val) => {
s = s.set_override("server.port", val).unwrap(); s = s.set_override("server.port", val).unwrap();
//settings.server.port = val.parse().unwrap();
} }
Err(e) => warn!("couldn't interpret PORT: {}", e), Err(e) => warn!("couldn't interpret PORT: {}", e),
} }
if let Ok(val) = env::var("DATABASE_URL") { 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"); let url = Url::parse(&val).expect("couldn't parse Database URL");
s = s.set_override("database.url", url.to_string()).unwrap(); s = s.set_override("database.url", url.to_string()).unwrap();
let database_type = DBType::from_url(&url).unwrap(); let database_type = DBType::from_url(&url).unwrap();
@ -142,9 +138,6 @@ impl Settings {
.unwrap(); .unwrap();
} }
// Err(_e) => {
// }
let intermediate_config = s.build_cloned().unwrap(); let intermediate_config = s.build_cloned().unwrap();
s = s s = s
@ -178,41 +171,41 @@ impl Settings {
} }
pub fn init(&self) { pub fn init(&self) {
for (index, page) in self.pages.iter().enumerate() { fn create_dir_util(path: &Path) {
Url::parse(&page.repo).unwrap(); if path.exists() && path.is_file() {
panic!("Path is a file, should be a directory: {:?}", path);
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();
}
} }
create_dir_util(Path::new(&page.path)); if !path.exists() {
create_dir_util(Path::new(&self.page.base_path)); std::fs::create_dir_all(&path).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}");
} }
} }
// 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))] #[cfg(not(tarpaulin_include))]

View File

@ -14,7 +14,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use actix_web::{ use actix_web::{
@ -27,12 +26,16 @@ use mktemp::Temp;
use serde::Serialize; use serde::Serialize;
use crate::ctx::api::v1::auth::{Login, Register}; use crate::ctx::api::v1::auth::{Login, Register};
use crate::ctx::api::v1::pages::AddSite;
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::errors::*; use crate::errors::*;
use crate::page::Page;
use crate::settings::Settings; use crate::settings::Settings;
use crate::*; use crate::*;
const HOSTNAME: &str = "example.org";
pub const REPO_URL: &str = "https://github.com/mCaptcha/website/";
pub const BRANCH: &str = "gh-pages";
pub async fn get_ctx() -> (Temp, Arc<Ctx>) { pub async fn get_ctx() -> (Temp, Arc<Ctx>) {
// mktemp::Temp is returned because the temp directory created // mktemp::Temp is returned because the temp directory created
// is removed once the variable goes out of scope // is removed once the variable goes out of scope
@ -40,23 +43,8 @@ pub async fn get_ctx() -> (Temp, Arc<Ctx>) {
let tmp_dir = Temp::new_dir().unwrap(); let tmp_dir = Temp::new_dir().unwrap();
println!("[log] Test temp directory: {}", tmp_dir.to_str().unwrap()); println!("[log] Test temp directory: {}", tmp_dir.to_str().unwrap());
let mut pages = Vec::with_capacity(settings.pages.len());
let page_base_path = tmp_dir.as_path().join("base_path"); let page_base_path = tmp_dir.as_path().join("base_path");
for page in settings.pages.iter() { settings.page.base_path = page_base_path.to_str().unwrap().into();
let name = Path::new(&page.path).file_name().unwrap().to_str().unwrap();
let path = tmp_dir.as_path().join(name);
let page = Page {
path: path.to_str().unwrap().to_string(),
secret: page.secret.clone(),
branch: page.branch.clone(),
repo: page.repo.clone(),
domain: "mcaptcha.org".into(),
};
pages.push(Arc::new(page));
}
settings.pages = pages;
settings.init(); settings.init();
println!("[log] Initialzing settings again with test config"); println!("[log] Initialzing settings again with test config");
settings.init(); settings.init();
@ -201,9 +189,10 @@ impl Ctx {
post_request!(&msg, crate::V1_API_ROUTES.auth.register).to_request(), post_request!(&msg, crate::V1_API_ROUTES.auth.register).to_request(),
) )
.await; .await;
// let resp_err: ErrorToResponse = actix_web::test::read_body_json(resp).await; if resp.status() != StatusCode::OK {
// panic!("{}", resp_err.error); let resp_err: ErrorToResponse = actix_web::test::read_body_json(resp).await;
assert_eq!(resp.status(), StatusCode::OK); panic!("{}", resp_err.error);
}
} }
/// signin util /// signin util
@ -278,4 +267,18 @@ impl Ctx {
//println!("{}", resp_err.error); //println!("{}", resp_err.error);
assert_eq!(resp_err.error, format!("{}", err)); assert_eq!(resp_err.error, format!("{}", err));
} }
pub async fn add_test_site(&self, owner: String, hostname: String) {
let msg = AddSite {
repo_url: REPO_URL.into(),
branch: BRANCH.into(),
hostname,
owner,
};
self.add_site(msg).await.unwrap();
}
pub fn get_test_hostname(&self, unique: &str) -> String {
format!("{unique}.{HOSTNAME}")
}
} }