/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import { useQuery } from '@tanstack/react-query'
import { getSystemOptions } from '../api'
export function useSystemOptions() {
return useQuery({
queryKey: ['system-options'],
queryFn: getSystemOptions,
staleTime: 5 * 60 * 1000,
})
}
type ParseResult =
| { success: true; value: T }
| { success: false; error: string; fallback: T }
function parseOptionValueSafe(
value: string,
defaultValue: T
): ParseResult {
if (typeof defaultValue === 'boolean') {
return {
success: true,
value: (value === 'true' || value === '1') as T,
}
}
if (typeof defaultValue === 'number') {
const trimmed = value.trim()
if (trimmed === '') {
return {
success: false,
error: 'Empty string for number field',
fallback: defaultValue,
}
}
const parsed = Number(trimmed)
if (Number.isNaN(parsed)) {
return {
success: false,
error: `Invalid number: "${value}"`,
fallback: defaultValue,
}
}
return { success: true, value: parsed as T }
}
if (Array.isArray(defaultValue)) {
try {
const parsed = JSON.parse(value)
if (!Array.isArray(parsed)) {
return {
success: false,
error: 'Expected array but got non-array JSON',
fallback: defaultValue,
}
}
if (defaultValue.length > 0) {
const expectedType = typeof defaultValue[0]
const invalidElement = parsed.find((el) => typeof el !== expectedType)
if (invalidElement !== undefined) {
return {
success: false,
error: `Array element type mismatch: expected ${expectedType}, got ${typeof invalidElement}`,
fallback: defaultValue,
}
}
}
return { success: true, value: parsed as T }
} catch (e) {
return {
success: false,
error: `JSON parse failed: ${e instanceof Error ? e.message : String(e)}`,
fallback: defaultValue,
}
}
}
return { success: true, value: value as T }
}
export function getOptionValue<
T extends Record,
>(options: Array<{ key: string; value: string }> | undefined, defaults: T): T {
if (!options) return defaults
const result = { ...defaults }
const errors: Array<{ key: string; error: string }> = []
options.forEach((option) => {
if (option.key in defaults) {
const parseResult = parseOptionValueSafe(
option.value,
defaults[option.key as keyof T]
)
if (parseResult.success) {
result[option.key as keyof T] = parseResult.value as T[keyof T]
} else {
result[option.key as keyof T] = parseResult.fallback as T[keyof T]
errors.push({ key: option.key, error: parseResult.error })
}
}
})
if (errors.length > 0 && import.meta.env.DEV) {
// eslint-disable-next-line no-console
console.warn('[System Options] Parsing errors detected:', errors)
}
return result
}