feat: add dynamix.cfg to store (#429)

Co-authored-by: Eli Bosley <ekbosley@gmail.com>
This commit is contained in:
Alexis Tyler
2023-04-28 00:15:03 +09:30
committed by GitHub
parent 69c634f9e4
commit 2594513199
5 changed files with 160 additions and 0 deletions

View File

@@ -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
View 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,
};

View 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;

View 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
View 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;
};