TTD4GDURZSACT7DDSH7GBP2VSA6DCEPZ3CLGRIZTSF7AI723FNIAC use diesel::prelude::*;use diesel_async::{AsyncPgConnection, RunQueryDsl};// ordinary diesel model setuptable! {users {id -> Integer,first_name -> Text,email -> Text,}}#[derive(Queryable, Selectable, Debug, Clone, serde::Serialize, serde::Deserialize)]#[diesel(table_name = users)]#[diesel(check_for_backend(diesel::pg::Pg))]pub struct User {pub id: i32,pub first_name: String,pub email: String,}table! {school_users(school_id, user_id){school_id -> Integer,user_id -> Integer,}}#[derive(Queryable,Selectable,Debug,Associations,Identifiable,PartialEq,Clone,serde::Serialize,serde::Deserialize,)]#[diesel(table_name = school_users)]#[diesel(belongs_to(School))]#[diesel(check_for_backend(diesel::pg::Pg))]pub struct SchoolUser {pub school_id: i32,pub user_id: i32,}table! {school{id -> Integer,name -> Text,manager-> Integer,}}#[derive(Queryable,Selectable,Debug,Identifiable,PartialEq,Clone,serde::Serialize,serde::Deserialize,)]#[diesel(table_name = school)]#[diesel(check_for_backend(diesel::pg::Pg))]pub struct School {pub id: i32,pub name: String,pub manager: i32,}diesel::joinable!(school_users -> school (school_id));diesel::allow_tables_to_appear_in_same_query!(school, school_users,);pub async fn get_user_with_email(email: &str,c: actix_web::web::Data<diesel_async::pooled_connection::bb8::Pool<AsyncPgConnection>>,) -> User {// use diesel::ExpressionMethods;// use diesel::QueryDsl;// use diesel::SelectableHelper;let conn = c.into_inner();let mut conn = conn.get().await.unwrap();let user: Vec<crate::users::User> = users::table.filter(users::email.eq(email)).select(crate::users::User::as_select()).load(&mut conn).await.unwrap();user[0].clone()}pub async fn get_schools(id: i32,c: actix_web::web::Data<diesel_async::pooled_connection::bb8::Pool<AsyncPgConnection>>,) -> Vec<School> {let conn = c.into_inner();let mut conn = conn.get().await.unwrap();let schools: Vec<crate::users::School> = school::table.filter(school_users::user_id.eq(id)).select(School::as_select()).load(&mut conn).await.unwrap();schools}
pub mod users;
use std::future::{ready, Ready};use actix_session::SessionExt;use actix_web::{body::EitherBody,dev::{self, Service, ServiceRequest, ServiceResponse, Transform},http, Error, HttpResponse,};use futures_util::future::LocalBoxFuture;use crate::models::users::User;pub struct CheckLogin;impl<S, B> Transform<S, ServiceRequest> for CheckLoginwhereS: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,S::Future: 'static,B: 'static,{type Response = ServiceResponse<EitherBody<B>>;type Error = Error;type InitError = ();type Transform = CheckLoginMiddleware<S>;type Future = Ready<Result<Self::Transform, Self::InitError>>;fn new_transform(&self, service: S) -> Self::Future {ready(Ok(CheckLoginMiddleware { service }))}}pub struct CheckLoginMiddleware<S> {service: S,}impl<S, B> Service<ServiceRequest> for CheckLoginMiddleware<S>whereS: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,S::Future: 'static,B: 'static,{type Response = ServiceResponse<EitherBody<B>>;type Error = Error;type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;dev::forward_ready!(service);fn call(&self, request: ServiceRequest) -> Self::Future {// Change this to see the change in outcome in the browser.// Usually this boolean would be acquired from a password check or other auth verification.let is_logged_in = request.get_session().get::<User>("user").is_ok();// Don't forward to `/login` if we are already on `/login`.if !is_logged_in && request.path() != "/login" {let (request, _pl) = request.into_parts();let response = HttpResponse::Found().insert_header((http::header::LOCATION, "/login")).finish()// constructed responses map to "right" body.map_into_right_body();return Box::pin(async { Ok(ServiceResponse::new(request, response)) });}let res = self.service.call(request);Box::pin(async move {// forwarded responses map to "left" bodyres.await.map(ServiceResponse::map_into_left_body)})}}
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"));// std::env::set_var("RUST_LOG", "debug");// tracing_subscriber::fmt::init();// get env varsdotenvy::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("192.168.1.101".to_string())).session_lifecycle(actix_session::config::SessionLifecycle::PersistentSession(PersistentSession::default().session_ttl(Duration::seconds(EXPIRES)),))// .execute().build(),).service(web::resource("/get_key/{id}").wrap(middlewares::CheckLogin).route(web::get().to(get_key)),).service(web::resource("/confirm_key/{key}")// .wrap(middlewares::CheckLogin).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)))})//.workers(2).bind(("127.0.0.1", 8080))?.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>>, // c: web::Data<Pool>,session: actix_session::Session,) -> actix_web::Result<impl Responder> {// use diesel_async::prelude::*;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>>, // c: web::Data<Pool>,session: actix_session::Session,) -> actix_web::Result<impl Responder> {// use diesel_async::prelude::*;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,}
use actix_web::web::Data;use redis::{self, AsyncCommands, Client};pub async fn is_key_exist(con: Data<Client>, k: &str) -> bool {let mut con = con.get_connection_manager().await.unwrap();let a: bool = con.sismember("etapkilit", k).await.unwrap();a}pub async fn add_key(con: Data<Client>, k: &str) -> bool {let mut con = con.get_connection_manager().await.unwrap();let a: bool = con.sadd("etapkilit", k).await.unwrap();a}
[package]name = "backend"version = "0.1.0"edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]actix-web = {version="*",features=["cookies"]}actix-session = { version = "*", features = ["redis-actor-session"] }env_logger = "*"log = "*"redis = { version = "0.24", default-features = false, features = ["tokio-comp", "connection-manager"] }serde = {version = "*", features=["derive"]}serde_json = "*"rand = "*"dotenvy="*"diesel = {version= "2.1.0", default-features=false,features=["postgres"]} # no backend features need to be enableddiesel-async = { version = "0.4.1", features = ["postgres","bb8"] }futures-util = "0.3.21"rustls = "0.20.8"rustls-native-certs = "0.6.2"tokio = { version = "1.2.0", default-features = false, features = ["macros", "rt-multi-thread"] }tokio-postgres = "0.7.7"tokio-postgres-rustls = "0.9.0"