use std::str::FromStr; use clap::builder::ValueParserFactory; use models::{FromStrParser, HostId}; use serde::{Deserialize, Serialize}; use ts_rs::TS; use crate::net::forward::AvailablePorts; use crate::net::vhost::AlpnInfo; use crate::prelude::*; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] pub struct BindId { pub id: HostId, pub internal_port: u16, } impl ValueParserFactory for BindId { type Parser = FromStrParser; fn value_parser() -> Self::Parser { FromStrParser::new() } } impl FromStr for BindId { type Err = Error; fn from_str(s: &str) -> Result { let (id, port) = s .split_once(":") .ok_or_else(|| Error::new(eyre!("expected :"), ErrorKind::ParseUrl))?; Ok(Self { id: id.parse()?, internal_port: port.parse()?, }) } } #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct BindInfo { pub enabled: bool, pub options: BindOptions, pub lan: LanInfo, } #[derive(Clone, Copy, Debug, Deserialize, Serialize, TS, PartialEq, Eq, PartialOrd, Ord)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct LanInfo { pub assigned_port: Option, pub assigned_ssl_port: Option, } impl BindInfo { pub fn new(available_ports: &mut AvailablePorts, options: BindOptions) -> Result { let mut assigned_port = None; let mut assigned_ssl_port = None; if options.secure.is_some() { assigned_port = Some(available_ports.alloc()?); } if options.add_ssl.is_some() { assigned_ssl_port = Some(available_ports.alloc()?); } Ok(Self { enabled: true, options, lan: LanInfo { assigned_port, assigned_ssl_port, }, }) } pub fn update( self, available_ports: &mut AvailablePorts, options: BindOptions, ) -> Result { let Self { mut lan, .. } = self; if options .secure .map_or(false, |s| !(s.ssl && options.add_ssl.is_some())) // doesn't make sense to have 2 listening ports, both with ssl { lan.assigned_port = if let Some(port) = lan.assigned_port.take() { Some(port) } else { Some(available_ports.alloc()?) }; } else { if let Some(port) = lan.assigned_port.take() { available_ports.free([port]); } } if options.add_ssl.is_some() { lan.assigned_ssl_port = if let Some(port) = lan.assigned_ssl_port.take() { Some(port) } else { Some(available_ports.alloc()?) }; } else { if let Some(port) = lan.assigned_ssl_port.take() { available_ports.free([port]); } } Ok(Self { enabled: true, options, lan, }) } pub fn disable(&mut self) { self.enabled = false; } } #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] pub struct Security { pub ssl: bool, } #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct BindOptions { pub preferred_external_port: u16, pub add_ssl: Option, pub secure: Option, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct AddSslOptions { pub preferred_external_port: u16, // #[serde(default)] // pub add_x_forwarded_headers: bool, // TODO pub alpn: Option, }