feat: expire soon notification
This commit is contained in:
parent
3e7e0a789b
commit
7bfeba0ad1
@ -5,16 +5,14 @@ use crate::worker::WorkJob;
|
|||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use lnvps_db::hydrate::Hydrate;
|
use lnvps_db::hydrate::Hydrate;
|
||||||
use lnvps_db::{LNVpsDb, UserSshKey, Vm, VmOsImage, VmPayment, VmTemplate};
|
use lnvps_db::{LNVpsDb, UserSshKey, Vm, VmOsImage, VmPayment, VmTemplate};
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error};
|
||||||
use nostr::util::hex;
|
use nostr::util::hex;
|
||||||
use rocket::futures::{Sink, SinkExt, StreamExt};
|
use rocket::futures::{Sink, SinkExt, StreamExt};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use rocket::{get, patch, post, routes, Responder, Route, State};
|
use rocket::{get, patch, post, routes, Responder, Route, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ssh_key::PublicKey;
|
use ssh_key::PublicKey;
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::mem::transmute;
|
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use ws::Message;
|
use ws::Message;
|
||||||
|
|
||||||
@ -305,7 +303,7 @@ async fn v1_terminal_proxy(
|
|||||||
id: u64,
|
id: u64,
|
||||||
ws: ws::WebSocket,
|
ws: ws::WebSocket,
|
||||||
) -> Result<ws::Channel<'static>, &'static str> {
|
) -> Result<ws::Channel<'static>, &'static str> {
|
||||||
let auth = Nip98Auth::from_base64(auth).map_err(|e| "Missing or invalid auth param")?;
|
let auth = Nip98Auth::from_base64(auth).map_err(|_| "Missing or invalid auth param")?;
|
||||||
if auth.check(&format!("/api/v1/console/{id}"), "GET").is_err() {
|
if auth.check(&format!("/api/v1/console/{id}"), "GET").is_err() {
|
||||||
return Err("Invalid auth event");
|
return Err("Invalid auth event");
|
||||||
}
|
}
|
||||||
@ -321,7 +319,7 @@ async fn v1_terminal_proxy(
|
|||||||
"Failed to open terminal proxy"
|
"Failed to open terminal proxy"
|
||||||
})?;
|
})?;
|
||||||
let ws = ws.config(Default::default());
|
let ws = ws.config(Default::default());
|
||||||
Ok(ws.channel(move |mut stream| {
|
Ok(ws.channel(move |stream| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let (mut tx_upstream, mut rx_upstream) = ws_upstream.split();
|
let (mut tx_upstream, mut rx_upstream) = ws_upstream.split();
|
||||||
let (mut tx_client, mut rx_client) = stream.split();
|
let (mut tx_client, mut rx_client) = stream.split();
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use reqwest::{ClientBuilder, Method, Url};
|
use reqwest::{ClientBuilder, Method, Url};
|
||||||
use rocket::futures::{SinkExt, StreamExt};
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tokio_tungstenite::tungstenite::handshake::client::{generate_key, Request};
|
use tokio_tungstenite::tungstenite::handshake::client::{generate_key, Request};
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
|
||||||
use tokio_tungstenite::{Connector, MaybeTlsStream, WebSocketStream};
|
use tokio_tungstenite::{Connector, MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
pub struct ProxmoxClient {
|
pub struct ProxmoxClient {
|
||||||
|
@ -3,7 +3,6 @@ use base64::prelude::BASE64_STANDARD;
|
|||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use nostr::{Event, JsonUtil, Kind, Timestamp};
|
use nostr::{Event, JsonUtil, Kind, Timestamp};
|
||||||
use reqwest::Url;
|
|
||||||
use rocket::http::uri::{Absolute, Uri};
|
use rocket::http::uri::{Absolute, Uri};
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::request::{FromRequest, Outcome};
|
use rocket::request::{FromRequest, Outcome};
|
||||||
|
@ -26,7 +26,7 @@ use std::collections::HashSet;
|
|||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::net::{TcpSocket, TcpStream};
|
use tokio::net::TcpStream;
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
|
@ -46,5 +46,8 @@ pub trait Provisioner: Send + Sync {
|
|||||||
async fn delete_vm(&self, vm_id: u64) -> Result<()>;
|
async fn delete_vm(&self, vm_id: u64) -> Result<()>;
|
||||||
|
|
||||||
/// Open terminal proxy connection
|
/// Open terminal proxy connection
|
||||||
async fn terminal_proxy(&self, vm_id: u64) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>>;
|
async fn terminal_proxy(
|
||||||
|
&self,
|
||||||
|
vm_id: u64,
|
||||||
|
) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>>;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use crate::provisioner::Provisioner;
|
|||||||
use crate::settings::{Settings, SmtpConfig};
|
use crate::settings::{Settings, SmtpConfig};
|
||||||
use crate::status::{VmRunningState, VmState, VmStateCache};
|
use crate::status::{VmRunningState, VmState, VmStateCache};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::{Days, Utc};
|
use chrono::{DateTime, Days, Utc};
|
||||||
use lettre::message::{MessageBuilder, MultiPart};
|
use lettre::message::{MessageBuilder, MultiPart};
|
||||||
use lettre::transport::smtp::authentication::Credentials;
|
use lettre::transport::smtp::authentication::Credentials;
|
||||||
use lettre::AsyncTransport;
|
use lettre::AsyncTransport;
|
||||||
@ -40,7 +40,7 @@ pub struct Worker {
|
|||||||
tx: UnboundedSender<WorkJob>,
|
tx: UnboundedSender<WorkJob>,
|
||||||
rx: UnboundedReceiver<WorkJob>,
|
rx: UnboundedReceiver<WorkJob>,
|
||||||
client: Option<Client>,
|
client: Option<Client>,
|
||||||
last_check_vms: u64,
|
last_check_vms: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorkerSettings {
|
pub struct WorkerSettings {
|
||||||
@ -74,7 +74,7 @@ impl Worker {
|
|||||||
tx,
|
tx,
|
||||||
rx,
|
rx,
|
||||||
client,
|
client,
|
||||||
last_check_vms: Utc::now().timestamp() as u64,
|
last_check_vms: Utc::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +102,22 @@ impl Worker {
|
|||||||
self.vm_state_cache.set_state(db_id, state).await?;
|
self.vm_state_cache.set_state(db_id, state).await?;
|
||||||
|
|
||||||
if let Ok(db_vm) = self.db.get_vm(db_id).await {
|
if let Ok(db_vm) = self.db.get_vm(db_id).await {
|
||||||
|
const BEFORE_EXPIRE_NOTIFICATION: u64 = 1;
|
||||||
|
|
||||||
|
// Send notification of VM expiring soon
|
||||||
|
if db_vm.expires < Utc::now().add(Days::new(BEFORE_EXPIRE_NOTIFICATION))
|
||||||
|
&& db_vm.expires
|
||||||
|
> self
|
||||||
|
.last_check_vms
|
||||||
|
.add(Days::new(BEFORE_EXPIRE_NOTIFICATION))
|
||||||
|
{
|
||||||
|
self.tx.send(WorkJob::SendNotification {
|
||||||
|
user_id: db_vm.user_id,
|
||||||
|
title: Some(format!("[VM{}] Expiring Soon", db_vm.id)),
|
||||||
|
message: format!("Your VM #{} will expire soon, please renew in the next {} days or your VM will be stopped.", db_vm.id, BEFORE_EXPIRE_NOTIFICATION)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
// Stop VM if expired and is running
|
// Stop VM if expired and is running
|
||||||
if db_vm.expires < Utc::now() && s.status == VmStatus::Running {
|
if db_vm.expires < Utc::now() && s.status == VmStatus::Running {
|
||||||
info!("Stopping expired VM {}", db_vm.id);
|
info!("Stopping expired VM {}", db_vm.id);
|
||||||
@ -171,7 +187,7 @@ impl Worker {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_vms(&self) -> Result<()> {
|
pub async fn check_vms(&mut self) -> Result<()> {
|
||||||
let hosts = self.db.list_hosts().await?;
|
let hosts = self.db.list_hosts().await?;
|
||||||
for host in hosts {
|
for host in hosts {
|
||||||
let client = get_host_client(&host)?;
|
let client = get_host_client(&host)?;
|
||||||
@ -208,6 +224,8 @@ impl Worker {
|
|||||||
self.check_vm(vm.id).await?;
|
self.check_vm(vm.id).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.last_check_vms = Utc::now();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user