mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
feat: cleanup config entries
This commit is contained in:
@@ -3,7 +3,6 @@ version="3.11.0"
|
|||||||
extraOrigins="https://google.com,https://test.com"
|
extraOrigins="https://google.com,https://test.com"
|
||||||
[local]
|
[local]
|
||||||
[notifier]
|
[notifier]
|
||||||
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
|
|
||||||
[remote]
|
[remote]
|
||||||
wanaccess="yes"
|
wanaccess="yes"
|
||||||
wanport="8443"
|
wanport="8443"
|
||||||
@@ -14,9 +13,9 @@ email="test@example.com"
|
|||||||
username="zspearmint"
|
username="zspearmint"
|
||||||
avatar="https://via.placeholder.com/200"
|
avatar="https://via.placeholder.com/200"
|
||||||
regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
||||||
idtoken=""
|
|
||||||
accesstoken=""
|
accesstoken=""
|
||||||
|
idtoken=""
|
||||||
refreshtoken=""
|
refreshtoken=""
|
||||||
dynamicRemoteAccessType="DISABLED"
|
dynamicRemoteAccessType="DISABLED"
|
||||||
|
ssoSubIds=""
|
||||||
[upc]
|
[upc]
|
||||||
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ version="3.11.0"
|
|||||||
extraOrigins="https://google.com,https://test.com"
|
extraOrigins="https://google.com,https://test.com"
|
||||||
[local]
|
[local]
|
||||||
[notifier]
|
[notifier]
|
||||||
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
|
|
||||||
[remote]
|
[remote]
|
||||||
wanaccess="yes"
|
wanaccess="yes"
|
||||||
wanport="8443"
|
wanport="8443"
|
||||||
@@ -14,12 +13,13 @@ email="test@example.com"
|
|||||||
username="zspearmint"
|
username="zspearmint"
|
||||||
avatar="https://via.placeholder.com/200"
|
avatar="https://via.placeholder.com/200"
|
||||||
regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
||||||
idtoken=""
|
|
||||||
accesstoken=""
|
accesstoken=""
|
||||||
|
idtoken=""
|
||||||
refreshtoken=""
|
refreshtoken=""
|
||||||
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"
|
|
||||||
dynamicRemoteAccessType="DISABLED"
|
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]
|
[upc]
|
||||||
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
|
|
||||||
[connectionStatus]
|
[connectionStatus]
|
||||||
minigraph="PRE_INIT"
|
minigraph="PRE_INIT"
|
||||||
|
upnpStatus=""
|
||||||
|
|||||||
@@ -42,23 +42,3 @@ export const getWriteableConfig = <T extends ConfigType>(
|
|||||||
|
|
||||||
return schema.parse(mergedConfig) as any; // Narrowing ensures correct typing
|
return schema.parse(mergedConfig) as any; // Narrowing ensures correct typing
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if two configurations are equivalent by normalizing them through the Zod schema.
|
|
||||||
*/
|
|
||||||
export const areConfigsEquivalent = (
|
|
||||||
newConfigFile: Partial<MyServersConfigMemory>, // Use Partial here for flexibility
|
|
||||||
currentConfig: MyServersConfig
|
|
||||||
): boolean => {
|
|
||||||
// Parse and validate the new config file using the schema (with default values applied)
|
|
||||||
const normalizedNewConfig = MyServersConfigSchema.parse({
|
|
||||||
...currentConfig, // Use currentConfig as a baseline to fill missing fields
|
|
||||||
...newConfigFile,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get the writeable configuration for the current config
|
|
||||||
const normalizedCurrentConfig = getWriteableConfig(currentConfig, 'flash');
|
|
||||||
|
|
||||||
// Compare the normalized configurations
|
|
||||||
return isEqual(normalizedNewConfig, normalizedCurrentConfig);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,32 +1,29 @@
|
|||||||
import { parseConfig } from '@app/core/utils/misc/parse-config';
|
|
||||||
import {
|
|
||||||
type MyServersConfig,
|
|
||||||
type MyServersConfigMemory,
|
|
||||||
} from '@app/types/my-servers-config';
|
|
||||||
import {
|
|
||||||
createAsyncThunk,
|
|
||||||
createSlice,
|
|
||||||
type 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 { F_OK } from 'constants';
|
||||||
import { type RecursivePartial } from '@app/types';
|
|
||||||
import { DynamicRemoteAccessType, MinigraphStatus, type Owner } from '@app/graphql/generated/api/types';
|
|
||||||
import { type RootState } from '@app/store';
|
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import { logger } from '@app/core/log';
|
|
||||||
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status';
|
|
||||||
import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer';
|
|
||||||
import { writeFileSync } from 'fs';
|
import { writeFileSync } from 'fs';
|
||||||
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
|
import { access } from 'fs/promises';
|
||||||
import { PUBSUB_CHANNEL, pubsub } from '@app/core/pubsub';
|
|
||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access';
|
import merge from 'lodash/merge';
|
||||||
|
|
||||||
|
import type { Owner } from '@app/graphql/generated/api/types';
|
||||||
|
import { logger } from '@app/core/log';
|
||||||
|
import { pubsub, PUBSUB_CHANNEL } from '@app/core/pubsub';
|
||||||
|
import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer';
|
||||||
|
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
|
||||||
|
import { parseConfig } from '@app/core/utils/misc/parse-config';
|
||||||
import { NODE_ENV } from '@app/environment';
|
import { NODE_ENV } from '@app/environment';
|
||||||
|
import { DynamicRemoteAccessType, MinigraphStatus } from '@app/graphql/generated/api/types';
|
||||||
import { GraphQLClient } from '@app/mothership/graphql-client';
|
import { GraphQLClient } from '@app/mothership/graphql-client';
|
||||||
import { stopPingTimeoutJobs } from '@app/mothership/jobs/ping-timeout-jobs';
|
import { stopPingTimeoutJobs } from '@app/mothership/jobs/ping-timeout-jobs';
|
||||||
|
import { type RootState } from '@app/store';
|
||||||
|
import { setGraphqlConnectionStatus } from '@app/store/actions/set-minigraph-status';
|
||||||
|
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';
|
||||||
|
|
||||||
export type SliceState = {
|
export type SliceState = {
|
||||||
status: FileLoadStatus;
|
status: FileLoadStatus;
|
||||||
@@ -51,7 +48,7 @@ export const initialState: SliceState = {
|
|||||||
refreshtoken: '',
|
refreshtoken: '',
|
||||||
allowedOrigins: '',
|
allowedOrigins: '',
|
||||||
dynamicRemoteAccessType: DynamicRemoteAccessType.DISABLED,
|
dynamicRemoteAccessType: DynamicRemoteAccessType.DISABLED,
|
||||||
ssoSubIds: ''
|
ssoSubIds: '',
|
||||||
},
|
},
|
||||||
local: {},
|
local: {},
|
||||||
api: {
|
api: {
|
||||||
@@ -71,8 +68,8 @@ export const initialState: SliceState = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const loginUser = createAsyncThunk<
|
export const loginUser = createAsyncThunk<
|
||||||
Pick<MyServersConfig['remote'], 'email' | 'avatar' | 'username' | 'apikey'| 'localApiKey'>,
|
Pick<MyServersConfig['remote'], 'email' | 'avatar' | 'username' | 'apikey' | 'localApiKey'>,
|
||||||
Pick<MyServersConfig['remote'], 'email' | 'avatar' | 'username' | 'apikey'| 'localApiKey'>,
|
Pick<MyServersConfig['remote'], 'email' | 'avatar' | 'username' | 'apikey' | 'localApiKey'>,
|
||||||
{ state: RootState }
|
{ state: RootState }
|
||||||
>('config/login-user', async (userInfo) => {
|
>('config/login-user', async (userInfo) => {
|
||||||
logger.info('Logging in user: %s', userInfo.username);
|
logger.info('Logging in user: %s', userInfo.username);
|
||||||
@@ -84,29 +81,28 @@ export const loginUser = createAsyncThunk<
|
|||||||
return userInfo;
|
return userInfo;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const logoutUser = createAsyncThunk<
|
export const logoutUser = createAsyncThunk<void, { reason?: string }, { state: RootState }>(
|
||||||
void,
|
'config/logout-user',
|
||||||
{ reason?: string },
|
async ({ reason }) => {
|
||||||
{ state: RootState }
|
logger.info('Logging out user: %s', reason ?? 'No reason provided');
|
||||||
>('config/logout-user', async ({ reason }) => {
|
const { pubsub } = await import('@app/core/pubsub');
|
||||||
logger.info('Logging out user: %s', reason ?? 'No reason provided');
|
|
||||||
const { pubsub } = await import('@app/core/pubsub');
|
|
||||||
|
|
||||||
// Publish to servers endpoint
|
// Publish to servers endpoint
|
||||||
await pubsub.publish(PUBSUB_CHANNEL.SERVERS, {
|
await pubsub.publish(PUBSUB_CHANNEL.SERVERS, {
|
||||||
servers: [],
|
servers: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const owner: Owner = {
|
const owner: Owner = {
|
||||||
username: 'root',
|
username: 'root',
|
||||||
url: '',
|
url: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
};
|
};
|
||||||
// Publish to owner endpoint
|
// Publish to owner endpoint
|
||||||
await pubsub.publish(PUBSUB_CHANNEL.OWNER, { owner });
|
await pubsub.publish(PUBSUB_CHANNEL.OWNER, { owner });
|
||||||
stopPingTimeoutJobs();
|
stopPingTimeoutJobs();
|
||||||
await GraphQLClient.clearInstance();
|
await GraphQLClient.clearInstance();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the myservers.cfg into the store. Returns null if the state after loading doesn't change
|
* Load the myservers.cfg into the store. Returns null if the state after loading doesn't change
|
||||||
@@ -128,32 +124,6 @@ type LoadFailureConfigEqual = {
|
|||||||
};
|
};
|
||||||
type ConfigRejectedValues = LoadFailureConfigEqual | LoadFailureWithConfig;
|
type ConfigRejectedValues = LoadFailureConfigEqual | LoadFailureWithConfig;
|
||||||
|
|
||||||
const generateApiKeysIfNotExistent = (
|
|
||||||
file: RecursivePartial<MyServersConfig>
|
|
||||||
): MyServersConfig => {
|
|
||||||
const newConfigFile = merge(file, {
|
|
||||||
upc: {
|
|
||||||
apikey:
|
|
||||||
file.upc?.apikey?.trim()?.length === 64
|
|
||||||
? file.upc?.apikey
|
|
||||||
: `unupc_${randomBytes(58).toString('hex')}`.substring(
|
|
||||||
0,
|
|
||||||
64
|
|
||||||
),
|
|
||||||
},
|
|
||||||
notifier: {
|
|
||||||
apikey:
|
|
||||||
file.notifier?.apikey?.trim().length === 64
|
|
||||||
? file.notifier?.apikey
|
|
||||||
: `unnotify_${randomBytes(58).toString('hex')}`.substring(
|
|
||||||
0,
|
|
||||||
64
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}) as MyServersConfig;
|
|
||||||
return newConfigFile;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const loadConfigFile = createAsyncThunk<
|
export const loadConfigFile = createAsyncThunk<
|
||||||
MyServersConfig,
|
MyServersConfig,
|
||||||
string | undefined,
|
string | undefined,
|
||||||
@@ -161,71 +131,51 @@ export const loadConfigFile = createAsyncThunk<
|
|||||||
state: RootState;
|
state: RootState;
|
||||||
rejectValue: ConfigRejectedValues;
|
rejectValue: ConfigRejectedValues;
|
||||||
}
|
}
|
||||||
>(
|
>('config/load-config-file', async (filePath, { getState, rejectWithValue }) => {
|
||||||
'config/load-config-file',
|
try {
|
||||||
async (filePath, { getState, rejectWithValue }) => {
|
const { paths, config } = getState();
|
||||||
try {
|
|
||||||
const { paths, config } = getState();
|
|
||||||
|
|
||||||
const path = filePath ?? paths['myservers-config'];
|
const path = filePath ?? paths['myservers-config'];
|
||||||
|
|
||||||
const fileExists = await access(path, F_OK)
|
const fileExists = await access(path, F_OK)
|
||||||
.then(() => true)
|
.then(() => true)
|
||||||
.catch(() => false);
|
.catch(() => false);
|
||||||
if (!fileExists) {
|
if (!fileExists) {
|
||||||
throw new Error('Config File Missing');
|
throw new Error('Config File Missing');
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = fileExists
|
const newConfigFile = getWriteableConfig(
|
||||||
? parseConfig<RecursivePartial<MyServersConfig>>({
|
parseConfig<MyServersConfig>({ filePath: path, type: 'ini' }),
|
||||||
filePath: path,
|
'flash'
|
||||||
type: 'ini',
|
);
|
||||||
})
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const newConfigFile = generateApiKeysIfNotExistent(file);
|
const isNewlyLoadedConfigEqual = isEqual(newConfigFile, getWriteableConfig(config, 'flash'));
|
||||||
|
if (isNewlyLoadedConfigEqual) {
|
||||||
const isNewlyLoadedConfigEqual = isEqual(
|
logger.warn('Not loading config because it is the same as before');
|
||||||
getWriteableConfig(newConfigFile as SliceState, 'flash'),
|
|
||||||
getWriteableConfig(config, 'flash')
|
|
||||||
);
|
|
||||||
if (isNewlyLoadedConfigEqual) {
|
|
||||||
logger.warn(
|
|
||||||
'Not loading config because it is the same as before'
|
|
||||||
);
|
|
||||||
return rejectWithValue({
|
|
||||||
type: CONFIG_LOAD_ERROR.CONFIG_EQUAL,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return newConfigFile;
|
|
||||||
} catch (error: unknown) {
|
|
||||||
logger.warn('Config file is corrupted with error: %o - recreating config', error);
|
|
||||||
const config = getWriteableConfig(initialState, 'flash');
|
|
||||||
const newConfig = generateApiKeysIfNotExistent(config);
|
|
||||||
newConfig.remote.wanaccess = 'no';
|
|
||||||
const serializedConfig = safelySerializeObjectToIni(newConfig);
|
|
||||||
writeFileSync(
|
|
||||||
getState().paths['myservers-config'],
|
|
||||||
serializedConfig
|
|
||||||
);
|
|
||||||
return rejectWithValue({
|
return rejectWithValue({
|
||||||
type: CONFIG_LOAD_ERROR.CONFIG_CORRUPTED,
|
type: CONFIG_LOAD_ERROR.CONFIG_EQUAL,
|
||||||
error:
|
|
||||||
error instanceof Error ? error : new Error('Unknown Error'),
|
|
||||||
config: newConfig,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return newConfigFile;
|
||||||
|
} catch (error: unknown) {
|
||||||
|
logger.warn('Config file is corrupted with error: %o - recreating config', error);
|
||||||
|
const newConfig = getWriteableConfig(initialState, 'flash');
|
||||||
|
newConfig.remote.wanaccess = 'no';
|
||||||
|
const serializedConfig = safelySerializeObjectToIni(newConfig);
|
||||||
|
writeFileSync(getState().paths['myservers-config'], serializedConfig);
|
||||||
|
return rejectWithValue({
|
||||||
|
type: CONFIG_LOAD_ERROR.CONFIG_CORRUPTED,
|
||||||
|
error: error instanceof Error ? error : new Error('Unknown Error'),
|
||||||
|
config: newConfig,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
export const config = createSlice({
|
export const config = createSlice({
|
||||||
name: 'config',
|
name: 'config',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateUserConfig(
|
updateUserConfig(state, action: PayloadAction<RecursivePartial<MyServersConfig>>) {
|
||||||
state,
|
|
||||||
action: PayloadAction<RecursivePartial<MyServersConfig>>
|
|
||||||
) {
|
|
||||||
return merge(state, action.payload);
|
return merge(state, action.payload);
|
||||||
},
|
},
|
||||||
updateAccessTokens(
|
updateAccessTokens(
|
||||||
@@ -287,10 +237,7 @@ export const config = createSlice({
|
|||||||
state.status = FileLoadStatus.LOADED;
|
state.status = FileLoadStatus.LOADED;
|
||||||
break;
|
break;
|
||||||
case CONFIG_LOAD_ERROR.CONFIG_CORRUPTED:
|
case CONFIG_LOAD_ERROR.CONFIG_CORRUPTED:
|
||||||
logger.debug(
|
logger.debug('Config File Load Failed - %o', action.payload.error);
|
||||||
'Config File Load Failed - %o',
|
|
||||||
action.payload.error
|
|
||||||
);
|
|
||||||
merge(state, action.payload.config);
|
merge(state, action.payload.config);
|
||||||
state.status = FileLoadStatus.LOADED;
|
state.status = FileLoadStatus.LOADED;
|
||||||
break;
|
break;
|
||||||
@@ -333,8 +280,7 @@ export const config = createSlice({
|
|||||||
|
|
||||||
builder.addCase(setupRemoteAccessThunk.fulfilled, (state, action) => {
|
builder.addCase(setupRemoteAccessThunk.fulfilled, (state, action) => {
|
||||||
state.remote.wanaccess = action.payload.wanaccess;
|
state.remote.wanaccess = action.payload.wanaccess;
|
||||||
state.remote.dynamicRemoteAccessType =
|
state.remote.dynamicRemoteAccessType = action.payload.dynamicRemoteAccessType;
|
||||||
action.payload.dynamicRemoteAccessType;
|
|
||||||
state.remote.wanport = action.payload.wanport;
|
state.remote.wanport = action.payload.wanport;
|
||||||
state.remote.upnpEnabled = action.payload.upnpEnabled;
|
state.remote.upnpEnabled = action.payload.upnpEnabled;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,29 +1,38 @@
|
|||||||
import { getters, store } from '@app/store';
|
|
||||||
import { watch } from 'chokidar';
|
|
||||||
import { loadConfigFile, logoutUser } from '@app/store/modules/config';
|
|
||||||
import { logger } from '@app/core/log';
|
|
||||||
import { existsSync, writeFileSync } from 'fs';
|
import { existsSync, writeFileSync } from 'fs';
|
||||||
|
|
||||||
|
import { watch } from 'chokidar';
|
||||||
|
|
||||||
|
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 { CHOKIDAR_USEPOLLING, ENVIRONMENT } from '@app/environment';
|
import { CHOKIDAR_USEPOLLING, ENVIRONMENT } from '@app/environment';
|
||||||
|
import { getters, store } from '@app/store';
|
||||||
|
import { initialState, loadConfigFile, logoutUser } from '@app/store/modules/config';
|
||||||
|
|
||||||
export const setupConfigPathWatch = () => {
|
export const setupConfigPathWatch = () => {
|
||||||
const myServersConfigPath = getters.paths()?.['myservers-config'];
|
const myServersConfigPath = getters.paths()?.['myservers-config'];
|
||||||
if (myServersConfigPath) {
|
if (myServersConfigPath) {
|
||||||
logger.info('Watch Setup on Config Path: %s', myServersConfigPath);
|
logger.info('Watch Setup on Config Path: %s', myServersConfigPath);
|
||||||
if (!existsSync(myServersConfigPath)) {
|
if (!existsSync(myServersConfigPath)) {
|
||||||
writeFileSync(myServersConfigPath, '', 'utf-8');
|
const config = safelySerializeObjectToIni(getWriteableConfig(initialState, 'flash'));
|
||||||
}
|
writeFileSync(myServersConfigPath, config, 'utf-8');
|
||||||
const watcher = watch(myServersConfigPath, {
|
}
|
||||||
persistent: true,
|
const watcher = watch(myServersConfigPath, {
|
||||||
ignoreInitial: false,
|
persistent: true,
|
||||||
usePolling: CHOKIDAR_USEPOLLING === true,
|
ignoreInitial: false,
|
||||||
}).on('change', async () => {
|
usePolling: CHOKIDAR_USEPOLLING === true,
|
||||||
await store.dispatch(loadConfigFile());
|
})
|
||||||
}).on('unlink', async () => {
|
.on('change', async () => {
|
||||||
watcher.close();
|
await store.dispatch(loadConfigFile());
|
||||||
setupConfigPathWatch();
|
})
|
||||||
store.dispatch(logoutUser({ reason: 'Config File was Deleted'}))
|
.on('unlink', async () => {
|
||||||
});
|
const config = safelySerializeObjectToIni(getWriteableConfig(initialState, 'flash'));
|
||||||
} else {
|
await writeFileSync(myServersConfigPath, config, 'utf-8');
|
||||||
logger.error('[FATAL] Failed to setup watch on My Servers Config (Could Not Read Config Path)');
|
watcher.close();
|
||||||
}
|
setupConfigPathWatch();
|
||||||
|
store.dispatch(logoutUser({ reason: 'Config File was Deleted' }));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error('[FATAL] Failed to setup watch on My Servers Config (Could Not Read Config Path)');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ const ApiConfigSchema = z.object({
|
|||||||
extraOrigins: z.string(),
|
extraOrigins: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const NotifierConfigSchema = z.object({
|
|
||||||
apikey: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const RemoteConfigSchema = z.object({
|
const RemoteConfigSchema = z.object({
|
||||||
wanaccess: z.string(),
|
wanaccess: z.string(),
|
||||||
wanport: z.string(),
|
wanport: z.string(),
|
||||||
@@ -29,20 +25,16 @@ const RemoteConfigSchema = z.object({
|
|||||||
ssoSubIds: z.string(),
|
ssoSubIds: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const UpcConfigSchema = z.object({
|
|
||||||
apikey: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const LocalConfigSchema = z.object({});
|
const LocalConfigSchema = z.object({});
|
||||||
|
|
||||||
// Base config schema
|
// Base config schema
|
||||||
export const MyServersConfigSchema = z.object({
|
export const MyServersConfigSchema = z
|
||||||
api: ApiConfigSchema,
|
.object({
|
||||||
local: LocalConfigSchema,
|
api: ApiConfigSchema,
|
||||||
notifier: NotifierConfigSchema,
|
local: LocalConfigSchema,
|
||||||
remote: RemoteConfigSchema,
|
remote: RemoteConfigSchema,
|
||||||
upc: UpcConfigSchema,
|
})
|
||||||
});
|
.strip();
|
||||||
|
|
||||||
// Memory config schema
|
// Memory config schema
|
||||||
export const ConnectionStatusSchema = z.object({
|
export const ConnectionStatusSchema = z.object({
|
||||||
@@ -55,16 +47,8 @@ export const MyServersConfigMemorySchema = MyServersConfigSchema.extend({
|
|||||||
remote: RemoteConfigSchema.extend({
|
remote: RemoteConfigSchema.extend({
|
||||||
allowedOrigins: z.string(),
|
allowedOrigins: z.string(),
|
||||||
}),
|
}),
|
||||||
});
|
}).strip();
|
||||||
|
|
||||||
// Memory config with mandatory hidden fields schema
|
|
||||||
export const MyServersConfigMemoryWithMandatoryHiddenFieldsSchema = MyServersConfigMemorySchema.extend({
|
|
||||||
connectionStatus: ConnectionStatusSchema,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Infer and export types from Zod schemas
|
// Infer and export types from Zod schemas
|
||||||
export type MyServersConfig = z.infer<typeof MyServersConfigSchema>;
|
export type MyServersConfig = z.infer<typeof MyServersConfigSchema>;
|
||||||
export type MyServersConfigMemory = z.infer<typeof MyServersConfigMemorySchema>;
|
export type MyServersConfigMemory = z.infer<typeof MyServersConfigMemorySchema>;
|
||||||
export type MyServersConfigMemoryWithMandatoryHiddenFields = z.infer<
|
|
||||||
typeof MyServersConfigMemoryWithMandatoryHiddenFieldsSchema
|
|
||||||
>;
|
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ export class ValidateTokenCommand extends CommandRunner {
|
|||||||
let caughtError: null | unknown = null;
|
let caughtError: null | unknown = null;
|
||||||
let tokenPayload: null | JWTPayload = null;
|
let tokenPayload: null | JWTPayload = null;
|
||||||
try {
|
try {
|
||||||
this.logger.debug('Attempting to validate token with local key');
|
// this.logger.debug('Attempting to validate token with local key');
|
||||||
tokenPayload = (await jwtVerify(token, this.JWKSOffline)).payload;
|
tokenPayload = (await jwtVerify(token, this.JWKSOffline)).payload;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
try {
|
try {
|
||||||
this.logger.debug('Local validation failed for key, trying remote validation');
|
// this.logger.debug('Local validation failed for key, trying remote validation');
|
||||||
tokenPayload = (await jwtVerify(token, this.JWKSOnline)).payload;
|
tokenPayload = (await jwtVerify(token, this.JWKSOnline)).payload;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
caughtError = error;
|
caughtError = error;
|
||||||
|
|||||||
Reference in New Issue
Block a user