From e7700b60f342c8a0ba02f4414d76683266ed735d Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Fri, 21 Nov 2025 15:10:54 -0600 Subject: [PATCH] Enhancement: Add validation for EPG objects and payloads in updateEPG functions to prevent errors from invalid data --- frontend/src/WebSocket.jsx | 16 ++++++++++++---- frontend/src/api.js | 12 ++++++++++++ frontend/src/components/forms/DummyEPG.jsx | 10 ++++++++++ frontend/src/components/forms/EPG.jsx | 11 +++++++++++ frontend/src/components/tables/EPGsTable.jsx | 6 ++++++ frontend/src/store/epgs.jsx | 20 +++++++++++++++++--- 6 files changed, 68 insertions(+), 7 deletions(-) diff --git a/frontend/src/WebSocket.jsx b/frontend/src/WebSocket.jsx index 1c576d23..f2e28ae9 100644 --- a/frontend/src/WebSocket.jsx +++ b/frontend/src/WebSocket.jsx @@ -569,14 +569,22 @@ export const WebsocketProvider = ({ children }) => { break; case 'epg_refresh': - // Update the store with progress information - updateEPGProgress(parsedEvent.data); - - // If we have source/account info, update the EPG source status + // If we have source/account info, check if EPG exists before processing if (parsedEvent.data.source || parsedEvent.data.account) { const sourceId = parsedEvent.data.source || parsedEvent.data.account; const epg = epgs[sourceId]; + + // Only update progress if the EPG still exists in the store + // This prevents crashes when receiving updates for deleted EPGs + if (epg) { + // Update the store with progress information + updateEPGProgress(parsedEvent.data); + } else { + // EPG was deleted, ignore this update + console.debug(`Ignoring EPG refresh update for deleted EPG ${sourceId}`); + break; + } if (epg) { // Check for any indication of an error (either via status or error field) diff --git a/frontend/src/api.js b/frontend/src/api.js index 3304e8ad..7eda6a3f 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -1053,8 +1053,20 @@ export default class API { } static async updateEPG(values, isToggle = false) { + // Validate that values is an object + if (!values || typeof values !== 'object') { + console.error('updateEPG called with invalid values:', values); + return; + } + const { id, ...payload } = values; + // Validate that we have an ID and payload is an object + if (!id || typeof payload !== 'object') { + console.error('updateEPG: invalid id or payload', { id, payload }); + return; + } + try { // If this is just toggling the active state, make a simpler request if ( diff --git a/frontend/src/components/forms/DummyEPG.jsx b/frontend/src/components/forms/DummyEPG.jsx index bb787722..9f9346da 100644 --- a/frontend/src/components/forms/DummyEPG.jsx +++ b/frontend/src/components/forms/DummyEPG.jsx @@ -728,6 +728,16 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => { const handleSubmit = async (values) => { try { if (epg?.id) { + // Validate that we have a valid EPG object before updating + if (!epg || typeof epg !== 'object' || !epg.id) { + notifications.show({ + title: 'Error', + message: 'Invalid EPG data. Please close and reopen this form.', + color: 'red', + }); + return; + } + await API.updateEPG({ ...values, id: epg.id }); notifications.show({ title: 'Success', diff --git a/frontend/src/components/forms/EPG.jsx b/frontend/src/components/forms/EPG.jsx index 8f8a3070..db4f8310 100644 --- a/frontend/src/components/forms/EPG.jsx +++ b/frontend/src/components/forms/EPG.jsx @@ -15,6 +15,7 @@ import { Text, } from '@mantine/core'; import { isNotEmpty, useForm } from '@mantine/form'; +import { notifications } from '@mantine/notifications'; const EPG = ({ epg = null, isOpen, onClose }) => { const [sourceType, setSourceType] = useState('xmltv'); @@ -40,6 +41,16 @@ const EPG = ({ epg = null, isOpen, onClose }) => { const values = form.getValues(); if (epg?.id) { + // Validate that we have a valid EPG object before updating + if (!epg || typeof epg !== 'object' || !epg.id) { + notifications.show({ + title: 'Error', + message: 'Invalid EPG data. Please close and reopen this form.', + color: 'red', + }); + return; + } + await API.updateEPG({ id: epg.id, ...values }); } else { await API.addEPG(values); diff --git a/frontend/src/components/tables/EPGsTable.jsx b/frontend/src/components/tables/EPGsTable.jsx index bb707984..71e920e0 100644 --- a/frontend/src/components/tables/EPGsTable.jsx +++ b/frontend/src/components/tables/EPGsTable.jsx @@ -131,6 +131,12 @@ const EPGsTable = () => { const toggleActive = async (epg) => { try { + // Validate that epg is a valid object with an id + if (!epg || typeof epg !== 'object' || !epg.id) { + console.error('toggleActive called with invalid epg:', epg); + return; + } + // Send only the is_active field to trigger our special handling await API.updateEPG( { diff --git a/frontend/src/store/epgs.jsx b/frontend/src/store/epgs.jsx index e0576364..305c564f 100644 --- a/frontend/src/store/epgs.jsx +++ b/frontend/src/store/epgs.jsx @@ -50,9 +50,17 @@ const useEPGsStore = create((set) => ({ })), updateEPG: (epg) => - set((state) => ({ - epgs: { ...state.epgs, [epg.id]: epg }, - })), + set((state) => { + // Validate that epg is an object with an id + if (!epg || typeof epg !== 'object' || !epg.id) { + console.error('updateEPG called with invalid epg:', epg); + return state; + } + + return { + epgs: { ...state.epgs, [epg.id]: epg }, + }; + }), removeEPGs: (epgIds) => set((state) => { @@ -66,6 +74,12 @@ const useEPGsStore = create((set) => ({ updateEPGProgress: (data) => set((state) => { + // Validate that data is an object with a source + if (!data || typeof data !== 'object' || !data.source) { + console.error('updateEPGProgress called with invalid data:', data); + return state; + } + // Early exit if source doesn't exist in our EPGs store if (!state.epgs[data.source] && !data.status) { return state;