diff --git a/Cargo.lock b/Cargo.lock
index 68ade2d..7316a52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1000,7 +1000,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
- "log",
+ "log 0.4.27",
"regex",
]
@@ -1014,7 +1014,7 @@ dependencies = [
"anstyle",
"env_filter",
"jiff",
- "log",
+ "log 0.4.27",
]
[[package]]
@@ -1266,7 +1266,7 @@ checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
dependencies = [
"cc",
"libc",
- "log",
+ "log 0.4.27",
"rustversion",
"windows 0.48.0",
]
@@ -1700,7 +1700,7 @@ dependencies = [
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
- "log",
+ "log 0.4.27",
"wasm-bindgen",
"windows-core 0.61.0",
]
@@ -1969,7 +1969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260"
dependencies = [
"jiff-static",
- "log",
+ "log 0.4.27",
"portable-atomic",
"portable-atomic-util",
"serde",
@@ -2142,7 +2142,8 @@ dependencies = [
"lnurl-rs",
"lnvps_common",
"lnvps_db",
- "log",
+ "log 0.4.27",
+ "mustache",
"native-tls",
"nostr",
"nostr-sdk",
@@ -2194,7 +2195,7 @@ dependencies = [
"hex",
"lnvps_common",
"lnvps_db",
- "log",
+ "log 0.4.27",
"rocket",
"serde",
"serde_json",
@@ -2211,6 +2212,15 @@ dependencies = [
"scopeguard",
]
+[[package]]
+name = "log"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
+dependencies = [
+ "log 0.4.27",
+]
+
[[package]]
name = "log"
version = "0.4.27"
@@ -2320,6 +2330,16 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
+[[package]]
+name = "mustache"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51956ef1c5d20a1384524d91e616fb44dfc7d8f249bf696d49c97dd3289ecab5"
+dependencies = [
+ "log 0.3.9",
+ "serde",
+]
+
[[package]]
name = "native-tls"
version = "0.2.14"
@@ -2327,7 +2347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
- "log",
+ "log 0.4.27",
"openssl",
"openssl-probe",
"openssl-sys",
@@ -2513,7 +2533,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64853d7ab065474e87696f7601cee817d200e86c42e04004e005cb3e20c3c5"
dependencies = [
- "log",
+ "log 0.4.27",
"schemars",
"serde",
"serde_json",
@@ -2938,7 +2958,7 @@ dependencies = [
"bytes",
"heck",
"itertools",
- "log",
+ "log 0.4.27",
"multimap",
"once_cell",
"petgraph",
@@ -3166,7 +3186,7 @@ dependencies = [
"hyper-util",
"ipnet",
"js-sys",
- "log",
+ "log 0.4.27",
"mime",
"native-tls",
"once_cell",
@@ -3228,7 +3248,7 @@ dependencies = [
"figment",
"futures",
"indexmap 2.8.0",
- "log",
+ "log 0.4.27",
"memchr",
"multer",
"num_cpus",
@@ -3280,7 +3300,7 @@ dependencies = [
"http 0.2.12",
"hyper 0.14.32",
"indexmap 2.8.0",
- "log",
+ "log 0.4.27",
"memchr",
"pear",
"percent-encoding",
@@ -3301,7 +3321,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074297bec35db2fc7ebb6ade6a955b5566de66f83d9af5b5602a350a71bdef43"
dependencies = [
- "log",
+ "log 0.4.27",
"okapi",
"rocket",
"rocket_okapi_codegen",
@@ -3402,7 +3422,7 @@ version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
- "log",
+ "log 0.4.27",
"ring",
"rustls-webpki 0.101.7",
"sct",
@@ -3814,7 +3834,7 @@ dependencies = [
"hashbrown 0.15.2",
"hashlink",
"indexmap 2.8.0",
- "log",
+ "log 0.4.27",
"memchr",
"once_cell",
"percent-encoding",
@@ -3893,7 +3913,7 @@ dependencies = [
"hkdf",
"hmac",
"itoa",
- "log",
+ "log 0.4.27",
"md-5",
"memchr",
"once_cell",
@@ -3933,7 +3953,7 @@ dependencies = [
"hmac",
"home",
"itoa",
- "log",
+ "log 0.4.27",
"md-5",
"memchr",
"once_cell",
@@ -3964,7 +3984,7 @@ dependencies = [
"futures-intrusive",
"futures-util",
"libsqlite3-sys",
- "log",
+ "log 0.4.27",
"percent-encoding",
"serde",
"serde_urlencoded",
@@ -4388,7 +4408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
dependencies = [
"futures-util",
- "log",
+ "log 0.4.27",
"native-tls",
"tokio",
"tokio-native-tls",
@@ -4402,7 +4422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
dependencies = [
"futures-util",
- "log",
+ "log 0.4.27",
"rustls 0.23.25",
"rustls-pki-types",
"tokio",
@@ -4554,7 +4574,7 @@ version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
- "log",
+ "log 0.4.27",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -4587,7 +4607,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
- "log",
+ "log 0.4.27",
"once_cell",
"tracing-core",
]
@@ -4633,7 +4653,7 @@ dependencies = [
"data-encoding",
"http 1.3.1",
"httparse",
- "log",
+ "log 0.4.27",
"native-tls",
"rand 0.8.5",
"sha1",
@@ -4652,7 +4672,7 @@ dependencies = [
"data-encoding",
"http 1.3.1",
"httparse",
- "log",
+ "log 0.4.27",
"rand 0.9.0",
"rustls 0.23.25",
"rustls-pki-types",
@@ -4885,7 +4905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
- "log",
+ "log 0.4.27",
"proc-macro2",
"quote",
"syn 2.0.100",
diff --git a/lnvps_api/Cargo.toml b/lnvps_api/Cargo.toml
index 00b0210..831a9c6 100644
--- a/lnvps_api/Cargo.toml
+++ b/lnvps_api/Cargo.toml
@@ -57,6 +57,7 @@ lettre = { version = "0.11.10", features = ["tokio1-native-tls"] }
ws = { package = "rocket_ws", version = "0.1.1" }
native-tls = "0.2.12"
lnurl-rs = { version = "0.9.0", default-features = false }
+mustache = "0.9.0"
futures = "0.3.31"
isocountry = "0.3.2"
diff --git a/lnvps_api/invoice.html b/lnvps_api/invoice.html
new file mode 100644
index 0000000..8a9a3ac
--- /dev/null
+++ b/lnvps_api/invoice.html
@@ -0,0 +1,159 @@
+
+
+
+ {{payment.id}}
+
+
+
+
+
+
+
+
+
+
+
Invoice
+
+
+ ID:
+ {{payment.id}}
+
+
+ Date:
+ {{payment.created}}
+
+
+ Status:
+ {{#payment.is_paid}}Paid{{/payment.is_paid}}
+ {{^payment.is_paid}}Unpaid{{/payment.is_paid}}
+
+
+ Nostr Pubkey:
+ {{npub}}
+
+
+
+
+
Bill To:
+
{{user.name}}
+
{{user.address_1}}
+
{{user.address_2}}
+
{{user.city}}
+
{{user.state}}
+
{{user.postcode}}
+
{{user.country}}
+
{{user.tax_id}}
+
+ {{#company}}
+
+
+
{{company.name}}
+
{{company.address_1}}
+
{{company.address_2}}
+
{{company.city}}
+
{{company.state}}
+
{{company.postcode}}
+
{{company.country}}
+
{{company.tax_id}}
+
+ {{/company}}
+
+
+
Details:
+
+
+
+ Description |
+ Currency |
+ Gross |
+ Taxes |
+
+
+
+
+
+ VM Renewal #{{vm.id}}
+ - {{vm.template.name}}
+ - {{vm.image.distribution}} {{vm.image.version}}
+ - {{payment.time}} seconds
+ |
+ {{payment.currency}} |
+ {{payment.amount}} |
+ {{payment.tax}} |
+
+
+
+
+
+ Total: {{total}}
+ |
+
+
+
+
+
+ All BTC amounts are in milli-satoshis and all fiat amounts are in cents.
+
+
+
+ (c) {{year}} LNVPS.net - Generated at {{current_date}}
+
+
+
+
\ No newline at end of file
diff --git a/lnvps_api/src/api/model.rs b/lnvps_api/src/api/model.rs
index 0da06bb..30cf0cf 100644
--- a/lnvps_api/src/api/model.rs
+++ b/lnvps_api/src/api/model.rs
@@ -425,6 +425,24 @@ pub struct AccountPatchRequest {
pub tax_id: Option,
}
+impl From for AccountPatchRequest {
+ fn from(user: lnvps_db::User) -> Self {
+ AccountPatchRequest {
+ email: user.email,
+ contact_nip17: user.contact_nip17,
+ contact_email: user.contact_email,
+ country_code: user.country_code,
+ name: user.billing_name,
+ address_1: user.billing_address_1,
+ address_2: user.billing_address_2,
+ state: user.billing_state,
+ city: user.billing_city,
+ postcode: user.billing_postcode,
+ tax_id: user.billing_tax_id,
+ }
+ }
+}
+
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct CreateVmRequest {
pub template_id: u64,
@@ -574,3 +592,45 @@ impl From for ApiPaymentMethod {
}
}
}
+
+#[derive(Serialize, Deserialize, JsonSchema)]
+pub struct ApiCompany {
+ pub id: u64,
+ pub name: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub email: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub country_code: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub address_1: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub address_2: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub state: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub city: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub postcode: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub tax_id: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub phone: Option,
+}
+
+impl From for ApiCompany {
+ fn from(value: lnvps_db::Company) -> Self {
+ Self {
+ email: value.email,
+ country_code: value.country_code,
+ name: value.name,
+ id: value.id,
+ address_1: value.address_1,
+ address_2: value.address_2,
+ state: value.state,
+ city: value.city,
+ postcode: value.postcode,
+ tax_id: value.tax_id,
+ phone: value.phone,
+ }
+ }
+}
diff --git a/lnvps_api/src/api/routes.rs b/lnvps_api/src/api/routes.rs
index 6e13fc4..e8d4f51 100644
--- a/lnvps_api/src/api/routes.rs
+++ b/lnvps_api/src/api/routes.rs
@@ -1,5 +1,5 @@
use crate::api::model::{
- AccountPatchRequest, ApiCustomTemplateParams, ApiCustomVmOrder, ApiCustomVmRequest,
+ AccountPatchRequest, ApiCompany, ApiCustomTemplateParams, ApiCustomVmOrder, ApiCustomVmRequest,
ApiPaymentInfo, ApiPaymentMethod, ApiPrice, ApiTemplatesResponse, ApiUserSshKey,
ApiVmIpAssignment, ApiVmOsImage, ApiVmPayment, ApiVmStatus, ApiVmTemplate, CreateSshKey,
CreateVmRequest, VMPatchRequest,
@@ -12,6 +12,7 @@ use crate::settings::Settings;
use crate::status::{VmState, VmStateCache};
use crate::worker::WorkJob;
use anyhow::{bail, Result};
+use chrono::{DateTime, Datelike, Utc};
use futures::future::join_all;
use futures::{SinkExt, StreamExt};
use isocountry::CountryCode;
@@ -22,7 +23,8 @@ use lnvps_db::{
};
use log::{error, info};
use nostr::util::hex;
-use nostr::Url;
+use nostr::{ToBech32, Url};
+use rocket::http::ContentType;
use rocket::serde::json::Json;
use rocket::{get, patch, post, routes, Responder, Route, State};
use rocket_okapi::gen::OpenApiGenerator;
@@ -34,6 +36,7 @@ use serde::{Deserialize, Serialize};
use ssh_key::PublicKey;
use std::collections::{HashMap, HashSet};
use std::fmt::Display;
+use std::io::{BufWriter, Cursor};
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::mpsc::{Sender, UnboundedSender};
@@ -71,7 +74,8 @@ pub fn routes() -> Vec {
routes.append(&mut routes![
v1_terminal_proxy,
v1_lnurlp,
- v1_renew_vm_lnurlp
+ v1_renew_vm_lnurlp,
+ v1_get_payment_invoice
]);
routes
@@ -156,19 +160,7 @@ async fn v1_get_account(
let uid = db.upsert_user(&pubkey).await?;
let user = db.get_user(uid).await?;
- ApiData::ok(AccountPatchRequest {
- email: user.email,
- contact_nip17: user.contact_nip17,
- contact_email: user.contact_email,
- country_code: user.country_code,
- name: user.billing_name,
- address_1: user.billing_address_1,
- address_2: user.billing_address_2,
- state: user.billing_state,
- city: user.billing_city,
- postcode: user.billing_postcode,
- tax_id: user.billing_tax_id,
- })
+ ApiData::ok(user.into())
}
async fn vm_to_status(
@@ -894,6 +886,99 @@ async fn v1_get_payment(
ApiData::ok(payment.into())
}
+/// Print payment invoice
+#[get("/api/v1/payment//invoice?")]
+async fn v1_get_payment_invoice(
+ db: &State>,
+ id: &str,
+ auth: &str,
+) -> Result<(ContentType, Vec), &'static str> {
+ let auth = Nip98Auth::from_base64(auth).map_err(|e| "Missing or invalid auth param")?;
+ if auth
+ .check(&format!("/api/v1/payment/{id}/invoice"), "GET")
+ .is_err()
+ {
+ return Err("Invalid auth event");
+ }
+ let pubkey = auth.event.pubkey.to_bytes();
+ let uid = db.upsert_user(&pubkey).await.map_err(|_| "Insert failed")?;
+ let id = if let Ok(i) = hex::decode(id) {
+ i
+ } else {
+ return Err("Invalid payment id");
+ };
+
+ let payment = db
+ .get_vm_payment(&id)
+ .await
+ .map_err(|_| "Payment not found")?;
+ let vm = db.get_vm(payment.vm_id).await.map_err(|_| "VM not found")?;
+ if vm.user_id != uid {
+ return Err("VM does not belong to you");
+ }
+
+ if !payment.is_paid {
+ return Err("Payment is not paid, can't generate invoice");
+ }
+
+ #[derive(Serialize)]
+ struct PaymentInfo {
+ year: i32,
+ current_date: DateTime,
+ vm: ApiVmStatus,
+ payment: ApiVmPayment,
+ user: AccountPatchRequest,
+ npub: String,
+ total: u64,
+ company: Option,
+ }
+
+ let host = db
+ .get_host(vm.host_id)
+ .await
+ .map_err(|_| "Host not found")?;
+ let region = db
+ .get_host_region(host.region_id)
+ .await
+ .map_err(|_| "Region not found")?;
+ let company = if let Some(c) = region.company_id {
+ Some(db.get_company(c).await.map_err(|_| "Company not found")?)
+ } else {
+ None
+ };
+ let user = db.get_user(uid).await.map_err(|_| "User not found")?;
+ #[cfg(debug_assertions)]
+ let template =
+ mustache::compile_path("lnvps_api/invoice.html").map_err(|_| "Invalid template")?;
+ #[cfg(not(debug_assertions))]
+ let template = mustache::compile_str(include_str!("../../invoice.html"))
+ .map_err(|_| "Invalid template")?;
+
+ let now = Utc::now();
+ let mut html = Cursor::new(Vec::new());
+ template
+ .render(
+ &mut html,
+ &PaymentInfo {
+ year: now.year(),
+ current_date: now,
+ vm: vm_to_status(db, vm, None)
+ .await
+ .map_err(|_| "Failed to get VM state")?,
+ total: payment.amount + payment.tax,
+ payment: payment.into(),
+ npub: nostr::PublicKey::from_slice(&user.pubkey)
+ .map_err(|_| "Invalid pubkey")?
+ .to_bech32()
+ .unwrap(),
+ user: user.into(),
+ company: company.map(|c| c.into()),
+ },
+ )
+ .map_err(|_| "Failed to generate invoice")?;
+ Ok((ContentType::HTML, html.into_inner()))
+}
+
/// List payment history of a VM
#[openapi(tag = "VM")]
#[get("/api/v1/vm//payments")]
diff --git a/lnvps_api/src/mocks.rs b/lnvps_api/src/mocks.rs
index 7c27406..5f0d670 100644
--- a/lnvps_api/src/mocks.rs
+++ b/lnvps_api/src/mocks.rs
@@ -10,12 +10,7 @@ use crate::status::{VmRunningState, VmState};
use anyhow::{anyhow, bail, ensure, Context};
use chrono::{DateTime, TimeDelta, Utc};
use fedimint_tonic_lnd::tonic::codegen::tokio_stream::Stream;
-use lnvps_db::{
- async_trait, AccessPolicy, DiskInterface, DiskType, IpRange, IpRangeAllocationMode,
- LNVPSNostrDb, LNVpsDb, NostrDomain, NostrDomainHandle, OsDistribution, User, UserSshKey, Vm,
- VmCostPlan, VmCostPlanIntervalType, VmCustomPricing, VmCustomPricingDisk, VmCustomTemplate,
- VmHost, VmHostDisk, VmHostKind, VmHostRegion, VmIpAssignment, VmOsImage, VmPayment, VmTemplate,
-};
+use lnvps_db::{async_trait, AccessPolicy, Company, DiskInterface, DiskType, IpRange, IpRangeAllocationMode, LNVPSNostrDb, LNVpsDb, NostrDomain, NostrDomainHandle, OsDistribution, User, UserSshKey, Vm, VmCostPlan, VmCostPlanIntervalType, VmCustomPricing, VmCustomPricingDisk, VmCustomTemplate, VmHost, VmHostDisk, VmHostKind, VmHostRegion, VmIpAssignment, VmOsImage, VmPayment, VmTemplate};
use std::collections::HashMap;
use std::ops::Add;
use std::pin::Pin;
@@ -108,6 +103,7 @@ impl Default for MockDb {
id: 1,
name: "Mock".to_string(),
enabled: true,
+ company_id: None,
},
);
let mut ip_ranges = HashMap::new();
@@ -695,6 +691,10 @@ impl LNVpsDb for MockDb {
.cloned()
.context("no access policy")?)
}
+
+ async fn get_company(&self, company_id: u64) -> anyhow::Result {
+ todo!()
+ }
}
#[derive(Debug, Clone)]
diff --git a/lnvps_db/migrations/20250501162308_company_info.sql b/lnvps_db/migrations/20250501162308_company_info.sql
new file mode 100644
index 0000000..e031038
--- /dev/null
+++ b/lnvps_db/migrations/20250501162308_company_info.sql
@@ -0,0 +1,19 @@
+-- Add migration script here
+create table company
+(
+ id integer unsigned not null auto_increment primary key,
+ created timestamp not null default current_timestamp,
+ name varchar(100) not null,
+ email varchar(100) not null,
+ phone varchar(100),
+ address_1 varchar(200),
+ address_2 varchar(200),
+ city varchar(100),
+ state varchar(100),
+ postcode varchar(50),
+ country_code varchar(3),
+ tax_id varchar(50)
+);
+alter table vm_host_region
+ add column company_id integer unsigned,
+ add constraint fk_host_region_company foreign key (company_id) references company (id);
\ No newline at end of file
diff --git a/lnvps_db/src/lib.rs b/lnvps_db/src/lib.rs
index 51dbf33..ea94c8a 100644
--- a/lnvps_db/src/lib.rs
+++ b/lnvps_db/src/lib.rs
@@ -172,6 +172,9 @@ pub trait LNVpsDb: LNVPSNostrDb + Send + Sync {
/// Get access policy
async fn get_access_policy(&self, access_policy_id: u64) -> Result;
+
+ /// Get company
+ async fn get_company(&self, company_id: u64) -> Result;
}
#[cfg(feature = "nostr-domain")]
diff --git a/lnvps_db/src/model.rs b/lnvps_db/src/model.rs
index 8187362..e9bc7c2 100644
--- a/lnvps_db/src/model.rs
+++ b/lnvps_db/src/model.rs
@@ -71,6 +71,7 @@ pub struct VmHostRegion {
pub id: u64,
pub name: String,
pub enabled: bool,
+ pub company_id: Option,
}
#[derive(FromRow, Clone, Debug, Default)]
@@ -524,3 +525,19 @@ pub struct NostrDomainHandle {
pub pubkey: Vec,
pub relays: Option,
}
+
+#[derive(FromRow, Clone, Debug, Default)]
+pub struct Company {
+ pub id: u64,
+ pub created: DateTime,
+ pub name: String,
+ pub address_1: Option,
+ pub address_2: Option,
+ pub city: Option,
+ pub state: Option,
+ pub country_code: Option,
+ pub tax_id: Option,
+ pub postcode: Option,
+ pub phone: Option,
+ pub email: Option,
+}
diff --git a/lnvps_db/src/mysql.rs b/lnvps_db/src/mysql.rs
index e443e13..d1cde7c 100644
--- a/lnvps_db/src/mysql.rs
+++ b/lnvps_db/src/mysql.rs
@@ -1,8 +1,4 @@
-use crate::{
- AccessPolicy, IpRange, LNVPSNostrDb, LNVpsDb, NostrDomain, NostrDomainHandle, Router, User,
- UserSshKey, Vm, VmCostPlan, VmCustomPricing, VmCustomPricingDisk, VmCustomTemplate, VmHost,
- VmHostDisk, VmHostRegion, VmIpAssignment, VmOsImage, VmPayment, VmTemplate,
-};
+use crate::{AccessPolicy, Company, IpRange, LNVPSNostrDb, LNVpsDb, NostrDomain, NostrDomainHandle, Router, User, UserSshKey, Vm, VmCostPlan, VmCustomPricing, VmCustomPricingDisk, VmCustomTemplate, VmHost, VmHostDisk, VmHostRegion, VmIpAssignment, VmOsImage, VmPayment, VmTemplate};
use anyhow::{bail, Error, Result};
use async_trait::async_trait;
use sqlx::{Executor, MySqlPool, Row};
@@ -562,6 +558,14 @@ impl LNVpsDb for LNVpsDbMysql {
.await
.map_err(Error::new)
}
+
+ async fn get_company(&self, company_id: u64) -> Result {
+ sqlx::query_as("select * from company where id=?")
+ .bind(company_id)
+ .fetch_one(&self.db)
+ .await
+ .map_err(Error::new)
+ }
}
#[cfg(feature = "nostr-domain")]