diff --git a/gameyfin/src/main/frontend/components/administration/UserManagement.tsx b/gameyfin/src/main/frontend/components/administration/UserManagement.tsx index d92365a..74a4a64 100644 --- a/gameyfin/src/main/frontend/components/administration/UserManagement.tsx +++ b/gameyfin/src/main/frontend/components/administration/UserManagement.tsx @@ -9,13 +9,10 @@ import {SmallInfoField} from "Frontend/components/general/SmallInfoField"; import {Info, UserPlus} from "@phosphor-icons/react"; import {Button, Divider, Tooltip, useDisclosure} from "@heroui/react"; import InviteUserModal from "Frontend/components/general/modals/InviteUserModal"; -import {useSnapshot} from "valtio/react"; -import {configState} from "Frontend/state/ConfigState"; function UserManagementLayout({getConfig, formik}: any) { const inviteUserModal = useDisclosure(); const [users, setUsers] = useState([]); - const config = useSnapshot(configState); useEffect(() => { UserEndpoint.getAllUsers().then( @@ -35,7 +32,7 @@ function UserManagementLayout({getConfig, formik}: any) {

Users

- {!config.configEntries["sso.oidc.auto-register-new-users"].value && + {!getConfig("sso.oidc.auto-register-new-users").value && } diff --git a/gameyfin/src/main/frontend/state/ConfigState.ts b/gameyfin/src/main/frontend/state/ConfigState.ts index 10fe55d..b554083 100644 --- a/gameyfin/src/main/frontend/state/ConfigState.ts +++ b/gameyfin/src/main/frontend/state/ConfigState.ts @@ -2,15 +2,19 @@ import {proxy} from 'valtio'; import ConfigEntryDto from "Frontend/generated/de/grimsi/gameyfin/config/dto/ConfigEntryDto"; import {ConfigEndpoint} from "Frontend/generated/endpoints"; import ConfigUpdateDto from "Frontend/generated/de/grimsi/gameyfin/config/dto/ConfigUpdateDto"; +import {Subscription} from "@vaadin/hilla-frontend"; type ConfigState = { + subscription?: Subscription; isLoaded: boolean; configEntries: Record; configNested: NestedConfig; }; export const configState = proxy({ - isLoaded: false, + get isLoaded() { + return this.subscription != null; + }, configEntries: {}, get configNested() { return toNestedConfig(Object.values(this.configEntries)); @@ -26,10 +30,9 @@ export async function initializeConfig() { initialEntries.forEach((entry) => { configState.configEntries[entry.key] = entry; }); - configState.isLoaded = true; // Subscribe to real-time updates - ConfigEndpoint.subscribe().onNext((updateDto: ConfigUpdateDto) => { + configState.subscription = ConfigEndpoint.subscribe().onNext((updateDto: ConfigUpdateDto) => { Object.entries(updateDto.updates).forEach(([key, value]) => { if (configState.configEntries[key]) { configState.configEntries[key].value = value; @@ -44,63 +47,24 @@ export type NestedConfig = { [field: string]: any; } -function toNestedConfig(configArray: ConfigEntryDto[]): NestedConfig { - const nestedConfig: NestedConfig = {}; +function toNestedConfig(entries: ConfigEntryDto[]): Record { + const result: Record = {}; - configArray.forEach(item => { - const keys = item.key!.split('.'); - let currentLevel = nestedConfig; + for (const entry of entries) { + const keys = entry.key.split('.'); + let current = result; - // Traverse the nested structure and create objects as needed - keys.forEach((key, index) => { - if (index === keys.length - 1) { - // Convert value to the appropriate type - let value: any; - switch (item.type) { - case 'Boolean': - value = typeof item.value == 'boolean' ? item.value : item.value === 'true'; - break; - case 'Int': - value = typeof item.value == 'number' ? item.value : 0; - break; - case 'Float': - value = typeof item.value == 'number' ? item.value : 0.0; - break; - case 'Array': - if (Array.isArray(item.value)) { - switch (item.elementType) { - case 'Boolean': - value = item.value.map(v => typeof v === 'boolean' ? v : v === 'true'); - break; - case 'Int': - case 'Integer': - value = item.value.map(v => typeof v == 'number' ? v : 0); - break; - case 'Float': - value = item.value.map(v => typeof v == 'number' ? v : 0.0); - break; - case 'String': - default: - value = item.value.map(v => v.toString()); - break; - } - } else { - value = []; - } - break; - case 'String': - default: - value = item.value; - break; - } - currentLevel[key] = value; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + + if (i === keys.length - 1) { + current[key] = entry.value; } else { - if (!currentLevel[key]) { - currentLevel[key] = {}; - } - currentLevel = currentLevel[key]; + current[key] = current[key] || {}; + current = current[key]; } - }); - }); - return nestedConfig; + } + } + + return result; } \ No newline at end of file diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigService.kt index 4fa8608..516ec60 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigService.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigService.kt @@ -97,9 +97,9 @@ class ConfigService( var configEntry = appConfigRepository.findByIdOrNull(key) val parsedValue = - if (value.javaClass.isArray) + if (value.javaClass.isArray) { (value as Array).joinToString(",") - else + } else value.toString() if (configEntry == null) { @@ -173,8 +173,11 @@ class ConfigService( configProperty.type.java.isArray -> { val componentType = configProperty.type.java.componentType // Remove the brackets and split the string by commas - val elements = value.removeSurrounding("[", "]").split(",") - if (elements.isEmpty()) return emptyArray() as T + val elements = value + .removeSurrounding("[", "]") + .split(",") + .filter { it.isNotBlank() } + when (componentType) { String::class.java -> elements.toTypedArray() as T Boolean::class.java -> elements.map { it.toBoolean() }.toTypedArray() as T diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/security/SecurityConfig.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/security/SecurityConfig.kt index 5db5821..42f89b2 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/security/SecurityConfig.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/security/SecurityConfig.kt @@ -32,6 +32,7 @@ class SecurityConfig( ) : VaadinWebSecurity() { private val ssoProviderKey: String = "oidc" + private val allowedOrigins: List? = config.get(ConfigProperties.System.Cors.AllowedOrigins)?.toList() @Throws(Exception::class) override fun configure(http: HttpSecurity) { @@ -55,7 +56,7 @@ class SecurityConfig( http.cors { cors -> cors.configurationSource { request -> val configuration = CorsConfiguration() - configuration.allowedOrigins = config.get(ConfigProperties.System.Cors.AllowedOrigins)?.toList() + configuration.allowedOrigins = allowedOrigins configuration } }