progress
This commit is contained in:
82
lnvps_db/src/lib.rs
Normal file
82
lnvps_db/src/lib.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
|
||||
mod model;
|
||||
#[cfg(feature = "mysql")]
|
||||
mod mysql;
|
||||
|
||||
pub use model::*;
|
||||
#[cfg(feature = "mysql")]
|
||||
pub use mysql::*;
|
||||
|
||||
#[async_trait]
|
||||
pub trait LNVpsDb: Sync + Send {
|
||||
/// Migrate database
|
||||
async fn migrate(&self) -> Result<()>;
|
||||
|
||||
/// Insert/Fetch user by pubkey
|
||||
async fn upsert_user(&self, pubkey: &[u8; 32]) -> Result<u64>;
|
||||
|
||||
/// Get a user by id
|
||||
async fn get_user(&self, id: u64) -> Result<User>;
|
||||
|
||||
/// Update user record
|
||||
async fn update_user(&self, user: &User) -> Result<()>;
|
||||
|
||||
/// Delete user record
|
||||
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>;
|
||||
|
||||
/// Get user ssh key by id
|
||||
async fn get_user_ssh_key(&self, id: u64) -> Result<UserSshKey>;
|
||||
|
||||
/// Delete a user ssh key by id
|
||||
async fn delete_user_ssh_key(&self, id: u64) -> Result<()>;
|
||||
|
||||
/// List a users ssh keys
|
||||
async fn list_user_ssh_key(&self, user_id: u64) -> Result<Vec<UserSshKey>>;
|
||||
|
||||
/// Get VM host region by id
|
||||
async fn get_host_region(&self, id: u64) -> Result<VmHostRegion>;
|
||||
|
||||
/// List VM's owned by a specific user
|
||||
async fn list_hosts(&self) -> Result<Vec<VmHost>>;
|
||||
|
||||
/// Update host resources (usually from [auto_discover])
|
||||
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>>;
|
||||
|
||||
/// List available OS images
|
||||
async fn list_os_image(&self) -> Result<Vec<VmOsImage>>;
|
||||
|
||||
/// List available IP Ranges
|
||||
async fn list_ip_range(&self) -> Result<Vec<IpRange>>;
|
||||
|
||||
/// Get a VM cost plan by id
|
||||
async fn get_cost_plan(&self, id: u64) -> Result<VmCostPlan>;
|
||||
|
||||
/// 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>>;
|
||||
|
||||
/// Insert a new VM record
|
||||
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>>;
|
||||
|
||||
/// List payments by VM id
|
||||
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>;
|
||||
|
||||
/// Update a VM payment record
|
||||
async fn update_vm_payment(&self, vm_payment: VmPayment) -> Result<()>;
|
||||
}
|
215
lnvps_db/src/model.rs
Normal file
215
lnvps_db/src/model.rs
Normal file
@ -0,0 +1,215 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
/// Users who buy VM's
|
||||
pub struct User {
|
||||
/// Unique ID of this user (database generated)
|
||||
pub id: u64,
|
||||
/// The nostr public key for this user
|
||||
pub pubkey: Vec<u8>,
|
||||
/// When this user first started using the service (first login)
|
||||
pub created: DateTime<Utc>,
|
||||
|
||||
pub email: Option<String>,
|
||||
pub contact_nip4: bool,
|
||||
pub contact_nip17: bool,
|
||||
pub contact_email: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct UserSshKey {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub user_id: u64,
|
||||
pub created: DateTime<Utc>,
|
||||
pub key_data: String,
|
||||
|
||||
#[sqlx(skip)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub vms: Option<Vec<Vm>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
|
||||
#[repr(u16)]
|
||||
/// The type of VM host
|
||||
pub enum VmHostKind {
|
||||
Proxmox = 0,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct VmHostRegion {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
/// A VM host
|
||||
pub struct VmHost {
|
||||
/// Unique id of this host
|
||||
pub id: u64,
|
||||
/// The host kind (Hypervisor)
|
||||
pub kind: VmHostKind,
|
||||
/// What region / group this host is part of
|
||||
pub region_id: u64,
|
||||
/// Internal name of this host
|
||||
pub name: String,
|
||||
/// Endpoint for controlling this host
|
||||
pub ip: String,
|
||||
/// Total number of CPU cores
|
||||
pub cpu: u16,
|
||||
/// Total memory size in bytes
|
||||
pub memory: u64,
|
||||
/// If VM's should be provisioned on this host
|
||||
pub enabled: bool,
|
||||
/// API token used to control this host via [ip]
|
||||
pub api_token: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct VmHostDisk {
|
||||
pub id: u64,
|
||||
pub host_id: u64,
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
pub kind: DiskType,
|
||||
pub interface: DiskInterface,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
|
||||
#[repr(u16)]
|
||||
pub enum DiskType {
|
||||
HDD = 0,
|
||||
SSD = 1,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
|
||||
#[repr(u16)]
|
||||
pub enum DiskInterface {
|
||||
SATA = 0,
|
||||
SCSI = 1,
|
||||
PCIe = 2,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
|
||||
#[repr(u16)]
|
||||
pub enum OsDistribution {
|
||||
Ubuntu = 0,
|
||||
Debian = 1,
|
||||
}
|
||||
|
||||
/// OS Images are templates which are used as a basis for
|
||||
/// provisioning new vms
|
||||
#[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,
|
||||
/// URL location of cloud image
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct IpRange {
|
||||
pub id: u64,
|
||||
pub cidr: String,
|
||||
pub enabled: bool,
|
||||
pub region_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, sqlx::Type)]
|
||||
#[repr(u16)]
|
||||
pub enum VmCostPlanIntervalType {
|
||||
Day = 0,
|
||||
Month = 1,
|
||||
Year = 2,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct VmCostPlan {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub created: DateTime<Utc>,
|
||||
pub amount: u64,
|
||||
pub currency: String,
|
||||
pub interval_amount: u64,
|
||||
pub interval_type: VmCostPlanIntervalType,
|
||||
}
|
||||
|
||||
/// Offers.
|
||||
/// These are the same as the offers visible to customers
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct VmTemplate {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub enabled: bool,
|
||||
pub created: DateTime<Utc>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expires: Option<DateTime<Utc>>,
|
||||
pub cpu: u16,
|
||||
pub memory: u64,
|
||||
pub disk_type: DiskType,
|
||||
pub disk_interface: DiskInterface,
|
||||
pub cost_plan_id: u64,
|
||||
pub region_id: u64,
|
||||
|
||||
#[sqlx(skip)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cost_plan: Option<VmCostPlan>,
|
||||
#[sqlx(skip)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub region: Option<VmHostRegion>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct Vm {
|
||||
/// Unique VM ID (Same in proxmox)
|
||||
pub id: u64,
|
||||
/// The host this VM is on
|
||||
pub host_id: u64,
|
||||
/// The user that owns this VM
|
||||
pub user_id: u64,
|
||||
/// The base image of this VM
|
||||
pub image_id: u64,
|
||||
/// The base image of this VM
|
||||
pub template_id: u64,
|
||||
/// Users ssh-key assigned to this VM
|
||||
pub ssh_key_id: u64,
|
||||
/// When the VM was created
|
||||
pub created: DateTime<Utc>,
|
||||
/// When the VM expires
|
||||
pub expires: DateTime<Utc>,
|
||||
/// How many vCPU's this VM has
|
||||
pub cpu: u16,
|
||||
/// How much RAM this VM has in bytes
|
||||
pub memory: u64,
|
||||
/// How big the disk is on this VM in bytes
|
||||
pub disk_size: u64,
|
||||
/// The [VmHostDisk] this VM is on
|
||||
pub disk_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct VmIpAssignment {
|
||||
pub id: u64,
|
||||
pub vm_id: u64,
|
||||
pub ip_range_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow, Clone, Debug)]
|
||||
pub struct VmPayment {
|
||||
pub id: u64,
|
||||
pub vm_id: u64,
|
||||
pub created: DateTime<Utc>,
|
||||
pub expires: DateTime<Utc>,
|
||||
pub amount: u64,
|
||||
pub invoice: String,
|
||||
pub time_value: u64,
|
||||
pub is_paid: bool,
|
||||
}
|
152
lnvps_db/src/mysql.rs
Normal file
152
lnvps_db/src/mysql.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use crate::{IpRange, LNVpsDb, User, UserSshKey, Vm, VmCostPlan, VmHost, VmHostDisk, VmHostRegion, VmIpAssignment, VmOsImage, VmPayment, VmTemplate};
|
||||
use anyhow::{Error, Result};
|
||||
use async_trait::async_trait;
|
||||
use sqlx::{Executor, MySqlPool, Row};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LNVpsDbMysql {
|
||||
db: MySqlPool,
|
||||
}
|
||||
|
||||
impl LNVpsDbMysql {
|
||||
pub async fn new(conn: &str) -> Result<Self> {
|
||||
let db = MySqlPool::connect(conn).await?;
|
||||
Ok(Self {
|
||||
db
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub async fn execute(&self, sql: &str) -> Result<()> {
|
||||
self.db.execute(sql).await.map_err(Error::new)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LNVpsDb for LNVpsDbMysql {
|
||||
async fn migrate(&self) -> anyhow::Result<()> {
|
||||
sqlx::migrate!().run(&self.db).await.map_err(Error::new)
|
||||
}
|
||||
|
||||
async fn upsert_user(&self, pubkey: &[u8; 32]) -> anyhow::Result<u64> {
|
||||
let res = sqlx::query("insert ignore into users(pubkey) values(?) returning id")
|
||||
.bind(pubkey.as_slice())
|
||||
.fetch_optional(&self.db)
|
||||
.await?;
|
||||
match res {
|
||||
None => sqlx::query("select id from users where pubkey = ?")
|
||||
.bind(pubkey.as_slice())
|
||||
.fetch_one(&self.db)
|
||||
.await?
|
||||
.try_get(0)
|
||||
.map_err(Error::new),
|
||||
Some(res) => res.try_get(0).map_err(Error::new),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_user(&self, id: u64) -> Result<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_user(&self, user: &User) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_user(&self, id: u64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn insert_user_ssh_key(&self, new_key: UserSshKey) -> Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_user_ssh_key(&self, id: u64) -> Result<UserSshKey> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_user_ssh_key(&self, id: u64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_user_ssh_key(&self, user_id: u64) -> Result<Vec<UserSshKey>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_host_region(&self, id: u64) -> Result<VmHostRegion> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_hosts(&self) -> anyhow::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<()> {
|
||||
sqlx::query("update vm_host set name = ?, cpu = ?, memory = ? where id = ?")
|
||||
.bind(&host.name)
|
||||
.bind(&host.cpu)
|
||||
.bind(&host.memory)
|
||||
.bind(&host.id)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_host_disks(&self, host_id: u64) -> anyhow::Result<Vec<VmHostDisk>> {
|
||||
sqlx::query_as("select * from vm_host_disk where host_id = ?")
|
||||
.bind(&host_id)
|
||||
.fetch_all(&self.db)
|
||||
.await
|
||||
.map_err(Error::new)
|
||||
}
|
||||
|
||||
async fn list_os_image(&self) -> Result<Vec<VmOsImage>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_ip_range(&self) -> Result<Vec<IpRange>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_cost_plan(&self, id: u64) -> Result<VmCostPlan> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
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())")
|
||||
.fetch_all(&self.db)
|
||||
.await
|
||||
.map_err(Error::new)
|
||||
}
|
||||
|
||||
async fn list_user_vms(&self, id: u64) -> Result<Vec<Vm>> {
|
||||
sqlx::query_as("select * from vm where user_id = ?")
|
||||
.bind(&id)
|
||||
.fetch_all(&self.db)
|
||||
.await
|
||||
.map_err(Error::new)
|
||||
}
|
||||
|
||||
async fn insert_vm(&self, vm: Vm) -> Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_vm_ip_assignments(&self, vm_id: u64) -> Result<Vec<VmIpAssignment>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_vm_payment(&self, vm_id: u64) -> Result<Vec<VmPayment>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn insert_vm_payment(&self, vm_payment: VmPayment) -> Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_vm_payment(&self, vm_payment: VmPayment) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user