chore: load app settings via app ctx and do away with global static loader
This commit is contained in:
parent
624646f836
commit
68d63bba07
7 changed files with 77 additions and 51 deletions
|
@ -21,7 +21,7 @@ my-codegen = {package = "actix-web-codegen", git ="https://github.com/realaravin
|
||||||
config = "0.13"
|
config = "0.13"
|
||||||
git2 = "0.14.2"
|
git2 = "0.14.2"
|
||||||
|
|
||||||
serde = { version = "1", features = ["derive"]}
|
serde = { version = "1", features = ["derive", "rc"]}
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.4"
|
||||||
|
|
30
src/ctx.rs
Normal file
30
src/ctx.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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 std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::settings::Settings;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Ctx {
|
||||||
|
pub settings: Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ctx {
|
||||||
|
pub fn new(settings: Settings) -> Arc<Self> {
|
||||||
|
Arc::new(Self { settings })
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::SETTINGS;
|
use crate::AppCtx;
|
||||||
|
|
||||||
pub mod routes {
|
pub mod routes {
|
||||||
pub struct Deploy {
|
pub struct Deploy {
|
||||||
|
@ -42,11 +42,12 @@ pub struct DeployEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.deploy.update")]
|
#[my_codegen::post(path = "crate::V1_API_ROUTES.deploy.update")]
|
||||||
async fn update(payload: web::Json<DeployEvent>) -> ServiceResult<impl Responder> {
|
async fn update(payload: web::Json<DeployEvent>, ctx: AppCtx) -> ServiceResult<impl Responder> {
|
||||||
for page in SETTINGS.pages.iter() {
|
for page in ctx.settings.pages.iter() {
|
||||||
if page.secret == payload.secret {
|
if page.secret == payload.secret {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
web::block(|| {
|
let page = page.clone();
|
||||||
|
web::block(move || {
|
||||||
tx.send(page.update()).unwrap();
|
tx.send(page.update()).unwrap();
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -65,18 +66,18 @@ pub fn services(cfg: &mut web::ServiceConfig) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::{http::StatusCode, test, App};
|
use actix_web::{http::StatusCode, test};
|
||||||
|
|
||||||
use crate::services;
|
use crate::tests;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn deploy_update_works() {
|
async fn deploy_update_works() {
|
||||||
let app = test::init_service(App::new().configure(services)).await;
|
let ctx = tests::get_data().await;
|
||||||
|
let app = get_app!(ctx).await;
|
||||||
let page = SETTINGS.pages.get(0);
|
let page = ctx.settings.pages.get(0);
|
||||||
let page = page.unwrap();
|
let page = page.unwrap();
|
||||||
|
|
||||||
let mut payload = DeployEvent {
|
let mut payload = DeployEvent {
|
||||||
|
@ -86,10 +87,7 @@ mod tests {
|
||||||
|
|
||||||
let resp = test::call_service(
|
let resp = test::call_service(
|
||||||
&app,
|
&app,
|
||||||
test::TestRequest::post()
|
post_request!(&payload, V1_API_ROUTES.deploy.update).to_request(),
|
||||||
.uri(V1_API_ROUTES.deploy.update)
|
|
||||||
.set_json(&payload)
|
|
||||||
.to_request(),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
@ -98,10 +96,7 @@ mod tests {
|
||||||
|
|
||||||
let resp = test::call_service(
|
let resp = test::call_service(
|
||||||
&app,
|
&app,
|
||||||
test::TestRequest::post()
|
post_request!(&payload, V1_API_ROUTES.deploy.update).to_request(),
|
||||||
.uri(V1_API_ROUTES.deploy.update)
|
|
||||||
.set_json(&payload)
|
|
||||||
.to_request(),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
//! represents all the ways a trait can fail using this crate
|
//! represents all the ways a trait can fail using this crate
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::io::Error as FSErrorInner;
|
use std::io::Error as FSErrorInner;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
error::ResponseError,
|
error::ResponseError,
|
||||||
|
@ -90,7 +91,7 @@ pub enum ServiceError {
|
||||||
_0,
|
_0,
|
||||||
_1
|
_1
|
||||||
)]
|
)]
|
||||||
PathTaken(Page, Page),
|
PathTaken(Arc<Page>, Arc<Page>),
|
||||||
|
|
||||||
/// when the a Secret configured for a page is already taken
|
/// when the a Secret configured for a page is already taken
|
||||||
#[display(
|
#[display(
|
||||||
|
@ -98,7 +99,7 @@ pub enum ServiceError {
|
||||||
_0,
|
_0,
|
||||||
_1
|
_1
|
||||||
)]
|
)]
|
||||||
SecretTaken(Page, Page),
|
SecretTaken(Arc<Page>, Arc<Page>),
|
||||||
|
|
||||||
/// when the a Repository URL configured for a page is already taken
|
/// when the a Repository URL configured for a page is already taken
|
||||||
#[display(
|
#[display(
|
||||||
|
@ -106,7 +107,7 @@ pub enum ServiceError {
|
||||||
_0,
|
_0,
|
||||||
_1
|
_1
|
||||||
)]
|
)]
|
||||||
DuplicateRepositoryURL(Page, Page),
|
DuplicateRepositoryURL(Arc<Page>, Arc<Page>),
|
||||||
|
|
||||||
#[display(fmt = "File System Error {}", _0)]
|
#[display(fmt = "File System Error {}", _0)]
|
||||||
FSError(FSError),
|
FSError(FSError),
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -15,28 +15,27 @@
|
||||||
* 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::env;
|
use std::env;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
error::InternalError, http::StatusCode, middleware as actix_middleware, web::JsonConfig, App,
|
error::InternalError, http::StatusCode, middleware as actix_middleware, web::Data as WebData,
|
||||||
HttpServer,
|
web::JsonConfig, App, HttpServer,
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
mod ctx;
|
||||||
mod deploy;
|
mod deploy;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod meta;
|
mod meta;
|
||||||
mod page;
|
mod page;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
pub use routes::ROUTES as V1_API_ROUTES;
|
pub use routes::ROUTES as V1_API_ROUTES;
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref SETTINGS: Settings = Settings::new().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const CACHE_AGE: u32 = 604800;
|
pub const CACHE_AGE: u32 = 604800;
|
||||||
|
|
||||||
pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
|
pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
|
||||||
|
@ -45,6 +44,8 @@ pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
pub const PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
pub const PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
||||||
pub const PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
|
pub const PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
|
||||||
|
|
||||||
|
pub type AppCtx = WebData<Arc<ctx::Ctx>>;
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
#[cfg(not(tarpaulin_include))]
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
|
@ -55,6 +56,9 @@ async fn main() -> std::io::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let settings = Settings::new().unwrap();
|
||||||
|
let ctx = WebData::new(ctx::Ctx::new(settings.clone()));
|
||||||
|
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
|
@ -62,12 +66,13 @@ async fn main() -> std::io::Result<()> {
|
||||||
PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, GIT_COMMIT_HASH
|
PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, GIT_COMMIT_HASH
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("Starting server on: http://{}", SETTINGS.server.get_ip());
|
info!("Starting server on: http://{}", settings.server.get_ip());
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(actix_middleware::Logger::default())
|
.wrap(actix_middleware::Logger::default())
|
||||||
.wrap(actix_middleware::Compress::default())
|
.wrap(actix_middleware::Compress::default())
|
||||||
|
.app_data(ctx.clone())
|
||||||
.app_data(get_json_err())
|
.app_data(get_json_err())
|
||||||
.wrap(
|
.wrap(
|
||||||
actix_middleware::DefaultHeaders::new()
|
actix_middleware::DefaultHeaders::new()
|
||||||
|
@ -78,8 +83,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
))
|
))
|
||||||
.configure(services)
|
.configure(services)
|
||||||
})
|
})
|
||||||
.workers(SETTINGS.server.workers.unwrap_or_else(num_cpus::get))
|
.workers(settings.server.workers.unwrap_or_else(num_cpus::get))
|
||||||
.bind(SETTINGS.server.get_ip())
|
.bind(settings.server.get_ip())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
|
20
src/meta.rs
20
src/meta.rs
|
@ -17,13 +17,13 @@
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{GIT_COMMIT_HASH, VERSION};
|
use crate::{AppCtx, GIT_COMMIT_HASH, VERSION};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct BuildDetails {
|
pub struct BuildDetails<'a> {
|
||||||
pub version: &'static str,
|
pub version: &'a str,
|
||||||
pub git_commit_hash: &'static str,
|
pub git_commit_hash: &'a str,
|
||||||
pub source_code: &'static str,
|
pub source_code: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod routes {
|
pub mod routes {
|
||||||
|
@ -44,11 +44,11 @@ pub mod routes {
|
||||||
|
|
||||||
/// emmits build details of the bninary
|
/// emmits build details of the bninary
|
||||||
#[my_codegen::get(path = "crate::V1_API_ROUTES.meta.build_details")]
|
#[my_codegen::get(path = "crate::V1_API_ROUTES.meta.build_details")]
|
||||||
async fn build_details() -> impl Responder {
|
async fn build_details(ctx: AppCtx) -> impl Responder {
|
||||||
let build = BuildDetails {
|
let build = BuildDetails {
|
||||||
version: VERSION,
|
version: VERSION,
|
||||||
git_commit_hash: GIT_COMMIT_HASH,
|
git_commit_hash: GIT_COMMIT_HASH,
|
||||||
source_code: &crate::SETTINGS.source_code,
|
source_code: &ctx.settings.source_code,
|
||||||
};
|
};
|
||||||
HttpResponse::Ok().json(build)
|
HttpResponse::Ok().json(build)
|
||||||
}
|
}
|
||||||
|
@ -59,14 +59,14 @@ pub fn services(cfg: &mut web::ServiceConfig) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::{http::StatusCode, test, App};
|
use actix_web::{http::StatusCode, test};
|
||||||
|
|
||||||
use crate::services;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn build_details_works() {
|
async fn build_details_works() {
|
||||||
let app = test::init_service(App::new().configure(services)).await;
|
let ctx = tests::get_data().await;
|
||||||
|
let app = get_app!(ctx).await;
|
||||||
|
|
||||||
let resp = test::call_service(
|
let resp = test::call_service(
|
||||||
&app,
|
&app,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use config::{Config, Environment, File};
|
use config::{Config, Environment, File};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
@ -43,7 +44,7 @@ impl Server {
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub server: Server,
|
pub server: Server,
|
||||||
pub source_code: String,
|
pub source_code: String,
|
||||||
pub pages: Vec<Page>,
|
pub pages: Vec<Arc<Page>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
@ -101,20 +102,14 @@ impl Settings {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if page.secret == page2.secret {
|
if page.secret == page2.secret {
|
||||||
log::error!(
|
log::error!("{}", ServiceError::SecretTaken(page.clone(), page2.clone()));
|
||||||
"{}",
|
|
||||||
ServiceError::SecretTaken(page.to_owned(), page2.to_owned())
|
|
||||||
);
|
|
||||||
} else if page.repo == page2.repo {
|
} else if page.repo == page2.repo {
|
||||||
log::error!(
|
log::error!(
|
||||||
"{}",
|
"{}",
|
||||||
ServiceError::DuplicateRepositoryURL(page.to_owned(), page2.to_owned(),)
|
ServiceError::DuplicateRepositoryURL(page.clone(), page2.clone(),)
|
||||||
);
|
);
|
||||||
} else if page.path == page2.path {
|
} else if page.path == page2.path {
|
||||||
log::error!(
|
log::error!("{}", ServiceError::PathTaken(page.clone(), page2.clone()));
|
||||||
"{}",
|
|
||||||
ServiceError::PathTaken(page.to_owned(), page2.to_owned())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = page.update() {
|
if let Err(e) = page.update() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue