123 lines
4.5 KiB
Rust
123 lines
4.5 KiB
Rust
//! HTTP management API mounted in-process alongside hbbs's rendezvous
|
|
//! listeners. The router is wired in via `src/rendezvous_server.rs`'s outer
|
|
//! `tokio::select!`. M1 covers auth + heartbeat + sysinfo; later milestones
|
|
//! add address book, audit, OIDC, etc.
|
|
|
|
pub mod ab;
|
|
pub mod admin;
|
|
pub mod audit;
|
|
pub mod auth;
|
|
pub mod device_auth;
|
|
pub mod devices_cli;
|
|
pub mod email;
|
|
pub mod error;
|
|
pub mod groups;
|
|
pub mod heartbeat;
|
|
pub mod http_proxy;
|
|
pub mod middleware;
|
|
pub mod oidc;
|
|
pub mod pagination;
|
|
pub mod peers;
|
|
pub mod plugin_sign;
|
|
pub mod record;
|
|
pub mod state;
|
|
pub mod strategy;
|
|
pub mod sysinfo;
|
|
pub mod twofa;
|
|
pub mod unattended;
|
|
pub mod users;
|
|
|
|
pub use state::AppState;
|
|
|
|
use axum::extract::Extension;
|
|
use axum::routing::{delete, get, post, put};
|
|
use axum::Router;
|
|
use hbb_common::{log, ResultType};
|
|
use std::net::SocketAddr;
|
|
use std::sync::Arc;
|
|
|
|
pub fn router(state: Arc<AppState>) -> Router {
|
|
let app = Router::new()
|
|
// M1: auth + heartbeat + sysinfo
|
|
.route(
|
|
"/api/login-options",
|
|
get(auth::login_options).head(auth::login_options_head),
|
|
)
|
|
.route("/api/login", post(auth::login))
|
|
.route("/api/currentUser", post(auth::current_user))
|
|
.route("/api/logout", post(auth::logout))
|
|
.route("/api/heartbeat", post(heartbeat::heartbeat))
|
|
.route("/api/sysinfo_ver", post(sysinfo::sysinfo_ver))
|
|
.route("/api/sysinfo", post(sysinfo::sysinfo))
|
|
.route(
|
|
"/api/unattended-password",
|
|
post(unattended::unattended_password),
|
|
)
|
|
// M2: address book — modern (shared + personal)
|
|
.route("/api/ab/settings", post(ab::settings::settings))
|
|
.route("/api/ab/personal", post(ab::profiles::personal))
|
|
.route(
|
|
"/api/ab/shared/profiles",
|
|
post(ab::profiles::shared_profiles),
|
|
)
|
|
.route("/api/ab/peers", post(ab::peers::list))
|
|
.route("/api/ab/tags/:guid", post(ab::tags::list))
|
|
.route("/api/ab/peer/add/:guid", post(ab::peers::add))
|
|
.route("/api/ab/peer/update/:guid", put(ab::peers::update))
|
|
.route("/api/ab/peer/:guid", delete(ab::peers::delete))
|
|
.route("/api/ab/tag/add/:guid", post(ab::tags::add))
|
|
.route("/api/ab/tag/rename/:guid", put(ab::tags::rename))
|
|
.route("/api/ab/tag/update/:guid", put(ab::tags::update))
|
|
.route("/api/ab/tag/:guid", delete(ab::tags::delete))
|
|
// M2: address book — legacy single-blob fallback
|
|
.route(
|
|
"/api/ab",
|
|
get(ab::legacy::get).post(ab::legacy::put),
|
|
)
|
|
// M2: group / users / peers panel
|
|
.route(
|
|
"/api/device-group/accessible",
|
|
get(groups::accessible),
|
|
)
|
|
.route("/api/users", get(users::list))
|
|
.route("/api/peers", get(peers::list))
|
|
.route("/api/peers/:id/managed", put(peers::set_managed))
|
|
// M3: audit
|
|
.route("/api/audit/conn", post(audit::conn::conn))
|
|
.route("/api/audit/file", post(audit::file::file))
|
|
.route("/api/audit/alarm", post(audit::alarm::alarm))
|
|
.route("/api/audit", put(audit::note::note))
|
|
// M3: session recording upload
|
|
.route("/api/record", post(record::record))
|
|
// M4: TOTP enrollment (admin-only)
|
|
.route("/api/2fa/enroll", post(twofa::enroll))
|
|
.route("/api/2fa/unenroll", post(twofa::unenroll))
|
|
// M4: rustdesk --assign target
|
|
.route("/api/devices/cli", post(devices_cli::assign))
|
|
// M4: plugin signing (no auth — protocol-level)
|
|
.route("/lic/web/api/plugin-sign", post(plugin_sign::plugin_sign))
|
|
// M4: OIDC device-flow login
|
|
.route("/api/oidc/auth", post(oidc::auth::auth))
|
|
.route("/api/oidc/auth-query", get(oidc::poll::auth_query))
|
|
.route("/oidc/callback", get(oidc::callback::callback));
|
|
// M5: admin dashboard (HTMX + embedded HTML). Merged BEFORE the
|
|
// Extension layer so the merged router carries the shared state.
|
|
let app = match admin::build(state.clone()) {
|
|
Some(admin_router) => app.merge(admin_router),
|
|
None => app,
|
|
};
|
|
app.layer(Extension(state))
|
|
}
|
|
|
|
pub async fn serve(addr: SocketAddr, state: Arc<AppState>) -> ResultType<()> {
|
|
log::info!("HTTP API listening on {}", addr);
|
|
let app = router(state);
|
|
// Share the same router with the rendezvous-TCP HttpProxyRequest path so
|
|
// both transports route through the exact same handlers.
|
|
http_proxy::install_router(app.clone());
|
|
axum::Server::bind(&addr)
|
|
.serve(app.into_make_service())
|
|
.await?;
|
|
Ok(())
|
|
}
|