136 lines
4.3 KiB
Rust
136 lines
4.3 KiB
Rust
use axum_gcra::{gcra::Quota, real_ip::RealIp, RateLimitLayer};
|
|
use axum_reverse_proxy::ReverseProxy;
|
|
use std::{num::NonZero, sync::Arc, time::Duration};
|
|
use tokio::sync::broadcast;
|
|
|
|
use async_graphql::{
|
|
http::{playground_source, GraphQLPlaygroundConfig},
|
|
Schema,
|
|
};
|
|
use async_graphql_axum::{GraphQLRequest, GraphQLResponse, GraphQLSubscription};
|
|
use axum::{
|
|
extract::{FromRef, State},
|
|
response::{Html, IntoResponse},
|
|
routing::get,
|
|
Router,
|
|
};
|
|
use jsonwebtoken::DecodingKey;
|
|
use sqlx::PgPool;
|
|
use tower_http::cors::CorsLayer;
|
|
|
|
use crate::{
|
|
auth::{AuthUserState, Claims as MyClaims},
|
|
config::Config,
|
|
graphql::{subscription::StatusUpdate, MutationRoot, QueryRoot, SubscriptionRoot},
|
|
services::{
|
|
blog_service::BlogService, casbin_service::CasbinService, config_manager::ConfigsManager,
|
|
config_service::ConfigsService, invite_code_service::InviteCodeService,
|
|
mosaic_service::MosaicService, system_config_service::SystemConfigService,
|
|
user_service::UserService,
|
|
},
|
|
};
|
|
|
|
use axum_jwt_auth::{JwtDecoderState, LocalDecoder};
|
|
use jsonwebtoken::Validation;
|
|
|
|
pub type AppSchema = Schema<QueryRoot, MutationRoot, SubscriptionRoot>;
|
|
|
|
#[derive(Clone, FromRef)]
|
|
pub struct AppState {
|
|
pub schema: AppSchema,
|
|
pub decoder: JwtDecoderState<MyClaims>,
|
|
pub status_sender: Option<broadcast::Sender<StatusUpdate>>,
|
|
}
|
|
|
|
pub async fn create_router(
|
|
pool: PgPool,
|
|
config: Config,
|
|
status_sender: Option<broadcast::Sender<StatusUpdate>>,
|
|
) -> Router {
|
|
let casbin_service = CasbinService::new(config.database_url.clone())
|
|
.await
|
|
.expect("Failed to initialize CasbinService");
|
|
|
|
let user_service = UserService::new(pool.clone(), config.jwt_secret.clone())
|
|
.with_casbin(casbin_service.clone());
|
|
|
|
let invite_code_service = InviteCodeService::new(pool.clone());
|
|
let system_config_service = SystemConfigService::new(pool.clone());
|
|
let mosaic_service = MosaicService::new(pool.clone());
|
|
let configs_service = ConfigsService::new(pool.clone());
|
|
let config_manager = ConfigsManager::new(configs_service).await;
|
|
let blog_service = BlogService::new(pool.clone());
|
|
|
|
let schema = Schema::build(
|
|
QueryRoot::default(),
|
|
MutationRoot::default(),
|
|
SubscriptionRoot,
|
|
)
|
|
.data(pool)
|
|
.data(user_service)
|
|
.data(invite_code_service)
|
|
.data(system_config_service)
|
|
.data(mosaic_service)
|
|
.data(config_manager)
|
|
.data(blog_service)
|
|
.data(casbin_service)
|
|
.data(config.clone())
|
|
.data(status_sender.clone())
|
|
.finish();
|
|
|
|
let keys = vec![DecodingKey::from_secret(config.jwt_secret.as_bytes())];
|
|
let validation = Validation::default();
|
|
let decoder = LocalDecoder::builder()
|
|
.keys(keys)
|
|
.validation(validation)
|
|
.build()
|
|
.unwrap();
|
|
|
|
let app_state = AppState {
|
|
schema: schema.clone(),
|
|
decoder: JwtDecoderState {
|
|
decoder: Arc::new(decoder),
|
|
},
|
|
status_sender,
|
|
};
|
|
|
|
let router = ReverseProxy::new("/api", &config.tile_server_url.as_str());
|
|
Router::new()
|
|
.route("/", get(graphql_playground))
|
|
.route("/graphql", get(graphql_playground).post(graphql_handler))
|
|
// .route_layer(
|
|
// RateLimitLayer::<RealIp>::builder()
|
|
// .with_route(
|
|
// (Method::GET, "/graphql"),
|
|
// Quota::new(Duration::from_millis(100), NonZero::new(10).unwrap()),
|
|
// )
|
|
// .with_route(
|
|
// (Method::POST, "/graphql"),
|
|
// Quota::new(Duration::from_millis(100), NonZero::new(10).unwrap()),
|
|
// )
|
|
// .with_gc_interval(1000)
|
|
// .default_handle_error(),
|
|
// )
|
|
.route_service("/ws", GraphQLSubscription::new(schema))
|
|
.layer(CorsLayer::permissive())
|
|
.merge(router)
|
|
.with_state(app_state)
|
|
}
|
|
|
|
#[axum::debug_handler]
|
|
async fn graphql_handler(
|
|
AuthUserState(user): AuthUserState,
|
|
State(state): State<AppState>,
|
|
req: GraphQLRequest,
|
|
) -> GraphQLResponse {
|
|
let mut request = req.into_inner();
|
|
if let Some(user) = user {
|
|
request = request.data(user);
|
|
}
|
|
state.schema.execute(request).await.into()
|
|
}
|
|
|
|
async fn graphql_playground() -> impl IntoResponse {
|
|
Html(playground_source(GraphQLPlaygroundConfig::new("/graphql")))
|
|
}
|