Merge branch 'master' into feature/nginx-handler
All checks were successful
Test / get-ci-image (pull_request) Successful in 6s
Test / lint-frontend (pull_request) Successful in 24s
Test / test-frontend (pull_request) Successful in 25s
Verify / get-ci-image (pull_request) Successful in 5s
Test / frontend-build (pull_request) Successful in 40s
Test / lint-crates (pull_request) Successful in 4m58s
Test / test-crates (pull_request) Successful in 5m16s
Verify / verify-generated-db-entities (pull_request) Successful in 5m42s

This commit is contained in:
GW_MC
2026-04-25 06:43:58 +00:00
10 changed files with 560 additions and 19 deletions

View File

@@ -45,13 +45,35 @@ jobs:
- name: Restore frontend build cache - name: Restore frontend build cache
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: apps/nxmesh-frontend/build path: apps/nxmesh-frontend/dist
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }} key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
restore-keys: | restore-keys: |
frontend-build-${{ runner.os }}- frontend-build-${{ runner.os }}-
# TODO: uncomment until artifact hanlding fixed in gitea
# - name: Download frontend build artifact
# uses: actions/download-artifact@v4
# with:
# name: frontend-dist
# path: apps/nxmesh-frontend/dist
# - name: Copy frontend build to expected location
# run: |
# # unlink frontend-dist
# rm -f apps/nxmesh-master/frontend-dist || true
# rm -rf apps/nxmesh-master/frontend-dist || true
# cp -r apps/nxmesh-frontend/dist apps/nxmesh-master/frontend-dist
# ls -la apps/nxmesh-master/frontend-dist
- name: Create dummy build artifacts
run: |
rm -f apps/nxmesh-master/frontend-dist || true
mkdir -p apps/nxmesh-master/frontend-dist
echo "<html><body><h1>Dummy Build</h1></body></html>" > apps/nxmesh-master/frontend-dist/index.html
- name: Run tests - name: Run tests
run: cargo test --all-features run: cargo test --all-features -- --show-output
lint-crates: lint-crates:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -74,11 +96,31 @@ jobs:
- name: Restore frontend build cache - name: Restore frontend build cache
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: apps/nxmesh-frontend/build path: apps/nxmesh-frontend/dist
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }} key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
restore-keys: | restore-keys: |
frontend-build-${{ runner.os }}- frontend-build-${{ runner.os }}-
# TODO: uncomment until artifact hanlding fixed in gitea
# - name: Download frontend build artifact
# uses: actions/download-artifact@v4
# with:
# name: frontend-dist
# path: apps/nxmesh-frontend/dist
# - name: Copy frontend build to expected location
# run: |
# # unlink frontend-dist
# rm -f apps/nxmesh-master/frontend-dist || true
# rm -rf apps/nxmesh-master/frontend-dist || true
# cp -r apps/nxmesh-frontend/dist apps/nxmesh-master/frontend-dist
- name: Create dummy build artifacts
run: |
rm -f apps/nxmesh-master/frontend-dist || true
mkdir -p apps/nxmesh-master/frontend-dist
echo "<html><body><h1>Dummy Build</h1></body></html>" > apps/nxmesh-master/frontend-dist/index.html
- name: Run clippy - name: Run clippy
run: cargo clippy --all-features run: cargo clippy --all-features
@@ -147,7 +189,12 @@ jobs:
- name: Cache frontend build - name: Cache frontend build
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: apps/nxmesh-frontend/build path: apps/nxmesh-frontend/dist
key: frontend-build-${{ runner.os }}-run-${{ github.run_id }} key: frontend-build-${{ runner.os }}-run-${{ github.run_id }}
restore-keys: | # IGNORE restore-keys
frontend-build-${{ runner.os }}- # TODO: uncomment until artifact hanlding fixed in gitea
# - name: Upload frontend build artifact
# uses: actions/upload-artifact@v4
# with:
# name: frontend-dist
# path: apps/nxmesh-frontend/dist

View File

@@ -109,14 +109,14 @@ jobs:
- name: Apply migrations - name: Apply migrations
if: steps.check_changes.outputs.changed == 'true' if: steps.check_changes.outputs.changed == 'true'
run: | run: |
cargo run -p nxmesh-migration -- up cd crates && sea-orm-cli migrate up
- name: Regenerate entities - name: Regenerate entities
if: steps.check_changes.outputs.changed == 'true' if: steps.check_changes.outputs.changed == 'true'
run: | run: |
sea-orm-cli generate entity \ cd crates && sea-orm-cli generate entity \
--database-url "$DATABASE_URL" \ --database-url "$DATABASE_URL" \
--output-dir apps/nxmesh-master/src/db/entities \ --output-dir ../apps/nxmesh-master/src/db/entities \
--with-serde both \ --with-serde both \
--with-copy-enums \ --with-copy-enums \
--date-time-crate chrono --date-time-crate chrono

257
Cargo.lock generated
View File

