From 29ca5829ffdc63df2a21c71d380c14647bcb1e03 Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Sun, 19 Jan 2025 19:07:21 -0500 Subject: [PATCH] feat: only write config when a specific config update action occurs --- api/dev/Unraid.net/myservers.cfg | 10 ++-- api/dev/states/myservers.cfg | 12 ++-- api/src/store/listeners/config-listener.ts | 64 ++++------------------ api/src/store/modules/config.ts | 23 +++++++- api/src/store/watch/config-watch.ts | 3 +- api/src/unraid-api/auth/header.strategy.ts | 4 +- 6 files changed, 45 insertions(+), 71 deletions(-) diff --git a/api/dev/Unraid.net/myservers.cfg b/api/dev/Unraid.net/myservers.cfg index 5b9ec0581..d66bffad2 100644 --- a/api/dev/Unraid.net/myservers.cfg +++ b/api/dev/Unraid.net/myservers.cfg @@ -2,20 +2,18 @@ version="3.11.0" extraOrigins="https://google.com,https://test.com" [local] -[notifier] [remote] wanaccess="yes" wanport="8443" upnpEnabled="no" -apikey="_______________________BIG_API_KEY_HERE_________________________" +apikey="unraid_SfxcHvPqI5MIUE51KHJCb5m21QbIZeowqN3XT4QFHLJm0NQ2ZHAqUuMKW" localApiKey="_______________________LOCAL_API_KEY_HERE_________________________" -email="test@example.com" -username="zspearmint" -avatar="https://via.placeholder.com/200" +email="ekbosley@gmail.com" +username="Hi" +avatar="" regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0" accesstoken="" idtoken="" refreshtoken="" dynamicRemoteAccessType="DISABLED" ssoSubIds="" -[upc] diff --git a/api/dev/states/myservers.cfg b/api/dev/states/myservers.cfg index 6f20d6ff5..4fad18602 100644 --- a/api/dev/states/myservers.cfg +++ b/api/dev/states/myservers.cfg @@ -2,16 +2,15 @@ version="3.11.0" extraOrigins="https://google.com,https://test.com" [local] -[notifier] [remote] wanaccess="yes" wanport="8443" upnpEnabled="no" -apikey="_______________________BIG_API_KEY_HERE_________________________" +apikey="unraid_SfxcHvPqI5MIUE51KHJCb5m21QbIZeowqN3XT4QFHLJm0NQ2ZHAqUuMKW" localApiKey="_______________________LOCAL_API_KEY_HERE_________________________" -email="test@example.com" -username="zspearmint" -avatar="https://via.placeholder.com/200" +email="" +username="Hi" +avatar="" regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0" accesstoken="" idtoken="" @@ -19,7 +18,6 @@ refreshtoken="" dynamicRemoteAccessType="DISABLED" ssoSubIds="" allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com" -[upc] [connectionStatus] -minigraph="PRE_INIT" +minigraph="ERROR_RETRYING" upnpStatus="" diff --git a/api/src/store/listeners/config-listener.ts b/api/src/store/listeners/config-listener.ts index 0b182c8db..fdd691caa 100644 --- a/api/src/store/listeners/config-listener.ts +++ b/api/src/store/listeners/config-listener.ts @@ -1,66 +1,22 @@ -import { startAppListening } from '@app/store/listeners/listener-middleware'; -import { isEqual } from 'lodash-es'; -import { logger } from '@app/core/log'; -import { - type ConfigType, - getWriteableConfig, -} from '@app/core/utils/files/config-file-normalizer'; -import { - loadConfigFile, - loginUser, - logoutUser, -} from '@app/store/modules/config'; -import { FileLoadStatus } from '@app/store/types'; -import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer'; -import { isFulfilled } from '@reduxjs/toolkit'; -import { environment } from '@app/environment'; import { writeFileSync } from 'fs'; -const actionIsLoginOrLogout = isFulfilled(logoutUser, loginUser); + +import type { ConfigType } from '@app/core/utils/files/config-file-normalizer'; +import { logger } from '@app/core/log'; +import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer'; +import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer'; +import { startAppListening } from '@app/store/listeners/listener-middleware'; +import { configUpdateActionsFlash, configUpdateActionsMemory } from '@app/store/modules/config'; export const enableConfigFileListener = (mode: ConfigType) => () => startAppListening({ - predicate(action, currentState, previousState) { - if (!environment.IS_MAIN_PROCESS) { - return false; - } - - if (currentState.config.status === FileLoadStatus.LOADED) { - const oldFlashConfig = previousState?.config.api.version - ? getWriteableConfig(previousState.config, mode) - : null; - const newFlashConfig = getWriteableConfig( - currentState.config, - mode - ); - - if ( - !isEqual(oldFlashConfig, newFlashConfig) && - action.type !== loadConfigFile.fulfilled.type && - action.type !== loadConfigFile.rejected.type - ) { - return true; - } - - if (actionIsLoginOrLogout(action) && mode === 'memory') { - logger.trace( - 'Logout / Login Action Encountered, writing memory config' - ); - return true; - } - } - - return false; - }, + matcher: mode === 'flash' ? configUpdateActionsFlash : configUpdateActionsMemory, async effect(_, { getState }) { const { paths, config } = getState(); const pathToWrite = - mode === 'flash' - ? paths['myservers-config'] - : paths['myservers-config-states']; + mode === 'flash' ? paths['myservers-config'] : paths['myservers-config-states']; const writeableConfig = getWriteableConfig(config, mode); - const serializedConfig = - safelySerializeObjectToIni(writeableConfig); + const serializedConfig = safelySerializeObjectToIni(writeableConfig); logger.debug('Writing updated config to %s', pathToWrite); writeFileSync(pathToWrite, serializedConfig); }, diff --git a/api/src/store/modules/config.ts b/api/src/store/modules/config.ts index 674330c63..c05991bc1 100644 --- a/api/src/store/modules/config.ts +++ b/api/src/store/modules/config.ts @@ -4,7 +4,7 @@ import { writeFileSync } from 'fs'; import { access } from 'fs/promises'; import type { PayloadAction } from '@reduxjs/toolkit'; -import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; +import { createAsyncThunk, createSlice, isAnyOf } from '@reduxjs/toolkit'; import { isEqual } from 'lodash-es'; import merge from 'lodash/merge'; @@ -24,6 +24,7 @@ import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access'; import { FileLoadStatus } from '@app/store/types'; import { type RecursivePartial } from '@app/types'; import { type MyServersConfig, type MyServersConfigMemory } from '@app/types/my-servers-config'; +import { isFulfilled } from '@app/utils'; export type SliceState = { status: FileLoadStatus; @@ -291,4 +292,24 @@ export const { setWanAccess, } = actions; +/** + * Actions that should trigger a flash write + */ +export const configUpdateActionsFlash = isAnyOf( + updateUserConfig, + updateAccessTokens, + updateAllowedOrigins, + setUpnpState, + setWanPortToValue, + setWanAccess, + setupRemoteAccessThunk.fulfilled, + logoutUser.fulfilled, + loginUser.fulfilled +); + +/** + * Actions that should trigger a memory write + */ +export const configUpdateActionsMemory = isAnyOf(configUpdateActionsFlash, setGraphqlConnectionStatus); + export const configReducer = reducer; diff --git a/api/src/store/watch/config-watch.ts b/api/src/store/watch/config-watch.ts index 14808f23b..efd4cbc90 100644 --- a/api/src/store/watch/config-watch.ts +++ b/api/src/store/watch/config-watch.ts @@ -22,7 +22,8 @@ export const setupConfigPathWatch = () => { ignoreInitial: false, usePolling: CHOKIDAR_USEPOLLING === true, }) - .on('change', async () => { + .on('change', async (change) => { + logger.trace('Config File Changed, Reloading Config %s', change); await store.dispatch(loadConfigFile()); }) .on('unlink', async () => { diff --git a/api/src/unraid-api/auth/header.strategy.ts b/api/src/unraid-api/auth/header.strategy.ts index e7c057090..1e36efaea 100644 --- a/api/src/unraid-api/auth/header.strategy.ts +++ b/api/src/unraid-api/auth/header.strategy.ts @@ -35,14 +35,14 @@ export class ServerHeaderStrategy extends PassportStrategy(Strategy, 'server-htt try { const user = await this.authService.validateApiKeyCasbin(key); - this.logger.debug('API key validation successful', { + this.logger.debug('API key validation successful %o', { userId: user?.id, roles: user?.roles, }); return user; } catch (error) { - this.logger.error('API key validation failed', { + this.logger.error('API key validation failed %o', { errorType: error instanceof Error ? error.constructor.name : 'Unknown', message: error instanceof Error ? error.message : 'Unknown error', });