scufflecloud_core/services/
mod.rs1use std::net::SocketAddr;
2use std::sync::Arc;
3
4use axum::http::StatusCode;
5use axum::{Extension, Json};
6use scuffle_http::http::Method;
7use tinc::TincService;
8use tinc::openapi::Server;
9use tower_http::cors::CorsLayer;
10use tower_http::trace::TraceLayer;
11
12use crate::CoreConfig;
13
14mod organization_invitations;
15mod organizations;
16mod sessions;
17mod users;
18
19#[derive(Debug)]
20pub struct CoreSvc<G> {
21 _phantom: std::marker::PhantomData<G>,
22}
23
24impl<G> Default for CoreSvc<G> {
25 fn default() -> Self {
26 Self {
27 _phantom: std::marker::PhantomData,
28 }
29 }
30}
31
32impl<G: CoreConfig> scuffle_bootstrap::Service<G> for CoreSvc<G> {
33 async fn run(self, global: Arc<G>, ctx: scuffle_context::Context) -> anyhow::Result<()> {
34 let organization_invitations_svc_tinc =
36 pb::scufflecloud::core::v1::organization_invitations_service_tinc::OrganizationInvitationsServiceTinc::new(
37 CoreSvc::<G>::default(),
38 );
39 let organizations_svc_tinc =
40 pb::scufflecloud::core::v1::organizations_service_tinc::OrganizationsServiceTinc::new(CoreSvc::<G>::default());
41 let sessions_svc_tinc =
42 pb::scufflecloud::core::v1::sessions_service_tinc::SessionsServiceTinc::new(CoreSvc::<G>::default());
43 let users_svc_tinc = pb::scufflecloud::core::v1::users_service_tinc::UsersServiceTinc::new(CoreSvc::<G>::default());
44
45 let cors = CorsLayer::new()
46 .allow_methods([Method::GET, Method::POST, Method::OPTIONS])
47 .allow_origin(tower_http::cors::Any)
48 .allow_headers(tower_http::cors::Any);
49
50 let mut openapi_schema = organization_invitations_svc_tinc.openapi_schema();
51 openapi_schema.merge(organizations_svc_tinc.openapi_schema());
52 openapi_schema.merge(sessions_svc_tinc.openapi_schema());
53 openapi_schema.merge(users_svc_tinc.openapi_schema());
54 openapi_schema.info.title = "Scuffle Cloud Core API".to_string();
55 openapi_schema.info.version = "v1".to_string();
56 openapi_schema.servers = Some(vec![Server::new("/v1")]);
57
58 let v1_rest_router = axum::Router::new()
59 .route("/openapi.json", axum::routing::get(Json(openapi_schema)))
60 .merge(organization_invitations_svc_tinc.into_router())
61 .merge(organizations_svc_tinc.into_router())
62 .merge(sessions_svc_tinc.into_router())
63 .merge(users_svc_tinc.into_router())
64 .layer(cors);
65
66 let organization_invitations_svc =
68 pb::scufflecloud::core::v1::organization_invitations_service_server::OrganizationInvitationsServiceServer::new(
69 CoreSvc::<G>::default(),
70 );
71 let organizations_svc = pb::scufflecloud::core::v1::organizations_service_server::OrganizationsServiceServer::new(
72 CoreSvc::<G>::default(),
73 );
74 let sessions_svc =
75 pb::scufflecloud::core::v1::sessions_service_server::SessionsServiceServer::new(CoreSvc::<G>::default());
76 let users_svc = pb::scufflecloud::core::v1::users_service_server::UsersServiceServer::new(CoreSvc::<G>::default());
77
78 let reflection_v1_svc = tonic_reflection::server::Builder::configure()
79 .register_encoded_file_descriptor_set(pb::ANNOTATIONS_PB)
80 .build_v1()?;
81 let reflection_v1alpha_svc = tonic_reflection::server::Builder::configure()
82 .register_encoded_file_descriptor_set(pb::ANNOTATIONS_PB)
83 .build_v1alpha()?;
84
85 let mut builder = tonic::service::Routes::builder();
86 builder.add_service(organization_invitations_svc);
87 builder.add_service(organizations_svc);
88 builder.add_service(sessions_svc);
89 builder.add_service(users_svc);
90 builder.add_service(reflection_v1_svc);
91 builder.add_service(reflection_v1alpha_svc);
92 let grpc_router = builder.routes().prepare().into_axum_router();
93
94 let mut router = axum::Router::new()
95 .nest("/v1", v1_rest_router)
96 .merge(grpc_router)
97 .route_layer(axum::middleware::from_fn(crate::middleware::auth::<G>))
98 .layer(axum::middleware::from_fn(crate::middleware::ip_address::<G>))
99 .layer(TraceLayer::new_for_http())
100 .layer(Extension(Arc::clone(&global)))
101 .fallback(StatusCode::NOT_FOUND);
102
103 if global.swagger_ui_enabled() {
104 router = router.merge(swagger_ui_dist::generate_routes(swagger_ui_dist::ApiDefinition {
105 uri_prefix: "/v1/docs",
106 api_definition: swagger_ui_dist::OpenApiSource::Uri("/v1/openapi.json"),
107 title: Some("V1 Api Docs"),
108 }));
109 }
110
111 scuffle_http::HttpServer::builder()
112 .tower_make_service_with_addr(router.into_make_service_with_connect_info::<SocketAddr>())
113 .bind(global.bind())
114 .ctx(ctx)
115 .build()
116 .run()
117 .await?;
118
119 Ok(())
120 }
121}