use actix_session::{
config::PersistentSession, storage::RedisActorSessionStore, SessionMiddleware,
};
use actix_web::{
cookie::time::Duration,
middleware,
web::{self, Data},
App, HttpRequest, HttpResponse, HttpServer, Responder,
};
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
use diesel_async::AsyncPgConnection;
use diesel_async::{pooled_connection::bb8, RunQueryDsl};
use env_logger;
use keys::{add_key, is_key_exist};
use models::users;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::io;
use crate::models::users::get_user_with_email;
mod db;
mod keys;
mod middlewares;
mod models;
type Pool = bb8::Pool<AsyncDieselConnectionManager<AsyncPgConnection>>;
const EXPIRES: i64 = 60 * 60 * 24 * 365;
#[actix_web::main]
async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
dotenvy::dotenv().ok();
log::info!("starting HTTP server at http://localhost:8080");
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file");
let redis = redis::Client::open("redis://127.0.0.1:6379").unwrap();
let private_key = actix_web::cookie::Key::generate();
let config = AsyncDieselConnectionManager::<diesel_async::AsyncPgConnection>::new(db_url);
let pool = bb8::Pool::builder().build(config).await.unwrap();
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(redis.clone()))
.wrap(middleware::Logger::default())
.app_data(web::Data::new(pool.clone()))
.wrap(
SessionMiddleware::builder(
RedisActorSessionStore::new("127.0.0.1:6379"),
private_key.clone(),
)
.cookie_same_site(actix_web::cookie::SameSite::Lax)
.cookie_name("name".to_string())
.cookie_secure(false)
.cookie_http_only(false)
.cookie_domain(Some("libredu.org".to_string()))
.session_lifecycle(actix_session::config::SessionLifecycle::PersistentSession(
PersistentSession::default().session_ttl(Duration::seconds(EXPIRES)),
))
.build(),
)
.service(
web::resource("/get_key/{id}")
.wrap(middlewares::CheckLogin)
.route(web::get().to(get_key)),
)
.service(
web::resource("/confirm_key/{key}")
.route(web::get().to(confirm_key)),
)
.service(web::resource("/").route(web::post().to(unlock)))
.service(
web::resource("/schools")
.wrap(middlewares::CheckLogin)
.route(web::get().to(schools)),
)
.service(web::resource("/login").route(web::post().to(login)))
})
.bind(("127.0.0.1", 8082))?
.run()
.await
}
async fn get_key(
con: Data<redis::Client>,
s: actix_session::Session,
s_id: web::Path<i32>,
) -> actix_web::Result<impl Responder> {
loop {
let key = rand::thread_rng().gen_range(100000..999999);
let key = format!("{}:{}", s_id, key);
if !is_key_exist(con.clone(), &key).await {
add_key(con.clone(), &key).await;
return Ok(HttpResponse::Ok().body(key.to_string()));
}
}
}
async fn confirm_key(
con: Data<redis::Client>,
_s: actix_session::Session,
key: web::Path<String>,
) -> actix_web::Result<impl Responder> {
if !is_key_exist(con.clone(), &key.into_inner()).await {
return Ok(HttpResponse::Ok().finish());
}
return Ok(HttpResponse::BadRequest().finish());
}
async fn unlock(
con: Data<redis::Client>,
password: web::Form<String>,
) -> actix_web::Result<impl Responder> {
if is_key_exist(con.clone(), &password).await {
return Ok(HttpResponse::Ok().body(""));
}
return Ok(HttpResponse::BadRequest().into());
}
async fn login(
_form: web::Json<LoginForm>,
c: web::Data<bb8::Pool<AsyncPgConnection>>, session: actix_session::Session,
) -> actix_web::Result<impl Responder> {
let user = get_user_with_email(&_form.email, c).await;
session.insert("user", user.clone()).unwrap();
let u = session.get::<crate::users::User>("user");
println!("{:?}", u);
return Ok(HttpResponse::Ok().json(user));
}
async fn schools(
c: web::Data<bb8::Pool<AsyncPgConnection>>, session: actix_session::Session,
) -> actix_web::Result<impl Responder> {
use crate::users::*;
let user = session.get::<User>("user").unwrap().unwrap();
let schs = get_schools(user.id, c).await;
println!("schools = {schs:?}");
return Ok(HttpResponse::Ok().json(schs));
}
#[derive(Clone, Deserialize, Serialize)]
struct LoginForm {
email: String,
password: String,
}