Update API Configuration and Introduce String ID Utilities
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

- Changed the backend URL in the development configuration for the API.
- Integrated a new utility function `bindStringIds` to enhance socket communication by automatically stringifying IDs.
- Added a new file `stringIds.js` containing functions for handling and converting IDs, improving data consistency across the application.
This commit is contained in:
Tom Butcher 2026-11-07 18:52:24 +00:00
parent 3cd93bff21
commit b8b8684344
3 changed files with 110 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import {
useMemo useMemo
} from 'react' } from 'react'
import io from 'socket.io-client' import io from 'socket.io-client'
import { bindStringIds } from '../../../utils/stringIds'
import { message, Modal, Space, Button, Typography, Flex } from 'antd' import { message, Modal, Space, Button, Typography, Flex } from 'antd'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { AuthContext } from './AuthContext' import { AuthContext } from './AuthContext'
@ -683,13 +684,15 @@ const ApiServerProvider = ({ children }) => {
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 = bindStringIds(
reconnection: false, io(config.apiServerUrl, {
reconnectionAttempts: 0, reconnection: false,
forceNew: true, reconnectionAttempts: 0,
timeout: 3000, forceNew: true,
auth: { type: 'user' } timeout: 3000,
}) auth: { type: 'user' }
})
)
if (typeof newSocket.io?.reconnection === 'function') { if (typeof newSocket.io?.reconnection === 'function') {
newSocket.io.reconnection(false) newSocket.io.reconnection(false)
} }

View File

@ -1,6 +1,6 @@
const config = { const config = {
development: { development: {
backendUrl: 'https://dev.tombutcher.work/api', backendUrl: 'https://dev.tombutcher.work/fcapi',
webAppUrl: 'https://dev.tombutcher.work', webAppUrl: 'https://dev.tombutcher.work',
printServerUrl: 'ws://192.168.68.53:8081', printServerUrl: 'ws://192.168.68.53:8081',
apiServerUrl: 'https://dev-wss.tombutcher.work', apiServerUrl: 'https://dev-wss.tombutcher.work',

99
src/utils/stringIds.js Normal file
View File

@ -0,0 +1,99 @@
function bytesOf(value) {
if (value == null) return null
if (value instanceof Uint8Array) return value.byteLength === 12 ? value : null
if (typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
return null
}
const keys = Object.keys(value)
if (keys.length !== 12) return null
const bytes = new Uint8Array(12)
for (let index = 0; index < 12; index += 1) {
if (!Object.prototype.hasOwnProperty.call(value, String(index)) &&
!Object.prototype.hasOwnProperty.call(value, index)) {
return null
}
const byte = Number(value[index])
if (!Number.isInteger(byte) || byte < 0 || byte > 255) return null
bytes[index] = byte
}
return bytes
}
function toHex(bytes) {
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')
}
const PURE_REF_KEYS = new Set(['_id', 'id', 'buffer', '$oid'])
export function refId(value) {
if (value == null || value === '') return null
if (typeof value === 'string') return value
if (typeof value === 'object' && typeof value.toHexString === 'function') {
return value.toHexString()
}
if (typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
return null
}
if (typeof value.$oid === 'string') return refId(value.$oid)
const keys = Object.keys(value)
if (
keys.length > 0 &&
keys.every((key) => key === 'buffer' || key === 'id' || key === 'type' || key === 'data')
) {
const bytes = bytesOf(value.buffer) || bytesOf(value.id) || bytesOf(value)
if (bytes) return toHex(bytes)
}
if (value._id != null) return refId(value._id)
return null
}
function isPureRef(value) {
if (value == null || typeof value !== 'object') return false
if (typeof value.toHexString === 'function') return true
if (Array.isArray(value) || value instanceof Date) return false
const keys = Object.keys(value)
return keys.length > 0 && keys.every((key) => PURE_REF_KEYS.has(key)) && refId(value) != null
}
function isBinaryPayload(value) {
if (value == null || typeof value !== 'object') return false
if (value instanceof Uint8Array || value instanceof ArrayBuffer) return true
const name = value.constructor?.name
return name === 'Blob' || name === 'File'
}
export function stringifyIds(value) {
if (value == null) return value
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return value
}
if (typeof value.toHexString === 'function') return value.toHexString()
if (value instanceof Date || typeof value === 'function') return value
if (isBinaryPayload(value)) return value
if (Array.isArray(value)) return value.map((item) => stringifyIds(item))
if (typeof value === 'object') {
if (isPureRef(value)) {
const id = refId(value)
if (typeof id === 'string') return id
}
const copy = {}
for (const [key, child] of Object.entries(value)) {
copy[key] = stringifyIds(child)
}
return copy
}
return value
}
export function bindStringIds(socket) {
const emit = socket.emit.bind(socket)
socket.emit = (event, ...args) =>
emit(
event,
...args.map((arg) => (typeof arg === 'function' ? arg : stringifyIds(arg)))
)
return socket
}