use std::error::Error; use base64::{Engine, prelude::BASE64_STANDARD}; use sqlx::{PgPool, query, query_as, types::ipnetwork::IpNetwork}; use x25519_dalek::StaticSecret; use crate::config::WireguardConfig; #[derive(Clone)] pub struct Interface { pub name: String, pub address: IpNetwork, pub port: u16, pub private_key: StaticSecret, pub default_network_netmask: u8, } struct InterfacePostgres { name: String, address: IpNetwork, port: i32, private_key: Vec, default_network_netmask: i16, } impl TryFrom for Interface { type Error = Box; fn try_from(config: WireguardConfig) -> Result { Ok(Self { name: config.interface, address: config.address, port: config.port, private_key: { let decoded_key = BASE64_STANDARD.decode(config.private_key)?; let key_array: [u8; 32] = decoded_key.try_into().map_err(|_| "Invalid key length")?; StaticSecret::from(key_array) }, default_network_netmask: config.default_network_netmask, }) } } // We allow .unwrap() here because we set the lengths of the variables ourselves #[allow(clippy::fallible_impl_from)] impl From for Interface { fn from(row: InterfacePostgres) -> Self { Self { name: row.name, address: row.address, port: row.port.try_into().unwrap(), private_key: { let key_array: [u8; 32] = row.private_key.try_into().unwrap(); StaticSecret::from(key_array) }, default_network_netmask: row.default_network_netmask.try_into().unwrap(), } } } impl From for InterfacePostgres { fn from(interface: Interface) -> Self { Self { name: interface.name, address: interface.address, port: i32::from(interface.port), private_key: interface.private_key.to_bytes().to_vec(), default_network_netmask: i16::from(interface.default_network_netmask), } } } impl Interface { pub async fn insert( pool: &PgPool, interface: Self, ) -> Result<(), Box> { let interface = InterfacePostgres::from(interface); query!( r#" INSERT INTO interfaces (name, address, port, private_key, default_network_netmask) VALUES ($1, $2, $3, $4, $5) "#, interface.name, interface.address, interface.port, interface.private_key, interface.default_network_netmask, ) .execute(pool) .await?; Ok(()) } pub async fn select_by_name( pool: &PgPool, name: &str, ) -> Result, Box> { let row = query_as!( InterfacePostgres, r#" SELECT name, address, port, private_key, default_network_netmask FROM interfaces WHERE name = $1 "#, name ) .fetch_optional(pool) .await?; row.map_or_else(|| Ok(None), |row| Ok(Some(Self::from(row)))) } }