@@ -538,6 +538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [ dependencies = [
"axum-core", "axum-core",
"axum-macros",
"base64", "base64",
"bytes", "bytes",
"form_urlencoded", "form_urlencoded",
@@ -586,6 +587,45 @@ dependencies = [
"tracing", "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 = "axum-test"
version = "20.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a86bfe2ef15bee102ac34912f7f4542b0bb37dc464fa55461763999c4d625e7"
dependencies = [
"anyhow",
"axum",
"bytes",
"bytesize",
"cookie",
"expect-json",
"http",
"http-body-util",
"hyper",
"hyper-util",
"mime",
"pretty_assertions",
"reserve-port",
"rust-multipart-rfc7578_2",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tower",
"url",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -727,6 +767,12 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "bytesize"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.6.1" version = "0.6.1"
@@ -911,6 +957,16 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "cookie"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
"time",
"version_check",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.10.1" version = "0.10.1"
@@ -1135,6 +1191,12 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@@ -1188,6 +1250,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "email_address"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.35"
@@ -1262,6 +1333,35 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "expect-json"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "869f97f4abe8e78fc812a94ad6b721d72c4fb5532877c79610f2c238d7ccf6c4"
dependencies = [
"chrono",
"email_address",
"expect-json-macros",
"num",
"regex",
"serde",
"serde_json",
"thiserror",
"typetag",
"uuid",
]
[[package]]
name = "expect-json-macros"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e6fdf550180a6c29a28cb9aac262dc0064c25735641d2317f670075e9a469d9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.3.0"
@@ -1975,6 +2075,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "inventory"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.2" version = "1.70.2"
@@ -2345,6 +2454,20 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.6" version = "0.4.6"
@@ -2421,6 +2544,17 @@ dependencies = [
"num-modular", "num-modular",
] ]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -2483,6 +2617,7 @@ dependencies = [
"async-stream", "async-stream",
"async-trait", "async-trait",
"axum", "axum",
"axum-test",
"chrono", "chrono",
"clap", "clap",
"config", "config",
@@ -2490,12 +2625,14 @@ dependencies = [
"handlebars", "handlebars",
"hex", "hex",
"jsonwebtoken", "jsonwebtoken",
"mime_guess",
"mockall", "mockall",
"nxmesh-core", "nxmesh-core",
"nxmesh-migration", "nxmesh-migration",
"nxmesh-proto", "nxmesh-proto",
"rand 0.10.0", "rand 0.10.0",
"rcgen", "rcgen",
"rust-embed",
"sea-orm", "sea-orm",
"sea-orm-migration", "sea-orm-migration",
"serde", "serde",
@@ -2945,6 +3082,16 @@ dependencies = [
"termtree", "termtree",
] ]
[[package]]
name = "pretty_assertions"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
]
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.37" version = "0.2.37"
@@ -3274,6 +3421,15 @@ dependencies = [
"bytecheck", "bytecheck",
] ]
[[package]]
name = "reserve-port"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94070964579245eb2f76e62a7668fe87bd9969ed6c41256f3bf614e3323dd3cc"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@@ -3351,6 +3507,40 @@ dependencies = [
"zeroize", "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]] [[package]]
name = "rust-ini" name = "rust-ini"
version = "0.21.3" version = "0.21.3"
@@ -3361,6 +3551,21 @@ dependencies = [
"ordered-multimap", "ordered-multimap",
] ]
[[package]]
name = "rust-multipart-rfc7578_2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00bdaa068902270ca7fa8619775e1838e23a63620abac0947ce0f715819b8cec"
dependencies = [
"bytes",
"futures-core",
"futures-util",
"http",
"mime",
"rand 0.10.0",
"thiserror",
]
[[package]] [[package]]
name = "rust_decimal" name = "rust_decimal"
version = "1.40.0" version = "1.40.0"
@@ -3467,6 +3672,15 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" 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]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.28" version = "0.1.28"
@@ -4712,6 +4926,30 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "typetag"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be2212c8a9b9bcfca32024de14998494cf9a5dfa59ea1b829de98bac374b86bf"
dependencies = [
"erased-serde",
"inventory",
"once_cell",
"serde",
"typetag-impl",
]
[[package]]
name = "typetag-impl"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "ucd-trie" name = "ucd-trie"
version = "0.1.7" version = "0.1.7"
@@ -4889,6 +5127,16 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 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]] [[package]]
name = "want" name = "want"
version = "0.3.1" version = "0.3.1"
@@ -5059,6 +5307,15 @@ dependencies = [
"wasite", "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]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.62.2" version = "0.62.2"

View File

@@ -84,7 +84,13 @@ time = "0.3"
# Cert handling # Cert handling
zip = { workspace = true } zip = { workspace = true }
rust-embed = { version = "8.11.0", features = [] }
mime_guess = "2.0.5"
axum-test = "20.0.0"
[dev-dependencies] [dev-dependencies]
tokio-test.workspace = true tokio-test.workspace = true
mockall.workspace = true mockall.workspace = true
[features]
dev-tools = ["axum/macros"]

View File

@@ -0,0 +1 @@
../nxmesh-frontend/dist/

View File

@@ -6,6 +6,7 @@ use nxmesh_proto::{
}; };
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter}; use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
use tonic::transport::Server; use tonic::transport::Server;
use tracing::info;
use crate::{db::entities::public_key_revocations, service::agent::AgentServerService}; use crate::{db::entities::public_key_revocations, service::agent::AgentServerService};
@@ -69,7 +70,14 @@ impl AgentConnectorTrait for SshAgentConnector {
.layer(ssh_interceptor) .layer(ssh_interceptor)
.add_service(agent_server_service); .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(()) Ok(())
} }
} }

