From 99008ac50198f16d85b0079c2f79430213a3c113 Mon Sep 17 00:00:00 2001 From: Richard Anderson Date: Sat, 6 Jun 2026 15:25:31 +0100 Subject: [PATCH] realtime filtering of refreshes --- resources/js/hooks/use-socket-events.ts | 13 +++++++++++-- resources/js/pages/backups/files.tsx | 2 +- resources/js/pages/backups/index.tsx | 2 +- resources/js/pages/commands/show.tsx | 2 +- resources/js/pages/php/index.tsx | 2 +- resources/js/pages/redirects/index.tsx | 2 +- resources/js/pages/scripts/show.tsx | 2 +- resources/js/pages/server-logs/components/logs.tsx | 2 +- resources/js/pages/server-logs/index.tsx | 7 +++++-- resources/js/pages/server-ssls/index.tsx | 2 +- resources/js/pages/servers/installing.tsx | 2 +- resources/js/pages/services/index.tsx | 2 +- resources/js/pages/sites/logs.tsx | 4 +++- resources/js/pages/workers/index.tsx | 6 +++++- resources/js/types/server-log.d.ts | 2 +- 15 files changed, 35 insertions(+), 17 deletions(-) diff --git a/resources/js/hooks/use-socket-events.ts b/resources/js/hooks/use-socket-events.ts index 67f2ec2ff..feb8a4273 100644 --- a/resources/js/hooks/use-socket-events.ts +++ b/resources/js/hooks/use-socket-events.ts @@ -95,16 +95,22 @@ export function useRealtimeRecord(initial: T | null | /** * Manages paginated Inertia data with realtime socket updates. * - * Listens for socket events matching `{eventPrefix}.updated` (replace row), - * and `{eventPrefix}.deleted` (remove row) automatically. + * Listens for socket events matching `{eventPrefix}.created` (prepend row), + * `{eventPrefix}.updated` (replace row), and `{eventPrefix}.deleted` (remove row) automatically. + * + * Socket events are broadcast project-wide, so pass a `scope` of field/value pairs + * (e.g. `{ server_id: server.id }`) that created records must match before trigger refreshes * * Returns the live data and setter for custom handling. */ export function useRealtime( initialData: PaginatedData, eventPrefix: string, + scope?: Partial, ): [PaginatedData, Dispatch>>] { const [data, setData] = useState>(initialData); + const scopeRef = useRef(scope); + scopeRef.current = scope; useEffect(() => { setData(initialData); @@ -122,6 +128,9 @@ export function useRealtime( switch (action) { case 'created': + if (!Object.entries(scopeRef.current ?? {}).every(([key, value]) => (eventData as Record)[key] === value)) { + break; + } setData((prev) => ({ ...prev, data: [eventData as unknown as T, ...prev.data], diff --git a/resources/js/pages/backups/files.tsx b/resources/js/pages/backups/files.tsx index eb5739c44..4c8c81781 100644 --- a/resources/js/pages/backups/files.tsx +++ b/resources/js/pages/backups/files.tsx @@ -21,7 +21,7 @@ type Page = { export default function Files() { const page = usePage(); - const [files] = useRealtime(page.props.files, 'backup-file'); + const [files] = useRealtime(page.props.files, 'backup-file', { backup_id: page.props.backup.id }); const runBackupForm = useForm(); const runBackup = () => { diff --git a/resources/js/pages/backups/index.tsx b/resources/js/pages/backups/index.tsx index 87e0006dc..079806dae 100644 --- a/resources/js/pages/backups/index.tsx +++ b/resources/js/pages/backups/index.tsx @@ -20,7 +20,7 @@ type Page = { export default function Backups() { const page = usePage(); - const [backups] = useRealtime(page.props.backups, 'backup'); + const [backups] = useRealtime(page.props.backups, 'backup', { server_id: page.props.server.id }); return ( diff --git a/resources/js/pages/commands/show.tsx b/resources/js/pages/commands/show.tsx index 822329a38..ff95ab99c 100644 --- a/resources/js/pages/commands/show.tsx +++ b/resources/js/pages/commands/show.tsx @@ -23,7 +23,7 @@ type Page = { export default function Show() { const page = usePage(); - const [executions] = useRealtime(page.props.executions, 'command-execution'); + const [executions] = useRealtime(page.props.executions, 'command-execution', { command_id: page.props.command.id }); const breadcrumbs: BreadcrumbItem[] = [ { diff --git a/resources/js/pages/php/index.tsx b/resources/js/pages/php/index.tsx index 56389d9e5..131d8502c 100644 --- a/resources/js/pages/php/index.tsx +++ b/resources/js/pages/php/index.tsx @@ -19,7 +19,7 @@ export default function PHP() { installedVersions: PaginatedData; }>(); - const [installedVersions] = useRealtime(page.props.installedVersions, 'service'); + const [installedVersions] = useRealtime(page.props.installedVersions, 'service', { server_id: page.props.server.id, type: 'php' }); return ( diff --git a/resources/js/pages/redirects/index.tsx b/resources/js/pages/redirects/index.tsx index ed27f4297..8c9dbae83 100644 --- a/resources/js/pages/redirects/index.tsx +++ b/resources/js/pages/redirects/index.tsx @@ -22,7 +22,7 @@ export default function Redirects() { redirects: PaginatedData; }>(); - const [redirects] = useRealtime(page.props.redirects, 'redirect'); + const [redirects] = useRealtime(page.props.redirects, 'redirect', { site_id: page.props.site.id }); return ( diff --git a/resources/js/pages/scripts/show.tsx b/resources/js/pages/scripts/show.tsx index 73d0fd146..a24a635aa 100644 --- a/resources/js/pages/scripts/show.tsx +++ b/resources/js/pages/scripts/show.tsx @@ -18,7 +18,7 @@ type Page = { export default function Show() { const page = usePage(); - const [executions] = useRealtime(page.props.executions, 'script-execution'); + const [executions] = useRealtime(page.props.executions, 'script-execution', { script_id: page.props.script.id }); const breadcrumbs: BreadcrumbItem[] = [ { diff --git a/resources/js/pages/server-logs/components/logs.tsx b/resources/js/pages/server-logs/components/logs.tsx index 79edf0cc2..9fcc295e1 100644 --- a/resources/js/pages/server-logs/components/logs.tsx +++ b/resources/js/pages/server-logs/components/logs.tsx @@ -11,7 +11,7 @@ export default function Logs({ server, site }: { server: Server; site?: Site }) const [currentPage, setCurrentPage] = useState(1); const query = useQuery({ - queryKey: ['serverLogs', currentPage], + queryKey: ['serverLogs', server.id, site?.id, currentPage], queryFn: async () => { return ( await axios.get(route('logs.json', { server: server.id, site: site?.id }), { diff --git a/resources/js/pages/server-logs/index.tsx b/resources/js/pages/server-logs/index.tsx index d0684bf77..46f9e1377 100644 --- a/resources/js/pages/server-logs/index.tsx +++ b/resources/js/pages/server-logs/index.tsx @@ -18,10 +18,13 @@ export default function ServerLogs() { title: string; server: Server; logs: PaginatedData; - remote: boolean; + remote?: boolean; }>(); - const [logs] = useRealtime(page.props.logs, 'server-log'); + const [logs] = useRealtime(page.props.logs, 'server-log', { + server_id: page.props.server.id, + is_remote: !!page.props.remote, + }); return ( diff --git a/resources/js/pages/server-ssls/index.tsx b/resources/js/pages/server-ssls/index.tsx index 955c886b6..d3a891e66 100644 --- a/resources/js/pages/server-ssls/index.tsx +++ b/resources/js/pages/server-ssls/index.tsx @@ -21,7 +21,7 @@ export default function ServerSsls() { domains: Domain[]; }>(); - const [ssls] = useRealtime(page.props.ssls, 'ssl'); + const [ssls] = useRealtime(page.props.ssls, 'ssl', { server_id: page.props.server.id, site_id: null }); return ( diff --git a/resources/js/pages/servers/installing.tsx b/resources/js/pages/servers/installing.tsx index d8f3605f0..8bbbb3246 100644 --- a/resources/js/pages/servers/installing.tsx +++ b/resources/js/pages/servers/installing.tsx @@ -14,7 +14,7 @@ export default function InstallingServer() { logs: PaginatedData; }>(); - const [logs] = useRealtime(page.props.logs, 'server-log'); + const [logs] = useRealtime(page.props.logs, 'server-log', { server_id: page.props.server.id }); return ( diff --git a/resources/js/pages/services/index.tsx b/resources/js/pages/services/index.tsx index e9f4bba03..674bb9832 100644 --- a/resources/js/pages/services/index.tsx +++ b/resources/js/pages/services/index.tsx @@ -19,7 +19,7 @@ export default function WorkerIndex() { services: PaginatedData; }>(); - const [services] = useRealtime(page.props.services, 'service'); + const [services] = useRealtime(page.props.services, 'service', { server_id: page.props.server.id }); return ( diff --git a/resources/js/pages/sites/logs.tsx b/resources/js/pages/sites/logs.tsx index 70bf979b2..6a2d1c8fd 100644 --- a/resources/js/pages/sites/logs.tsx +++ b/resources/js/pages/sites/logs.tsx @@ -10,6 +10,7 @@ import { PaginatedData } from '@/types'; import { ServerLog } from '@/types/server-log'; import { DataTable } from '@/components/data-table'; import { columns } from '@/pages/server-logs/components/columns'; +import { useRealtime } from '@/hooks/use-socket-events'; type Page = { server: Server; @@ -19,6 +20,7 @@ type Page = { export default function ShowSite() { const page = usePage(); + const [logs] = useRealtime(page.props.logs, 'server-log', { site_id: page.props.site.id }); return ( @@ -31,7 +33,7 @@ export default function ShowSite() { - + ); diff --git a/resources/js/pages/workers/index.tsx b/resources/js/pages/workers/index.tsx index 138f56f1f..a4168dccf 100644 --- a/resources/js/pages/workers/index.tsx +++ b/resources/js/pages/workers/index.tsx @@ -24,7 +24,11 @@ export default function WorkerIndex() { }>(); const dialog = useDialog(); - const [workers] = useRealtime(page.props.workers, 'worker'); + const [workers] = useRealtime( + page.props.workers, + 'worker', + page.props.site ? { site_id: page.props.site.id } : { server_id: page.props.server.id }, + ); return ( diff --git a/resources/js/types/server-log.d.ts b/resources/js/types/server-log.d.ts index 1acf5d827..b39ed181b 100644 --- a/resources/js/types/server-log.d.ts +++ b/resources/js/types/server-log.d.ts @@ -1,7 +1,7 @@ export interface ServerLog { id: number; server_id: number; - site_id?: number; + site_id: number | null; type: string; name: string; disk: string;