mirror of
https://github.com/unraid/api.git
synced 2026-01-02 22:50:02 -06:00
feat: add dynamix.cfg to store (#429)
Co-authored-by: Eli Bosley <ekbosley@gmail.com>
This commit is contained in:
@@ -24,6 +24,8 @@ import { PORT, environment } from '@app/environment';
|
||||
import { shutdownApiEvent } from '@app/store/actions/shutdown-api-event';
|
||||
import { PingTimeoutJobs } from '@app/mothership/jobs/ping-timeout-jobs';
|
||||
import { type BaseContext, type ApolloServer } from '@apollo/server';
|
||||
import { loadDynamixConfigFile } from '@app/store/modules/dynamix';
|
||||
import { setupDynamixConfigWatch } from '@app/store/watch/dynamix-config-watch';
|
||||
|
||||
let server: ApolloServer<BaseContext>;
|
||||
|
||||
@@ -56,6 +58,9 @@ void am(
|
||||
// Load initial registration key into store
|
||||
await store.dispatch(loadRegistrationKey());
|
||||
|
||||
// Load my dynamix config file into store
|
||||
await store.dispatch(loadDynamixConfigFile());
|
||||
|
||||
// Start listening to file updates
|
||||
StateManager.getInstance();
|
||||
|
||||
@@ -65,6 +70,9 @@ void am(
|
||||
// Start listening to docker events
|
||||
setupDockerWatch();
|
||||
|
||||
// Start listening to dynamix config file changes
|
||||
setupDynamixConfigWatch();
|
||||
|
||||
// Try and load the HTTP server
|
||||
logger.debug('Starting HTTP server');
|
||||
|
||||
|
||||
54
api/src/store/index.ts
Normal file
54
api/src/store/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { paths } from '@app/store/modules/paths';
|
||||
import { mothership } from '@app/store/modules/minigraph';
|
||||
import { configReducer } from '@app/store/modules/config';
|
||||
import { emhttp } from '@app/store/modules/emhttp';
|
||||
import { registration } from '@app/store/modules/registration';
|
||||
import { cache } from '@app/store/modules/cache';
|
||||
import { dashboard } from '@app/store/modules/dashboard';
|
||||
import { docker } from '@app/store/modules/docker';
|
||||
import { upnp } from '@app/store/modules/upnp';
|
||||
import { listenerMiddleware } from '@app/store/listeners/listener-middleware';
|
||||
import { apiKeyReducer } from '@app/store/modules/apikey';
|
||||
import { dynamicRemoteAccessReducer } from '@app/store/modules/dynamic-remote-access';
|
||||
import { remoteGraphQLReducer } from '@app/store/modules/remote-graphql';
|
||||
import { dynamix } from '@app/store/modules/dynamix';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
apiKey: apiKeyReducer,
|
||||
config: configReducer,
|
||||
dynamicRemoteAccess: dynamicRemoteAccessReducer,
|
||||
minigraph: mothership.reducer,
|
||||
paths: paths.reducer,
|
||||
emhttp: emhttp.reducer,
|
||||
registration: registration.reducer,
|
||||
remoteGraphQL: remoteGraphQLReducer,
|
||||
cache: cache.reducer,
|
||||
dashboard: dashboard.reducer,
|
||||
docker: docker.reducer,
|
||||
upnp: upnp.reducer,
|
||||
dynamix: dynamix.reducer,
|
||||
},
|
||||
middleware: getDefaultMiddleware => getDefaultMiddleware({
|
||||
serializableCheck: false,
|
||||
}).prepend(listenerMiddleware.middleware),
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
export const getters = {
|
||||
apiKey: () => store.getState().apiKey,
|
||||
config: () => store.getState().config,
|
||||
minigraph: () => store.getState().minigraph,
|
||||
paths: () => store.getState().paths,
|
||||
emhttp: () => store.getState().emhttp,
|
||||
registration: () => store.getState().registration,
|
||||
remoteGraphQL: () => store.getState().remoteGraphQL,
|
||||
cache: () => store.getState().cache,
|
||||
dashboard: () => store.getState().dashboard,
|
||||
docker: () => store.getState().docker,
|
||||
upnp: () => store.getState().upnp,
|
||||
dynamix: () => store.getState().dynamix,
|
||||
};
|
||||
73
api/src/store/modules/dynamix.ts
Normal file
73
api/src/store/modules/dynamix.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { parseConfig } from '@app/core/utils/misc/parse-config';
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { access } from 'fs/promises';
|
||||
import merge from 'lodash/merge';
|
||||
import { FileLoadStatus } from '@app/store/types';
|
||||
import { F_OK } from 'constants';
|
||||
import { RecursivePartial, RecursiveNullable } from '@app/types';
|
||||
import { toBoolean } from '@app/core/utils/casting';
|
||||
import { DynamixConfig } from '@app/core/types/ini';
|
||||
|
||||
export type SliceState = {
|
||||
status: FileLoadStatus;
|
||||
} & DynamixConfig;
|
||||
|
||||
export const initialState: Partial<SliceState> = {
|
||||
status: FileLoadStatus.UNLOADED,
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the dynamix.cfg into the store.
|
||||
*
|
||||
* Note: If the file doesn't exist this will fallback to default values.
|
||||
*/
|
||||
export const loadDynamixConfigFile = createAsyncThunk<RecursiveNullable<RecursivePartial<DynamixConfig>>, string | undefined>('config/load-dynamix-config-file', async filePath => {
|
||||
const store = await import('@app/store');
|
||||
const paths = store.getters.paths();
|
||||
const path = filePath ?? paths['dynamix-config'];
|
||||
const fileExists = await access(path, F_OK).then(() => true).catch(() => false);
|
||||
const file = fileExists ? parseConfig<RecursivePartial<DynamixConfig>>({
|
||||
filePath: path,
|
||||
type: 'ini',
|
||||
}) : {};
|
||||
const { display } = file;
|
||||
return merge(file, {
|
||||
...(display?.scale ? { scale: toBoolean(display?.scale) } : {}),
|
||||
...(display?.tabs ? { tabs: toBoolean(display?.tabs) } : {}),
|
||||
...(display?.resize ? { resize: toBoolean(display?.resize) } : {}),
|
||||
...(display?.wwn ? { wwn: toBoolean(display?.wwn) } : {}),
|
||||
...(display?.total ? { total: toBoolean(display?.total) } : {}),
|
||||
...(display?.usage ? { usage: toBoolean(display?.usage) } : {}),
|
||||
...(display?.text ? { text: toBoolean(display?.text) } : {}),
|
||||
...(display?.warning ? { warning: Number.parseInt(display?.warning, 10) } : {}),
|
||||
...(display?.critical ? { critical: Number.parseInt(display?.critical, 10) } : {}),
|
||||
...(display?.hot ? { hot: Number.parseInt(display?.hot, 10) } : {}),
|
||||
...(display?.max ? { max: Number.parseInt(display?.max, 10) } : {}),
|
||||
locale: display?.locale ?? 'en_US',
|
||||
}) as RecursivePartial<DynamixConfig>;
|
||||
});
|
||||
|
||||
export const dynamix = createSlice({
|
||||
name: 'dynamix',
|
||||
initialState,
|
||||
reducers: {
|
||||
updateDynamixConfig(state, action: PayloadAction<RecursivePartial<SliceState>>) {
|
||||
return merge(state, action.payload);
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(loadDynamixConfigFile.pending, (state, _action) => {
|
||||
state.status = FileLoadStatus.LOADING;
|
||||
});
|
||||
|
||||
builder.addCase(loadDynamixConfigFile.fulfilled, (state, action) => {
|
||||
merge(state, action.payload, { status: FileLoadStatus.LOADED });
|
||||
});
|
||||
|
||||
builder.addCase(loadDynamixConfigFile.rejected, (state, action) => {
|
||||
merge(state, action.payload, { status: FileLoadStatus.FAILED_LOADING });
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { updateDynamixConfig } = dynamix.actions;
|
||||
16
api/src/store/watch/dynamix-config-watch.ts
Normal file
16
api/src/store/watch/dynamix-config-watch.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { getters, store } from '@app/store';
|
||||
import { watch } from 'chokidar';
|
||||
import { loadDynamixConfigFile } from '@app/store/modules/dynamix';
|
||||
|
||||
export const setupDynamixConfigWatch = () => {
|
||||
const configPath = getters.paths()?.['dynamix-config'];
|
||||
|
||||
// Update store when cfg changes
|
||||
watch(configPath, {
|
||||
persistent: true,
|
||||
ignoreInitial: true,
|
||||
}).on('change', async () => {
|
||||
// Load updated dynamix config file into store
|
||||
await store.dispatch(loadDynamixConfigFile());
|
||||
});
|
||||
};
|
||||
9
api/src/types/index.d.ts
vendored
Normal file
9
api/src/types/index.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
declare module '*.json';
|
||||
|
||||
export type RecursivePartial<T> = {
|
||||
[P in keyof T]?: RecursivePartial<T[P]>;
|
||||
};
|
||||
|
||||
export type RecursiveNullable<T> = {
|
||||
[P in keyof T]: RecursiveNullable<T[P]> | null;
|
||||
};
|
||||
Reference in New Issue
Block a user