Skip to content

Commit

Permalink
Serialize Error (#31)
Browse files Browse the repository at this point in the history
* Serialize error
  • Loading branch information
dzania authored Oct 6, 2023
1 parent 94930df commit 7e02cf2
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 110 deletions.
150 changes: 48 additions & 102 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
models::{instance::InstanceInfo, location::peer_to_location_stats},
Connection, ConnectionInfo, Instance, Location, LocationStats, WireguardKeys,
},
error::Error,
utils::{remove_whitespace, setup_interface, IS_MACOS},
AppState,
};
Expand All @@ -21,20 +22,15 @@ struct Payload {

// Create new wireguard interface
#[tauri::command(async)]
pub async fn connect(location_id: i64, handle: tauri::AppHandle) -> Result<(), String> {
pub async fn connect(location_id: i64, handle: tauri::AppHandle) -> Result<(), Error> {
let state = handle.state::<AppState>();
if let Some(location) = Location::find_by_id(&state.get_pool(), location_id)
.await
.map_err(|err| err.to_string())?
{
if let Some(location) = Location::find_by_id(&state.get_pool(), location_id).await? {
debug!(
"Creating new interface connection for location: {}",
location.name
);
let api = setup_interface(&location, &state.get_pool())
.await
.map_err(|err| err.to_string())?;
let address = local_ip().map_err(|err| err.to_string())?;
let api = setup_interface(&location, &state.get_pool()).await?;
let address = local_ip()?;
let connection = Connection::new(location_id, address.to_string());
state.active_connections.lock().unwrap().push(connection);
debug!(
Expand All @@ -58,10 +54,10 @@ pub async fn connect(location_id: i64, handle: tauri::AppHandle) -> Result<(), S
Ok(host) => {
let peers = host.peers;
for (_, peer) in peers {
// TODO: refactor
let mut location_stats =
peer_to_location_stats(&peer, &state.get_pool())
.await
.map_err(|err| err.to_string())
.unwrap();
debug!("Saving location stats: {:#?}", location_stats);
let _ = location_stats.save(&state.get_pool()).await;
Expand Down Expand Up @@ -89,25 +85,18 @@ pub async fn connect(location_id: i64, handle: tauri::AppHandle) -> Result<(), S
}

#[tauri::command]
pub async fn disconnect(location_id: i64, handle: tauri::AppHandle) -> Result<(), String> {
pub async fn disconnect(location_id: i64, handle: tauri::AppHandle) -> Result<(), Error> {
debug!("Disconnecting location with id: {}", location_id);
let state = handle.state::<AppState>();
if let Some(location) = Location::find_by_id(&state.get_pool(), location_id)
.await
.map_err(|err| err.to_string())?
{
let api =
WGApi::new(remove_whitespace(&location.name), IS_MACOS).map_err(|e| e.to_string())?;
if let Some(location) = Location::find_by_id(&state.get_pool(), location_id).await? {
let api = WGApi::new(remove_whitespace(&location.name), IS_MACOS)?;
debug!("Removing interface");
api.remove_interface().map_err(|err| err.to_string())?;
api.remove_interface()?;
debug!("Removed interface");
if let Some(mut connection) = state.find_and_remove_connection(location_id) {
debug!("Saving connection: {:#?}", connection);
connection.end = Some(Utc::now().naive_utc()); // Get the current time as NaiveDateTime in UTC
connection
.save(&state.get_pool())
.await
.map_err(|err| err.to_string())?;
connection.save(&state.get_pool()).await?;
debug!("Saved connection: {:#?}", connection);
}
handle
Expand Down Expand Up @@ -173,56 +162,40 @@ pub async fn save_device_config(
private_key: String,
response: CreateDeviceResponse,
app_state: State<'_, AppState>,
) -> Result<(), String> {
) -> Result<(), Error> {
debug!("Received device configuration: {:#?}", response);
let mut transaction = app_state
.get_pool()
.begin()
.await
.map_err(|err| err.to_string())?;
let mut transaction = app_state.get_pool().begin().await?;
let mut instance = Instance::new(
response.instance.name,
response.instance.id,
response.instance.url,
);
instance
.save(&mut *transaction)
.await
.map_err(|e| e.to_string())?;
instance.save(&mut *transaction).await?;
let mut keys = WireguardKeys::new(instance.id.unwrap(), response.device.pubkey, private_key);
keys.save(&mut *transaction)
.await
.map_err(|err| err.to_string())?;
keys.save(&mut *transaction).await?;
for location in response.configs {
let mut new_location = device_config_to_location(location, instance.id.unwrap());
new_location
.save(&mut *transaction)
.await
.map_err(|err| err.to_string())?;
new_location.save(&mut *transaction).await?;
}
transaction.commit().await.map_err(|err| err.to_string())?;
transaction.commit().await?;
info!("Instance created.");
debug!("Created following instance: {:#?}", instance);
let locations = Location::find_by_instance_id(&app_state.get_pool(), instance.id.unwrap())
.await
.map_err(|err| err.to_string())?;
let locations =
Location::find_by_instance_id(&app_state.get_pool(), instance.id.unwrap()).await?;
debug!("Created following locations: {:#?}", locations);
Ok(())
}

#[tauri::command(async)]
pub async fn all_instances(app_state: State<'_, AppState>) -> Result<Vec<InstanceInfo>, String> {
pub async fn all_instances(app_state: State<'_, AppState>) -> Result<Vec<InstanceInfo>, Error> {
debug!("Retrieving all instances.");
let instances = Instance::all(&app_state.get_pool())
.await
.map_err(|err| err.to_string())?;
let instances = Instance::all(&app_state.get_pool()).await?;
debug!("Found following instances: {:#?}", instances);
let mut instance_info: Vec<InstanceInfo> = vec![];
for instance in &instances {
debug!("Checking if instance: {:#?} is active", instance.uuid);
let locations = Location::find_by_instance_id(&app_state.get_pool(), instance.id.unwrap())
.await
.map_err(|err| err.to_string())?;
let locations =
Location::find_by_instance_id(&app_state.get_pool(), instance.id.unwrap()).await?;
let connection_ids: Vec<i64> = app_state
.active_connections
.lock()
Expand All @@ -238,8 +211,7 @@ pub async fn all_instances(app_state: State<'_, AppState>) -> Result<Vec<Instanc
.iter()
.any(|item1| location_ids.iter().any(|item2| item1 == item2));
let keys = WireguardKeys::find_by_instance_id(&app_state.get_pool(), instance.id.unwrap())
.await
.map_err(|err| err.to_string())?
.await?
.unwrap();
instance_info.push(InstanceInfo {
id: instance.id,
Expand Down Expand Up @@ -269,11 +241,9 @@ pub struct LocationInfo {
pub async fn all_locations(
instance_id: i64,
app_state: State<'_, AppState>,
) -> Result<Vec<LocationInfo>, String> {
) -> Result<Vec<LocationInfo>, Error> {
debug!("Retrieving all locations.");
let locations = Location::find_by_instance_id(&app_state.get_pool(), instance_id)
.await
.map_err(|err| err.to_string())?;
let locations = Location::find_by_instance_id(&app_state.get_pool(), instance_id).await?;
let active_locations_ids: Vec<i64> = app_state
.active_connections
.lock()
Expand Down Expand Up @@ -303,73 +273,53 @@ pub async fn update_instance(
instance_id: i64,
response: CreateDeviceResponse,
app_state: State<'_, AppState>,
) -> Result<(), String> {
) -> Result<(), Error> {
debug!("Received following response: {:#?}", response);
let instance = Instance::find_by_id(&app_state.get_pool(), instance_id)
.await
.map_err(|err| err.to_string())?;
let instance = Instance::find_by_id(&app_state.get_pool(), instance_id).await?;
if let Some(mut instance) = instance {
let mut transaction = app_state
.get_pool()
.begin()
.await
.map_err(|err| err.to_string())?;
let mut transaction = app_state.get_pool().begin().await?;
instance.name = response.instance.name;
instance.url = response.instance.url;
instance
.save(&mut *transaction)
.await
.map_err(|err| err.to_string())?;
instance.save(&mut *transaction).await?;

for location in response.configs {
let mut new_location = device_config_to_location(location, instance_id);
let old_location =
Location::find_by_native_id(&mut *transaction, new_location.network_id)
.await
.map_err(|err| err.to_string())?;
Location::find_by_native_id(&mut *transaction, new_location.network_id).await?;
if let Some(mut old_location) = old_location {
old_location.name = new_location.name;
old_location.address = new_location.address;
old_location.pubkey = new_location.pubkey;
old_location.endpoint = new_location.endpoint;
old_location.allowed_ips = new_location.allowed_ips;
old_location
.save(&mut *transaction)
.await
.map_err(|err| err.to_string())?;
old_location.save(&mut *transaction).await?;
} else {
new_location
.save(&mut *transaction)
.await
.map_err(|err| err.to_string())?;
new_location.save(&mut *transaction).await?;
}
}
transaction.commit().await.map_err(|err| err.to_string())?;
debug!("Updated instance with id: {}.", instance_id);
transaction.commit().await?;
info!("Updated instance with id: {}.", instance_id);
Ok(())
} else {
Err("Instance not found".into())
Err(Error::NotFound)
}
}
#[tauri::command]
pub async fn location_stats(
location_id: i64,
app_state: State<'_, AppState>,
) -> Result<Vec<LocationStats>, String> {
LocationStats::all_by_location_id(&app_state.get_pool(), location_id)
.await
.map_err(|err| err.to_string())
) -> Result<Vec<LocationStats>, Error> {
LocationStats::all_by_location_id(&app_state.get_pool(), location_id).await
}

#[tauri::command]
pub async fn all_connections(
location_id: i64,
app_state: State<'_, AppState>,
) -> Result<Vec<ConnectionInfo>, String> {
) -> Result<Vec<ConnectionInfo>, Error> {
debug!("Retrieving all conections.");
let connections = ConnectionInfo::all_by_location_id(&app_state.get_pool(), location_id)
.await
.map_err(|err| err.to_string())?;
let connections =
ConnectionInfo::all_by_location_id(&app_state.get_pool(), location_id).await?;
debug!(
"Returning all connections for location with id: {}, {:#?} ",
location_id, connections
Expand All @@ -380,40 +330,36 @@ pub async fn all_connections(
pub async fn active_connection(
location_id: i64,
handle: tauri::AppHandle,
) -> Result<Option<Connection>, String> {
) -> Result<Option<Connection>, Error> {
let state = handle.state::<AppState>();
debug!(
"Retrieving active connection for location with id: {}",
location_id
);
if let Some(location) = Location::find_by_id(&state.get_pool(), location_id)
.await
.map_err(|err| err.to_string())?
{
if let Some(location) = Location::find_by_id(&state.get_pool(), location_id).await? {
debug!(
"Returning active connection: {:#?}",
state.find_connection(location.id.unwrap())
);
Ok(state.find_connection(location.id.unwrap()))
} else {
error!("Location with id: {} not found.", location_id);
Err("Location not found".into())
Err(Error::NotFound)
}
}

#[tauri::command]
pub async fn last_connection(
location_id: i64,
app_state: State<'_, AppState>,
) -> Result<Connection, String> {
if let Some(connection) = Connection::latest_by_location_id(&app_state.get_pool(), location_id)
.await
.map_err(|err| err.to_string())?
) -> Result<Connection, Error> {
if let Some(connection) =
Connection::latest_by_location_id(&app_state.get_pool(), location_id).await?
{
debug!("Returning last connection: {:#?}", connection);
Ok(connection)
} else {
error!("No connections for location: {}", location_id);
Err("No connections for this device".into())
Err(Error::NotFound)
}
}
12 changes: 12 additions & 0 deletions src-tauri/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,16 @@ pub enum Error {
LocalIpError(#[from] LocalIpError),
#[error("Internal error")]
InternalError,
#[error("Object not found")]
NotFound,
}

// we must manually implement serde::Serialize
impl serde::Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
2 changes: 1 addition & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ fn main() {
info!("Database initialization completed");
info!("Starting main app thread.");
let result = database::info(&app_state.get_pool()).await;
info!("database info result: {:#?}", result);
info!("Database info result: {:#?}", result);
});
Ok(())
})
Expand Down
16 changes: 11 additions & 5 deletions src-tauri/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,25 @@ pub static IS_MACOS: bool = cfg!(target_os = "macos");
/// Setup client interface
pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<WGApi, Error> {
let interface_name = remove_whitespace(&location.name);
debug!("Creating new interface: {}", interface_name);
debug!(
"Creating new interface: {} for location: {:#?}",
interface_name, location
);
let api = create_api(&interface_name)?;

api.create_interface()?;

if let Some(keys) = WireguardKeys::find_by_instance_id(pool, location.instance_id).await? {
// TODO: handle unwrap
debug!("Decoding location public key: {}.", location.pubkey);
let peer_key: Key = Key::from_str(&location.pubkey).unwrap();
let mut peer = Peer::new(peer_key);
debug!("Creating interface for location: {:#?}", location);
let endpoint: SocketAddr = location.endpoint.parse().unwrap();
debug!("Parsing location endpoint: {}", location.endpoint);
let endpoint: SocketAddr = location.endpoint.parse()?;
peer.endpoint = Some(endpoint);
peer.persistent_keepalive_interval = Some(25);

debug!("Parsing location allowed ips: {}", location.allowed_ips);
let allowed_ips: Vec<String> = location
.allowed_ips
.split(',')
Expand All @@ -40,12 +46,12 @@ pub async fn setup_interface(location: &Location, pool: &DbPool) -> Result<WGApi
match IpAddrMask::from_str(&allowed_ip) {
Ok(addr) => {
peer.allowed_ips.push(addr);
// TODO: Handle windows later
// TODO: Handle windows when wireguard_rs adds support
// Add a route for the allowed IP using the `ip -4 route add` command
if let Err(err) = add_route(&allowed_ip, &interface_name) {
error!("Error adding route for {}: {}", allowed_ip, err);
} else {
info!("Added route for {}", allowed_ip);
debug!("Added route for {}", allowed_ip);
}
}
Err(err) => {
Expand Down
3 changes: 1 addition & 2 deletions src/pages/client/components/LocationsList/LocationsList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { listen, UnlistenFn } from '@tauri-apps/api/event';
import { useEffect } from 'react';
import { debug, error } from 'tauri-plugin-log-api';
import { error } from 'tauri-plugin-log-api';

import { clientApi } from '../../clientAPI/clientApi';
import { useClientStore } from '../../hooks/useClientStore';
Expand Down Expand Up @@ -49,7 +49,6 @@ export const LocationsList = ({ layoutType }: Props) => {

// TODO: add loader or another placeholder view pointing to opening enter token modal if no instances are found / present
if (!selectedInstance || !locations) return null;
debug(`Selected instance: ${JSON.stringify(selectedInstance)}`);

return (
<>
Expand Down

0 comments on commit 7e02cf2

Please sign in to comment.