feat: ref codes

closes #8
This commit is contained in:
2025-03-05 11:11:57 +00:00
parent b5654a533a
commit 6b909ba5bd
8 changed files with 32 additions and 12 deletions

View File

@ -0,0 +1,5 @@
-- Add migration script here
alter table users
drop column contact_nip4;
alter table vm
add column ref_code varchar(20);

View File

@ -14,9 +14,11 @@ pub struct User {
pub pubkey: Vec<u8>, pub pubkey: Vec<u8>,
/// When this user first started using the service (first login) /// When this user first started using the service (first login)
pub created: DateTime<Utc>, pub created: DateTime<Utc>,
/// Users email address for notifications
pub email: Option<String>, pub email: Option<String>,
pub contact_nip4: bool, /// If user should be contacted via NIP-17 for notifications
pub contact_nip17: bool, pub contact_nip17: bool,
/// If user should be contacted via email for notifications
pub contact_email: bool, pub contact_email: bool,
} }
@ -222,6 +224,8 @@ pub struct Vm {
pub mac_address: String, pub mac_address: String,
/// Is the VM deleted /// Is the VM deleted
pub deleted: bool, pub deleted: bool,
/// Referral code (recorded during ordering)
pub ref_code: Option<String>,
} }
#[derive(FromRow, Clone, Debug, Default)] #[derive(FromRow, Clone, Debug, Default)]

View File

@ -69,7 +69,7 @@ impl LNVpsDb for LNVpsDbMysql {
} }
async fn delete_user(&self, _id: u64) -> Result<()> { async fn delete_user(&self, _id: u64) -> Result<()> {
todo!() bail!("Deleting users is not supported")
} }
async fn insert_user_ssh_key(&self, new_key: &UserSshKey) -> Result<u64> { async fn insert_user_ssh_key(&self, new_key: &UserSshKey) -> Result<u64> {
@ -247,7 +247,7 @@ impl LNVpsDb for LNVpsDbMysql {
} }
async fn insert_vm(&self, vm: &Vm) -> Result<u64> { 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,disk_id,mac_address) values(?, ?, ?, ?, ?, ?, ?, ?, ?) returning id") Ok(sqlx::query("insert into vm(host_id,user_id,image_id,template_id,ssh_key_id,created,expires,disk_id,mac_address,ref_code) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) returning id")
.bind(vm.host_id) .bind(vm.host_id)
.bind(vm.user_id) .bind(vm.user_id)
.bind(vm.image_id) .bind(vm.image_id)
@ -257,6 +257,7 @@ impl LNVpsDb for LNVpsDbMysql {
.bind(vm.expires) .bind(vm.expires)
.bind(vm.disk_id) .bind(vm.disk_id)
.bind(&vm.mac_address) .bind(&vm.mac_address)
.bind(&vm.ref_code)
.fetch_one(&self.db) .fetch_one(&self.db)
.await .await
.map_err(Error::new)? .map_err(Error::new)?

View File

@ -207,6 +207,7 @@ pub struct CreateVmRequest {
pub template_id: u64, pub template_id: u64,
pub image_id: u64, pub image_id: u64,
pub ssh_key_id: u64, pub ssh_key_id: u64,
pub ref_code: Option<String>
} }
#[derive(Serialize, Deserialize, JsonSchema)] #[derive(Serialize, Deserialize, JsonSchema)]

View File

@ -361,14 +361,20 @@ async fn v1_create_vm_order(
auth: Nip98Auth, auth: Nip98Auth,
db: &State<Arc<dyn LNVpsDb>>, db: &State<Arc<dyn LNVpsDb>>,
provisioner: &State<Arc<LNVpsProvisioner>>, provisioner: &State<Arc<LNVpsProvisioner>>,
req: Json<CreateVmRequest>, req: Json<CreateVmRequest>
) -> ApiResult<ApiVmStatus> { ) -> ApiResult<ApiVmStatus> {
let pubkey = auth.event.pubkey.to_bytes(); let pubkey = auth.event.pubkey.to_bytes();
let uid = db.upsert_user(&pubkey).await?; let uid = db.upsert_user(&pubkey).await?;
let req = req.0; let req = req.0;
let rsp = provisioner let rsp = provisioner
.provision(uid, req.template_id, req.image_id, req.ssh_key_id) .provision(
uid,
req.template_id,
req.image_id,
req.ssh_key_id,
req.ref_code,
)
.await?; .await?;
ApiData::ok(vm_to_status(db, rsp, None).await?) ApiData::ok(vm_to_status(db, rsp, None).await?)
} }

