diff options
| author | Jokler <jokler@protonmail.com> | 2020-06-23 16:26:07 +0200 |
|---|---|---|
| committer | Jokler <jokler@protonmail.com> | 2020-06-23 16:26:07 +0200 |
| commit | fc7bd403bf41d724bd8dc3bf3b827c592c539171 (patch) | |
| tree | 1b816f68f1793dc8bf70e490196da6ae58d6f16a /src | |
| download | rgit-fc7bd403bf41d724bd8dc3bf3b827c592c539171.tar.gz rgit-fc7bd403bf41d724bd8dc3bf3b827c592c539171.zip | |
Diffstat (limited to 'src')
| -rw-r--r-- | src/error.rs | 44 | ||||
| -rw-r--r-- | src/error_handlers.rs | 33 | ||||
| -rw-r--r-- | src/main.rs | 85 | ||||
| -rw-r--r-- | src/model.rs | 10 | ||||
| -rw-r--r-- | src/public.rs | 14 | ||||
| -rw-r--r-- | src/schema.rs | 26 |
6 files changed, 212 insertions, 0 deletions
diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..062bb3d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,44 @@ +use actix_web::{error::ResponseError, HttpResponse}; +use argonautica::Error as HashError; +use derive_more::Display; +use diesel::result::Error as DieselError; +use log::error; +use std::convert::From; + +#[derive(Debug, Display)] +pub enum ServiceError { + #[display(fmt = "Internal Server Error")] + InternalServerError, + + #[display(fmt = "Unauthorized")] + Unauthorized, + + #[display(fmt = "Not Found")] + NotFound, +} + +impl ResponseError for ServiceError { + fn error_response(&self) -> HttpResponse { + match *self { + ServiceError::InternalServerError => { + HttpResponse::InternalServerError().body("Internal Server Error") + } + ServiceError::Unauthorized => HttpResponse::Unauthorized().body("Unauthorized"), + ServiceError::NotFound => HttpResponse::NotFound().body("404 Not Found"), + } + } +} + +impl From<DieselError> for ServiceError { + fn from(e: DieselError) -> ServiceError { + error!("Database error: {}", e); + ServiceError::InternalServerError + } +} + +impl From<HashError> for ServiceError { + fn from(e: HashError) -> ServiceError { + error!("Hash error: {}", e); + ServiceError::InternalServerError + } +} diff --git a/src/error_handlers.rs b/src/error_handlers.rs new file mode 100644 index 0000000..c25b1c5 --- /dev/null +++ b/src/error_handlers.rs @@ -0,0 +1,33 @@ +use actix_files::NamedFile; +use actix_web::middleware::errhandlers::ErrorHandlerResponse; +use actix_web::{dev, Responder, Result}; + +pub fn bad_request<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { + let new_resp = NamedFile::open("static/errors/400.htm")? + .set_status_code(res.status()) + .respond_to(res.request()) + .into_inner()?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) +} + +pub fn not_found<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { + let new_resp = NamedFile::open("static/errors/404.htm")? + .set_status_code(res.status()) + .respond_to(res.request()) + .into_inner()?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) +} + +pub fn internal_server_error<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> { + let new_resp = NamedFile::open("static/errors/500.htm")? + .set_status_code(res.status()) + .respond_to(res.request()) + .into_inner()?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0353479 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,85 @@ +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate diesel_migrations; + +use actix::prelude::*; +use actix_web::middleware::{errhandlers::ErrorHandlers, Logger}; + +use actix_identity::{CookieIdentityPolicy, IdentityService}; +use actix_web::{http, web, App, HttpServer}; + +use chrono::Duration; +use diesel::mysql::MysqlConnection; +use dotenv::dotenv; +use r2d2_diesel::ConnectionManager; + +mod public; +mod error; +mod error_handlers; +mod schema; +mod model; + +use crate::model::DbExecutor; + +embed_migrations!(); + +pub struct Secret(String); + +#[actix_rt::main] +async fn main() -> std::io::Result<()> { + dotenv().ok(); + + std::env::set_var( + "RUST_LOG", + "rgit=debug,actix_web=trace,actix_server=debug", + ); + env_logger::init(); + + let bind_host: String = + std::env::var("BIND_HOST").unwrap_or_else(|_| "127.0.0.1:8000".to_string()); + + let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + let manager = ConnectionManager::<MysqlConnection>::new(database_url); + let pool = r2d2::Pool::builder() + .build(manager) + .expect("Can to build connection pool"); + embedded_migrations::run(&*pool.get().expect("Can connect to mysql")) + .expect("Can run migrations"); + + let db_addr: Addr<DbExecutor> = SyncArbiter::start(4, move || DbExecutor(pool.clone())); + + HttpServer::new(move || { + let secret: String = std::env::var("SECRET_KEY").expect("SECRET_KEY env var should be set"); + let domain: String = std::env::var("DOMAIN").unwrap_or_else(|_| "localhost".to_string()); + let secure_cookies = &domain != "localhost"; + + let error_handlers = ErrorHandlers::new() + .handler( + http::StatusCode::INTERNAL_SERVER_ERROR, + error_handlers::internal_server_error, + ) + .handler(http::StatusCode::BAD_REQUEST, error_handlers::bad_request) + .handler(http::StatusCode::NOT_FOUND, error_handlers::not_found); + + App::new() + .data(db_addr.clone()) + .data(Secret(secret.clone())) + .wrap(error_handlers) + .wrap(IdentityService::new( + CookieIdentityPolicy::new(secret.as_bytes()) + .name("rgit-session") + .domain(domain.as_str()) + .max_age_time(Duration::days(30)) + .secure(secure_cookies), // this has to reflect the usage of https + )) + .wrap(Logger::default()) + .route("/", web::get().to(public::index)) + .service(actix_files::Files::new("/static", "static/")) + }) + .bind(&bind_host)? + .run() + .await?; + + Ok(()) +} diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..3409910 --- /dev/null +++ b/src/model.rs @@ -0,0 +1,10 @@ +use actix::{Actor, SyncContext}; +use diesel::mysql::MysqlConnection; +use r2d2::Pool; +use r2d2_diesel::ConnectionManager; + +pub struct DbExecutor(pub Pool<ConnectionManager<MysqlConnection>>); + +impl Actor for DbExecutor { + type Context = SyncContext<Self>; +} diff --git a/src/public.rs b/src/public.rs new file mode 100644 index 0000000..6a5f8c8 --- /dev/null +++ b/src/public.rs @@ -0,0 +1,14 @@ +use actix_web::{HttpResponse, Responder}; +use askama::Template; + +#[derive(Template)] +#[template(path = "index.htm")] +struct IndexTemplate<'a> { + text: &'a str, +} + +pub async fn index() -> impl Responder { + HttpResponse::Ok() + .content_type("text/html") + .body(IndexTemplate { text: "Hello" }.render().unwrap()) +} diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..0e42988 --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,26 @@ +table! { + repositories (id) { + id -> Unsigned<Bigint>, + name -> Varchar, + description -> Varchar, + owner -> Unsigned<Bigint>, + created -> Timestamp, + } +} + +table! { + users (id) { + id -> Unsigned<Bigint>, + power_level -> Integer, + name -> Varchar, + created -> Timestamp, + hash -> Char, + } +} + +joinable!(repositories -> users (owner)); + +allow_tables_to_appear_in_same_query!( + repositories, + users, +); |
