cat_gateway/service/api/
mod.rs1use std::net::IpAddr;
6
7use config::ConfigApi;
8use documents::DocumentApi;
9use gethostname::gethostname;
10use health::HealthApi;
11use local_ip_address::list_afinet_netifas;
12use poem_openapi::{ContactObject, LicenseObject, OpenApiService, ServerObject};
13
14use self::cardano::CardanoApi;
15use crate::settings::Settings;
16
17pub(crate) mod cardano;
18mod config;
19mod documents;
20pub(crate) mod health;
21
22const API_TITLE: &str = "Catalyst Gateway";
24
25const API_VERSION: &str = "0.8.0";
27
28fn get_api_contact() -> ContactObject {
30 ContactObject::new()
31 .name("Project Catalyst Team")
32 .email("contact@projectcatalyst.io")
33 .url("https://projectcatalyst.io")
34}
35
36const API_DESCRIPTION: &str = "# Catalyst Gateway API.
38
39The Catalyst Gateway API provides realtime data for all prior, current and future Catalyst Voices voting events.
40
41⚠️ Warning: This API is currently unstable and may change at any time without prior notice. Backwards compatibility is not guaranteed, and future versions may introduce breaking changes. It is intended for experimental or internal use only. Use at your own risk.
42";
43
44fn get_api_license() -> LicenseObject {
46 LicenseObject::new("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0")
47}
48
49const TERMS_OF_SERVICE: &str =
51 "https://github.com/input-output-hk/catalyst-voices/blob/main/CODE_OF_CONDUCT.md";
52
53pub(crate) fn mk_api() -> OpenApiService<(HealthApi, CardanoApi, ConfigApi, DocumentApi), ()> {
55 let mut service = OpenApiService::new(
56 (
57 HealthApi,
58 (
59 cardano::rbac::Api,
60 cardano::staking::Api,
61 cardano::cip36::Api,
62 ),
63 ConfigApi,
64 DocumentApi,
65 ),
66 API_TITLE,
67 API_VERSION,
68 )
69 .contact(get_api_contact())
70 .description(API_DESCRIPTION)
71 .license(get_api_license())
72 .terms_of_service(TERMS_OF_SERVICE)
73 .url_prefix(Settings::api_url_prefix());
74
75 let hosts = Settings::api_host_names();
76
77 if hosts.is_empty() {
78 service = set_localhost_addresses(service);
79 } else {
80 for host in hosts {
81 service = service.server(
82 ServerObject::new(host).description("Server host staging/production location."),
83 );
84 }
85 }
86
87 if let Some(name) = Settings::server_name() {
89 service = service.server(ServerObject::new(name).description("Server at server name."));
90 }
91 service
92}
93
94fn set_localhost_addresses<T>(mut service: OpenApiService<T, ()>) -> OpenApiService<T, ()> {
96 let port = Settings::bound_address().port();
97
98 if let Ok(hostname) = gethostname().into_string() {
100 let hostname_address = format!("http://{hostname}:{port}",);
101 service = service
102 .server(ServerObject::new(hostname_address).description("Server at localhost name."));
103 }
104
105 if let Ok(network_interfaces) = list_afinet_netifas() {
107 for (name, ip) in &network_interfaces {
108 if *name == "en0" {
109 let (address, desc) = match ip {
110 IpAddr::V4(_) => {
111 (
112 format!("http://{ip}:{port}"),
113 "Server at local IPv4 address.",
114 )
115 },
116 IpAddr::V6(_) => {
117 (
118 format!("http://[{ip}]:{port}"),
119 "Server at local IPv6 address.",
120 )
121 },
122 };
123 service = service.server(ServerObject::new(address).description(desc));
124 }
125 }
126 }
127 service
128}