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