Skip to content

Commit d27902d

Browse files
authored
Merge pull request #177 from AnthonyGress/dev
- Added LAN IP to the Navbar and System monitor widget - Release notes history when clicking on the version number - Fixed a styling bug in the media queue management widget in the context menu
2 parents 03a20c3 + a16bd23 commit d27902d

File tree

16 files changed

+457
-334
lines changed

16 files changed

+457
-334
lines changed

backend/src/routes/health.route.ts

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import axios from 'axios';
22
import { exec } from 'child_process';
33
import { Request, Response, Router } from 'express';
44
import https from 'https';
5-
import { URL } from 'url';
5+
import os from 'os';
66

77
export const healthRoute = Router();
88

@@ -66,15 +66,63 @@ healthRoute.get('/', async (req: Request, res: Response): Promise<void> => {
6666
}
6767
});
6868

69-
// Endpoint to get public IP address
70-
healthRoute.get('/public-ip', async (req: Request, res: Response): Promise<void> => {
69+
// Helper function to get LAN IP address
70+
const getLanIP = (): string | null => {
71+
const interfaces = os.networkInterfaces();
72+
73+
// Priority order: en0 (WiFi/Ethernet on macOS), eth0 (Ethernet on Linux), wlan0 (WiFi on Linux)
74+
const priorityInterfaces = ['en0', 'eth0', 'wlan0'];
75+
76+
// First try priority interfaces
77+
for (const ifaceName of priorityInterfaces) {
78+
const iface = interfaces[ifaceName];
79+
if (iface) {
80+
for (const details of iface) {
81+
if (details.family === 'IPv4' && !details.internal) {
82+
return details.address;
83+
}
84+
}
85+
}
86+
}
87+
88+
// Fallback: find first non-internal IPv4 address
89+
for (const ifaceName in interfaces) {
90+
const iface = interfaces[ifaceName];
91+
if (iface) {
92+
for (const details of iface) {
93+
if (details.family === 'IPv4' && !details.internal) {
94+
return details.address;
95+
}
96+
}
97+
}
98+
}
99+
100+
return null;
101+
};
102+
103+
// Endpoint to get both WAN and LAN IP addresses
104+
healthRoute.get('/ip', async (req: Request, res: Response): Promise<void> => {
71105
try {
72-
const response = await axios.get('https://api.ipify.org?format=json', {
73-
timeout: 5000
106+
// Get LAN IP synchronously
107+
const lanIP = getLanIP();
108+
109+
// Get WAN IP asynchronously
110+
let wanIP: string | null = null;
111+
try {
112+
const response = await axios.get('https://api.ipify.org?format=json', {
113+
timeout: 5000
114+
});
115+
wanIP = response.data.ip;
116+
} catch (error) {
117+
console.error('Failed to fetch WAN IP:', error);
118+
}
119+
120+
res.json({
121+
wan: wanIP,
122+
lan: lanIP
74123
});
75-
res.json({ ip: response.data.ip });
76124
} catch (error) {
77-
console.error('Failed to fetch public IP:', error);
78-
res.status(500).json({ error: 'Failed to fetch public IP' });
125+
console.error('Failed to fetch IP addresses:', error);
126+
res.status(500).json({ error: 'Failed to fetch IP addresses' });
79127
}
80128
});

backend/src/types/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ export type Config = {
4040
notes?: Note[];
4141
themeColor?: string;
4242
showInternetIndicator?: boolean;
43-
showPublicIP?: boolean;
43+
showIP?: boolean;
44+
ipDisplayType?: 'wan' | 'lan' | 'both';
4445
}
4546

4647
export type Note = {

frontend/src/api/dash-api.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -615,16 +615,16 @@ export class DashApi {
615615
}
616616
}
617617

618-
public static async getPublicIP(): Promise<string | null> {
618+
public static async getIPAddresses(): Promise<{ wan: string | null; lan: string | null }> {
619619
try {
620-
const res = await axios.get(`${BACKEND_URL}/api/health/public-ip`, {
620+
const res = await axios.get(`${BACKEND_URL}/api/health/ip`, {
621621
withCredentials: false,
622622
timeout: 5000
623623
});
624-
return res.data.ip;
624+
return { wan: res.data.wan, lan: res.data.lan };
625625
} catch (error) {
626-
console.error('Failed to fetch public IP:', error);
627-
return null;
626+
console.error('Failed to fetch IP addresses:', error);
627+
return { wan: null, lan: null };
628628
}
629629
}
630630

frontend/src/components/dashboard/base-items/widgets/MediaRequestManagerWidget.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -717,14 +717,6 @@ export const MediaRequestManagerWidget: React.FC<MediaRequestManagerWidgetProps>
717717
lineHeight: '1.5'
718718
}
719719
}
720-
},
721-
paper: {
722-
sx: {
723-
backgroundColor: 'rgba(30, 30, 30, 0.95)',
724-
backdropFilter: 'blur(10px)',
725-
border: '1px solid rgba(255,255,255,0.1)',
726-
maxHeight: 450 // Ensure paper can accommodate the listbox
727-
}
728720
}
729721
}}
730722
/>

frontend/src/components/dashboard/base-items/widgets/QueueManagementWidget.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -276,24 +276,17 @@ const QueueItemComponent: React.FC<QueueItemComponentProps> = ({ item, serviceNa
276276
anchorEl={menuAnchorEl}
277277
open={menuOpen}
278278
onClose={handleMenuClose}
279-
PaperProps={{
280-
sx: {
281-
backgroundColor: 'rgba(30, 30, 30, 0.95)',
282-
backdropFilter: 'blur(10px)',
283-
border: '1px solid rgba(255, 255, 255, 0.1)'
284-
}
285-
}}
286279
>
287280
<MenuItem
288281
onClick={() => handleRemove(true, false)}
289-
sx={{ fontSize: '0.9rem', py: 1, color: 'white' }}
282+
sx={{ fontSize: '0.9rem', py: 1 }}
290283
>
291284
<Delete fontSize='small' sx={{ mr: 1 }} />
292285
Remove from Queue
293286
</MenuItem>
294287
<MenuItem
295288
onClick={() => handleRemove(false, false)}
296-
sx={{ fontSize: '0.9rem', py: 1, color: 'white' }}
289+
sx={{ fontSize: '0.9rem', py: 1 }}
297290
>
298291
<Delete fontSize='small' sx={{ mr: 1 }} />
299292
Remove (Keep in Client)

frontend/src/components/dashboard/base-items/widgets/SystemMonitorWidget/SystemMonitorWidget.tsx

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { PiGlobeSimple, PiGlobeSimpleX } from 'react-icons/pi';
77
import { DiskUsageBar } from './DiskUsageWidget';
88
import { GaugeWidget } from './GaugeWidget';
99
import { DashApi } from '../../../../../api/dash-api';
10+
import { useAppContext } from '../../../../../context/useAppContext';
1011
import { useInternetStatus } from '../../../../../hooks/useInternetStatus';
1112
import { useIsMobile } from '../../../../../hooks/useIsMobile';
1213
import { COLORS } from '../../../../../theme/styles';
@@ -26,12 +27,14 @@ interface SystemMonitorWidgetProps {
2627
showDiskUsage?: boolean;
2728
showSystemInfo?: boolean;
2829
showInternetStatus?: boolean;
29-
showPublicIP?: boolean;
30+
showIP?: boolean;
31+
ipDisplayType?: 'wan' | 'lan' | 'both';
3032
};
3133
editMode?: boolean;
3234
}
3335

3436
export const SystemMonitorWidget = ({ config, editMode }: SystemMonitorWidgetProps) => {
37+
const { config: globalConfig } = useAppContext();
3538
const [systemInformation, setSystemInformation] = useState<any>();
3639
const [memoryInformation, setMemoryInformation] = useState<any>(0);
3740
const [diskInformation, setDiskInformation] = useState<any>();
@@ -49,7 +52,7 @@ export const SystemMonitorWidget = ({ config, editMode }: SystemMonitorWidgetPro
4952
const [errorMessage, setErrorMessage] = useState<string | null>(null);
5053
const [isLoading, setIsLoading] = useState(false);
5154
const [internetTooltipOpen, setInternetTooltipOpen] = useState(false);
52-
const [publicIP, setPublicIP] = useState<string | null>(null);
55+
const [ipAddress, setIPAddress] = useState<{ wan?: string | null; lan?: string | null } | string | null>(null);
5356

5457
const { internetStatus } = useInternetStatus();
5558

@@ -63,7 +66,7 @@ export const SystemMonitorWidget = ({ config, editMode }: SystemMonitorWidgetPro
6366
const showDiskUsage = config?.showDiskUsage !== false;
6467
const showSystemInfo = config?.showSystemInfo !== false;
6568
const showInternetStatus = config?.showInternetStatus !== false;
66-
const showPublicIP = config?.showPublicIP || false;
69+
const showIP = config?.showIP ?? (config as any)?.showPublicIP ?? false;
6770

6871
const isMobile = useIsMobile();
6972

@@ -441,18 +444,26 @@ export const SystemMonitorWidget = ({ config, editMode }: SystemMonitorWidgetPro
441444
};
442445
}, [internetTooltipOpen]);
443446

444-
// Fetch public IP when showPublicIP is enabled
447+
// Fetch IP addresses when showIP is enabled
445448
useEffect(() => {
446-
if (showPublicIP && internetStatus === 'online') {
447-
const fetchPublicIP = async () => {
448-
const ip = await DashApi.getPublicIP();
449-
setPublicIP(ip);
449+
if (showIP && internetStatus === 'online') {
450+
const fetchIPs = async () => {
451+
const ips = await DashApi.getIPAddresses();
452+
const ipType = config?.ipDisplayType || 'wan';
453+
454+
if (ipType === 'both') {
455+
setIPAddress({ wan: ips.wan, lan: ips.lan });
456+
} else if (ipType === 'lan') {
457+
setIPAddress(ips.lan);
458+
} else {
459+
setIPAddress(ips.wan);
460+
}
450461
};
451-
fetchPublicIP();
462+
fetchIPs();
452463
} else {
453-
setPublicIP(null);
464+
setIPAddress(null);
454465
}
455-
}, [showPublicIP, internetStatus]);
466+
}, [showIP, internetStatus, config?.ipDisplayType]);
456467

457468
// Determine layout styles based on dual widget position
458469
const containerStyles = {
@@ -537,13 +548,32 @@ export const SystemMonitorWidget = ({ config, editMode }: SystemMonitorWidgetPro
537548
<Tooltip
538549
title={
539550
<Box>
540-
<Typography variant='body2'>
551+
<Typography variant='body2' sx={{ textAlign: 'center' }}>
541552
{internetStatus === 'online' ? 'Internet Connected' : internetStatus === 'offline' ? 'No Internet Connection' : 'Checking Internet...'}
542553
</Typography>
543-
{showPublicIP && publicIP && internetStatus === 'online' && (
544-
<Typography variant='caption' sx={{ display: 'block', mt: 0.5 }}>
545-
IP: {publicIP}
546-
</Typography>
554+
{showIP && ipAddress && internetStatus === 'online' && (
555+
<Box sx={{ mt: 0.5 }}>
556+
{typeof ipAddress === 'object' ? (
557+
<>
558+
{ipAddress.wan && (
559+
<Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2 }}>
560+
<Typography variant='caption'>WAN:</Typography>
561+
<Typography variant='caption'>{ipAddress.wan}</Typography>
562+
</Box>
563+
)}
564+
{ipAddress.lan && (
565+
<Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2 }}>
566+
<Typography variant='caption'>LAN:</Typography>
567+
<Typography variant='caption'>{ipAddress.lan}</Typography>
568+
</Box>
569+
)}
570+
</>
571+
) : (
572+
<Typography variant='caption' sx={{ display: 'block', textAlign: 'right' }}>
573+
{(config?.ipDisplayType || 'wan') === 'wan' ? 'WAN: ' : 'LAN: '}{ipAddress}
574+
</Typography>
575+
)}
576+
</Box>
547577
)}
548578
</Box>
549579
}
@@ -558,7 +588,7 @@ export const SystemMonitorWidget = ({ config, editMode }: SystemMonitorWidgetPro
558588
disableFocusListener
559589
disableTouchListener
560590
PopperProps={{
561-
disablePortal: true,
591+
disablePortal: false,
562592
}}
563593
slotProps={{
564594
tooltip: {

frontend/src/components/forms/AddEditForm/AddEditForm.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ export const AddEditForm = ({ handleClose, existingItem, onSubmit }: Props) => {
193193
showDiskUsage: data.showDiskUsage !== false, // Default to true
194194
showSystemInfo: data.showSystemInfo !== false, // Default to true
195195
showInternetStatus: data.showInternetStatus !== false, // Default to true
196-
showPublicIP: data.showPublicIP || false
196+
showIP: data.showIP || false,
197+
ipDisplayType: data.ipDisplayType || 'wan'
197198
};
198199

199200
// Add network interface to config if a network gauge is included
@@ -470,7 +471,8 @@ export const AddEditForm = ({ handleClose, existingItem, onSubmit }: Props) => {
470471
showDiskUsage: data.top_showDiskUsage,
471472
showSystemInfo: data.top_showSystemInfo,
472473
showInternetStatus: data.top_showInternetStatus,
473-
showPublicIP: data.top_showPublicIP,
474+
showIP: data.top_showIP,
475+
ipDisplayType: data.top_ipDisplayType,
474476
selectedDisks: data.top_selectedDisks,
475477
showIcons: data.top_showIcons,
476478
showMountPath: data.top_showMountPath,
@@ -503,7 +505,8 @@ export const AddEditForm = ({ handleClose, existingItem, onSubmit }: Props) => {
503505
showDiskUsage: data.bottom_showDiskUsage,
504506
showSystemInfo: data.bottom_showSystemInfo,
505507
showInternetStatus: data.bottom_showInternetStatus,
506-
showPublicIP: data.bottom_showPublicIP,
508+
showIP: data.bottom_showIP,
509+
ipDisplayType: data.bottom_ipDisplayType,
507510
selectedDisks: data.bottom_selectedDisks,
508511
showIcons: data.bottom_showIcons,
509512
showMountPath: data.bottom_showMountPath,

frontend/src/components/forms/AddEditForm/types.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export type FormValues = {
2020
showDiskUsage?: boolean;
2121
showSystemInfo?: boolean;
2222
showInternetStatus?: boolean;
23-
showPublicIP?: boolean;
23+
showIP?: boolean;
24+
ipDisplayType?: 'wan' | 'lan' | 'both';
2425
// Disk monitor widget
2526
selectedDisks?: Array<{ mount: string; customName: string; showMountPath?: boolean }>;
2627
showIcons?: boolean;
@@ -106,7 +107,8 @@ export type FormValues = {
106107
top_showDiskUsage?: boolean;
107108
top_showSystemInfo?: boolean;
108109
top_showInternetStatus?: boolean;
109-
top_showPublicIP?: boolean;
110+
top_showIP?: boolean;
111+
top_ipDisplayType?: 'wan' | 'lan' | 'both';
110112
top_selectedDisks?: Array<{ mount: string; customName: string; showMountPath?: boolean }>;
111113
top_showIcons?: boolean;
112114
top_showMountPath?: boolean;
@@ -137,7 +139,8 @@ export type FormValues = {
137139
bottom_showDiskUsage?: boolean;
138140
bottom_showSystemInfo?: boolean;
139141
bottom_showInternetStatus?: boolean;
140-
bottom_showPublicIP?: boolean;
142+
bottom_showIP?: boolean;
143+
bottom_ipDisplayType?: 'wan' | 'lan' | 'both';
141144
bottom_selectedDisks?: Array<{ mount: string; customName: string; showMountPath?: boolean }>;
142145
bottom_showIcons?: boolean;
143146
bottom_showMountPath?: boolean;

frontend/src/components/forms/AddEditForm/useExistingItem.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ export const useExistingItem = ({ existingItem, formContext, setCustomIconFile }
220220
showDiskUsage: existingItem?.config?.showDiskUsage !== false, // Default to true
221221
showSystemInfo: existingItem?.config?.showSystemInfo !== false, // Default to true
222222
showInternetStatus: existingItem?.config?.showInternetStatus !== false, // Default to true
223-
showPublicIP: existingItem?.config?.showPublicIP || false,
223+
showIP: existingItem?.config?.showIP ?? existingItem?.config?.showPublicIP ?? false,
224+
ipDisplayType: existingItem?.config?.ipDisplayType || 'wan',
224225

225226
// Disk monitor widget values
226227
selectedDisks: existingItem?.type === ITEM_TYPE.DISK_MONITOR_WIDGET ? (existingItem?.config?.selectedDisks || []) : [],
@@ -242,7 +243,8 @@ export const useExistingItem = ({ existingItem, formContext, setCustomIconFile }
242243
top_showDiskUsage: true,
243244
top_showSystemInfo: true,
244245
top_showInternetStatus: true,
245-
top_showPublicIP: false,
246+
top_showIP: false,
247+
top_ipDisplayType: 'wan',
246248
top_selectedDisks: [],
247249
top_showIcons: true,
248250
top_layout: '2x2',
@@ -270,7 +272,8 @@ export const useExistingItem = ({ existingItem, formContext, setCustomIconFile }
270272
bottom_showDiskUsage: true,
271273
bottom_showSystemInfo: true,
272274
bottom_showInternetStatus: true,
273-
bottom_showPublicIP: false,
275+
bottom_showIP: false,
276+
bottom_ipDisplayType: 'wan',
274277
bottom_selectedDisks: [],
275278
bottom_showIcons: true,
276279
bottom_layout: '2x2',
@@ -321,7 +324,8 @@ export const useExistingItem = ({ existingItem, formContext, setCustomIconFile }
321324
formContext.setValue('top_showDiskUsage', topConfig.showDiskUsage !== false);
322325
formContext.setValue('top_showSystemInfo', topConfig.showSystemInfo !== false);
323326
formContext.setValue('top_showInternetStatus', topConfig.showInternetStatus !== false);
324-
formContext.setValue('top_showPublicIP', topConfig.showPublicIP || false);
327+
formContext.setValue('top_showIP', topConfig.showIP ?? topConfig.showPublicIP ?? false);
328+
formContext.setValue('top_ipDisplayType', topConfig.ipDisplayType || 'wan');
325329
}
326330

327331
// Handle top disk monitor widget
@@ -394,7 +398,8 @@ export const useExistingItem = ({ existingItem, formContext, setCustomIconFile }
394398
formContext.setValue('bottom_showDiskUsage', bottomConfig.showDiskUsage !== false);
395399
formContext.setValue('bottom_showSystemInfo', bottomConfig.showSystemInfo !== false);
396400
formContext.setValue('bottom_showInternetStatus', bottomConfig.showInternetStatus !== false);
397-
formContext.setValue('bottom_showPublicIP', bottomConfig.showPublicIP || false);
401+
formContext.setValue('bottom_showIP', bottomConfig.showIP ?? bottomConfig.showPublicIP ?? false);
402+
formContext.setValue('bottom_ipDisplayType', bottomConfig.ipDisplayType || 'wan');
398403
}
399404

400405
// Handle bottom disk monitor widget

0 commit comments

Comments
 (0)