mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 13:23:28 +00:00
Reintroduce P2P Settings (#2365)
* redo backend to be less cringe * fixed up
This commit is contained in:
parent
ef969f1ada
commit
959ccdfd98
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
invalidate_query,
|
||||
node::{
|
||||
config::{NodeConfig, NodePreferences, P2PDiscoveryState, Port},
|
||||
config::{NodeConfig, NodeConfigP2P, NodePreferences, P2PDiscoveryState},
|
||||
get_hardware_model_name, HardwareModel,
|
||||
},
|
||||
old_job::JobProgressEvent,
|
||||
|
@ -84,8 +84,7 @@ pub struct SanitisedNodeConfig {
|
|||
/// name is the display name of the current node. This is set by the user and is shown in the UI. // TODO: Length validation so it can fit in DNS record
|
||||
pub name: String,
|
||||
pub identity: RemoteIdentity,
|
||||
pub p2p_ipv4_port: Port,
|
||||
pub p2p_ipv6_port: Port,
|
||||
pub p2p: NodeConfigP2P,
|
||||
pub p2p_discovery: P2PDiscoveryState,
|
||||
pub features: Vec<BackendFeature>,
|
||||
pub preferences: NodePreferences,
|
||||
|
@ -98,8 +97,7 @@ impl From<NodeConfig> for SanitisedNodeConfig {
|
|||
id: value.id,
|
||||
name: value.name,
|
||||
identity: value.identity.to_remote_identity(),
|
||||
p2p_ipv4_port: value.p2p_ipv4_port,
|
||||
p2p_ipv6_port: value.p2p_ipv6_port,
|
||||
p2p: value.p2p,
|
||||
p2p_discovery: value.p2p_discovery,
|
||||
features: value.features,
|
||||
preferences: value.preferences,
|
||||
|
|
|
@ -19,8 +19,9 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
#[derive(Deserialize, Type)]
|
||||
pub struct ChangeNodeNameArgs {
|
||||
pub name: Option<String>,
|
||||
pub p2p_ipv4_port: Option<Port>,
|
||||
pub p2p_ipv6_port: Option<Port>,
|
||||
pub p2p_port: Option<Port>,
|
||||
pub p2p_ipv4_enabled: Option<bool>,
|
||||
pub p2p_ipv6_enabled: Option<bool>,
|
||||
pub p2p_discovery: Option<P2PDiscoveryState>,
|
||||
pub image_labeler_version: Option<String>,
|
||||
}
|
||||
|
@ -43,14 +44,14 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|||
config.name = name;
|
||||
}
|
||||
|
||||
if let Some(port) = args.p2p_ipv4_port {
|
||||
config.p2p_ipv4_port = port;
|
||||
if let Some(port) = args.p2p_port {
|
||||
config.p2p.port = port;
|
||||
};
|
||||
if let Some(port) = args.p2p_ipv6_port {
|
||||
config.p2p_ipv6_port = port;
|
||||
if let Some(enabled) = args.p2p_ipv4_enabled {
|
||||
config.p2p.ipv4 = enabled;
|
||||
};
|
||||
if let Some(v) = args.p2p_discovery {
|
||||
config.p2p_discovery = v;
|
||||
if let Some(enabled) = args.p2p_ipv6_enabled {
|
||||
config.p2p.ipv6 = enabled;
|
||||
};
|
||||
|
||||
#[cfg(feature = "ai")]
|
||||
|
|
|
@ -37,20 +37,54 @@ pub enum P2PDiscoveryState {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Type)]
|
||||
#[serde(rename_all = "snake_case", untagged)]
|
||||
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
|
||||
pub enum Port {
|
||||
Disabled,
|
||||
#[default]
|
||||
Random,
|
||||
Discrete(u16),
|
||||
}
|
||||
|
||||
impl Port {
|
||||
pub fn get(&self) -> u16 {
|
||||
match self {
|
||||
Port::Random => 0,
|
||||
Port::Discrete(port) => *port,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_random(&self) -> bool {
|
||||
matches!(self, Port::Random)
|
||||
}
|
||||
}
|
||||
|
||||
fn default_as_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn skip_if_true(value: &bool) -> bool {
|
||||
*value == true
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
|
||||
pub struct NodeConfigP2P {
|
||||
#[serde(default, skip_serializing_if = "Port::is_random")]
|
||||
pub port: Port,
|
||||
#[serde(default = "default_as_true", skip_serializing_if = "skip_if_true")]
|
||||
pub ipv4: bool,
|
||||
#[serde(default = "default_as_true", skip_serializing_if = "skip_if_true")]
|
||||
pub ipv6: bool,
|
||||
}
|
||||
|
||||
impl Default for NodeConfigP2P {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
port: Port::Random,
|
||||
ipv4: true,
|
||||
ipv6: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// NodeConfig is the configuration for a node. This is shared between all libraries and is stored in a JSON file on disk.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)] // If you are adding `specta::Type` on this your probably about to leak the P2P private key
|
||||
pub struct NodeConfig {
|
||||
|
@ -66,10 +100,8 @@ pub struct NodeConfig {
|
|||
#[serde(with = "identity_serde")]
|
||||
pub identity: Identity,
|
||||
/// P2P config
|
||||
#[serde(default, skip_serializing_if = "Port::is_random")]
|
||||
pub p2p_ipv4_port: Port,
|
||||
#[serde(default, skip_serializing_if = "Port::is_random")]
|
||||
pub p2p_ipv6_port: Port,
|
||||
#[serde(default)]
|
||||
pub p2p: NodeConfigP2P,
|
||||
#[serde(default)]
|
||||
pub p2p_discovery: P2PDiscoveryState,
|
||||
/// Feature flags enabled on the node
|
||||
|
@ -153,8 +185,7 @@ impl ManagedVersion<NodeConfigVersion> for NodeConfig {
|
|||
id: Uuid::new_v4(),
|
||||
name,
|
||||
identity: Identity::default(),
|
||||
p2p_ipv4_port: Port::Random,
|
||||
p2p_ipv6_port: Port::Random,
|
||||
p2p: NodeConfigP2P::default(),
|
||||
p2p_discovery: P2PDiscoveryState::Everyone,
|
||||
version: Self::LATEST_VERSION,
|
||||
features: vec![],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
node::{
|
||||
config::{self, P2PDiscoveryState, Port},
|
||||
config::{self, P2PDiscoveryState},
|
||||
get_hardware_model_name, HardwareModel,
|
||||
},
|
||||
p2p::{
|
||||
|
@ -146,18 +146,19 @@ impl P2PManager {
|
|||
}
|
||||
.update(&mut self.p2p.metadata_mut());
|
||||
|
||||
let port = match config.p2p_ipv4_port {
|
||||
Port::Disabled => None,
|
||||
Port::Random => Some(0),
|
||||
Port::Discrete(port) => Some(port),
|
||||
};
|
||||
info!("Setting quic ipv4 listener to: {port:?}");
|
||||
if let Err(err) = self.quic.set_ipv4_enabled(port).await {
|
||||
error!("Failed to enabled quic ipv4 listener: {err}");
|
||||
self.node_config
|
||||
.write(|c| c.p2p_ipv4_port = Port::Disabled)
|
||||
let port = config.p2p.port.get();
|
||||
|
||||
info!(
|
||||
"Setting quic ipv4 listener to: {:?}",
|
||||
config.p2p.ipv4.then(|| port)
|
||||
);
|
||||
if let Err(err) = self
|
||||
.quic
|
||||
.set_ipv4_enabled(config.p2p.ipv4.then(|| port))
|
||||
.await
|
||||
.ok();
|
||||
{
|
||||
error!("Failed to enabled quic ipv4 listener: {err}");
|
||||
self.node_config.write(|c| c.p2p.ipv4 = false).await.ok();
|
||||
|
||||
self.listener_errors
|
||||
.lock()
|
||||
|
@ -165,18 +166,17 @@ impl P2PManager {
|
|||
.ipv4 = Some(format!("{err}"));
|
||||
}
|
||||
|
||||
let port = match config.p2p_ipv6_port {
|
||||
Port::Disabled => None,
|
||||
Port::Random => Some(0),
|
||||
Port::Discrete(port) => Some(port),
|
||||
};
|
||||
info!("Setting quic ipv6 listener to: {port:?}");
|
||||
if let Err(err) = self.quic.set_ipv6_enabled(port).await {
|
||||
error!("Failed to enabled quic ipv6 listener: {err}");
|
||||
self.node_config
|
||||
.write(|c| c.p2p_ipv6_port = Port::Disabled)
|
||||
info!(
|
||||
"Setting quic ipv6 listener to: {:?}",
|
||||
config.p2p.ipv6.then(|| port)
|
||||
);
|
||||
if let Err(err) = self
|
||||
.quic
|
||||
.set_ipv6_enabled(config.p2p.ipv6.then(|| port))
|
||||
.await
|
||||
.ok();
|
||||
{
|
||||
error!("Failed to enabled quic ipv6 listener: {err}");
|
||||
self.node_config.write(|c| c.p2p.ipv6 = false).await.ok();
|
||||
|
||||
self.listener_errors
|
||||
.lock()
|
||||
|
@ -270,11 +270,7 @@ impl P2PManager {
|
|||
"name": name,
|
||||
"listener_addrs": listeners.iter().find(|l| l.is_hook_id(*id)).map(|l| l.addrs.clone()),
|
||||
})).collect::<Vec<_>>(),
|
||||
"config": json!({
|
||||
"p2p_ipv4_port": node_config.p2p_ipv4_port,
|
||||
"p2p_ipv6_port": node_config.p2p_ipv6_port,
|
||||
"p2p_discovery": node_config.p2p_discovery,
|
||||
}),
|
||||
"config": node_config.p2p,
|
||||
"relay_config": self.quic.get_relay_config(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { FormProvider } from 'react-hook-form';
|
||||
import clsx from 'clsx';
|
||||
import { Controller, FormProvider } from 'react-hook-form';
|
||||
import {
|
||||
useBridgeMutation,
|
||||
useBridgeQuery,
|
||||
|
@ -37,6 +38,8 @@ const LANGUAGE_OPTIONS = [
|
|||
// Sort the languages by their label
|
||||
LANGUAGE_OPTIONS.sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
const u16 = () => z.number().min(0).max(65535);
|
||||
|
||||
export const Component = () => {
|
||||
const node = useBridgeQuery(['nodeState']);
|
||||
const platform = usePlatform();
|
||||
|
@ -50,9 +53,12 @@ export const Component = () => {
|
|||
schema: z
|
||||
.object({
|
||||
name: z.string().min(1).max(250).optional(),
|
||||
// p2p_enabled: z.boolean().optional(),
|
||||
// p2p_port: u16,
|
||||
// customOrDefault: z.enum(['Custom', 'Default']),
|
||||
p2p_port: z.discriminatedUnion('type', [
|
||||
z.object({ type: z.literal('random') }),
|
||||
z.object({ type: z.literal('discrete'), value: u16() })
|
||||
]),
|
||||
p2p_ipv4_enabled: z.boolean().optional(),
|
||||
p2p_ipv6_enabled: z.boolean().optional(),
|
||||
image_labeler_version: z.string().optional(),
|
||||
background_processing_percentage: z.coerce
|
||||
.number({
|
||||
|
@ -66,25 +72,26 @@ export const Component = () => {
|
|||
reValidateMode: 'onChange',
|
||||
defaultValues: {
|
||||
name: node.data?.name,
|
||||
// p2p_port: node.data?.p2p_port || 0,
|
||||
// p2p_enabled: node.data?.p2p_enabled,
|
||||
// customOrDefault: node.data?.p2p_port ? 'Custom' : 'Default',
|
||||
p2p_port: node.data?.p2p.port || { type: 'random' },
|
||||
p2p_ipv4_enabled: node.data?.p2p.ipv4 || true,
|
||||
p2p_ipv6_enabled: node.data?.p2p.ipv6 || true,
|
||||
image_labeler_version: node.data?.image_labeler_version ?? undefined,
|
||||
background_processing_percentage:
|
||||
node.data?.preferences.thumbnailer.background_processing_percentage || 50
|
||||
}
|
||||
});
|
||||
const p2p_port = form.watch('p2p_port');
|
||||
|
||||
// const watchCustomOrDefault = form.watch('customOrDefault');
|
||||
// const watchP2pEnabled = form.watch('p2p_enabled');
|
||||
const watchBackgroundProcessingPercentage = form.watch('background_processing_percentage');
|
||||
|
||||
useDebouncedFormWatch(form, async (value) => {
|
||||
if (await form.trigger()) {
|
||||
await editNode.mutateAsync({
|
||||
name: value.name || null,
|
||||
p2p_ipv4_port: null,
|
||||
p2p_ipv6_port: null,
|
||||
|
||||
p2p_port: (value.p2p_port as any) ?? null,
|
||||
p2p_ipv4_enabled: value.p2p_ipv4_enabled ?? null,
|
||||
p2p_ipv6_enabled: value.p2p_ipv6_enabled ?? null,
|
||||
p2p_discovery: null,
|
||||
// p2p_port: value.customOrDefault === 'Default' ? 0 : Number(value.p2p_port),
|
||||
// p2p_enabled: value.p2p_enabled ?? null,
|
||||
|
@ -101,11 +108,11 @@ export const Component = () => {
|
|||
node.refetch();
|
||||
});
|
||||
|
||||
// form.watch((data) => {
|
||||
// if (Number(data.p2p_port) > 65535) {
|
||||
// form.setValue('p2p_port', 65535);
|
||||
// }
|
||||
// });
|
||||
form.watch((data) => {
|
||||
if (data.p2p_port?.type == 'discrete' && Number(data.p2p_port.value) > 65535) {
|
||||
form.setValue('p2p_port', { type: 'discrete', value: 65535 });
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useLocale();
|
||||
|
||||
|
@ -288,20 +295,10 @@ export const Component = () => {
|
|||
/>
|
||||
</div>
|
||||
</Setting> */}
|
||||
{/* <div className="flex flex-col gap-4">
|
||||
<h1 className="mb-3 text-lg font-bold text-ink">{t('networking')}</h1> */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<h1 className="mb-3 text-lg font-bold text-ink">{t('networking')}</h1>
|
||||
|
||||
{/* TODO: Add some UI for this stuff */}
|
||||
{/* {node.data?.p2p.ipv4.status === 'Listening' ||
|
||||
node.data?.p2p.ipv4.status === 'Enabling'
|
||||
? `0.0.0.0:${node.data?.p2p.ipv4?.port || 0}`
|
||||
: ''}
|
||||
{node.data?.p2p.ipv6.status === 'Listening' ||
|
||||
node.data?.p2p.ipv6.status === 'Enabling'
|
||||
? `[::1]:${node.data?.p2p.ipv6?.port || 0}`
|
||||
: ''} */}
|
||||
|
||||
{/* <Setting
|
||||
<Setting
|
||||
mini
|
||||
title={t('enable_networking')}
|
||||
description={
|
||||
|
@ -317,56 +314,69 @@ export const Component = () => {
|
|||
>
|
||||
<Switch
|
||||
size="md"
|
||||
// checked={watchP2pEnabled || false}
|
||||
// onClick={() => form.setValue('p2p_enabled', !form.getValues('p2p_enabled'))}
|
||||
// disabled
|
||||
onClick={() => toast.info(t('coming_soon'))}
|
||||
checked={form.watch('p2p_ipv4_enabled') && form.watch('p2p_ipv6_enabled')}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setValue('p2p_ipv4_enabled', checked);
|
||||
form.setValue('p2p_ipv6_enabled', checked);
|
||||
}}
|
||||
/>
|
||||
</Setting> */}
|
||||
{/* <Setting
|
||||
</Setting>
|
||||
|
||||
{form.watch('p2p_ipv4_enabled') && form.watch('p2p_ipv6_enabled') ? (
|
||||
<>
|
||||
<Setting
|
||||
mini
|
||||
title={t('networking_port')}
|
||||
description={t('networking_port_description')}
|
||||
>
|
||||
<div className="flex h-[30px] gap-2">
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="customOrDefault"
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
value={p2p_port.type}
|
||||
containerClassName="h-[30px]"
|
||||
disabled={!watchP2pEnabled}
|
||||
className={clsx(!watchP2pEnabled && 'opacity-50', 'h-full')}
|
||||
{...field}
|
||||
onChange={(e) => {
|
||||
field.onChange(e);
|
||||
form.setValue('p2p_port', 0);
|
||||
className="h-full"
|
||||
onChange={(type) => {
|
||||
form.setValue('p2p_port', {
|
||||
type: type as any
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectOption value="Default">{t('default')}</SelectOption>
|
||||
<SelectOption value="Custom">{t('custom')}</SelectOption>
|
||||
<SelectOption value="random">{t('random')}</SelectOption>
|
||||
<SelectOption value="discrete">{t('custom')}</SelectOption>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
<Input
|
||||
value={p2p_port.type === 'discrete' ? p2p_port.value : 0}
|
||||
className={clsx(
|
||||
'w-[66px]',
|
||||
watchCustomOrDefault === 'Default' || !watchP2pEnabled
|
||||
? 'opacity-50'
|
||||
: 'opacity-100'
|
||||
p2p_port.type === 'random' ? 'opacity-50' : 'opacity-100'
|
||||
)}
|
||||
disabled={watchCustomOrDefault === 'Default' || !watchP2pEnabled}
|
||||
{...form.register('p2p_port')}
|
||||
disabled={p2p_port.type === 'random'}
|
||||
onChange={(e) => {
|
||||
form.setValue(
|
||||
'p2p_port',
|
||||
Number(e.target.value.replace(/[^0-9]/g, ''))
|
||||
);
|
||||
form.setValue('p2p_port', {
|
||||
type: 'discrete',
|
||||
value: Number(e.target.value.replace(/[^0-9]/g, ''))
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Setting> */}
|
||||
{/* </div> */}
|
||||
</Setting>
|
||||
<Setting
|
||||
mini
|
||||
title={t('ipv6')}
|
||||
description={
|
||||
<p className="text-sm text-gray-400">{t('ipv6_description')}</p>
|
||||
}
|
||||
>
|
||||
<Switch
|
||||
size="md"
|
||||
checked={form.watch('p2p_ipv6_enabled')}
|
||||
onCheckedChange={(checked) =>
|
||||
form.setValue('p2p_ipv6_enabled', checked)
|
||||
}
|
||||
/>
|
||||
</Setting>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -94,6 +94,9 @@
|
|||
"debug_mode": "Debug mode",
|
||||
"debug_mode_description": "Enable extra debugging features within the app.",
|
||||
"default": "Default",
|
||||
"random": "Random",
|
||||
"ipv6": "IPv6 networking",
|
||||
"ipv6_description": "Allow peer-to-peer communication using IPv6 networking",
|
||||
"default_settings": "Default Settings",
|
||||
"delete": "Delete",
|
||||
"delete_dialog_title": "Delete {{prefix}} {{type}}",
|
||||
|
|
|
@ -169,7 +169,7 @@ export type CacheNode = { __type: string; __id: string; "#node": any }
|
|||
|
||||
export type CameraData = { device_make: string | null; device_model: string | null; color_space: string | null; color_profile: ColorProfile | null; focal_length: number | null; shutter_speed: number | null; flash: Flash | null; orientation: Orientation; lens_make: string | null; lens_model: string | null; bit_depth: number | null; red_eye: boolean | null; zoom: number | null; iso: number | null; software: string | null; serial_number: string | null; lens_serial_number: string | null; contrast: number | null; saturation: number | null; sharpness: number | null; composite: Composite | null }
|
||||
|
||||
export type ChangeNodeNameArgs = { name: string | null; p2p_ipv4_port: Port | null; p2p_ipv6_port: Port | null; p2p_discovery: P2PDiscoveryState | null; image_labeler_version: string | null }
|
||||
export type ChangeNodeNameArgs = { name: string | null; p2p_port: Port | null; p2p_ipv4_enabled: boolean | null; p2p_ipv6_enabled: boolean | null; p2p_discovery: P2PDiscoveryState | null; image_labeler_version: string | null }
|
||||
|
||||
export type CloudInstance = { id: string; uuid: string; identity: RemoteIdentity; nodeId: string; metadata: { [key in string]: string } }
|
||||
|
||||
|
@ -450,6 +450,8 @@ export type MediaLocation = { latitude: number; longitude: number; pluscode: Plu
|
|||
|
||||
export type MediaMetadata = ({ type: "Image" } & ImageMetadata) | ({ type: "Video" } & VideoMetadata) | ({ type: "Audio" } & AudioMetadata)
|
||||
|
||||
export type NodeConfigP2P = { port: Port; ipv4: boolean; ipv6: boolean }
|
||||
|
||||
export type NodePreferences = { thumbnailer: ThumbnailerPreferences }
|
||||
|
||||
export type NodeState = ({
|
||||
|
@ -460,7 +462,7 @@ id: string;
|
|||
/**
|
||||
* name is the display name of the current node. This is set by the user and is shown in the UI. // TODO: Length validation so it can fit in DNS record
|
||||
*/
|
||||
name: string; identity: RemoteIdentity; p2p_ipv4_port: Port; p2p_ipv6_port: Port; p2p_discovery: P2PDiscoveryState; features: BackendFeature[]; preferences: NodePreferences; image_labeler_version: string | null }) & { data_path: string; device_model: string | null }
|
||||
name: string; identity: RemoteIdentity; p2p: NodeConfigP2P; p2p_discovery: P2PDiscoveryState; features: BackendFeature[]; preferences: NodePreferences; image_labeler_version: string | null }) & { data_path: string; device_model: string | null }
|
||||
|
||||
export type NonIndexedPathItem = { path: string; name: string; extension: string; kind: number; is_dir: boolean; date_created: string; date_modified: string; size_in_bytes_bytes: number[]; hidden: boolean }
|
||||
|
||||
|
@ -539,7 +541,7 @@ export type PeerMetadata = { name: string; operating_system: OperatingSystem | n
|
|||
|
||||
export type PlusCode = string
|
||||
|
||||
export type Port = null | number
|
||||
export type Port = { type: "random" } | { type: "discrete"; value: number }
|
||||
|
||||
export type Range<T> = { from: T } | { to: T }
|
||||
|
||||
|
|
Loading…
Reference in a new issue