como/como_bin/src/main.rs

177 lines
5.0 KiB
Rust
Raw Normal View History

2022-10-02 12:12:08 +02:00
use std::env::{self, current_dir};
2022-10-02 20:51:06 +02:00
mod error;
2022-10-02 14:15:45 +02:00
mod gqlx;
2022-10-02 12:12:08 +02:00
mod graphql;
2022-10-02 14:15:45 +02:00
mod services;
2022-10-02 12:12:08 +02:00
2022-10-02 20:51:06 +02:00
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
2022-10-02 12:12:08 +02:00
use axum::{
extract::Extension,
2022-10-02 20:51:06 +02:00
http::{Method, StatusCode},
2022-10-02 12:12:08 +02:00
response::{Html, IntoResponse},
2022-10-02 20:51:06 +02:00
routing::{get, post},
2022-10-02 12:12:08 +02:00
Json, Router,
};
2022-10-03 22:08:25 +02:00
use axum_extra::extract::cookie::Key;
2022-10-02 12:12:08 +02:00
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
2022-10-02 20:52:29 +02:00
EmptySubscription, Schema,
2022-10-02 12:12:08 +02:00
};
2022-10-02 20:51:06 +02:00
use axum_sessions::{
async_session::MemoryStore,
extractors::{ReadableSession, WritableSession},
SessionLayer,
};
use error::AppError;
2022-10-02 12:12:08 +02:00
use graphql::CibusSchema;
2022-10-02 20:51:06 +02:00
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
2022-10-02 14:15:45 +02:00
use services::users_service;
2022-10-02 12:12:08 +02:00
use sqlx::PgPool;
use tower_http::{cors::CorsLayer, trace::TraceLayer};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
2022-10-02 14:15:45 +02:00
use crate::graphql::{MutationRoot, QueryRoot};
2022-10-02 12:12:08 +02:00
2022-10-02 20:51:06 +02:00
async fn graphql_handler(
schema: Extension<CibusSchema>,
session: ReadableSession,
req: GraphQLRequest,
) -> Result<GraphQLResponse, StatusCode> {
2022-10-03 22:08:25 +02:00
let req = req.into_inner();
2022-10-03 23:00:31 +02:00
2022-10-03 22:08:25 +02:00
//if let Some(user_id) = session.get::<String>("userId") {
// req = req.data(user_id);
return Ok(schema.execute(req).await.into());
//} else if let Some(on) = &req.operation_name {
// if on == "IntrospectionQuery" {
// return Ok(schema.execute(req).await.into());
// }
//}
2022-10-02 20:51:06 +02:00
2022-10-03 22:08:25 +02:00
//Err(StatusCode::FORBIDDEN)
2022-10-02 12:12:08 +02:00
}
async fn graphql_playground() -> impl IntoResponse {
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Environment
tracing::info!("Loading dotenv");
dotenv::dotenv()?;
// Logging
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| {
2022-10-02 14:15:45 +02:00
"como_bin=debug,tower_http=debug,axum_extra=debug,hyper=info,mio=info,sqlx=info,async_graphql=debug"
.into()
2022-10-02 12:12:08 +02:00
}),
))
.with(tracing_subscriber::fmt::layer())
.init();
// Database
tracing::info!("Creating pool");
let db_url = env::var("DATABASE_URL")?;
let pool = PgPool::connect(&db_url).await?;
// Database Migrate
tracing::info!("Migrating db");
sqlx::migrate!("db/migrations").run(&pool).await?;
tracing::info!("current path: {}", current_dir()?.to_string_lossy());
// Schema
println!("Building schema");
2022-10-02 14:15:45 +02:00
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
2022-10-02 20:51:06 +02:00
.data(users_service::UserService::new(pool.clone()))
2022-10-02 14:15:45 +02:00
.finish();
2022-10-02 12:12:08 +02:00
// CORS
let cors = vec!["http://localhost:3000".parse().unwrap()];
2022-10-02 20:51:06 +02:00
// Key
let key = Key::generate();
let store = MemoryStore::new();
let session_layer = SessionLayer::new(store, key.master());
2022-10-02 12:12:08 +02:00
// Webserver
tracing::info!("Building router");
let app = Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
2022-10-02 20:51:06 +02:00
.route("/auth/login", post(login))
.route("/auth/register", post(register))
2022-10-02 12:12:08 +02:00
.layer(TraceLayer::new_for_http())
2022-10-02 20:51:06 +02:00
.layer(Extension(schema))
.layer(Extension(key))
.layer(Extension(pool))
.layer(session_layer)
2022-10-02 12:12:08 +02:00
.layer(
CorsLayer::new()
.allow_origin(cors)
.allow_headers([axum::http::header::CONTENT_TYPE])
.allow_methods([Method::GET, Method::POST, Method::OPTIONS]),
);
tracing::info!("Starting webserver");
axum::Server::bind(&"0.0.0.0:3001".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
Ok(())
}
2022-10-02 20:51:06 +02:00
#[derive(Serialize, Deserialize)]
pub struct Credentials {
pub username: String,
pub password: String,
}
async fn login(
Json(credentials): Json<Credentials>,
Extension(pool): Extension<PgPool>,
mut session: WritableSession,
) -> Result<Json<Value>, error::AppError> {
let us = users_service::UserService::new(pool);
match us
.validate_user(credentials.username, credentials.password)
.await
.map_err(|e| {
tracing::error!("could not validate user: {}", e);
AppError::InternalServerError
})? {
Some(user_id) => {
if let Err(e) = session.insert("userId", user_id.clone()) {
tracing::error!("could not insert session: {}", e);
return Err(AppError::InternalServerError);
}
Ok(Json(json!({ "userId": user_id })))
}
None => Err(AppError::WrongCredentials),
}
}
async fn register(
Json(credentials): Json<Credentials>,
Extension(pool): Extension<PgPool>,
) -> Result<Json<Value>, error::AppError> {
let us = users_service::UserService::new(pool)
.add_user(credentials.username, credentials.password)
.await
.map_err(|e| {
tracing::error!("could not add user: {}", e);
AppError::InternalServerError
})?;
Ok(Json(json!({ "userId": us })))
}