Moved more common functions into ApiServerContext
This commit is contained in:
parent
b71537dc64
commit
3ad0002bbb
@ -4,7 +4,8 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
useContext,
|
useContext,
|
||||||
useRef
|
useRef,
|
||||||
|
useCallback
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import io from 'socket.io-client'
|
import io from 'socket.io-client'
|
||||||
import { message, notification, Modal, Space, Button } from 'antd'
|
import { message, notification, Modal, Space, Button } from 'antd'
|
||||||
@ -22,8 +23,9 @@ logger.setLevel(config.logLevel)
|
|||||||
const ApiServerContext = createContext()
|
const ApiServerContext = createContext()
|
||||||
|
|
||||||
const ApiServerProvider = ({ children }) => {
|
const ApiServerProvider = ({ children }) => {
|
||||||
const { token, userProfile } = useContext(AuthContext)
|
const { token, userProfile, authenticated } = useContext(AuthContext)
|
||||||
const socketRef = useRef(null)
|
const socketRef = useRef(null)
|
||||||
|
const [connected, setConnected] = useState(false)
|
||||||
const [connecting, setConnecting] = useState(false)
|
const [connecting, setConnecting] = useState(false)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
@ -32,9 +34,43 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
const [showErrorModal, setShowErrorModal] = useState(false)
|
const [showErrorModal, setShowErrorModal] = useState(false)
|
||||||
const [errorModalContent, setErrorModalContent] = useState('')
|
const [errorModalContent, setErrorModalContent] = useState('')
|
||||||
const [retryCallback, setRetryCallback] = useState(null)
|
const [retryCallback, setRetryCallback] = useState(null)
|
||||||
|
const subscribedCallbacksRef = useRef(new Map())
|
||||||
|
const subscribedLockCallbacksRef = useRef(new Map())
|
||||||
|
|
||||||
useEffect(() => {
|
const notifyLockUpdate = useCallback(
|
||||||
if (token) {
|
async (lockData) => {
|
||||||
|
logger.debug('Notifying lock update:', lockData)
|
||||||
|
const objectId = lockData._id || lockData.id
|
||||||
|
|
||||||
|
if (
|
||||||
|
objectId &&
|
||||||
|
subscribedLockCallbacksRef.current.has(objectId) &&
|
||||||
|
lockData.user != userProfile?._id
|
||||||
|
) {
|
||||||
|
const callbacks = subscribedLockCallbacksRef.current.get(objectId)
|
||||||
|
logger.debug(
|
||||||
|
`Calling ${callbacks.length} lock callbacks for object:`,
|
||||||
|
objectId
|
||||||
|
)
|
||||||
|
callbacks.forEach((callback) => {
|
||||||
|
try {
|
||||||
|
callback(lockData)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error in lock update callback:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
`No lock callbacks found for object: ${objectId}, subscribed lock callbacks:`,
|
||||||
|
Array.from(subscribedLockCallbacksRef.current.keys())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[userProfile?._id]
|
||||||
|
)
|
||||||
|
|
||||||
|
const connectToServer = useCallback(() => {
|
||||||
|
if (token && authenticated == true) {
|
||||||
logger.debug('Token is available, connecting to api server...')
|
logger.debug('Token is available, connecting to api server...')
|
||||||
|
|
||||||
const newSocket = io(config.apiServerUrl, {
|
const newSocket = io(config.apiServerUrl, {
|
||||||
@ -48,18 +84,25 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
newSocket.on('connect', () => {
|
newSocket.on('connect', () => {
|
||||||
logger.debug('Api Server connected')
|
logger.debug('Api Server connected')
|
||||||
setConnecting(false)
|
setConnecting(false)
|
||||||
|
setConnected(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
newSocket.on('notify_object_update', notifyObjectUpdate)
|
||||||
|
newSocket.on('notify_object_new', notifyObjectNew)
|
||||||
|
newSocket.on('notify_lock_update', notifyLockUpdate)
|
||||||
|
|
||||||
newSocket.on('disconnect', () => {
|
newSocket.on('disconnect', () => {
|
||||||
logger.debug('Api Server disconnected')
|
logger.debug('Api Server disconnected')
|
||||||
setError('Api Server disconnected')
|
setError('Api Server disconnected')
|
||||||
|
setConnected(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
newSocket.on('connect_error', (err) => {
|
newSocket.on('connect_error', (err) => {
|
||||||
logger.error('Api Server connection error:', err)
|
logger.error('Api Server connection error:', err)
|
||||||
messageApi.error('Api Server connection error: ' + err.message)
|
messageApi.error('Api Server connection error: ' + err.message)
|
||||||
setError('Api Server connection error')
|
setError('Api Server connection error')
|
||||||
|
setConnected(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
newSocket.on('bridge.notification', (data) => {
|
newSocket.on('bridge.notification', (data) => {
|
||||||
@ -75,21 +118,27 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
socketRef.current = newSocket
|
socketRef.current = newSocket
|
||||||
|
}
|
||||||
|
}, [token, authenticated, messageApi, notificationApi, notifyLockUpdate])
|
||||||
|
|
||||||
// Clean up function
|
useEffect(() => {
|
||||||
return () => {
|
if (token && authenticated == true) {
|
||||||
if (socketRef.current) {
|
connectToServer()
|
||||||
logger.debug('Cleaning up api server connection...')
|
|
||||||
socketRef.current.disconnect()
|
|
||||||
socketRef.current = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!token && socketRef.current) {
|
} else if (!token && socketRef.current) {
|
||||||
logger.debug('Token not available, disconnecting api server...')
|
logger.debug('Token not available, disconnecting api server...')
|
||||||
socketRef.current.disconnect()
|
socketRef.current.disconnect()
|
||||||
socketRef.current = null
|
socketRef.current = null
|
||||||
}
|
}
|
||||||
}, [token, messageApi])
|
|
||||||
|
// Clean up function
|
||||||
|
return () => {
|
||||||
|
if (socketRef.current) {
|
||||||
|
logger.debug('Cleaning up api server connection...')
|
||||||
|
socketRef.current.disconnect()
|
||||||
|
socketRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [token, authenticated, connectToServer])
|
||||||
|
|
||||||
const lockObject = (id, type) => {
|
const lockObject = (id, type) => {
|
||||||
logger.debug('Locking ' + id)
|
logger.debug('Locking ' + id)
|
||||||
@ -118,8 +167,12 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
type: type
|
type: type
|
||||||
},
|
},
|
||||||
(lockEvent) => {
|
(lockEvent) => {
|
||||||
logger.debug('Received lock event for object:', id, lockEvent)
|
logger.debug('Received lock status for object:', id, lockEvent)
|
||||||
resolve(lockEvent)
|
if (lockEvent.user != userProfile?._id) {
|
||||||
|
resolve(lockEvent)
|
||||||
|
} else {
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
logger.debug('Sent fetch lock command for object:', id)
|
logger.debug('Sent fetch lock command for object:', id)
|
||||||
@ -127,65 +180,186 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLockEvent = (id, callback) => {
|
const notifyObjectUpdate = async (object) => {
|
||||||
|
logger.debug('Notifying object update:', object)
|
||||||
|
const objectId = object._id || object.id
|
||||||
|
|
||||||
|
if (objectId && subscribedCallbacksRef.current.has(objectId)) {
|
||||||
|
const callbacks = subscribedCallbacksRef.current.get(objectId)
|
||||||
|
logger.debug(
|
||||||
|
`Calling ${callbacks.length} callbacks for object:`,
|
||||||
|
objectId
|
||||||
|
)
|
||||||
|
callbacks.forEach((callback) => {
|
||||||
|
try {
|
||||||
|
callback(object)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error in object update callback:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
`No callbacks found for object: ${objectId}, subscribed callbacks:`,
|
||||||
|
Array.from(subscribedCallbacksRef.current.keys())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifyObjectNew = async (object) => {
|
||||||
|
logger.debug('Notifying object new:', object)
|
||||||
|
const objectType = object.type || 'unknown'
|
||||||
|
|
||||||
|
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
|
||||||
|
const callbacks = subscribedCallbacksRef.current.get(objectType)
|
||||||
|
logger.debug(
|
||||||
|
`Calling ${callbacks.length} callbacks for type:`,
|
||||||
|
objectType
|
||||||
|
)
|
||||||
|
callbacks.forEach((callback) => {
|
||||||
|
try {
|
||||||
|
callback(object)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error in object new callback:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
`No callbacks found for object: ${objectType}, subscribed callbacks:`,
|
||||||
|
Array.from(subscribedCallbacksRef.current.keys())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const offUpdateEvent = useCallback((id, type, callback) => {
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
const eventHandler = (data) => {
|
// Remove callback from the subscribed callbacks map
|
||||||
if (data._id === id && data?.user !== userProfile._id) {
|
if (subscribedCallbacksRef.current.has(id)) {
|
||||||
logger.debug(
|
const callbacks = subscribedCallbacksRef.current
|
||||||
'Lock update received for object:',
|
.get(id)
|
||||||
id,
|
.filter((cb) => cb !== callback)
|
||||||
'locked:',
|
if (callbacks.length === 0) {
|
||||||
data.locked
|
subscribedCallbacksRef.current.delete(id)
|
||||||
)
|
socketRef.current.emit('unsubscribe', { id: id, type: type })
|
||||||
callback(data)
|
} else {
|
||||||
|
subscribedCallbacksRef.current.set(id, callbacks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socketRef.current.on('notify_lock_update', eventHandler)
|
|
||||||
logger.debug('Registered lock event listener for object:', id)
|
|
||||||
|
|
||||||
// Return cleanup function
|
|
||||||
return () => offLockEvent(id, eventHandler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const offLockEvent = (id, eventHandler) => {
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
socketRef.current.off('notify_lock_update', eventHandler)
|
|
||||||
logger.debug('Removed lock event listener for object:', id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onUpdateEvent = (id, callback) => {
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
const eventHandler = (data) => {
|
|
||||||
if (data._id === id && data?.user !== userProfile._id) {
|
|
||||||
logger.debug(
|
|
||||||
'Update event received for object:',
|
|
||||||
id,
|
|
||||||
'updatedAt:',
|
|
||||||
data.updatedAt
|
|
||||||
)
|
|
||||||
callback(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
socketRef.current.on('notify_object_update', eventHandler)
|
|
||||||
logger.debug('Registered update event listener for object:', id)
|
|
||||||
|
|
||||||
// Return cleanup function
|
|
||||||
return () => offUpdateEvent(id, eventHandler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const offUpdateEvent = (id, eventHandler) => {
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
socketRef.current.off('notify_update', eventHandler)
|
|
||||||
logger.debug('Removed update event listener for object:', id)
|
logger.debug('Removed update event listener for object:', id)
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const showError = (content, callback = null) => {
|
const offTypeEvent = useCallback((type, callback) => {
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
// Remove callback from the subscribed callbacks map
|
||||||
|
if (subscribedCallbacksRef.current.has(type)) {
|
||||||
|
const callbacks = subscribedCallbacksRef.current
|
||||||
|
.get(type)
|
||||||
|
.filter((cb) => cb !== callback)
|
||||||
|
if (callbacks.length === 0) {
|
||||||
|
subscribedCallbacksRef.current.delete(type)
|
||||||
|
socketRef.current.emit('unsubscribe', { type: type })
|
||||||
|
} else {
|
||||||
|
subscribedCallbacksRef.current.set(type, callbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Removed new event listener for type:', type)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const subscribeToObject = useCallback(
|
||||||
|
(id, type, callback) => {
|
||||||
|
logger.debug('Subscribing to object:', id, 'type:', type)
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
// Add callback to the subscribed callbacks map immediately
|
||||||
|
if (!subscribedCallbacksRef.current.has(id)) {
|
||||||
|
subscribedCallbacksRef.current.set(id, [])
|
||||||
|
}
|
||||||
|
subscribedCallbacksRef.current.get(id).push(callback)
|
||||||
|
logger.debug(
|
||||||
|
`Added callback for object ${id}, total callbacks: ${subscribedCallbacksRef.current.get(id).length}`
|
||||||
|
)
|
||||||
|
|
||||||
|
socketRef.current.emit('subscribe', { id: id, type: type })
|
||||||
|
logger.debug('Registered update event listener for object:', id)
|
||||||
|
|
||||||
|
// Return cleanup function
|
||||||
|
return () => offUpdateEvent(id, type, callback)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[offUpdateEvent]
|
||||||
|
)
|
||||||
|
|
||||||
|
const subscribeToType = useCallback(
|
||||||
|
(type, callback) => {
|
||||||
|
logger.debug('Subscribing to type:', type)
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
// Add callback to the subscribed callbacks map immediately
|
||||||
|
if (!subscribedCallbacksRef.current.has(type)) {
|
||||||
|
subscribedCallbacksRef.current.set(type, [])
|
||||||
|
}
|
||||||
|
subscribedCallbacksRef.current.get(type).push(callback)
|
||||||
|
logger.debug(
|
||||||
|
`Added callback for type ${type}, total callbacks: ${subscribedCallbacksRef.current.get(type).length}`
|
||||||
|
)
|
||||||
|
|
||||||
|
socketRef.current.emit('subscribe', { type: type })
|
||||||
|
logger.debug('Registered update event listener for object:', type)
|
||||||
|
|
||||||
|
// Return cleanup function
|
||||||
|
return () => offTypeEvent(type, callback)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[offTypeEvent]
|
||||||
|
)
|
||||||
|
|
||||||
|
const offLockEvent = useCallback((id, callback) => {
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
// Remove callback from the subscribed lock callbacks map
|
||||||
|
if (subscribedLockCallbacksRef.current.has(id)) {
|
||||||
|
const callbacks = subscribedLockCallbacksRef.current
|
||||||
|
.get(id)
|
||||||
|
.filter((cb) => cb !== callback)
|
||||||
|
if (callbacks.length === 0) {
|
||||||
|
subscribedLockCallbacksRef.current.delete(id)
|
||||||
|
} else {
|
||||||
|
subscribedLockCallbacksRef.current.set(id, callbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Removed lock event listener for object:', id)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const subscribeToLock = useCallback(
|
||||||
|
(id, type, callback) => {
|
||||||
|
logger.debug('Subscribing to lock for object:', id, 'type:', type)
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
// Add callback to the subscribed lock callbacks map immediately
|
||||||
|
if (!subscribedLockCallbacksRef.current.has(id)) {
|
||||||
|
subscribedLockCallbacksRef.current.set(id, [])
|
||||||
|
}
|
||||||
|
subscribedLockCallbacksRef.current.get(id).push(callback)
|
||||||
|
logger.debug(
|
||||||
|
`Added lock callback for object ${id}, total lock callbacks: ${subscribedLockCallbacksRef.current.get(id).length}`
|
||||||
|
)
|
||||||
|
|
||||||
|
socketRef.current.emit('subscribe_lock', { id: id, type: type })
|
||||||
|
logger.debug('Registered lock event listener for object:', id)
|
||||||
|
|
||||||
|
// Return cleanup function
|
||||||
|
return () => offLockEvent(id, callback)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[offLockEvent]
|
||||||
|
)
|
||||||
|
|
||||||
|
const showError = (error, callback = null) => {
|
||||||
|
var content = `Error ${error.code} (${error.status}): ${error.message}`
|
||||||
|
if (error.response?.data?.error) {
|
||||||
|
content = `${error.response?.data?.error} (${error.status})`
|
||||||
|
}
|
||||||
setErrorModalContent(content)
|
setErrorModalContent(content)
|
||||||
setRetryCallback(() => callback)
|
setRetryCallback(() => callback)
|
||||||
setShowErrorModal(true)
|
setShowErrorModal(true)
|
||||||
@ -200,8 +374,8 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
setRetryCallback(null)
|
setRetryCallback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generalized fetchObjectInfo function
|
// Generalized fetchObject function
|
||||||
const fetchObjectInfo = async (id, type) => {
|
const fetchObject = async (id, type) => {
|
||||||
const fetchUrl = `${config.backendUrl}/${type}s/${id}`
|
const fetchUrl = `${config.backendUrl}/${type}s/${id}`
|
||||||
setFetchLoading(true)
|
setFetchLoading(true)
|
||||||
logger.debug('Fetching from ' + fetchUrl)
|
logger.debug('Fetching from ' + fetchUrl)
|
||||||
@ -214,62 +388,17 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to fetch object information:', err)
|
showError(err, () => {
|
||||||
// Don't automatically show error - let the component handle it
|
fetchObject(id, type)
|
||||||
throw err
|
})
|
||||||
|
return {}
|
||||||
} finally {
|
} finally {
|
||||||
setFetchLoading(false)
|
setFetchLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update filament information
|
|
||||||
const updateObjectInfo = async (id, type, value) => {
|
|
||||||
const updateUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
|
|
||||||
logger.debug('Updating info for ' + id)
|
|
||||||
try {
|
|
||||||
const response = await axios.put(updateUrl, value, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
logger.debug('Filament updated successfully')
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
await socketRef.current.emit('update', {
|
|
||||||
_id: id,
|
|
||||||
type: type,
|
|
||||||
updatedAt: response.data.updatedAt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Failed to update filament information:', err)
|
|
||||||
// Don't automatically show error - let the component handle it
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update filament information
|
|
||||||
const createObject = async (type, value) => {
|
|
||||||
const createUrl = `${config.backendUrl}/${type.toLowerCase()}s`
|
|
||||||
logger.debug('Creating object...')
|
|
||||||
try {
|
|
||||||
const response = await axios.post(createUrl, value, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Failed to update filament information:', err)
|
|
||||||
// Don't automatically show error - let the component handle it
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch table data with pagination, filtering, and sorting
|
// Fetch table data with pagination, filtering, and sorting
|
||||||
const fetchTableData = async (type, params = {}) => {
|
const fetchObjects = async (type, params = {}) => {
|
||||||
const {
|
const {
|
||||||
page = 1,
|
page = 1,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
@ -319,9 +448,122 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
hasMore,
|
hasMore,
|
||||||
page
|
page
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
logger.error('Failed to fetch table data:', error)
|
showError(err, () => {
|
||||||
throw error
|
fetchObjects(type, params)
|
||||||
|
})
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch table data with pagination, filtering, and sorting
|
||||||
|
const fetchObjectsByProperty = async (type, params = {}) => {
|
||||||
|
const { filter = {}, properties = [] } = params
|
||||||
|
|
||||||
|
logger.debug('Fetching property object data from:', type, {
|
||||||
|
properties,
|
||||||
|
filter
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.backendUrl}/${type.toLowerCase()}s/properties`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
...filter,
|
||||||
|
properties: properties.join(',') // Convert array to comma-separated string
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const newData = response.data
|
||||||
|
|
||||||
|
return newData
|
||||||
|
} catch (err) {
|
||||||
|
showError(err, () => {
|
||||||
|
fetchObjectsByProperty(type, params)
|
||||||
|
})
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update filament information
|
||||||
|
const updateObject = async (id, type, value) => {
|
||||||
|
const updateUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
|
||||||
|
logger.debug('Updating info for ' + id)
|
||||||
|
try {
|
||||||
|
const response = await axios.put(updateUrl, value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
logger.debug('Object updated successfully')
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
await socketRef.current.emit('update', {
|
||||||
|
_id: id,
|
||||||
|
type: type,
|
||||||
|
updatedAt: response.data.updatedAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return response.data
|
||||||
|
} catch (err) {
|
||||||
|
setError(err, () => {
|
||||||
|
updateObject(id, type, value)
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update filament information
|
||||||
|
const deleteObject = async (id, type) => {
|
||||||
|
const deleteUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
|
||||||
|
logger.debug('Deleting object ID: ' + id)
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(deleteUrl, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
logger.debug('Object deleted successfully')
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
await socketRef.current.emit('update', {
|
||||||
|
_id: id,
|
||||||
|
type: type,
|
||||||
|
updatedAt: response.data.updatedAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return response.data
|
||||||
|
} catch (err) {
|
||||||
|
showError(err, () => {
|
||||||
|
deleteObject(id, type)
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update filament information
|
||||||
|
const createObject = async (type, value) => {
|
||||||
|
const createUrl = `${config.backendUrl}/${type.toLowerCase()}s`
|
||||||
|
logger.debug('Creating object...')
|
||||||
|
try {
|
||||||
|
const response = await axios.post(createUrl, value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
} catch (err) {
|
||||||
|
showError(err, () => {
|
||||||
|
createObject(type, value)
|
||||||
|
})
|
||||||
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,27 +590,35 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
document.body.appendChild(fileLink)
|
document.body.appendChild(fileLink)
|
||||||
fileLink.click()
|
fileLink.click()
|
||||||
fileLink.parentNode.removeChild(fileLink)
|
fileLink.parentNode.removeChild(fileLink)
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
logger.error('Failed to download GCode file content:', error)
|
showError(err, () => {
|
||||||
|
fetchObjectContent(id, type, fileName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error.response) {
|
// Fetch notes for a specific parent
|
||||||
if (error.response.status === 404) {
|
const fetchNotes = async (parentId) => {
|
||||||
showError(
|
logger.debug('Fetching notes for parent:', parentId)
|
||||||
`The ${type} file "${fileName}" was not found on the server. It may have been deleted or moved.`,
|
try {
|
||||||
() => fetchObjectContent(id, type, fileName)
|
const response = await axios.get(`${config.backendUrl}/notes`, {
|
||||||
)
|
params: {
|
||||||
} else {
|
parent: parentId,
|
||||||
showError(
|
sort: 'createdAt',
|
||||||
`Error downloading ${type} file: ${error.response.status} - ${error.response.statusText}`,
|
order: 'ascend'
|
||||||
() => fetchObjectContent(id, type, fileName)
|
},
|
||||||
)
|
headers: {
|
||||||
}
|
Accept: 'application/json'
|
||||||
} else {
|
},
|
||||||
showError(
|
withCredentials: true
|
||||||
'An unexpected error occurred while downloading. Please check your connection and try again.',
|
})
|
||||||
() => fetchObjectContent(id, type, fileName)
|
|
||||||
)
|
const notesData = response.data
|
||||||
}
|
logger.debug('Fetched notes:', notesData.length)
|
||||||
|
return notesData
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to fetch notes:', error)
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,19 +628,24 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
apiServer: socketRef.current,
|
apiServer: socketRef.current,
|
||||||
error,
|
error,
|
||||||
connecting,
|
connecting,
|
||||||
|
connected,
|
||||||
lockObject,
|
lockObject,
|
||||||
unlockObject,
|
unlockObject,
|
||||||
fetchObjectLock,
|
fetchObjectLock,
|
||||||
updateObjectInfo,
|
updateObject,
|
||||||
createObject,
|
createObject,
|
||||||
onLockEvent,
|
deleteObject,
|
||||||
onUpdateEvent,
|
subscribeToObject,
|
||||||
|
subscribeToType,
|
||||||
|
subscribeToLock,
|
||||||
offUpdateEvent,
|
offUpdateEvent,
|
||||||
fetchObjectInfo,
|
fetchObject,
|
||||||
fetchTableData,
|
fetchObjects,
|
||||||
|
fetchObjectsByProperty,
|
||||||
fetchLoading,
|
fetchLoading,
|
||||||
showError,
|
showError,
|
||||||
fetchObjectContent
|
fetchObjectContent,
|
||||||
|
fetchNotes
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
|||||||
@ -53,8 +53,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.status === 200 && response.data) {
|
if (response.status === 200 && response.data) {
|
||||||
logger.debug('User is authenticated!')
|
logger.debug('Got auth token!')
|
||||||
setAuthenticated(true)
|
|
||||||
setToken(response.data.access_token)
|
setToken(response.data.access_token)
|
||||||
setExpiresAt(response.data.expires_at)
|
setExpiresAt(response.data.expires_at)
|
||||||
setUserProfile(response.data)
|
setUserProfile(response.data)
|
||||||
@ -89,75 +88,6 @@ const AuthProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const showTokenExpirationMessage = useCallback(
|
|
||||||
(expiresAt) => {
|
|
||||||
const now = new Date()
|
|
||||||
const expirationDate = new Date(expiresAt)
|
|
||||||
const timeRemaining = expirationDate - now
|
|
||||||
|
|
||||||
if (timeRemaining <= 0) {
|
|
||||||
if (authenticated) {
|
|
||||||
setShowSessionExpiredModal(true)
|
|
||||||
setAuthenticated(false)
|
|
||||||
notificationApi.destroy('token-expiration')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const minutes = Math.floor(timeRemaining / 60000)
|
|
||||||
const seconds = Math.floor((timeRemaining % 60000) / 1000)
|
|
||||||
|
|
||||||
// Only show notification in the final minute
|
|
||||||
if (minutes === 0) {
|
|
||||||
const totalSeconds = 60
|
|
||||||
const remainingSeconds = totalSeconds - seconds
|
|
||||||
const progress = (remainingSeconds / totalSeconds) * 100
|
|
||||||
|
|
||||||
notificationApi.info({
|
|
||||||
message: 'Session Expiring Soon',
|
|
||||||
description: (
|
|
||||||
<div>
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
Your session will expire in {seconds} seconds
|
|
||||||
</div>
|
|
||||||
<Progress
|
|
||||||
percent={progress}
|
|
||||||
size='small'
|
|
||||||
status='active'
|
|
||||||
showInfo={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
duration: 0,
|
|
||||||
key: 'token-expiration',
|
|
||||||
icon: null,
|
|
||||||
placement: 'bottomRight',
|
|
||||||
style: {
|
|
||||||
width: 360
|
|
||||||
},
|
|
||||||
className: 'token-expiration-notification',
|
|
||||||
closeIcon: null,
|
|
||||||
onClose: () => {},
|
|
||||||
btn: (
|
|
||||||
<Button
|
|
||||||
type='primary'
|
|
||||||
size='small'
|
|
||||||
onClick={() => {
|
|
||||||
notificationApi.destroy('token-expiration')
|
|
||||||
refreshToken()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reload Session
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else if (minutes === 1) {
|
|
||||||
// Clear any existing notification when we enter the final minute
|
|
||||||
notificationApi.destroy('token-expiration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[authenticated, notificationApi]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleSessionExpiredModalOk = () => {
|
const handleSessionExpiredModalOk = () => {
|
||||||
setShowSessionExpiredModal(false)
|
setShowSessionExpiredModal(false)
|
||||||
loginWithSSO()
|
loginWithSSO()
|
||||||
@ -167,22 +97,89 @@ const AuthProvider = ({ children }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let intervalId
|
let intervalId
|
||||||
|
|
||||||
const tokenRefreshInterval = () => {
|
const tokenRefresh = () => {
|
||||||
if (expiresAt) {
|
if (expiresAt) {
|
||||||
showTokenExpirationMessage(expiresAt)
|
const now = new Date()
|
||||||
|
const expirationDate = new Date(expiresAt)
|
||||||
|
const timeRemaining = expirationDate - now
|
||||||
|
|
||||||
|
if (timeRemaining <= 0) {
|
||||||
|
if (authenticated == true) {
|
||||||
|
setAuthenticated(false)
|
||||||
|
}
|
||||||
|
setShowSessionExpiredModal(true)
|
||||||
|
notificationApi.destroy('token-expiration')
|
||||||
|
} else {
|
||||||
|
if (authenticated == false) {
|
||||||
|
setAuthenticated(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const minutes = Math.floor(timeRemaining / 60000)
|
||||||
|
const seconds = Math.floor((timeRemaining % 60000) / 1000)
|
||||||
|
|
||||||
|
// Only show notification in the final minute
|
||||||
|
if (minutes === 0) {
|
||||||
|
const totalSeconds = 60
|
||||||
|
const remainingSeconds = totalSeconds - seconds
|
||||||
|
const progress = (remainingSeconds / totalSeconds) * 100
|
||||||
|
|
||||||
|
notificationApi.info({
|
||||||
|
message: 'Session Expiring Soon',
|
||||||
|
description: (
|
||||||
|
<div>
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
Your session will expire in {seconds} seconds
|
||||||
|
</div>
|
||||||
|
<Progress
|
||||||
|
percent={progress}
|
||||||
|
size='small'
|
||||||
|
status='active'
|
||||||
|
showInfo={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
duration: 0,
|
||||||
|
key: 'token-expiration',
|
||||||
|
icon: null,
|
||||||
|
placement: 'bottomRight',
|
||||||
|
style: {
|
||||||
|
width: 360
|
||||||
|
},
|
||||||
|
className: 'token-expiration-notification',
|
||||||
|
closeIcon: null,
|
||||||
|
onClose: () => {},
|
||||||
|
btn: (
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
size='small'
|
||||||
|
onClick={() => {
|
||||||
|
notificationApi.destroy('token-expiration')
|
||||||
|
refreshToken()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reload Session
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else if (minutes === 1) {
|
||||||
|
// Clear any existing notification when we enter the final minute
|
||||||
|
notificationApi.destroy('token-expiration')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authenticated) {
|
intervalId = setInterval(tokenRefresh, 1000)
|
||||||
intervalId = setInterval(tokenRefreshInterval, 1000)
|
|
||||||
}
|
console.log('fresh', authenticated)
|
||||||
|
tokenRefresh()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (intervalId) {
|
if (intervalId) {
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [expiresAt, authenticated, showTokenExpirationMessage])
|
}, [expiresAt, authenticated, notificationApi, refreshToken])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkAuthStatus()
|
checkAuthStatus()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user