Add public IPv4 and IPv6 addresses to the dashboard

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/62c62847-d567-4968-a098-0a0bd2b3fa96.jpg
This commit is contained in:
alphaeusmote
2025-04-10 00:39:50 +00:00
parent 1a78682695
commit d1683efc08
4 changed files with 155 additions and 34 deletions
+16 -28
View File
@@ -1,35 +1,23 @@
import { Button } from "@/components/ui/button";
import { ChevronLeft } from "lucide-react";
import { useLocation } from "wouter";
import { Link } from 'wouter';
import { Button } from '@/components/ui/button';
import { ArrowLeft } from 'lucide-react';
interface BackButtonProps {
to?: string;
fallbackPath?: string;
className?: string;
href: string;
label?: string;
}
export function BackButton({ to, fallbackPath = "/", className = "" }: BackButtonProps) {
const [_, setLocation] = useLocation();
const handleBack = () => {
if (to) {
setLocation(to);
} else if (window.history.length > 2) {
window.history.back();
} else {
setLocation(fallbackPath);
}
};
export function BackButton({ href, label = 'Back' }: BackButtonProps) {
return (
<Button
variant="ghost"
size="sm"
onClick={handleBack}
className={`flex items-center gap-1 px-2 ${className}`}
>
<ChevronLeft className="h-4 w-4" />
<span>Back</span>
</Button>
<Link href={href}>
<Button
variant="ghost"
size="sm"
className="mb-4 gap-1 pl-0 hover:pl-1 transition-all"
>
<ArrowLeft className="h-4 w-4" />
{label}
</Button>
</Link>
);
}
@@ -0,0 +1,122 @@
import { useState, useEffect } from 'react';
import { Card, CardContent, CardTitle } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { Network, Info, Copy } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { toast } from '@/hooks/use-toast';
export function PublicIpCard() {
const [ipv4, setIpv4] = useState<string | null>(null);
const [ipv6, setIpv6] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
const fetchPublicIp = async () => {
try {
setLoading(true);
// Fetch IPv4
const ipv4Response = await fetch('https://api.ipify.org?format=json')
.then(res => res.json())
.catch(() => ({ ip: null }));
// Fetch IPv6 (might not be available for all users)
const ipv6Response = await fetch('https://api64.ipify.org?format=json')
.then(res => res.json())
.catch(() => ({ ip: null }));
setIpv4(ipv4Response.ip);
setIpv6(ipv6Response.ip);
setError(false);
} catch (err) {
console.error('Error fetching public IP:', err);
setError(true);
} finally {
setLoading(false);
}
};
fetchPublicIp();
// Refresh every 5 minutes
const interval = setInterval(fetchPublicIp, 300000);
return () => clearInterval(interval);
}, []);
const copyToClipboard = (text: string | null) => {
if (text) {
navigator.clipboard.writeText(text);
toast({
title: 'Copied to clipboard',
description: text,
});
}
};
return (
<Card>
<CardContent className="pt-6">
<div className="flex items-center mb-4">
<Network className="h-5 w-5 text-primary mr-2" />
<CardTitle className="text-lg font-semibold">Public IP Addresses</CardTitle>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8 ml-1">
<Info className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p>Your current public IP addresses as seen from the internet.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div className="space-y-3">
<div>
<div className="flex items-center justify-between mb-1">
<span className="text-sm text-muted-foreground">IPv4 Address</span>
{ipv4 && (
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={() => copyToClipboard(ipv4)}
>
<Copy className="h-3.5 w-3.5" />
</Button>
)}
</div>
<div className="bg-card border rounded-md p-3 text-sm font-mono">
{loading ? 'Loading...' : error ? 'Error fetching IP' : ipv4 || 'N/A'}
</div>
</div>
<Separator />
<div>
<div className="flex items-center justify-between mb-1">
<span className="text-sm text-muted-foreground">IPv6 Address</span>
{ipv6 && (
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={() => copyToClipboard(ipv6)}
>
<Copy className="h-3.5 w-3.5" />
</Button>
)}
</div>
<div className="bg-card border rounded-md p-3 text-sm font-mono overflow-x-auto">
{loading ? 'Loading...' : error ? 'Error fetching IP' : ipv6 || 'N/A'}
</div>
</div>
</div>
</CardContent>
</Card>
);
}