Compare commits

...

2 Commits

Author SHA1 Message Date
38a73fd003
App contract list
Implement retrieve app contracts by UUID, admin, and operator
2025-05-14 00:09:36 +05:30
1e8014a5d6
App node
get one app node
2025-05-13 16:07:57 +05:30
3 changed files with 84 additions and 11 deletions

@ -94,7 +94,7 @@ impl NewAppReq {
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AppNodeWithReports { pub struct AppNodeWithReports {
pub id: RecordId, pub id: RecordId,
pub operator: RecordId, pub operator: RecordId,
@ -116,6 +116,7 @@ impl AppNodeWithReports {
pub async fn find_by_filters( pub async fn find_by_filters(
db: &Surreal<Client>, db: &Surreal<Client>,
filters: app_proto::AppNodeFilters, filters: app_proto::AppNodeFilters,
limit_one: bool,
) -> Result<Vec<Self>, Error> { ) -> Result<Vec<Self>, Error> {
let mut filter_query = format!( let mut filter_query = format!(
"select *, <-report.* from {APP_NODE} where "select *, <-report.* from {APP_NODE} where
@ -123,7 +124,7 @@ impl AppNodeWithReports {
max_ports_per_app >= {} && max_ports_per_app >= {} &&
avail_vcpus >= {} && avail_vcpus >= {} &&
avail_mem_mb >= {} && avail_mem_mb >= {} &&
avail_storage_gbs >= {}\n", avail_storage_gbs >= {} ",
filters.free_ports, filters.free_ports,
filters.free_ports, filters.free_ports,
filters.vcpus, filters.vcpus,
@ -143,8 +144,12 @@ impl AppNodeWithReports {
if !filters.ip.is_empty() { if !filters.ip.is_empty() {
filter_query += &format!("&& ip = '{}' ", filters.ip); filter_query += &format!("&& ip = '{}' ", filters.ip);
} }
if limit_one {
filter_query += "limit 1";
}
filter_query += ";"; filter_query += ";";
dbg!(&filter_query);
let mut query_resp = db.query(filter_query).await?; let mut query_resp = db.query(filter_query).await?;
let app_nodes: Vec<Self> = query_resp.take(0)?; let app_nodes: Vec<Self> = query_resp.take(0)?;
Ok(app_nodes) Ok(app_nodes)
@ -228,6 +233,12 @@ pub struct ActiveAppWithNode {
} }
impl ActiveAppWithNode { impl ActiveAppWithNode {
pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> {
let contract: Option<Self> =
db.query(format!("select * from {ACTIVE_APP}:{uuid} fetch out;")).await?.take(0)?;
Ok(contract)
}
pub async fn list_by_node(db: &Surreal<Client>, node_pubkey: &str) -> Result<Vec<Self>, Error> { pub async fn list_by_node(db: &Surreal<Client>, node_pubkey: &str) -> Result<Vec<Self>, Error> {
let mut query_result = db let mut query_result = db
.query(format!( .query(format!(
@ -239,10 +250,38 @@ impl ActiveAppWithNode {
Ok(contract) Ok(contract)
} }
pub async fn get_by_uuid(db: &Surreal<Client>, uuid: &str) -> Result<Option<Self>, Error> { pub async fn list_by_admin(db: &Surreal<Client>, admin: &str) -> Result<Vec<Self>, Error> {
let contract: Option<Self> = let mut query_result = db
db.query(format!("select * from {ACTIVE_APP}:{uuid} fetch out;")).await?.take(0)?; .query(format!("select * from {ACTIVE_APP} where in = {ACCOUNT}:{admin} fetch out;"))
Ok(contract) .await?;
let app_contracts: Vec<Self> = query_result.take(0)?;
Ok(app_contracts)
}
pub async fn list_by_operator(
db: &Surreal<Client>,
operator: &str,
) -> Result<Vec<Self>, Error> {
let mut query_result = db
.query(format!(
"select
(select * from ->operator->app_node<-{ACTIVE_APP} fetch out)
as app_contracts
from {ACCOUNT}:{operator}"
))
.await?;
#[derive(Deserialize)]
struct Wrapper {
app_contracts: Vec<ActiveAppWithNode>,
}
let c: Option<Wrapper> = query_result.take(0)?;
match c {
Some(contracts_wrapper) => Ok(contracts_wrapper.app_contracts),
None => Ok(vec![]),
}
} }
} }

@ -113,7 +113,7 @@ pub struct Kick {
contract: RecordId, contract: RecordId,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Report { pub struct Report {
#[serde(rename = "in")] #[serde(rename = "in")]
from_account: RecordId, from_account: RecordId,

@ -210,7 +210,36 @@ impl BrainAppCli for AppCliServer {
let req = check_sig_from_req(req)?; let req = check_sig_from_req(req)?;
info!("list_app_contracts process starting for {:?}", req); info!("list_app_contracts process starting for {:?}", req);
todo!() let mut app_contracts = Vec::new();
if !req.uuid.is_empty() {
if let Some(app_contract) =
db::ActiveAppWithNode::get_by_uuid(&self.db, &req.uuid).await?
{
if app_contract.admin.key().to_string() == req.admin_pubkey {
app_contracts.push(app_contract);
}
// TODO: allow operator to inspect contracts
}
} else if req.as_operator {
app_contracts.append(
&mut db::ActiveAppWithNode::list_by_operator(&self.db, &req.admin_pubkey).await?,
);
} else {
app_contracts.append(
&mut db::ActiveAppWithNode::list_by_admin(&self.db, &req.admin_pubkey).await?,
);
}
let (tx, rx) = mpsc::channel(6);
tokio::spawn(async move {
for app_contract in app_contracts {
let _ = tx.send(Ok(app_contract.into())).await;
}
});
let resp_stream = ReceiverStream::new(rx);
Ok(Response::new(Box::pin(resp_stream)))
} }
async fn list_app_nodes( async fn list_app_nodes(
@ -219,7 +248,7 @@ impl BrainAppCli for AppCliServer {
) -> Result<tonic::Response<<Self as BrainAppCli>::ListAppNodesStream>, tonic::Status> { ) -> Result<tonic::Response<<Self as BrainAppCli>::ListAppNodesStream>, tonic::Status> {
let req = check_sig_from_req(req)?; let req = check_sig_from_req(req)?;
info!("list_app_nodes process starting for {:?}", req); info!("list_app_nodes process starting for {:?}", req);
let app_nodes = db::AppNodeWithReports::find_by_filters(&self.db, req).await?; let app_nodes = db::AppNodeWithReports::find_by_filters(&self.db, req, false).await?;
let (tx, rx) = mpsc::channel(6); let (tx, rx) = mpsc::channel(6);
tokio::spawn(async move { tokio::spawn(async move {
for app_node in app_nodes { for app_node in app_nodes {
@ -236,7 +265,12 @@ impl BrainAppCli for AppCliServer {
) -> Result<tonic::Response<AppNodeListResp>, tonic::Status> { ) -> Result<tonic::Response<AppNodeListResp>, tonic::Status> {
let req = check_sig_from_req(req)?; let req = check_sig_from_req(req)?;
info!("get_one_app_node process starting for {:?}", req); info!("get_one_app_node process starting for {:?}", req);
let app_node = db::AppNodeWithReports::find_by_filters(&self.db, req, true)
.await?
.first()
.ok_or(Status::not_found("No app node found"))?
.clone();
todo!() Ok(Response::new(app_node.into()))
} }
} }