forked from Txio-labs/txio
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rs
More file actions
174 lines (149 loc) · 6.17 KB
/
Copy pathmain.rs
File metadata and controls
174 lines (149 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use axum::{Router, http::HeaderValue, routing::get};
use dotenvy::{dotenv, from_path_override};
use std::{net::SocketAddr, path::PathBuf};
use tower_http::cors::{Any, CorsLayer};
use ::txio_api::{
api,
infra::db::{describe_connection_error, establish_connection},
model, repositories, services, utils,
utils::config::Config,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv().ok();
load_project_env();
// 1. Initialize Logging
utils::logger::init();
// 2. Load Config
tracing::info!("Loading configuration from environment...");
let config = Config::from_env().map_err(|e| {
tracing::error!(error = %e, "Failed to load configuration");
Box::new(e) as Box<dyn std::error::Error>
})?;
tracing::info!(
"Config loaded. MONGO_URI set={}, JWT_SECRET length={}, BREVO_API_KEY set={}",
!config.mongo_uri.trim().is_empty(),
config.jwt_secret.len(),
!config.brevo_api_key.trim().is_empty()
);
// 3. Connect to Database
tracing::info!("Connecting to MongoDB at {}...", config.mongo_uri);
let db_client = establish_connection(&config.mongo_uri).await.map_err(|e| {
let message = describe_connection_error(&config.mongo_uri, &e);
tracing::error!(error = %message, "Failed to connect to MongoDB");
Box::new(std::io::Error::other(message)) as Box<dyn std::error::Error>
})?;
tracing::info!("Connected to MongoDB at {}", config.mongo_uri);
// 4. Initialize Repositories
let user_repo = repositories::user_repository::UserRepository::new(&db_client);
let otp_repo = repositories::otp_repository::OTPRepository::new(&db_client);
let rpc_repo = repositories::rpc_repository::RpcRepository::new(&db_client);
let collection_repo =
repositories::collection_repository::CollectionRepository::new(&db_client);
let request_repo = repositories::request_repository::RequestRepository::new(&db_client);
let workspace_repo = repositories::workspace_repository::WorkspaceRepository::new(&db_client);
// 5. Initialize JWT Helper
let jwt_helper = utils::auth_jwt::JwtHelper::new(config.jwt_secret);
// 5.1 Initialize Support Services
let email_service = services::email_service::EmailService::new(config.brevo_api_key);
let otp_service = services::otp_service::OTPService::new(otp_repo.clone());
// Default Mainnet URL for SuiService (can be overridden dynamically)
let default_sui_url = model::user::SuiNetwork::Mainnet.url().to_string();
let sui_service = services::sui_service::SuiService::new(rpc_repo.clone(), default_sui_url);
// 6. Initialize Services (Dependency Injection)
let auth_service = services::auth_service::AuthService::new(
user_repo.clone(),
rpc_repo,
jwt_helper,
otp_service,
email_service,
);
let collection_service = services::collection_service::CollectionService::new(
collection_repo.clone(),
request_repo,
user_repo.clone(),
workspace_repo.clone(),
sui_service,
);
let workspace_service =
services::workspace_service::WorkspaceService::new(workspace_repo, collection_repo);
let terminal_service = services::terminal_service::TerminalService::new();
let ai_service = services::ai_service::AiService::from_env();
tracing::info!(
groq_key_count = ai_service.configured_key_count(),
groq_model = %ai_service.model(),
"Configured AI service"
);
let frontend_url =
std::env::var("FRONTEND_URL").unwrap_or_else(|_| "http://localhost:3000".to_string());
let frontend_origin = reqwest::Url::parse(&frontend_url)
.map(|url| url.origin().ascii_serialization())
.unwrap_or_else(|_| frontend_url.trim_end_matches('/').to_string());
let cors_origin = HeaderValue::from_str(&frontend_origin).map_err(|e| {
tracing::error!(
error = %e,
frontend_origin = %frontend_origin,
"Invalid frontend origin for CORS"
);
Box::new(e) as Box<dyn std::error::Error>
})?;
let allowed_origins = vec![
cors_origin,
HeaderValue::from_static("http://localhost:3000"),
HeaderValue::from_static("http://127.0.0.1:3000"),
];
let cors = CorsLayer::new()
.allow_origin(allowed_origins)
.allow_methods(Any)
.allow_headers(Any);
tracing::info!(
frontend_origin = %frontend_origin,
"Configured CORS for frontend access"
);
// 7. Build Router
let app = Router::new()
.route("/health", get(|| async { "txio Backend Operational" }))
.nest(
"/api/v1/auth",
api::routers::auth_router::router(auth_service),
)
.nest("/api/v1/ai", api::routers::ai_router::router(ai_service))
.nest(
"/api/v1/collections",
api::routers::collection_router::router(collection_service),
)
.nest(
"/api/v1/workspaces",
api::routers::workspace_router::router(workspace_service),
)
.nest(
"/api/v1/terminal",
api::routers::terminal_router::router(terminal_service),
)
.layer(cors);
// 8. Run Server
let port = std::env::var("PORT")
.unwrap_or_else(|_| "8000".to_string())
.parse::<u16>()
.unwrap_or(8000);
let addr = SocketAddr::from(([0, 0, 0, 0], port));
let listener = tokio::net::TcpListener::bind(addr).await.map_err(|e| {
tracing::error!(error = %e, %addr, "Failed to bind API server");
Box::new(std::io::Error::new(
e.kind(),
format!(
"Failed to bind API server to {}. Set PORT to override the default bind port. {}",
addr, e
),
)) as Box<dyn std::error::Error>
})?;
tracing::info!("Server listening on {}", addr);
axum::serve(listener, app).await?;
Ok(())
}
fn load_project_env() {
let manifest_env = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".env");
let workspace_env = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../.env");
from_path_override(&manifest_env).ok();
from_path_override(&workspace_env).ok();
}