From 1671a245f22f770eddbeaa10e0b627c55f9a2d9b Mon Sep 17 00:00:00 2001 From: Siyuan Date: Mon, 10 Nov 2025 17:22:40 +0000 Subject: [PATCH] cosm(public-ip): update public IP card to show last updated time --- pkg/myip/check.go | 32 +++++++++++++++++++++++------- ui/src/components/PublicIPCard.tsx | 18 +++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/pkg/myip/check.go b/pkg/myip/check.go index 9879bee7..86d3ba50 100644 --- a/pkg/myip/check.go +++ b/pkg/myip/check.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "net/url" + "strconv" "strings" "sync" "time" @@ -52,19 +53,36 @@ func (ps *PublicIPState) checkCloudflare(ctx context.Context, family int) (*Publ return nil, err } + values := make(map[string]string) for line := range strings.SplitSeq(string(body), "\n") { key, value, ok := strings.Cut(line, "=") - if !ok || key != "ip" { + if !ok { continue } - - return &PublicIP{ - IPAddress: net.ParseIP(value), - LastUpdated: time.Now(), - }, nil + values[key] = value } - return nil, fmt.Errorf("no IP address found") + ps.lastUpdated = time.Now() + if ts, ok := values["ts"]; ok { + if ts, err := strconv.ParseFloat(ts, 64); err == nil { + ps.lastUpdated = time.Unix(int64(ts), 0) + } + } + + ipStr, ok := values["ip"] + if !ok { + return nil, fmt.Errorf("no IP address found") + } + + ip := net.ParseIP(ipStr) + if ip == nil { + return nil, fmt.Errorf("invalid IP address: %s", ipStr) + } + + return &PublicIP{ + IPAddress: ip, + LastUpdated: ps.lastUpdated, + }, nil } // checkAPI uses the API endpoint to get the public IP address diff --git a/ui/src/components/PublicIPCard.tsx b/ui/src/components/PublicIPCard.tsx index 041412e5..55c1c407 100644 --- a/ui/src/components/PublicIPCard.tsx +++ b/ui/src/components/PublicIPCard.tsx @@ -7,8 +7,25 @@ import { PublicIP } from "@hooks/stores"; import { m } from "@localizations/messages.js"; import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import notifications from "@/notifications"; +import { formatters } from "@/utils"; +const TimeAgoLabel = ({ date }: { date: Date }) => { + const [timeAgo, setTimeAgo] = useState(formatters.timeAgo(date)); + useEffect(() => { + const interval = setInterval(() => { + setTimeAgo(formatters.timeAgo(date)); + }, 1000); + return () => clearInterval(interval); + }, [date]); + + return ( + + {timeAgo} + + ); +}; + export default function PublicIPCard() { const { send } = useJsonRpc(); @@ -76,6 +93,7 @@ export default function PublicIPCard() { {ip.ip} + {ip.last_updated && } ))}