View File

@ -173,7 +173,6 @@ impl LNVpsDb for MockDb {
pubkey: pubkey.to_vec(), pubkey: pubkey.to_vec(),
created: Utc::now(), created: Utc::now(),
email: None, email: None,
contact_nip4: false,
contact_nip17: false, contact_nip17: false,
contact_email: false, contact_email: false,
}, },
@ -193,7 +192,6 @@ impl LNVpsDb for MockDb {
u.email = user.email.clone(); u.email = user.email.clone();
u.contact_email = user.contact_email; u.contact_email = user.contact_email;
u.contact_nip17 = user.contact_nip17; u.contact_nip17 = user.contact_nip17;
u.contact_nip4 = user.contact_nip4;
} }
Ok(()) Ok(())
} }
@ -377,6 +375,7 @@ impl LNVpsDb for MockDb {
disk_id: vm.disk_id, disk_id: vm.disk_id,
mac_address: vm.mac_address.clone(), mac_address: vm.mac_address.clone(),
deleted: false, deleted: false,
ref_code: vm.ref_code.clone(),
}, },
); );
Ok(max_id + 1) Ok(max_id + 1)

View File

@ -249,6 +249,7 @@ impl LNVpsProvisioner {
template_id: u64, template_id: u64,
image_id: u64, image_id: u64,
ssh_key_id: u64, ssh_key_id: u64,
ref_code: Option<String>,
) -> Result<Vm> { ) -> Result<Vm> {
let user = self.db.get_user(user_id).await?; let user = self.db.get_user(user_id).await?;
let template = self.db.get_vm_template(template_id).await?; let template = self.db.get_vm_template(template_id).await?;
@ -272,6 +273,7 @@ impl LNVpsProvisioner {
let client = get_host_client(&pick_host, &self.provisioner_config)?; let client = get_host_client(&pick_host, &self.provisioner_config)?;
let mut new_vm = Vm { let mut new_vm = Vm {
id: 0,
host_id: pick_host.id, host_id: pick_host.id,
user_id: user.id, user_id: user.id,
image_id: image.id, image_id: image.id,
@ -280,7 +282,9 @@ impl LNVpsProvisioner {
created: Utc::now(), created: Utc::now(),
expires: Utc::now(), expires: Utc::now(),
disk_id: pick_disk.id, disk_id: pick_disk.id,
..Default::default() mac_address: "NOT FILLED YET".to_string(),
deleted: false,
ref_code,
}; };
// ask host client to generate the mac address // ask host client to generate the mac address
@ -468,7 +472,9 @@ mod tests {
}; };
let ssh_key = db.insert_user_ssh_key(&new_key).await?; let ssh_key = db.insert_user_ssh_key(&new_key).await?;
let vm = provisioner.provision(user_id, 1, 1, ssh_key).await?; let vm = provisioner
.provision(user_id, 1, 1, ssh_key, Some("mock-ref".to_string()))
.await?;
println!("{:?}", vm); println!("{:?}", vm);
provisioner.spawn_vm(vm.id).await?; provisioner.spawn_vm(vm.id).await?;
@ -477,6 +483,7 @@ mod tests {
assert_eq!(1, arp.len()); assert_eq!(1, arp.len());
let arp = arp.first().unwrap(); let arp = arp.first().unwrap();
assert_eq!(&vm.mac_address, &arp.mac_address); assert_eq!(&vm.mac_address, &arp.mac_address);
assert_eq!(vm.ref_code, Some("mock-ref".to_string()));
assert_eq!(ROUTER_BRIDGE, arp.interface.as_ref().unwrap()); assert_eq!(ROUTER_BRIDGE, arp.interface.as_ref().unwrap());
println!("{:?}", arp); println!("{:?}", arp);

View File

@ -241,9 +241,6 @@ impl Worker {
sender.send(msg).await?; sender.send(msg).await?;
} }
} }
if user.contact_nip4 {
// TODO: send nip4 dm
}
if user.contact_nip17 { if user.contact_nip17 {
if let Some(c) = self.nostr.as_ref() { if let Some(c) = self.nostr.as_ref() {
let sig = c.signer().await?; let sig = c.signer().await?;