View File

@@ -14,6 +14,7 @@ mod cli;
mod config; mod config;
mod connector; mod connector;
mod db; mod db;
mod routes;
mod service; mod service;
#[tokio::main] #[tokio::main]

View File

@@ -0,0 +1,160 @@
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()),
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[tokio::test]
async fn test_asset() {
// list all embedded assets for debugging
let assets = FrontendAssets::iter().collect::<Vec<_>>();
println!("Embedded assets: {:?}", assets);
assert!(
!assets.is_empty(),
"Expected to find embedded assets, but found none"
);
}
#[tokio::test]
async fn test_get_index_html() {
let index_html = get_index_html();
assert!(
index_html.is_some(),
"Expected to find index.html in embedded assets"
);
}
#[tokio::test]
async fn test_get_file_handler_existing_file() {
let response = get_file_handler(axum::extract::Path("index.html".to_string())).await;
assert!(
response.is_ok(),
"Expected to successfully retrieve index.html"
);
let response = response.expect("Expected response to be Ok");
assert_eq!(response.status(), axum::http::StatusCode::OK);
assert!(
response
.headers()
.get(axum::http::header::CONTENT_TYPE)
.map(|ct| ct.to_str().unwrap_or(""))
.expect("Content-Type header should be present")
.starts_with("text/html")
);
}
#[tokio::test]
async fn test_get_file_handler_nonexistent_file() {
let response = get_file_handler(axum::extract::Path("nonexistent.txt".to_string())).await;
assert!(
response.is_ok(),
"Expected to fallback to index.html for nonexistent file"
);
let response = response.expect("Expected response to be Ok");
assert_eq!(response.status(), axum::http::StatusCode::OK);
assert!(
response
.headers()
.get(axum::http::header::CONTENT_TYPE)
.map(|ct| ct.to_str().unwrap_or(""))
.expect("Content-Type header should be present")
.starts_with("text/html")
)
}
}
#[cfg(test)]
mod axum_tests {
use super::*;
use axum_test::TestServer;
#[tokio::test]
async fn test_should_return_index_html_for_root_path() {
let router = get_router().await;
let server = TestServer::new(router);
let response = server.get("/").await;
assert_eq!(response.status_code(), 200);
}
#[tokio::test]
async fn test_should_return_index_html_for_nonexistent_path() {
let router = get_router().await;
let server = TestServer::new(router);
let fallback_response = server.get("/nonexistent").await;
assert_eq!(fallback_response.status_code(), 200);
let index_response = server.get("/").await;
assert_eq!(index_response.status_code(), 200);
assert_eq!(fallback_response.text(), index_response.text());
}
}

View File

@@ -0,0 +1,36 @@
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)
}
#[cfg(test)]
mod tests {
use super::*;
use axum_test::TestServer;
#[tokio::test]
async fn test_should_return_index_html_for_root_path() {
let router = get_root_router().await;
let server = TestServer::new(router);
let response = server.get("/").await;
assert_eq!(response.status_code(), 200);
}
#[tokio::test]
async fn test_should_return_index_html_for_nonexistent_path() {
let router = get_root_router().await;
let server = TestServer::new(router);
let fallback_response = server.get("/nonexistent").await;
assert_eq!(fallback_response.status_code(), 200);
let index_response = server.get("/").await;
assert_eq!(index_response.status_code(), 200);
assert_eq!(fallback_response.text(), index_response.text());
}
}

View File

@@ -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}; use crate::{connector::agent::AgentConnectorTrait, service::certificate::CertificateService};
@@ -26,15 +28,38 @@ pub async fn start_master_server(
println!("Certificate generated and stored successfully."); println!("Certificate generated and stored successfully.");
} }
// Initialize agent connector let ssh_connector = crate::connector::agent::ssh::SshAgentConnector::new(settings.clone())?;
let mut agent_connector = crate::connector::agent::AgentConnector::new(Box::new( let cert_service_for_agent = cert_service.clone();
crate::connector::agent::ssh::SshAgentConnector::new(settings.clone())?, let settings_for_agent = settings.clone();
)); let connection_for_agent = db_connection.clone();
// Start the agent server tokio::spawn(async move {
agent_connector let mut connector = ssh_connector;
.start_server(&settings, cert_service, db_connection) tracing::info!("Starting agent server...");
.await?; 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(()) Ok(())
} }