Compare commits
7 Commits
workflows
...
1eeca606ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eeca606ec | ||
|
|
6ce8850ddb | ||
|
|
a9a08d2ef8 | ||
|
|
bbde3c1b60 | ||
|
|
e2d364722b | ||
|
|
da0ab29b2a | ||
| a1996adc45 |
16
.github/workflows/test.yaml
vendored
16
.github/workflows/test.yaml
vendored
@@ -45,11 +45,16 @@ jobs:
|
||||
- name: Restore frontend build cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/nxmesh-frontend/build
|
||||
path: apps/nxmesh-frontend/dist
|
||||
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
frontend-build-${{ runner.os }}-
|
||||
|
||||
- name: Add dummy fonrtend build artifact to prevent build failure
|
||||
run: |
|
||||
mkdir -p apps/nxmesh-frontend/dist
|
||||
touch apps/nxmesh-frontend/dist/dummy
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --all-features
|
||||
|
||||
@@ -74,11 +79,16 @@ jobs:
|
||||
- name: Restore frontend build cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/nxmesh-frontend/build
|
||||
path: apps/nxmesh-frontend/dist
|
||||
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
frontend-build-${{ runner.os }}-
|
||||
|
||||
- name: Add dummy fonrtend build artifact to prevent build failure
|
||||
run: |
|
||||
mkdir -p apps/nxmesh-frontend/dist
|
||||
touch apps/nxmesh-frontend/dist/dummy
|
||||
|
||||
- name: Run clippy
|
||||
run: cargo clippy --all-features
|
||||
|
||||
@@ -147,7 +157,7 @@ jobs:
|
||||
- name: Cache frontend build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: apps/nxmesh-frontend/build
|
||||
path: apps/nxmesh-frontend/dist
|
||||
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
frontend-build-${{ runner.os }}-
|
||||
|
||||
76
Cargo.lock
generated
76
Cargo.lock
generated
@@ -538,6 +538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"base64",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
@@ -586,6 +587,17 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@@ -2477,12 +2489,14 @@ dependencies = [
|
||||
"handlebars",
|
||||
"hex",
|
||||
"jsonwebtoken",
|
||||
"mime_guess",
|
||||
"mockall",
|
||||
"nxmesh-core",
|
||||
"nxmesh-migration",
|
||||
"nxmesh-proto",
|
||||
"rand 0.10.0",
|
||||
"rcgen",
|
||||
"rust-embed",
|
||||
"sea-orm",
|
||||
"sea-orm-migration",
|
||||
"serde",
|
||||
@@ -3338,6 +3352,40 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.117",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.21.3"
|
||||
@@ -3454,6 +3502,15 @@ version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.28"
|
||||
@@ -4876,6 +4933,16 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
@@ -5046,6 +5113,15 @@ dependencies = [
|
||||
"wasite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
|
||||
@@ -84,7 +84,12 @@ time = "0.3"
|
||||
|
||||
# Cert handling
|
||||
zip = { workspace = true }
|
||||
rust-embed = { version = "8.11.0", features = [] }
|
||||
mime_guess = "2.0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test.workspace = true
|
||||
mockall.workspace = true
|
||||
|
||||
[features]
|
||||
dev-tools = ["axum/macros"]
|
||||
|
||||
1
apps/nxmesh-master/frontend-dist
Symbolic link
1
apps/nxmesh-master/frontend-dist
Symbolic link
@@ -0,0 +1 @@
|
||||
../nxmesh-frontend/dist/
|
||||
@@ -6,6 +6,7 @@ use nxmesh_proto::{
|
||||
};
|
||||
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
|
||||
use tonic::transport::Server;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{db::entities::public_key_revocations, service::agent::AgentServerService};
|
||||
|
||||
@@ -69,7 +70,14 @@ impl AgentConnectorTrait for SshAgentConnector {
|
||||
.layer(ssh_interceptor)
|
||||
.add_service(agent_server_service);
|
||||
|
||||
router.serve(addr).await?;
|
||||
info!("SSH Agent gRPC server is listening on {}", addr);
|
||||
router
|
||||
.serve(addr)
|
||||
.await
|
||||
.inspect(|_| info!("SSH Agent gRPC server stopped gracefully."))
|
||||
.inspect_err(|e| {
|
||||
tracing::error!("SSH Agent gRPC server failed: {}", e);
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ mod cli;
|
||||
mod config;
|
||||
mod connector;
|
||||
mod db;
|
||||
mod routes;
|
||||
mod service;
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
69
apps/nxmesh-master/src/routes/frontend/mod.rs
Normal file
69
apps/nxmesh-master/src/routes/frontend/mod.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use axum::{Router, response::IntoResponse};
|
||||
use tracing::error;
|
||||
|
||||
// In development, build the frontend from the source directory, the soft link will handle the path resolution
|
||||
// In deployment, pre-build the frontend and replace the frontend-dist folder with the built assets, the rust-embed will handle the embedding and path resolution
|
||||
#[derive(rust_embed::Embed)]
|
||||
#[folder = "./frontend-dist/"]
|
||||
struct FrontendAssets;
|
||||
|
||||
const INDEX_HTML: &str = "index.html";
|
||||
|
||||
pub async fn get_router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/",
|
||||
axum::routing::get(get_fallback_handler)
|
||||
.head(get_fallback_handler)
|
||||
.options(get_fallback_handler),
|
||||
)
|
||||
.route(
|
||||
"/{*path}",
|
||||
axum::routing::get(get_file_handler)
|
||||
.head(get_file_handler)
|
||||
.options(get_file_handler),
|
||||
)
|
||||
//
|
||||
.fallback(get_fallback_handler().await)
|
||||
}
|
||||
|
||||
pub async fn get_fallback_handler() -> Result<axum::response::Html<Vec<u8>>, axum::http::StatusCode>
|
||||
{
|
||||
let index_html = get_index_html();
|
||||
match index_html {
|
||||
Some(html) => Ok(axum::response::Html(html)),
|
||||
None => Err(axum::http::StatusCode::NOT_FOUND),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_index_html() -> Option<Vec<u8>> {
|
||||
FrontendAssets::get(INDEX_HTML).map(|asset| asset.data.as_ref().to_owned())
|
||||
}
|
||||
|
||||
async fn get_file_handler(
|
||||
axum::extract::Path(path): axum::extract::Path<String>,
|
||||
) -> Result<axum::response::Response, axum::http::StatusCode> {
|
||||
let file_path = if path.is_empty() {
|
||||
INDEX_HTML.to_string()
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
match FrontendAssets::get(&file_path) {
|
||||
Some(asset) => {
|
||||
let content_type = mime_guess::from_path(&file_path).first_or_octet_stream();
|
||||
let response = axum::response::Response::builder()
|
||||
.header(axum::http::header::CONTENT_TYPE, content_type.as_ref())
|
||||
.body(asset.data.into_owned().into())
|
||||
.map_err(|e| {
|
||||
error!("Failed to build response for {}: {}", file_path, e);
|
||||
axum::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
Ok(response)
|
||||
}
|
||||
// return index.html for any file not found to support client-side routing in the frontend
|
||||
None => get_fallback_handler()
|
||||
.await
|
||||
.map(|html| html.into_response()),
|
||||
}
|
||||
}
|
||||
9
apps/nxmesh-master/src/routes/mod.rs
Normal file
9
apps/nxmesh-master/src/routes/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use axum::Router;
|
||||
|
||||
mod frontend;
|
||||
|
||||
pub async fn get_root_router() -> Router {
|
||||
Router::new()
|
||||
.merge(frontend::get_router().await)
|
||||
.fallback(frontend::get_fallback_handler().await)
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
use std::{net::ToSocketAddrs, sync::Arc};
|
||||
|
||||
use tracing::info;
|
||||
|
||||
use crate::{connector::agent::AgentConnectorTrait, service::certificate::CertificateService};
|
||||
|
||||
@@ -26,15 +28,38 @@ pub async fn start_master_server(
|
||||
println!("Certificate generated and stored successfully.");
|
||||
}
|
||||
|
||||
// Initialize agent connector
|
||||
let mut agent_connector = crate::connector::agent::AgentConnector::new(Box::new(
|
||||
crate::connector::agent::ssh::SshAgentConnector::new(settings.clone())?,
|
||||
));
|
||||
let ssh_connector = crate::connector::agent::ssh::SshAgentConnector::new(settings.clone())?;
|
||||
let cert_service_for_agent = cert_service.clone();
|
||||
let settings_for_agent = settings.clone();
|
||||
let connection_for_agent = db_connection.clone();
|
||||
|
||||
// Start the agent server
|
||||
agent_connector
|
||||
.start_server(&settings, cert_service, db_connection)
|
||||
.await?;
|
||||
tokio::spawn(async move {
|
||||
let mut connector = ssh_connector;
|
||||
tracing::info!("Starting agent server...");
|
||||
if let Err(e) = connector
|
||||
.start_server(
|
||||
&settings_for_agent,
|
||||
cert_service_for_agent,
|
||||
connection_for_agent,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!("Agent server failed: {}", e);
|
||||
} else {
|
||||
tracing::info!("Agent server stopped.");
|
||||
}
|
||||
});
|
||||
|
||||
let axum_router = crate::routes::get_root_router().await;
|
||||
|
||||
// Start the HTTP server
|
||||
let addr = format!("{}:{}", settings.server.bind_address, settings.server.port)
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or("Invalid bind address")?;
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
info!("Web/API server is listening on {}", addr);
|
||||
axum::serve(listener, axum_router).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user