1pub mod docker {
3 use std::path::{Path, PathBuf};
4
5 pub const COMPOSE_FILE_NAME: &str = "docker-compose.yml";
7
8 pub const DOCKER_DIR_NAME: &str = "docker";
10
11 pub const ENV_FILE_NAME: &str = ".env";
13
14 pub const IMAGES_DIR_NAME: &str = "images";
16
17 pub const DATA_DIR_NAME: &str = "data";
19
20 pub const APP_DIR_NAME: &str = "app";
22
23 pub const CONFIG_DIR_NAME: &str = "config";
25
26 pub const UPLOAD_DIR_NAME: &str = "upload";
28
29 pub const BACKUPS_DIR_NAME: &str = "backups";
31
32 pub const LOGS_DIR_NAME: &str = "logs";
34
35 pub mod data_dirs {
37 pub const MYSQL_DATA_DIR: &str = "data/mysql";
39
40 pub const REDIS_DATA_DIR: &str = "data/redis";
42
43 pub const MILVUS_DATA_DIR: &str = "data/milvus";
45
46 pub const MILVUS_DATA_STORAGE_DIR: &str = "data/milvus/data";
48
49 pub const MILVUS_ETCD_DATA_DIR: &str = "data/milvus/etcd";
51 }
52
53 pub mod log_dirs {
55 pub const AGENT_LOG_DIR: &str = "logs/agent";
57
58 pub const MYSQL_LOG_DIR: &str = "logs/mysql";
60
61 pub const REDIS_LOG_DIR: &str = "logs/redis";
63
64 pub const MILVUS_LOG_DIR: &str = "logs/milvus";
66 }
67
68 pub mod ports {
70 pub const DEFAULT_FRONTEND_PORT: u16 = 80;
72
73 pub const DEFAULT_BACKEND_PORT: u16 = 8080;
75
76 pub const DEFAULT_BACKEND_DEBUG_PORT: u16 = 5005;
78
79 pub const DEFAULT_MYSQL_PORT: u16 = 3306;
81
82 pub const DEFAULT_REDIS_PORT: u16 = 6379;
84
85 pub const DEFAULT_MILVUS_PORT: u16 = 19530;
87
88 pub const DEFAULT_MILVUS_MANAGEMENT_PORT: u16 = 9091;
90
91 pub const DEFAULT_ETCD_PORT: u16 = 2379;
93
94 pub const DEFAULT_MINIO_API_PORT: u16 = 9000;
96
97 pub const DEFAULT_MINIO_CONSOLE_PORT: u16 = 9001;
99
100 pub const DEFAULT_LOG_PLATFORM_PORT: u16 = 8097;
102
103 pub const DEFAULT_QUICKWIT_PORT: u16 = 7280;
105
106 pub const DEFAULT_QUICKWIT_ADMIN_PORT: u16 = 7281;
108
109 pub const DEFAULT_VIDEO_ANALYSIS_MASTER_PORT: u16 = 8989;
111
112 pub const DEFAULT_MCP_PROXY_PORT: u16 = 8020;
114 }
115
116 pub mod env_vars {
118 pub const FRONTEND_HOST_PORT: &str = "FRONTEND_HOST_PORT";
120
121 pub const APP_PORT: &str = "APP_PORT";
123
124 pub const APP_DEBUG_PORT: &str = "APP_DEBUG_PORT";
126
127 pub const MYSQL_PORT: &str = "MYSQL_PORT";
129
130 pub const REDIS_PORT: &str = "REDIS_PORT";
132
133 pub const MILVUS_PORT: &str = "MILVUS_PORT";
135
136 pub const LOG_PLATFORM_HOST_PORT: &str = "LOG_PLATFORM_HOST_PORT";
138
139 pub const VIDEO_ANALYSIS_MASTER_HOST_PORT: &str = "VIDEO_ANALYSIS_MASTER_HOST_PORT";
141
142 pub const MASTER_APP_PORT: &str = "MASTER_APP_PORT";
144 }
145
146 #[cfg(unix)]
150 pub const DOCKER_SOCKET_PATH: &str = "/var/run/docker.sock";
151
152 #[cfg(windows)]
153 pub const DOCKER_SOCKET_PATH: &str = r"\\.\pipe\docker_engine";
154
155 pub fn get_compose_file_path() -> PathBuf {
157 Path::new(".").join(DOCKER_DIR_NAME).join(COMPOSE_FILE_NAME)
158 }
159
160 pub fn get_docker_work_dir() -> PathBuf {
162 Path::new(".").join(DOCKER_DIR_NAME)
163 }
164
165 pub fn get_compose_file_path_str() -> String {
167 get_compose_file_path().to_string_lossy().to_string()
168 }
169
170 pub fn get_env_file_path() -> PathBuf {
172 Path::new(".").join(DOCKER_DIR_NAME).join(ENV_FILE_NAME)
173 }
174
175 pub fn get_env_file_path_str() -> String {
177 get_env_file_path().to_string_lossy().to_string()
178 }
179
180 pub fn get_images_dir_path() -> PathBuf {
182 Path::new(".").join(DOCKER_DIR_NAME).join(IMAGES_DIR_NAME)
183 }
184
185 pub fn get_data_dir_path() -> PathBuf {
187 Path::new(".").join(DOCKER_DIR_NAME).join(DATA_DIR_NAME)
188 }
189
190 pub fn get_app_dir_path() -> PathBuf {
192 Path::new(".").join(DOCKER_DIR_NAME).join(APP_DIR_NAME)
193 }
194
195 pub fn get_config_dir_path() -> PathBuf {
197 Path::new(".").join(DOCKER_DIR_NAME).join(CONFIG_DIR_NAME)
198 }
199
200 pub fn get_upload_dir_path() -> PathBuf {
202 Path::new(".").join(DOCKER_DIR_NAME).join(UPLOAD_DIR_NAME)
203 }
204
205 pub fn get_backups_dir_path() -> PathBuf {
207 Path::new(".").join(DOCKER_DIR_NAME).join(BACKUPS_DIR_NAME)
208 }
209
210 pub fn get_logs_dir_path() -> PathBuf {
212 Path::new(".").join(DOCKER_DIR_NAME).join(LOGS_DIR_NAME)
213 }
214
215 pub fn get_all_required_directories() -> Vec<&'static str> {
217 vec![
218 DATA_DIR_NAME,
219 APP_DIR_NAME,
220 data_dirs::MYSQL_DATA_DIR,
221 data_dirs::REDIS_DATA_DIR,
222 data_dirs::MILVUS_DATA_DIR,
223 data_dirs::MILVUS_DATA_STORAGE_DIR,
224 data_dirs::MILVUS_ETCD_DATA_DIR,
225 LOGS_DIR_NAME,
226 log_dirs::AGENT_LOG_DIR,
227 log_dirs::MYSQL_LOG_DIR,
228 log_dirs::REDIS_LOG_DIR,
229 log_dirs::MILVUS_LOG_DIR,
230 UPLOAD_DIR_NAME,
231 CONFIG_DIR_NAME,
232 BACKUPS_DIR_NAME,
233 ]
234 }
235
236 pub const EXCLUDE_DIRS: [&str; 8] = [
247 "upload",
248 "project_workspace",
249 "computer-project-workspace",
250 "project_zips",
251 "project_nginx",
252 "project_init",
253 "uv_cache",
254 "data",
255 ];
256}
257
258pub mod api {
260 use crate::environment::Environment;
261 use url::Url;
262
263 pub const NUWAX_API_BASE_URL_ENV: &str = "NUWAX_API_BASE_URL";
265
266 pub const NUWAX_API_DOCKER_VERSION_URL_ENV: &str = "NUWAX_API_DOCKER_VERSION_URL";
268
269 const PRODUCTION_BASE_URL: &str = "https://api-version.nuwax.com";
271
272 const TESTING_BASE_URL: &str = "http://192.168.32.226:3000";
274
275 fn is_valid_url(url: &str) -> bool {
277 Url::parse(url).is_ok_and(|parsed_url| {
278 matches!(parsed_url.scheme(), "http" | "https")
280 })
281 }
282
283 pub fn get_base_url() -> String {
302 if let Ok(custom_url) = std::env::var(NUWAX_API_BASE_URL_ENV) {
304 if is_valid_url(&custom_url) {
305 tracing::info!("Using custom API server: {}", custom_url);
306 return custom_url;
307 } else {
308 tracing::warn!(
309 "Invalid NUWAX_API_BASE_URL: '{}'. Expected to start with http:// or https://. Falling back to environment mode.",
310 custom_url
311 );
312 }
313 }
314
315 match Environment::from_env() {
317 Environment::Test => TESTING_BASE_URL.to_string(),
318 Environment::Production => PRODUCTION_BASE_URL.to_string(),
319 }
320 }
321
322 pub fn get_base_url_dynamic() -> String {
326 get_base_url()
327 }
328
329 pub const fn get_production_base_url() -> &'static str {
331 PRODUCTION_BASE_URL
332 }
333
334 pub const fn get_testing_base_url() -> &'static str {
336 TESTING_BASE_URL
337 }
338
339 pub const VERSION_PREFIX: &str = "/api/v1";
341
342 pub mod endpoints {
344 pub const CLIENT_REGISTER: &str = "/api/v1/clients/register";
346
347 pub const ANNOUNCEMENTS: &str = "/api/v1/clients/announcements";
349
350 pub const DOCKER_CHECK_VERSION: &str = "/api/v1/docker/checkVersion";
352
353 pub const DOCKER_UPDATE_VERSION_LIST: &str = "/api/v1/docker/updateVersionList";
355
356 pub const DOCKER_UPGRADE_VERSION_LATEST: &str =
358 "/api/v1/docker/upgrade/versions/latest.json";
359
360 pub const DOCKER_VERSION_OSS_PROD: &str = "https://nuwa-packages.oss-rg-china-mainland.aliyuncs.com/docker-version/prod/latest.json";
362
363 pub const DOCKER_VERSION_OSS_BETA: &str = "https://nuwa-packages.oss-rg-china-mainland.aliyuncs.com/docker-version/beta/latest.json";
365
366 pub const DOCKER_DOWNLOAD_FULL: &str =
368 "/api/v1/clients/downloads/docker/services/full/latest";
369
370 pub const CLIENT_SELF_UPGRADE_HISTORY: &str = "/api/v1/clients/self-upgrade-history";
372
373 pub const SERVICE_UPGRADE_HISTORY: &str =
375 "/api/v1/clients/services/{service_name}/upgrade-history";
376
377 pub const TELEMETRY: &str = "/api/v1/clients/telemetry";
379
380 pub const OPENAPI_DOCS: &str = "/api-docs/openapi.json";
382 }
383
384 pub mod http {
386 pub const DEFAULT_TIMEOUT: u64 = 30;
388
389 pub const DEFAULT_RETRY_COUNT: u8 = 3;
391
392 pub const USER_AGENT: &str = "nuwax-cli/1.0";
394 }
395
396 #[cfg(test)]
397 mod tests {
398 use super::*;
399
400 #[test]
401 fn test_get_base_url_production_default() {
402 unsafe {
403 std::env::remove_var(NUWAX_API_BASE_URL_ENV);
404 std::env::remove_var("NUWAX_CLI_ENV");
405 }
406 assert_eq!(get_base_url(), PRODUCTION_BASE_URL);
407 }
408
409 #[test]
410 fn test_get_base_url_testing_env() {
411 unsafe {
412 std::env::remove_var(NUWAX_API_BASE_URL_ENV);
413 std::env::set_var("NUWAX_CLI_ENV", "testing");
414 }
415 assert_eq!(get_base_url(), TESTING_BASE_URL);
416 unsafe {
417 std::env::remove_var("NUWAX_CLI_ENV");
418 }
419 }
420
421 #[test]
422 fn test_is_valid_url() {
423 assert!(is_valid_url("http://example.com"));
424 assert!(is_valid_url("https://example.com"));
425 assert!(is_valid_url("http://localhost:8080"));
426 assert!(is_valid_url("https://192.168.1.1:3000"));
427 assert!(!is_valid_url("ftp://example.com"));
428 assert!(!is_valid_url("example.com"));
429 assert!(!is_valid_url(""));
430 }
431 }
432}
433
434pub mod backup {
436 use std::path::{Path, PathBuf};
437
438 pub const DATA_DIR_NAME: &str = "data";
440
441 pub const BACKUP_DIR_NAME: &str = "backups";
443
444 pub const BACKUP_PREFIX: &str = "backup_";
446
447 pub const BACKUP_EXTENSION: &str = ".zip";
449
450 pub const MIN_ZIP_FILE_SIZE: u64 = 100;
452
453 pub fn get_backup_dir() -> PathBuf {
455 Path::new(".").join(DATA_DIR_NAME).join(BACKUP_DIR_NAME)
456 }
457
458 pub fn get_default_storage_dir() -> PathBuf {
460 Path::new(".").join(BACKUP_DIR_NAME)
461 }
462}
463
464pub mod upgrade {
466 use std::path::{Path, PathBuf};
467
468 pub const DATA_DIR_NAME: &str = "data";
470
471 pub const DOWNLOAD_DIR_NAME: &str = "downloads";
473
474 pub const TEMP_DIR_NAME: &str = "temp";
476
477 pub const DEFAULT_UPDATE_PACKAGE: &str = "update.zip";
479
480 pub fn get_download_dir() -> PathBuf {
482 Path::new(".").join(DATA_DIR_NAME).join(DOWNLOAD_DIR_NAME)
483 }
484
485 pub fn get_temp_extract_dir() -> PathBuf {
487 Path::new(".").join(DATA_DIR_NAME).join(TEMP_DIR_NAME)
488 }
489}
490
491pub mod file_format {
493 pub const ZIP_EXTENSION: &str = ".zip";
495
496 pub const TOML_EXTENSION: &str = ".toml";
498
499 pub const DB_EXTENSION: &str = ".db";
501
502 pub const ZIP_MAGIC_LOCAL_HEADER: [u8; 4] = [0x50, 0x4B, 0x03, 0x04];
504
505 pub const ZIP_MAGIC_CENTRAL_DIR_END: [u8; 4] = [0x50, 0x4B, 0x05, 0x06];
507
508 pub const ZIP_MAGIC_DATA_DESCRIPTOR: [u8; 4] = [0x50, 0x4B, 0x07, 0x08];
510
511 pub const ZIP_MAGIC_PK_PREFIX: [u8; 2] = [0x50, 0x4B];
513}
514
515pub mod timeout {
517 pub const SERVICE_STOP_TIMEOUT: u64 = 30;
519
520 pub const SERVICE_START_TIMEOUT: u64 = 60;
522
523 pub const DEPLOY_START_TIMEOUT: u64 = 90;
525
526 pub const SERVICE_CHECK_INTERVAL: u64 = 2;
528
529 pub const HEALTH_CHECK_TIMEOUT: u64 = 180;
531
532 pub const HEALTH_CHECK_INTERVAL: u64 = 5;
534
535 pub const RESTART_INTERVAL: u64 = 2;
537
538 pub const SERVICE_VERIFY_WAIT: u64 = 5;
540}
541
542pub mod sql {
544 pub const DEFAULT_RETRY_COUNT: u8 = 3;
546
547 pub const DEFAULT_MYSQL_CONTAINER_PORT: u16 = 13306;
549
550 pub const MYSQL_READY_TIMEOUT: u64 = 60;
552
553 pub const OTHER_SERVICES_TIMEOUT: u64 = 120;
555
556 pub const TEMP_SQL_DIR: &str = "temp_sql";
558
559 pub const OLD_SQL_FILE: &str = "init_mysql_old.sql";
561
562 pub const NEW_SQL_FILE: &str = "init_mysql_new.sql";
564
565 pub const DIFF_SQL_FILE: &str = "upgrade_diff.sql";
567
568 pub const CURRENT_SQL_PATH: &str = "docker/config/init_mysql.sql";
570
571 pub const CRITICAL_UPGRADE_FILES: &[&str] = &[
574 "config/init_mysql.sql",
575 ];
579
580 pub const MAX_CLEANUP_ATTEMPTS: usize = 3;
582}
583
584pub mod network {
586 pub const LOCALHOST_IPV4: &str = "127.0.0.1";
588
589 pub const LOCALHOST_IPV6: &str = "::1";
591
592 pub const ALL_INTERFACES: &str = "0.0.0.0";
594
595 pub const PORT_MAPPING_EXAMPLES: [&str; 3] = ["8080:80", "127.0.0.1:8080:80", "8080:80/tcp"];
597}
598
599pub mod logging {
601 use std::path::{Path, PathBuf};
602
603 pub const DEFAULT_LOG_LEVEL: &str = "info";
605
606 pub const DATA_DIR_NAME: &str = "data";
608
609 pub const LOG_DIR_NAME: &str = "logs";
611
612 pub fn get_log_dir() -> PathBuf {
614 Path::new(".").join(DATA_DIR_NAME).join(LOG_DIR_NAME)
615 }
616}
617
618pub mod cron {
620 pub const DEFAULT_BACKUP_CRON: &str = "0 2 * * *";
622
623 pub const CRON_FIELDS_COUNT: usize = 5;
625}
626
627pub mod config {
629 use std::path::{Path, PathBuf};
630
631 pub const DATA_DIR_NAME: &str = "data";
633
634 pub const CONFIG_FILE_NAME: &str = "config.toml";
636
637 pub const DATABASE_FILE_NAME: &str = "duck_client.db";
639
640 pub const CACHE_DIR_NAME: &str = "cacheDuckData";
642
643 pub const DOWNLOAD_DIR_NAME: &str = "download";
645
646 pub fn get_config_file_path() -> PathBuf {
648 Path::new(".").join(DATA_DIR_NAME).join(CONFIG_FILE_NAME)
649 }
650
651 pub fn get_config_file_path_for_env() -> PathBuf {
657 let config_file_name = match crate::environment::Environment::from_env() {
658 crate::environment::Environment::Test => "config-test.toml",
659 crate::environment::Environment::Production => CONFIG_FILE_NAME,
660 };
661 Path::new(".").join(DATA_DIR_NAME).join(config_file_name)
662 }
663
664 pub fn get_config_file_name_for_env() -> &'static str {
666 match crate::environment::Environment::from_env() {
667 crate::environment::Environment::Test => "config-test.toml",
668 crate::environment::Environment::Production => CONFIG_FILE_NAME,
669 }
670 }
671
672 pub fn get_database_path() -> PathBuf {
674 Path::new(".").join(DATA_DIR_NAME).join(DATABASE_FILE_NAME)
675 }
676
677 pub fn get_database_path_for_env() -> PathBuf {
683 let db_file_name = match crate::environment::Environment::from_env() {
684 crate::environment::Environment::Test => "duck_client_test.db",
685 crate::environment::Environment::Production => DATABASE_FILE_NAME,
686 };
687 Path::new(".").join(DATA_DIR_NAME).join(db_file_name)
688 }
689
690 pub fn get_default_cache_dir() -> PathBuf {
692 Path::new(".").join(CACHE_DIR_NAME)
693 }
694
695 pub fn get_default_download_dir() -> PathBuf {
697 get_default_cache_dir().join(DOWNLOAD_DIR_NAME)
698 }
699}
700
701pub mod version {
703 pub mod version_info {
705 pub const CORE_VERSION: &str = env!("CARGO_PKG_VERSION");
707
708 pub const DEFAULT_DOCKER_SERVICE_VERSION: &str = "0.0.1";
710
711 pub const MIN_DOCKER_VERSION: &str = "20.10.0";
713
714 pub const MIN_COMPOSE_VERSION: &str = "2.0.0";
716
717 pub const API_VERSION: &str = "v1";
719
720 pub const CONFIG_FORMAT_VERSION: &str = "1.0";
722
723 pub const DATABASE_SCHEMA_VERSION: &str = "1.0";
725 }
726}
727
728pub mod updates {
730 pub const DEFAULT_CHECK_FREQUENCY: &str = "daily";
732}