retrom_grpc_service/
lib.rs

1use axum::Router;
2use diesel_async::pooled_connection::AsyncDieselConnectionManager;
3use emulators::EmulatorServiceHandlers;
4use file_explorer::FileExplorerServiceHandlers;
5use games::GameServiceHandlers;
6use http::HeaderName;
7use jobs::{job_manager::JobManager, JobServiceHandlers};
8use library::LibraryServiceHandlers;
9use metadata::MetadataServiceHandlers;
10use platforms::PlatformServiceHandlers;
11use retrom_codegen::retrom::{
12    client_service_server::ClientServiceServer, emulator_service_server::EmulatorServiceServer,
13    file_explorer_service_server::FileExplorerServiceServer,
14    game_service_server::GameServiceServer, job_service_server::JobServiceServer,
15    library_service_server::LibraryServiceServer, metadata_service_server::MetadataServiceServer,
16    platform_service_server::PlatformServiceServer, saves_service_server::SavesServiceServer,
17    server_service_server::ServerServiceServer, FILE_DESCRIPTOR_SET,
18};
19use retrom_service_common::{
20    config::ServerConfigManager,
21    media_cache::MediaCache,
22    metadata_providers::{igdb::provider::IGDBProvider, steam::provider::SteamWebApiProvider},
23    retrom_dirs::RetromDirs,
24};
25use retrom_telemetry::grpc::{GrpcOnRequestSpan, GrpcOnResponseSpanHandler};
26use saves::SavesServiceHandlers;
27use std::{sync::Arc, time::Duration};
28use tower_http::cors::{AllowOrigin, CorsLayer};
29
30pub mod clients;
31pub mod emulators;
32pub mod file_explorer;
33pub mod games;
34pub mod jobs;
35pub mod library;
36pub mod metadata;
37pub mod platforms;
38mod saves;
39pub mod server;
40
41const DEFAULT_MAX_AGE: Duration = Duration::from_secs(24 * 60 * 60);
42const DEFAULT_EXPOSED_HEADERS: [HeaderName; 3] = [
43    HeaderName::from_static("grpc-status"),
44    HeaderName::from_static("grpc-message"),
45    HeaderName::from_static("grpc-status-details-bin"),
46];
47
48const DEFAULT_ALLOW_HEADERS: [HeaderName; 7] = [
49    HeaderName::from_static("x-grpc-web"),
50    http::header::CONTENT_TYPE,
51    HeaderName::from_static("x-user-agent"),
52    HeaderName::from_static("grpc-timeout"),
53    HeaderName::from_static("x-client-id"),
54    HeaderName::from_static("traceparent"),
55    HeaderName::from_static("tracestate"),
56];
57
58pub fn grpc_service(db_url: &str, config_manager: Arc<ServerConfigManager>) -> Router {
59    use std::num::NonZeroUsize;
60    let shared_pool_config =
61        AsyncDieselConnectionManager::<diesel_async::AsyncPgConnection>::new(db_url);
62
63    let library_pool_config =
64        AsyncDieselConnectionManager::<diesel_async::AsyncPgConnection>::new(db_url);
65
66    let num_cpus: usize = std::thread::available_parallelism()
67        .unwrap_or(NonZeroUsize::new(2_usize).unwrap())
68        .into();
69
70    // shared pool used for service endpoints w/ light usage
71    let shared_pool = Arc::new(
72        deadpool::managed::Pool::builder(shared_pool_config)
73            .max_size(num_cpus * 3)
74            .build()
75            .expect("Could not create pool"),
76    );
77
78    // The library service triggers jobs w/ many DB ops -- so we give it its own pool
79    // so as to not starve the other services
80    let library_pool = Arc::new(
81        deadpool::managed::Pool::builder(library_pool_config)
82            .max_size(num_cpus)
83            .build()
84            .expect("Could not create pool"),
85    );
86
87    let igdb_client = Arc::new(IGDBProvider::new(config_manager.clone()));
88    let steam_web_api_client = Arc::new(SteamWebApiProvider::new(config_manager.clone()));
89
90    let _retrom_dirs = RetromDirs::new();
91    let media_cache = Arc::new(MediaCache::new(config_manager.clone()));
92
93    let job_manager = Arc::new(JobManager::new());
94
95    let reflection_service = tonic_reflection::server::Builder::configure()
96        .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
97        .build_v1()
98        .unwrap();
99
100    let library_service = LibraryServiceServer::new(LibraryServiceHandlers::new(
101        library_pool.clone(),
102        igdb_client.clone(),
103        steam_web_api_client.clone(),
104        job_manager.clone(),
105        config_manager.clone(),
106    ));
107
108    let metadata_service = MetadataServiceServer::new(MetadataServiceHandlers::new(
109        shared_pool.clone(),
110        igdb_client.clone(),
111        steam_web_api_client.clone(),
112        media_cache.clone(),
113        job_manager.clone(),
114        config_manager.clone(),
115    ));
116
117    let game_service = GameServiceServer::new(GameServiceHandlers::new(shared_pool.clone()));
118    let platform_service =
119        PlatformServiceServer::new(PlatformServiceHandlers::new(shared_pool.clone()));
120
121    let client_service =
122        ClientServiceServer::new(clients::ClientServiceHandlers::new(shared_pool.clone()));
123
124    let server_service = ServerServiceServer::new(server::ServerServiceHandlers {
125        config: config_manager.clone(),
126    });
127
128    let emulator_service =
129        EmulatorServiceServer::new(EmulatorServiceHandlers::new(shared_pool.clone()));
130
131    let job_service = JobServiceServer::new(JobServiceHandlers::new(job_manager.clone()));
132    let file_explorer_service = FileExplorerServiceServer::new(FileExplorerServiceHandlers::new());
133
134    let saves_service = SavesServiceServer::new(SavesServiceHandlers::new(
135        shared_pool.clone(),
136        config_manager.clone(),
137    ));
138
139    let mut routes_builder = tonic::service::Routes::builder();
140
141    routes_builder
142        .add_service(reflection_service)
143        .add_service(library_service)
144        .add_service(game_service)
145        .add_service(platform_service)
146        .add_service(metadata_service)
147        .add_service(client_service)
148        .add_service(server_service)
149        .add_service(emulator_service)
150        .add_service(job_service)
151        .add_service(file_explorer_service)
152        .add_service(saves_service);
153
154    routes_builder
155        .routes()
156        .into_axum_router()
157        .layer(tonic_web::GrpcWebLayer::new())
158        .layer(
159            tower_http::trace::TraceLayer::new_for_grpc()
160                .make_span_with(GrpcOnRequestSpan::default())
161                .on_response(GrpcOnResponseSpanHandler::default()),
162        )
163        .layer(
164            CorsLayer::new()
165                .allow_origin(AllowOrigin::mirror_request())
166                .allow_credentials(true)
167                .expose_headers(DEFAULT_EXPOSED_HEADERS)
168                .allow_headers(DEFAULT_ALLOW_HEADERS)
169                .max_age(DEFAULT_MAX_AGE),
170        )
171}