177 lines
5.0 KiB
Rust
177 lines
5.0 KiB
Rust
use std::env::{self, current_dir};
|
|
|
|
mod error;
|
|
mod gqlx;
|
|
mod graphql;
|
|
mod services;
|
|
|
|
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
|
|
use axum::{
|
|
extract::Extension,
|
|
http::{Method, StatusCode},
|
|
response::{Html, IntoResponse},
|
|
routing::{get, post},
|
|
Json, Router,
|
|
};
|
|
use axum_extra::extract::cookie::Key;
|
|
|
|
use async_graphql::{
|
|
http::{playground_source, GraphQLPlaygroundConfig},
|
|
EmptySubscription, Schema,
|
|
};
|
|
use axum_sessions::{
|
|
async_session::MemoryStore,
|
|
extractors::{ReadableSession, WritableSession},
|
|
SessionLayer,
|
|
};
|
|
use error::AppError;
|
|
use graphql::CibusSchema;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::{json, Value};
|
|
use services::users_service;
|
|
use sqlx::PgPool;
|
|
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
|
|
use crate::graphql::{MutationRoot, QueryRoot};
|
|
|
|
async fn graphql_handler(
|
|
schema: Extension<CibusSchema>,
|
|
session: ReadableSession,
|
|
req: GraphQLRequest,
|
|
) -> Result<GraphQLResponse, StatusCode> {
|
|
let req = req.into_inner();
|
|
|
|
//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());
|
|
// }
|
|
//}
|
|
|
|
//Err(StatusCode::FORBIDDEN)
|
|
}
|
|
|
|
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(|_| {
|
|
"como_bin=debug,tower_http=debug,axum_extra=debug,hyper=info,mio=info,sqlx=info,async_graphql=debug"
|
|
.into()
|
|
}),
|
|
))
|
|
.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");
|
|
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
|
|
.data(users_service::UserService::new(pool.clone()))
|
|
.finish();
|
|
|
|
// CORS
|
|
let cors = vec!["http://localhost:3000".parse().unwrap()];
|
|
|
|
// Key
|
|
let key = Key::generate();
|
|
|
|
let store = MemoryStore::new();
|
|
let session_layer = SessionLayer::new(store, key.master());
|
|
|
|
// Webserver
|
|
tracing::info!("Building router");
|
|
let app = Router::new()
|
|
.route("/", get(graphql_playground).post(graphql_handler))
|
|
.route("/auth/login", post(login))
|
|
.route("/auth/register", post(register))
|
|
.layer(TraceLayer::new_for_http())
|
|
.layer(Extension(schema))
|
|
.layer(Extension(key))
|
|
.layer(Extension(pool))
|
|
.layer(session_layer)
|
|
.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(())
|
|
}
|
|
|
|
#[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 })))
|
|
}
|