1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::Path;
6
7use super::auth::AuthConfig;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
12pub struct HttpValidationConfig {
13 pub mode: String,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, Default)]
19#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
20pub struct HttpCorsConfig {
21 pub enabled: bool,
23 #[serde(default)]
25 pub allowed_origins: Vec<String>,
26 #[serde(default)]
28 pub allowed_methods: Vec<String>,
29 #[serde(default)]
31 pub allowed_headers: Vec<String>,
32 #[serde(default = "default_cors_allow_credentials")]
35 pub allow_credentials: bool,
36}
37
38fn default_cors_allow_credentials() -> bool {
39 false
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
45#[serde(default)]
46pub struct HttpConfig {
47 pub enabled: bool,
49 pub port: u16,
51 pub host: String,
53 pub openapi_spec: Option<String>,
55 pub cors: Option<HttpCorsConfig>,
57 pub request_timeout_secs: u64,
59 pub validation: Option<HttpValidationConfig>,
61 pub aggregate_validation_errors: bool,
63 pub validate_responses: bool,
65 pub response_template_expand: bool,
67 pub validation_status: Option<u16>,
69 pub validation_overrides: HashMap<String, String>,
71 pub skip_admin_validation: bool,
73 pub auth: Option<AuthConfig>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub tls: Option<HttpTlsConfig>,
78}
79
80impl Default for HttpConfig {
81 fn default() -> Self {
82 Self {
83 enabled: true,
84 port: 3000,
85 host: "0.0.0.0".to_string(),
86 openapi_spec: None,
87 cors: Some(HttpCorsConfig {
88 enabled: true,
89 allowed_origins: vec!["*".to_string()],
90 allowed_methods: vec![
91 "GET".to_string(),
92 "POST".to_string(),
93 "PUT".to_string(),
94 "DELETE".to_string(),
95 "PATCH".to_string(),
96 "OPTIONS".to_string(),
97 ],
98 allowed_headers: vec!["content-type".to_string(), "authorization".to_string()],
99 allow_credentials: false, }),
101 request_timeout_secs: 30,
102 validation: Some(HttpValidationConfig {
103 mode: "enforce".to_string(),
104 }),
105 aggregate_validation_errors: true,
106 validate_responses: false,
107 response_template_expand: false,
108 validation_status: None,
109 validation_overrides: HashMap::new(),
110 skip_admin_validation: true,
111 auth: None,
112 tls: None,
113 }
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
120pub struct HttpTlsConfig {
121 pub enabled: bool,
123 pub cert_file: String,
125 pub key_file: String,
127 #[serde(skip_serializing_if = "Option::is_none")]
129 pub ca_file: Option<String>,
130 #[serde(default = "default_tls_min_version")]
132 pub min_version: String,
133 #[serde(default, skip_serializing_if = "Vec::is_empty")]
135 pub cipher_suites: Vec<String>,
136 #[serde(default)]
138 pub require_client_cert: bool,
139 #[serde(default = "default_mtls_mode")]
141 pub mtls_mode: String,
142}
143
144fn default_mtls_mode() -> String {
145 "off".to_string()
146}
147
148fn default_tls_min_version() -> String {
149 "1.2".to_string()
150}
151
152impl Default for HttpTlsConfig {
153 fn default() -> Self {
154 Self {
155 enabled: true,
156 cert_file: String::new(),
157 key_file: String::new(),
158 ca_file: None,
159 min_version: "1.2".to_string(),
160 cipher_suites: Vec::new(),
161 require_client_cert: false,
162 mtls_mode: "off".to_string(),
163 }
164 }
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
170#[serde(default)]
171pub struct WebSocketConfig {
172 pub enabled: bool,
174 pub port: u16,
176 pub host: String,
178 pub replay_file: Option<String>,
180 pub connection_timeout_secs: u64,
182}
183
184impl Default for WebSocketConfig {
185 fn default() -> Self {
186 Self {
187 enabled: true,
188 port: 3001,
189 host: "0.0.0.0".to_string(),
190 replay_file: None,
191 connection_timeout_secs: 300,
192 }
193 }
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
198#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
199#[serde(default)]
200pub struct GrpcConfig {
201 pub enabled: bool,
203 pub port: u16,
205 pub host: String,
207 pub proto_dir: Option<String>,
209 pub tls: Option<TlsConfig>,
211 #[serde(default, skip_serializing_if = "Vec::is_empty")]
214 pub overrides: Vec<GrpcOverride>,
215}
216
217impl Default for GrpcConfig {
218 fn default() -> Self {
219 Self {
220 enabled: true,
221 port: 50051,
222 host: "0.0.0.0".to_string(),
223 proto_dir: None,
224 tls: None,
225 overrides: Vec::new(),
226 }
227 }
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
238#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
239pub struct GrpcOverride {
240 pub service: String,
243 pub method: String,
245 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
249 pub r#match: HashMap<String, String>,
250 pub response: GrpcOverrideResponse,
252}
253
254#[derive(Debug, Clone, Default, Serialize, Deserialize)]
259#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
260#[serde(default)]
261pub struct GrpcOverrideResponse {
262 #[serde(default, skip_serializing_if = "Option::is_none")]
265 pub status: Option<String>,
266 #[serde(default, skip_serializing_if = "Option::is_none")]
268 pub message: Option<String>,
269 #[serde(default, skip_serializing_if = "Option::is_none")]
272 pub body: Option<serde_json::Value>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
278#[serde(default)]
279pub struct GraphQLConfig {
280 pub enabled: bool,
282 pub port: u16,
284 pub host: String,
286 pub schema_path: Option<String>,
288 pub handlers_dir: Option<String>,
290 pub playground_enabled: bool,
292 pub upstream_url: Option<String>,
294 pub introspection_enabled: bool,
296}
297
298impl Default for GraphQLConfig {
299 fn default() -> Self {
300 Self {
301 enabled: true,
302 port: 4000,
303 host: "0.0.0.0".to_string(),
304 schema_path: None,
305 handlers_dir: None,
306 playground_enabled: true,
307 upstream_url: None,
308 introspection_enabled: true,
309 }
310 }
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
316pub struct TlsConfig {
317 pub cert_path: String,
319 pub key_path: String,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
325#[serde(default)]
326#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
327pub struct MqttConfig {
328 pub enabled: bool,
330 pub port: u16,
332 pub host: String,
334 pub max_connections: usize,
336 pub max_packet_size: usize,
338 pub keep_alive_secs: u16,
340 pub fixtures_dir: Option<std::path::PathBuf>,
342 pub enable_retained_messages: bool,
344 pub max_retained_messages: usize,
346}
347
348impl Default for MqttConfig {
349 fn default() -> Self {
350 Self {
351 enabled: false,
352 port: 1883,
353 host: "0.0.0.0".to_string(),
354 max_connections: 1000,
355 max_packet_size: 268435456, keep_alive_secs: 60,
357 fixtures_dir: None,
358 enable_retained_messages: true,
359 max_retained_messages: 10000,
360 }
361 }
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
366#[serde(default)]
367#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
368pub struct SmtpConfig {
369 pub enabled: bool,
371 pub port: u16,
373 pub host: String,
375 pub hostname: String,
377 pub fixtures_dir: Option<std::path::PathBuf>,
379 pub timeout_secs: u64,
381 pub max_connections: usize,
383 pub enable_mailbox: bool,
385 pub max_mailbox_messages: usize,
387 pub enable_starttls: bool,
389 pub tls_cert_path: Option<std::path::PathBuf>,
391 pub tls_key_path: Option<std::path::PathBuf>,
393}
394
395impl Default for SmtpConfig {
396 fn default() -> Self {
397 Self {
398 enabled: false,
399 port: 1025,
400 host: "0.0.0.0".to_string(),
401 hostname: "mockforge-smtp".to_string(),
402 fixtures_dir: Some(std::path::PathBuf::from("./fixtures/smtp")),
403 timeout_secs: 300,
404 max_connections: 10,
405 enable_mailbox: true,
406 max_mailbox_messages: 1000,
407 enable_starttls: false,
408 tls_cert_path: None,
409 tls_key_path: None,
410 }
411 }
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
416#[serde(default)]
417#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
418pub struct FtpConfig {
419 pub enabled: bool,
421 pub port: u16,
423 pub host: String,
425 pub passive_ports: (u16, u16),
427 pub max_connections: usize,
429 pub timeout_secs: u64,
431 pub allow_anonymous: bool,
433 pub fixtures_dir: Option<std::path::PathBuf>,
435 pub virtual_root: std::path::PathBuf,
437}
438
439impl Default for FtpConfig {
440 fn default() -> Self {
441 Self {
442 enabled: false,
443 port: 2121,
444 host: "0.0.0.0".to_string(),
445 passive_ports: (50000, 51000),
446 max_connections: 100,
447 timeout_secs: 300,
448 allow_anonymous: true,
449 fixtures_dir: None,
450 virtual_root: std::path::PathBuf::from("/mockforge"),
451 }
452 }
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457#[serde(default)]
458#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
459pub struct KafkaConfig {
460 pub enabled: bool,
462 pub port: u16,
464 pub host: String,
466 pub broker_id: i32,
468 pub max_connections: usize,
470 pub log_retention_ms: i64,
472 pub log_segment_bytes: i64,
474 pub fixtures_dir: Option<std::path::PathBuf>,
476 pub auto_create_topics: bool,
478 pub default_partitions: i32,
480 pub default_replication_factor: i16,
482 pub advertised_host: Option<String>,
490 pub advertised_port: Option<u16>,
494 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
500 pub seed_messages: HashMap<String, Vec<KafkaSeedMessage>>,
501 #[serde(default, skip_serializing_if = "Vec::is_empty")]
507 pub faults: Vec<KafkaFault>,
508}
509
510impl Default for KafkaConfig {
511 fn default() -> Self {
512 Self {
513 enabled: false,
514 port: 9092, host: "0.0.0.0".to_string(),
516 broker_id: 1,
517 max_connections: 1000,
518 log_retention_ms: 604800000, log_segment_bytes: 1073741824, fixtures_dir: None,
521 auto_create_topics: true,
522 default_partitions: 3,
523 default_replication_factor: 1,
524 advertised_host: None,
525 advertised_port: None,
526 seed_messages: HashMap::new(),
527 faults: Vec::new(),
528 }
529 }
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
534#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
535pub struct KafkaFault {
536 pub topic: String,
538 #[serde(default, skip_serializing_if = "Option::is_none")]
541 pub partition: Option<i32>,
542 pub kind: KafkaFaultKind,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
547 pub delay_ms: Option<u64>,
548 #[serde(default, skip_serializing_if = "Option::is_none")]
551 pub probability: Option<f64>,
552}
553
554#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
560#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
561#[serde(rename_all = "snake_case")]
562pub enum KafkaFaultKind {
563 ProduceThrottle,
566 ProduceNotLeader,
569 OffsetOutOfRange,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize)]
578#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
579pub struct KafkaSeedMessage {
580 #[serde(default, skip_serializing_if = "Option::is_none")]
584 pub key: Option<String>,
585 pub value: String,
588 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
590 pub headers: HashMap<String, String>,
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize)]
595#[serde(default)]
596#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
597pub struct AmqpConfig {
598 pub enabled: bool,
600 pub port: u16,
602 pub host: String,
604 pub max_connections: usize,
606 pub max_channels_per_connection: u16,
608 pub frame_max: u32,
610 pub heartbeat_interval: u16,
612 pub fixtures_dir: Option<std::path::PathBuf>,
614 pub virtual_hosts: Vec<String>,
616 pub tls_enabled: bool,
618 pub tls_port: u16,
620 pub tls_cert_path: Option<std::path::PathBuf>,
622 pub tls_key_path: Option<std::path::PathBuf>,
624 pub tls_ca_path: Option<std::path::PathBuf>,
626 pub tls_client_auth: bool,
628}
629
630impl Default for AmqpConfig {
631 fn default() -> Self {
632 Self {
633 enabled: false,
634 port: 5672, host: "0.0.0.0".to_string(),
636 max_connections: 1000,
637 max_channels_per_connection: 100,
638 frame_max: 131072, heartbeat_interval: 60,
640 fixtures_dir: None,
641 virtual_hosts: vec!["/".to_string()],
642 tls_enabled: false,
643 tls_port: 5671, tls_cert_path: None,
645 tls_key_path: None,
646 tls_ca_path: None,
647 tls_client_auth: false,
648 }
649 }
650}
651
652#[derive(Debug, Clone, Serialize, Deserialize)]
654#[serde(default)]
655#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
656pub struct TcpConfig {
657 pub enabled: bool,
659 pub port: u16,
661 pub host: String,
663 pub max_connections: usize,
665 pub timeout_secs: u64,
667 pub fixtures_dir: Option<std::path::PathBuf>,
669 pub echo_mode: bool,
671 pub enable_tls: bool,
673 pub tls_cert_path: Option<std::path::PathBuf>,
675 pub tls_key_path: Option<std::path::PathBuf>,
677}
678
679impl Default for TcpConfig {
680 fn default() -> Self {
681 Self {
682 enabled: false,
683 port: 9999,
684 host: "0.0.0.0".to_string(),
685 max_connections: 1000,
686 timeout_secs: 300,
687 fixtures_dir: Some(std::path::PathBuf::from("./fixtures/tcp")),
688 echo_mode: true,
689 enable_tls: false,
690 tls_cert_path: None,
691 tls_key_path: None,
692 }
693 }
694}
695
696#[derive(Debug, Clone, Serialize, Deserialize)]
698#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
699#[serde(default)]
700pub struct AdminConfig {
701 pub enabled: bool,
703 pub port: u16,
705 pub host: String,
707 pub auth_required: bool,
709 pub username: Option<String>,
711 pub password: Option<String>,
713 pub mount_path: Option<String>,
715 pub api_enabled: bool,
717 pub prometheus_url: String,
719}
720
721impl Default for AdminConfig {
722 fn default() -> Self {
723 let default_host = if std::env::var("DOCKER_CONTAINER").is_ok()
726 || std::env::var("container").is_ok()
727 || Path::new("/.dockerenv").exists()
728 {
729 "0.0.0.0".to_string()
730 } else {
731 "127.0.0.1".to_string()
732 };
733
734 Self {
735 enabled: false,
736 port: 9080,
737 host: default_host,
738 auth_required: false,
739 username: None,
740 password: None,
741 mount_path: None,
742 api_enabled: true,
743 prometheus_url: "http://localhost:9090".to_string(),
744 }
745 }
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
750#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
751pub struct ProtocolConfig {
752 pub enabled: bool,
754}
755
756#[derive(Debug, Clone, Serialize, Deserialize)]
758#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
759pub struct ProtocolsConfig {
760 pub http: ProtocolConfig,
762 pub graphql: ProtocolConfig,
764 pub grpc: ProtocolConfig,
766 pub websocket: ProtocolConfig,
768 pub smtp: ProtocolConfig,
770 pub mqtt: ProtocolConfig,
772 pub ftp: ProtocolConfig,
774 pub kafka: ProtocolConfig,
776 pub rabbitmq: ProtocolConfig,
778 pub amqp: ProtocolConfig,
780 pub tcp: ProtocolConfig,
782}
783
784impl Default for ProtocolsConfig {
785 fn default() -> Self {
786 Self {
787 http: ProtocolConfig { enabled: true },
788 graphql: ProtocolConfig { enabled: true },
789 grpc: ProtocolConfig { enabled: true },
790 websocket: ProtocolConfig { enabled: true },
791 smtp: ProtocolConfig { enabled: false },
792 mqtt: ProtocolConfig { enabled: true },
793 ftp: ProtocolConfig { enabled: false },
794 kafka: ProtocolConfig { enabled: false },
795 rabbitmq: ProtocolConfig { enabled: false },
796 amqp: ProtocolConfig { enabled: false },
797 tcp: ProtocolConfig { enabled: false },
798 }
799 }
800}