make all frontend functions works (#466)
This commit is contained in:
@@ -6,8 +6,9 @@ use easytier::{
|
||||
rpc_impl::bidirect::BidirectRpcManager,
|
||||
rpc_types::{self, controller::BaseController},
|
||||
web::{
|
||||
HeartbeatRequest, HeartbeatResponse, RunNetworkInstanceRequest, WebClientService,
|
||||
WebClientServiceClientFactory, WebServerService, WebServerServiceServer,
|
||||
HeartbeatRequest, HeartbeatResponse, NetworkConfig, RunNetworkInstanceRequest,
|
||||
WebClientService, WebClientServiceClientFactory, WebServerService,
|
||||
WebServerServiceServer,
|
||||
},
|
||||
},
|
||||
tunnel::Tunnel,
|
||||
@@ -160,7 +161,13 @@ impl Session {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let req = req.unwrap();
|
||||
if req.machine_id.is_none() {
|
||||
tracing::warn!(?req, "Machine id is not set, ignore");
|
||||
continue;
|
||||
}
|
||||
|
||||
let running_inst_ids = req
|
||||
.running_network_instances
|
||||
.iter()
|
||||
@@ -187,7 +194,11 @@ impl Session {
|
||||
}
|
||||
};
|
||||
|
||||
let local_configs = match storage.db.list_network_configs(user_id, true).await {
|
||||
let local_configs = match storage
|
||||
.db
|
||||
.list_network_configs(user_id, Some(req.machine_id.unwrap().into()), true)
|
||||
.await
|
||||
{
|
||||
Ok(configs) => configs,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to list network configs, error: {:?}", e);
|
||||
@@ -206,7 +217,9 @@ impl Session {
|
||||
BaseController::default(),
|
||||
RunNetworkInstanceRequest {
|
||||
inst_id: Some(c.network_instance_id.clone().into()),
|
||||
config: c.network_config,
|
||||
config: Some(
|
||||
serde_json::from_str::<NetworkConfig>(&c.network_config).unwrap(),
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -16,7 +16,7 @@ pub struct StorageToken {
|
||||
pub struct StorageInner {
|
||||
// some map for indexing
|
||||
pub token_clients_map: DashMap<String, DashSet<url::Url>>,
|
||||
pub machine_client_url_map: DashMap<uuid::Uuid, url::Url>,
|
||||
pub machine_client_url_map: DashMap<uuid::Uuid, DashSet<url::Url>>,
|
||||
pub db: Db,
|
||||
}
|
||||
|
||||
@@ -51,7 +51,9 @@ impl Storage {
|
||||
|
||||
self.0
|
||||
.machine_client_url_map
|
||||
.insert(stoken.machine_id, stoken.client_url.clone());
|
||||
.entry(stoken.machine_id)
|
||||
.or_insert_with(DashSet::new)
|
||||
.insert(stoken.client_url.clone());
|
||||
}
|
||||
|
||||
pub fn remove_client(&self, stoken: &StorageToken) {
|
||||
@@ -60,7 +62,12 @@ impl Storage {
|
||||
set.is_empty()
|
||||
});
|
||||
|
||||
self.0.machine_client_url_map.remove(&stoken.machine_id);
|
||||
self.0
|
||||
.machine_client_url_map
|
||||
.remove_if(&stoken.machine_id, |_, set| {
|
||||
set.remove(&stoken.client_url);
|
||||
set.is_empty()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn weak_ref(&self) -> WeakRefStorage {
|
||||
@@ -71,7 +78,8 @@ impl Storage {
|
||||
self.0
|
||||
.machine_client_url_map
|
||||
.get(&machine_id)
|
||||
.map(|url| url.clone())
|
||||
.map(|url| url.iter().next().map(|url| url.clone()))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn list_token_clients(&self, token: &str) -> Vec<url::Url> {
|
||||
|
||||
@@ -9,6 +9,8 @@ pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
#[sea_orm(column_type = "Text")]
|
||||
pub device_id: String,
|
||||
#[sea_orm(column_type = "Text", unique)]
|
||||
pub network_instance_id: String,
|
||||
#[sea_orm(column_type = "Text")]
|
||||
|
||||
@@ -65,6 +65,7 @@ impl Db {
|
||||
pub async fn insert_or_update_user_network_config<T: ToString>(
|
||||
&self,
|
||||
user_id: UserIdInDb,
|
||||
device_id: uuid::Uuid,
|
||||
network_inst_id: uuid::Uuid,
|
||||
network_config: T,
|
||||
) -> Result<(), DbErr> {
|
||||
@@ -81,6 +82,7 @@ impl Db {
|
||||
.to_owned();
|
||||
let insert_m = urnc::ActiveModel {
|
||||
user_id: sea_orm::Set(user_id),
|
||||
device_id: sea_orm::Set(device_id.to_string()),
|
||||
network_instance_id: sea_orm::Set(network_inst_id.to_string()),
|
||||
network_config: sea_orm::Set(network_config.to_string()),
|
||||
disabled: sea_orm::Set(false),
|
||||
@@ -116,6 +118,7 @@ impl Db {
|
||||
pub async fn list_network_configs(
|
||||
&self,
|
||||
user_id: UserIdInDb,
|
||||
device_id: Option<uuid::Uuid>,
|
||||
only_enabled: bool,
|
||||
) -> Result<Vec<user_running_network_configs::Model>, DbErr> {
|
||||
use entity::user_running_network_configs as urnc;
|
||||
@@ -126,6 +129,11 @@ impl Db {
|
||||
} else {
|
||||
configs
|
||||
};
|
||||
let configs = if let Some(device_id) = device_id {
|
||||
configs.filter(urnc::Column::DeviceId.eq(device_id.to_string()))
|
||||
} else {
|
||||
configs
|
||||
};
|
||||
|
||||
let configs = configs.all(self.orm_db()).await?;
|
||||
|
||||
@@ -167,8 +175,9 @@ mod tests {
|
||||
let user_id = 1;
|
||||
let network_config = "test_config";
|
||||
let inst_id = uuid::Uuid::new_v4();
|
||||
let device_id = uuid::Uuid::new_v4();
|
||||
|
||||
db.insert_or_update_user_network_config(user_id, inst_id, network_config)
|
||||
db.insert_or_update_user_network_config(user_id, device_id, inst_id, network_config)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -183,7 +192,7 @@ mod tests {
|
||||
|
||||
// overwrite the config
|
||||
let network_config = "test_config2";
|
||||
db.insert_or_update_user_network_config(user_id, inst_id, network_config)
|
||||
db.insert_or_update_user_network_config(user_id, device_id, inst_id, network_config)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -193,14 +202,17 @@ mod tests {
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
println!("{:?}", result2);
|
||||
println!("device: {}, {:?}", device_id, result2);
|
||||
assert_eq!(result2.network_config, network_config);
|
||||
|
||||
assert_eq!(result.create_time, result2.create_time);
|
||||
assert_ne!(result.update_time, result2.update_time);
|
||||
|
||||
assert_eq!(
|
||||
db.list_network_configs(user_id, true).await.unwrap().len(),
|
||||
db.list_network_configs(user_id, Some(device_id), true)
|
||||
.await
|
||||
.unwrap()
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
|
||||
|
||||
@@ -4,94 +4,6 @@ use sea_orm_migration::{prelude::*, schema::*};
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
/*
|
||||
-- # Entity schema.
|
||||
|
||||
-- Create `users` table.
|
||||
create table if not exists users (
|
||||
id integer primary key autoincrement,
|
||||
username text not null unique,
|
||||
password text not null
|
||||
);
|
||||
|
||||
-- Create `groups` table.
|
||||
create table if not exists groups (
|
||||
id integer primary key autoincrement,
|
||||
name text not null unique
|
||||
);
|
||||
|
||||
-- Create `permissions` table.
|
||||
create table if not exists permissions (
|
||||
id integer primary key autoincrement,
|
||||
name text not null unique
|
||||
);
|
||||
|
||||
|
||||
-- # Join tables.
|
||||
|
||||
-- Create `users_groups` table for many-to-many relationships between users and groups.
|
||||
create table if not exists users_groups (
|
||||
user_id integer references users(id),
|
||||
group_id integer references groups(id),
|
||||
primary key (user_id, group_id)
|
||||
);
|
||||
|
||||
-- Create `groups_permissions` table for many-to-many relationships between groups and permissions.
|
||||
create table if not exists groups_permissions (
|
||||
group_id integer references groups(id),
|
||||
permission_id integer references permissions(id),
|
||||
primary key (group_id, permission_id)
|
||||
);
|
||||
|
||||
|
||||
-- # Fixture hydration.
|
||||
|
||||
-- Insert "user" user. password: "user"
|
||||
insert into users (username, password)
|
||||
values (
|
||||
'user',
|
||||
'$argon2i$v=19$m=16,t=2,p=1$dHJ5dXZkYmZkYXM$UkrNqWz0BbSVBq4ykLSuJw'
|
||||
);
|
||||
|
||||
-- Insert "admin" user. password: "admin"
|
||||
insert into users (username, password)
|
||||
values (
|
||||
'admin',
|
||||
'$argon2i$v=19$m=16,t=2,p=1$Ymd1Y2FlcnQ$x0q4oZinW9S1ZB9BcaHEpQ'
|
||||
);
|
||||
|
||||
-- Insert "users" and "superusers" groups.
|
||||
insert into groups (name) values ('users');
|
||||
insert into groups (name) values ('superusers');
|
||||
|
||||
-- Insert individual permissions.
|
||||
insert into permissions (name) values ('sessions');
|
||||
insert into permissions (name) values ('devices');
|
||||
|
||||
-- Insert group permissions.
|
||||
insert into groups_permissions (group_id, permission_id)
|
||||
values (
|
||||
(select id from groups where name = 'users'),
|
||||
(select id from permissions where name = 'devices')
|
||||
), (
|
||||
(select id from groups where name = 'superusers'),
|
||||
(select id from permissions where name = 'sessions')
|
||||
);
|
||||
|
||||
-- Insert users into groups.
|
||||
insert into users_groups (user_id, group_id)
|
||||
values (
|
||||
(select id from users where username = 'user'),
|
||||
(select id from groups where name = 'users')
|
||||
), (
|
||||
(select id from users where username = 'admin'),
|
||||
(select id from groups where name = 'users')
|
||||
), (
|
||||
(select id from users where username = 'admin'),
|
||||
(select id from groups where name = 'superusers')
|
||||
);
|
||||
*/
|
||||
|
||||
impl MigrationName for Migration {
|
||||
fn name(&self) -> &str {
|
||||
"m20241029_000001_init"
|
||||
@@ -141,6 +53,7 @@ enum UserRunningNetworkConfigs {
|
||||
Table,
|
||||
Id,
|
||||
UserId,
|
||||
DeviceId,
|
||||
NetworkInstanceId,
|
||||
NetworkConfig,
|
||||
Disabled,
|
||||
@@ -273,6 +186,7 @@ impl MigrationTrait for Migration {
|
||||
.table(UserRunningNetworkConfigs::Table)
|
||||
.col(pk_auto(UserRunningNetworkConfigs::Id).not_null())
|
||||
.col(integer(UserRunningNetworkConfigs::UserId).not_null())
|
||||
.col(text(UserRunningNetworkConfigs::DeviceId).not_null())
|
||||
.col(
|
||||
text(UserRunningNetworkConfigs::NetworkInstanceId)
|
||||
.unique_key()
|
||||
|
||||
@@ -22,6 +22,10 @@ pub struct LoginResult {
|
||||
pub fn router() -> Router<AppStateInner> {
|
||||
let r = Router::new()
|
||||
.route("/api/v1/auth/password", put(self::put::change_password))
|
||||
.route(
|
||||
"/api/v1/auth/check_login_status",
|
||||
get(self::get::check_login_status),
|
||||
)
|
||||
.route_layer(login_required!(Backend));
|
||||
Router::new()
|
||||
.merge(r)
|
||||
@@ -168,4 +172,17 @@ mod get {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn check_login_status(
|
||||
auth_session: AuthSession,
|
||||
) -> Result<Json<Void>, HttpHandleError> {
|
||||
if auth_session.user.is_some() {
|
||||
Ok(Json(Void::default()))
|
||||
} else {
|
||||
Err((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
Json::from(other_error("Not logged in")),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use axum_login::tower_sessions::{ExpiredDeletion, SessionManagerLayer};
|
||||
use axum_login::{login_required, AuthManagerLayerBuilder, AuthzBackend};
|
||||
use axum_messages::MessagesManagerLayer;
|
||||
use easytier::common::scoped_task::ScopedTask;
|
||||
use easytier::proto::{rpc_types};
|
||||
use easytier::proto::rpc_types;
|
||||
use network::NetworkApi;
|
||||
use sea_orm::DbErr;
|
||||
use tokio::net::TcpListener;
|
||||
@@ -43,6 +43,11 @@ type AppState = State<AppStateInner>;
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
struct ListSessionJsonResp(Vec<StorageToken>);
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
struct GetSummaryJsonResp {
|
||||
device_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Error {
|
||||
message: String,
|
||||
@@ -98,16 +103,32 @@ impl RestfulServer {
|
||||
auth_session: AuthSession,
|
||||
State(client_mgr): AppState,
|
||||
) -> Result<Json<ListSessionJsonResp>, HttpHandleError> {
|
||||
let pers = auth_session
|
||||
let perms = auth_session
|
||||
.backend
|
||||
.get_group_permissions(auth_session.user.as_ref().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
println!("{:?}", pers);
|
||||
println!("{:?}", perms);
|
||||
let ret = client_mgr.list_sessions().await;
|
||||
Ok(ListSessionJsonResp(ret).into())
|
||||
}
|
||||
|
||||
async fn handle_get_summary(
|
||||
auth_session: AuthSession,
|
||||
State(client_mgr): AppState,
|
||||
) -> Result<Json<GetSummaryJsonResp>, HttpHandleError> {
|
||||
let Some(user) = auth_session.user else {
|
||||
return Err((StatusCode::UNAUTHORIZED, other_error("No such user").into()));
|
||||
};
|
||||
|
||||
let machines = client_mgr.list_machine_by_token(user.tokens[0].clone());
|
||||
|
||||
Ok(GetSummaryJsonResp {
|
||||
device_count: machines.len() as u32,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) -> Result<(), anyhow::Error> {
|
||||
let listener = TcpListener::bind(self.bind_addr).await?;
|
||||
|
||||
@@ -143,6 +164,7 @@ impl RestfulServer {
|
||||
let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/api/v1/summary", get(Self::handle_get_summary))
|
||||
.route("/api/v1/sessions", get(Self::handle_list_all_sessions))
|
||||
.merge(self.network_api.build_route())
|
||||
.route_layer(login_required!(Backend))
|
||||
|
||||
@@ -9,7 +9,7 @@ use dashmap::DashSet;
|
||||
use easytier::launcher::NetworkConfig;
|
||||
use easytier::proto::common::Void;
|
||||
use easytier::proto::rpc_types::controller::BaseController;
|
||||
use easytier::proto::{web::*};
|
||||
use easytier::proto::web::*;
|
||||
|
||||
use crate::client_manager::session::Session;
|
||||
use crate::client_manager::ClientManager;
|
||||
@@ -38,7 +38,7 @@ struct ValidateConfigJsonReq {
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
struct RunNetworkJsonReq {
|
||||
config: String,
|
||||
config: NetworkConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
@@ -145,7 +145,7 @@ impl NetworkApi {
|
||||
BaseController::default(),
|
||||
RunNetworkInstanceRequest {
|
||||
inst_id: None,
|
||||
config: config.clone(),
|
||||
config: Some(config.clone()),
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -155,8 +155,9 @@ impl NetworkApi {
|
||||
.db()
|
||||
.insert_or_update_user_network_config(
|
||||
auth_session.user.as_ref().unwrap().id(),
|
||||
machine_id,
|
||||
resp.inst_id.clone().unwrap_or_default().into(),
|
||||
config,
|
||||
serde_json::to_string(&config).unwrap(),
|
||||
)
|
||||
.await
|
||||
.map_err(convert_db_error)?;
|
||||
@@ -288,6 +289,36 @@ impl NetworkApi {
|
||||
Ok(Json(ListMachineJsonResp { machines }))
|
||||
}
|
||||
|
||||
async fn handle_get_network_config(
|
||||
auth_session: AuthSession,
|
||||
State(client_mgr): AppState,
|
||||
Path((machine_id, inst_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
) -> Result<Json<NetworkConfig>, HttpHandleError> {
|
||||
let inst_id = inst_id.to_string();
|
||||
|
||||
let db_row = client_mgr
|
||||
.db()
|
||||
.list_network_configs(auth_session.user.unwrap().id(), Some(machine_id), false)
|
||||
.await
|
||||
.map_err(convert_db_error)?
|
||||
.iter()
|
||||
.find(|x| x.network_instance_id == inst_id)
|
||||
.map(|x| x.network_config.clone())
|
||||
.ok_or((
|
||||
StatusCode::NOT_FOUND,
|
||||
other_error(format!("No such network instance: {}", inst_id)).into(),
|
||||
))?;
|
||||
|
||||
Ok(serde_json::from_str::<NetworkConfig>(&db_row)
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
other_error(format!("Failed to parse network config: {:?}", e)).into(),
|
||||
)
|
||||
})?
|
||||
.into())
|
||||
}
|
||||
|
||||
pub fn build_route(&mut self) -> Router<AppStateInner> {
|
||||
Router::new()
|
||||
.route("/api/v1/machines", get(Self::handle_list_machines))
|
||||
@@ -311,5 +342,9 @@ impl NetworkApi {
|
||||
"/api/v1/machines/:machine-id/networks/info/:inst-id",
|
||||
get(Self::handle_collect_one_network_info),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/machines/:machine-id/networks/config/:inst-id",
|
||||
get(Self::handle_get_network_config),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user