api progress

This commit is contained in:
2024-11-25 22:45:27 +00:00
parent 13f59908fb
commit a0e49d83bd
13 changed files with 1414 additions and 163 deletions

View File

@ -6,7 +6,7 @@ create table users
email varchar(200),
contact_nip4 bit(1) not null,
contact_nip17 bit(1) not null,
contact_email bit(1) not null,
contact_email bit(1) not null
);
create unique index ix_user_pubkey on users (pubkey);
create unique index ix_user_email on users (email);
@ -55,14 +55,14 @@ create table vm_host_disk
create table vm_os_image
(
id integer unsigned not null auto_increment primary key,
name varchar(200) not null,
distribution smallint unsigned not null,
flavour varchar(50) not null,
version varchar(50) not null,
enabled bit(1) not null,
url varchar(1024) not null,
release_date timestamp not null,
url varchar(1024) not null
);
create unique index ix_vm_os_image_name on vm_os_image (name);
create unique index ix_vm_os_image on vm_os_image (distribution, flavour, version);
create table ip_range
(
id integer unsigned not null auto_increment primary key,
@ -72,6 +72,7 @@ create table ip_range
constraint fk_ip_range_region foreign key (region_id) references vm_host_region (id)
);
create unique index ix_ip_range_cidr on ip_range (cidr);
create table vm_cost_plan
(
id integer unsigned not null auto_increment primary key,

48
lnvps_db/src/hydrate.rs Normal file
View File

@ -0,0 +1,48 @@
use crate::{LNVpsDb, Vm, VmTemplate};
use anyhow::Result;
use async_trait::async_trait;
#[async_trait]
pub trait Hydrate {
/// Load parent resources
async fn hydrate_up(&mut self, db: &Box<dyn LNVpsDb>) -> Result<()>;
/// Load child resources
async fn hydrate_down(&mut self, db: &Box<dyn LNVpsDb>) -> Result<()>;
}
#[async_trait]
impl Hydrate for Vm {
async fn hydrate_up(&mut self, db: &Box<dyn LNVpsDb>) -> Result<()> {
let image = db.get_os_image(self.image_id).await?;
let template = db.get_vm_template(self.template_id).await?;
let ssh_key = db.get_user_ssh_key(self.ssh_key_id).await?;
self.image = Some(image);
self.template = Some(template);
self.ssh_key = Some(ssh_key);
Ok(())
}
async fn hydrate_down(&mut self, db: &Box<dyn LNVpsDb>) -> Result<()> {
let payments = db.list_vm_payment(self.id).await?;
self.payments = Some(payments);
Ok(())
}
}
#[async_trait]
impl Hydrate for VmTemplate {
async fn hydrate_up(&mut self, db: &Box<dyn LNVpsDb>) -> Result<()> {
let cost_plan = db.get_cost_plan(self.cost_plan_id).await?;
let region = db.get_host_region(self.region_id).await?;
self.cost_plan = Some(cost_plan);
self.region = Some(region);
Ok(())
}
async fn hydrate_down(&mut self, db: &Box<dyn LNVpsDb>) -> Result<()> {
todo!()
}
}

View File

@ -4,6 +4,7 @@ use async_trait::async_trait;
mod model;
#[cfg(feature = "mysql")]
mod mysql;
pub mod hydrate;
pub use model::*;
#[cfg(feature = "mysql")]
@ -27,7 +28,7 @@ pub trait LNVpsDb: Sync + Send {
async fn delete_user(&self, id: u64) -> Result<()>;
/// Insert a new user ssh key
async fn insert_user_ssh_key(&self, new_key: UserSshKey) -> Result<u64>;
async fn insert_user_ssh_key(&self, new_key: &UserSshKey) -> Result<u64>;
/// Get user ssh key by id
async fn get_user_ssh_key(&self, id: u64) -> Result<UserSshKey>;
@ -45,11 +46,14 @@ pub trait LNVpsDb: Sync + Send {
async fn list_hosts(&self) -> Result<Vec<VmHost>>;
/// Update host resources (usually from [auto_discover])
async fn update_host(&self, host: VmHost) -> Result<()>;
async fn update_host(&self, host: &VmHost) -> Result<()>;
/// List VM's owned by a specific user
async fn list_host_disks(&self, host_id: u64) -> Result<Vec<VmHostDisk>>;
/// Get OS image by id
async fn get_os_image(&self, id: u64) -> Result<VmOsImage>;
/// List available OS images
async fn list_os_image(&self) -> Result<Vec<VmOsImage>>;
@ -59,14 +63,20 @@ pub trait LNVpsDb: Sync + Send {
/// Get a VM cost plan by id
async fn get_cost_plan(&self, id: u64) -> Result<VmCostPlan>;
/// Get VM template by id
async fn get_vm_template(&self, id: u64) -> Result<VmTemplate>;
/// List VM templates
async fn list_vm_templates(&self) -> Result<Vec<VmTemplate>>;
/// List VM's owned by a specific user
async fn list_user_vms(&self, id: u64) -> Result<Vec<Vm>>;
/// Get a VM by id
async fn get_vm(&self, vm_id: u64) -> Result<Vm>;
/// Insert a new VM record
async fn insert_vm(&self, vm: Vm) -> Result<u64>;
async fn insert_vm(&self, vm: &Vm) -> Result<u64>;
/// List VM ip assignments
async fn get_vm_ip_assignments(&self, vm_id: u64) -> Result<Vec<VmIpAssignment>>;
@ -75,8 +85,8 @@ pub trait LNVpsDb: Sync + Send {
async fn list_vm_payment(&self, vm_id: u64) -> Result<Vec<VmPayment>>;
/// Insert a new VM payment record
async fn insert_vm_payment(&self, vm_payment: VmPayment) -> Result<u64>;
async fn insert_vm_payment(&self, vm_payment: &VmPayment) -> Result<u64>;
/// Update a VM payment record
async fn update_vm_payment(&self, vm_payment: VmPayment) -> Result<()>;
async fn update_vm_payment(&self, vm_payment: &VmPayment) -> Result<()>;
}

View File

@ -18,12 +18,13 @@ pub struct User {
pub contact_email: bool,
}
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
#[derive(Serialize, Deserialize, FromRow, Clone, Debug, Default)]
pub struct UserSshKey {
pub id: u64,
pub name: String,
pub user_id: u64,
pub created: DateTime<Utc>,
#[serde(skip_serializing)]
pub key_data: String,
#[sqlx(skip)]
@ -79,24 +80,29 @@ pub struct VmHostDisk {
pub enabled: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type, Default)]
#[serde(rename_all = "lowercase")]
#[repr(u16)]
pub enum DiskType {
#[default]
HDD = 0,
SSD = 1,
}
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type, Default)]
#[serde(rename_all = "lowercase")]
#[repr(u16)]
pub enum DiskInterface {
#[default]
SATA = 0,
SCSI = 1,
PCIe = 2,
}
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type, Default)]
#[repr(u16)]
pub enum OsDistribution {
#[default]
Ubuntu = 0,
Debian = 1,
}
@ -106,11 +112,12 @@ pub enum OsDistribution {
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
pub struct VmOsImage {
pub id: u64,
pub name: String,
pub distribution: OsDistribution,
pub flavour: String,
pub version: String,
pub enabled: bool,
pub release_date: DateTime<Utc>,
#[serde(skip_serializing)]
/// URL location of cloud image
pub url: String,
}
@ -124,6 +131,7 @@ pub struct IpRange {
}
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
#[serde(rename_all = "lowercase")]
#[repr(u16)]
pub enum VmCostPlanIntervalType {
Day = 0,
@ -144,7 +152,7 @@ pub struct VmCostPlan {
/// Offers.
/// These are the same as the offers visible to customers
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
#[derive(Serialize, Deserialize, FromRow, Clone, Debug, Default)]
pub struct VmTemplate {
pub id: u64,
pub name: String,
@ -154,6 +162,7 @@ pub struct VmTemplate {
pub expires: Option<DateTime<Utc>>,
pub cpu: u16,
pub memory: u64,
pub disk_size: u64,
pub disk_type: DiskType,
pub disk_interface: DiskInterface,
pub cost_plan_id: u64,
@ -167,7 +176,7 @@ pub struct VmTemplate {
pub region: Option<VmHostRegion>,
}
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
#[derive(Serialize, Deserialize, FromRow, Clone, Debug, Default)]
pub struct Vm {
/// Unique VM ID (Same in proxmox)
pub id: u64,
@ -193,6 +202,19 @@ pub struct Vm {
pub disk_size: u64,
/// The [VmHostDisk] this VM is on
pub disk_id: u64,
#[sqlx(skip)]
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<VmOsImage>,
#[sqlx(skip)]
#[serde(skip_serializing_if = "Option::is_none")]
pub template: Option<VmTemplate>,
#[sqlx(skip)]
#[serde(skip_serializing_if = "Option::is_none")]
pub ssh_key: Option<UserSshKey>,
#[sqlx(skip)]
#[serde(skip_serializing_if = "Option::is_none")]
pub payments: Option<Vec<VmPayment>>,
}
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]

View File

@ -25,11 +25,11 @@ impl LNVpsDbMysql {
#[async_trait]
impl LNVpsDb for LNVpsDbMysql {
async fn migrate(&self) -> anyhow::Result<()> {
async fn migrate(&self) -> Result<()> {
sqlx::migrate!().run(&self.db).await.map_err(Error::new)
}
async fn upsert_user(&self, pubkey: &[u8; 32]) -> anyhow::Result<u64> {
async fn upsert_user(&self, pubkey: &[u8; 32]) -> Result<u64> {
let res = sqlx::query("insert ignore into users(pubkey) values(?) returning id")
.bind(pubkey.as_slice())
.fetch_optional(&self.db)
@ -46,7 +46,11 @@ impl LNVpsDb for LNVpsDbMysql {
}
async fn get_user(&self, id: u64) -> Result<User> {
todo!()
sqlx::query_as("select * from users where id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn update_user(&self, user: &User) -> Result<()> {
@ -57,12 +61,23 @@ impl LNVpsDb for LNVpsDbMysql {
todo!()
}
async fn insert_user_ssh_key(&self, new_key: UserSshKey) -> Result<u64> {
todo!()
async fn insert_user_ssh_key(&self, new_key: &UserSshKey) -> Result<u64> {
Ok(sqlx::query("insert into user_ssh_key(name,user_id,key_data) values(?, ?, ?) returning id")
.bind(&new_key.name)
.bind(&new_key.user_id)
.bind(&new_key.key_data)
.fetch_one(&self.db)
.await
.map_err(Error::new)?
.try_get(0)?)
}
async fn get_user_ssh_key(&self, id: u64) -> Result<UserSshKey> {
todo!()
sqlx::query_as("select * from user_ssh_key where id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn delete_user_ssh_key(&self, id: u64) -> Result<()> {
@ -70,21 +85,29 @@ impl LNVpsDb for LNVpsDbMysql {
}
async fn list_user_ssh_key(&self, user_id: u64) -> Result<Vec<UserSshKey>> {
todo!()
sqlx::query_as("select * from user_ssh_key where user_id = ?")
.bind(user_id)
.fetch_all(&self.db)
.await
.map_err(Error::new)
}
async fn get_host_region(&self, id: u64) -> Result<VmHostRegion> {
todo!()
sqlx::query_as("select * from vm_host_region where id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn list_hosts(&self) -> anyhow::Result<Vec<VmHost>> {
async fn list_hosts(&self) -> Result<Vec<VmHost>> {
sqlx::query_as("select * from vm_host")
.fetch_all(&self.db)
.await
.map_err(Error::new)
}
async fn update_host(&self, host: VmHost) -> anyhow::Result<()> {
async fn update_host(&self, host: &VmHost) -> Result<()> {
sqlx::query("update vm_host set name = ?, cpu = ?, memory = ? where id = ?")
.bind(&host.name)
.bind(&host.cpu)
@ -95,7 +118,7 @@ impl LNVpsDb for LNVpsDbMysql {
Ok(())
}
async fn list_host_disks(&self, host_id: u64) -> anyhow::Result<Vec<VmHostDisk>> {
async fn list_host_disks(&self, host_id: u64) -> Result<Vec<VmHostDisk>> {
sqlx::query_as("select * from vm_host_disk where host_id = ?")
.bind(&host_id)
.fetch_all(&self.db)
@ -103,20 +126,46 @@ impl LNVpsDb for LNVpsDbMysql {
.map_err(Error::new)
}
async fn get_os_image(&self, id: u64) -> Result<VmOsImage> {
sqlx::query_as("select * from vm_os_image where id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn list_os_image(&self) -> Result<Vec<VmOsImage>> {
todo!()
sqlx::query_as("select * from vm_os_image")
.fetch_all(&self.db)
.await
.map_err(Error::new)
}
async fn list_ip_range(&self) -> Result<Vec<IpRange>> {
todo!()
sqlx::query_as("select * from ip_range")
.fetch_all(&self.db)
.await
.map_err(Error::new)
}
async fn get_cost_plan(&self, id: u64) -> Result<VmCostPlan> {
todo!()
sqlx::query_as("select * from vm_cost_plan where id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn list_vm_templates(&self) -> anyhow::Result<Vec<VmTemplate>> {
sqlx::query_as("select * from vm_template where enabled = 1 and (expires is null or expires > now())")
async fn get_vm_template(&self, id: u64) -> Result<VmTemplate> {
sqlx::query_as("select * from vm_template where id=?")
.bind(id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn list_vm_templates(&self) -> Result<Vec<VmTemplate>> {
sqlx::query_as("select * from vm_template")
.fetch_all(&self.db)
.await
.map_err(Error::new)
@ -130,23 +179,71 @@ impl LNVpsDb for LNVpsDbMysql {
.map_err(Error::new)
}
async fn insert_vm(&self, vm: Vm) -> Result<u64> {
todo!()
async fn get_vm(&self, vm_id: u64) -> Result<Vm> {
sqlx::query_as("select * from vm where id = ?")
.bind(&vm_id)
.fetch_one(&self.db)
.await
.map_err(Error::new)
}
async fn insert_vm(&self, vm: &Vm) -> Result<u64> {
Ok(sqlx::query("insert into vm(host_id,user_id,image_id,template_id,ssh_key_id,created,expires,cpu,memory,disk_size,disk_id) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) returning id")
.bind(&vm.host_id)
.bind(&vm.user_id)
.bind(&vm.image_id)
.bind(&vm.template_id)
.bind(&vm.ssh_key_id)
.bind(&vm.created)
.bind(&vm.expires)
.bind(&vm.cpu)
.bind(&vm.memory)
.bind(&vm.disk_size)
.bind(&vm.disk_id)
.fetch_one(&self.db)
.await
.map_err(Error::new)?
.try_get(0)?)
}
async fn get_vm_ip_assignments(&self, vm_id: u64) -> Result<Vec<VmIpAssignment>> {
todo!()
sqlx::query_as("select * from vm_ip_assignment where vm_id=?")
.bind(vm_id)
.fetch_all(&self.db)
.await
.map_err(Error::new)
}
async fn list_vm_payment(&self, vm_id: u64) -> Result<Vec<VmPayment>> {
todo!()
sqlx::query_as("select * from vm_payment where vm_id=?")
.bind(vm_id)
.fetch_all(&self.db)
.await
.map_err(Error::new)
}
async fn insert_vm_payment(&self, vm_payment: VmPayment) -> Result<u64> {
todo!()
async fn insert_vm_payment(&self, vm_payment: &VmPayment) -> Result<u64> {
Ok(sqlx::query("insert into vm_payment(vm_id,created,expires,amount,invoice,time_value,is_paid) values(?,?,?,?,?,?,?) returning id")
.bind(&vm_payment.vm_id)
.bind(&vm_payment.created)
.bind(&vm_payment.expires)
.bind(&vm_payment.amount)
.bind(&vm_payment.invoice)
.bind(&vm_payment.time_value)
.bind(&vm_payment.is_paid)
.fetch_one(&self.db)
.await
.map_err(Error::new)?
.try_get(0)?)
}
async fn update_vm_payment(&self, vm_payment: VmPayment) -> Result<()> {
todo!()
async fn update_vm_payment(&self, vm_payment: &VmPayment) -> Result<()> {
sqlx::query("update vm_payment set is_paid = ? where id = ?")
.bind(&vm_payment.is_paid)
.bind(&vm_payment.id)
.execute(&self.db)
.await
.map_err(Error::new)?;
Ok(())
}
}