|
| 1 | +import { isEqual } from 'lodash-es'; |
| 2 | + |
1 | 3 | import { getAllowedOrigins } from '@app/common/allowed-origins'; |
2 | | -import { DynamicRemoteAccessType } from '@app/graphql/generated/api/types'; |
| 4 | +import { initialState } from '@app/store/modules/config'; |
3 | 5 | import { |
4 | | - type SliceState as ConfigSliceState, |
5 | | - initialState, |
6 | | -} from '@app/store/modules/config'; |
7 | | -import { type RecursivePartial } from '@app/types'; |
8 | | -import type { |
9 | 6 | MyServersConfig, |
10 | 7 | MyServersConfigMemory, |
| 8 | + MyServersConfigMemorySchema, |
| 9 | + MyServersConfigSchema, |
11 | 10 | } from '@app/types/my-servers-config'; |
12 | | -import { isEqual } from 'lodash-es'; |
13 | 11 |
|
| 12 | +// Define ConfigType and ConfigObject |
14 | 13 | export type ConfigType = 'flash' | 'memory'; |
15 | | -type ConfigObject<T> = T extends 'flash' |
16 | | - ? MyServersConfig |
17 | | - : T extends 'memory' |
18 | | - ? MyServersConfigMemory |
19 | | - : never; |
| 14 | + |
20 | 15 | /** |
21 | | - * |
22 | | - * @param config Config to read from to create a new formatted server config to write |
23 | | - * @param mode 'flash' or 'memory', changes what fields are included in the writeable payload |
24 | | - * @returns |
| 16 | + * Get a writeable configuration based on the mode ('flash' or 'memory'). |
25 | 17 | */ |
26 | | - |
27 | 18 | export const getWriteableConfig = <T extends ConfigType>( |
28 | | - config: ConfigSliceState, |
| 19 | + config: T extends 'memory' ? MyServersConfigMemory : MyServersConfig, |
29 | 20 | mode: T |
30 | | -): ConfigObject<T> => { |
31 | | - // Get current state |
32 | | - const { api, local, notifier, remote, upc, connectionStatus } = config; |
| 21 | +): T extends 'memory' ? MyServersConfigMemory : MyServersConfig => { |
| 22 | + const schema = mode === 'memory' ? MyServersConfigMemorySchema : MyServersConfigSchema; |
33 | 23 |
|
34 | | - // Create new state |
35 | | - |
36 | | - const newState: ConfigObject<T> = { |
37 | | - api: { |
38 | | - version: api?.version ?? initialState.api.version, |
39 | | - extraOrigins: api?.extraOrigins ?? initialState.api.extraOrigins, |
40 | | - }, |
41 | | - local: {}, |
42 | | - notifier: { |
43 | | - apikey: notifier.apikey ?? initialState.notifier.apikey, |
44 | | - }, |
| 24 | + const defaultConfig = schema.parse(initialState); |
| 25 | + // Use a type assertion for the mergedConfig to include `connectionStatus` only if `mode === 'memory` |
| 26 | + const mergedConfig = { |
| 27 | + ...defaultConfig, |
| 28 | + ...config, |
45 | 29 | remote: { |
46 | | - wanaccess: remote.wanaccess ?? initialState.remote.wanaccess, |
47 | | - wanport: remote.wanport ?? initialState.remote.wanport, |
48 | | - ...(remote.upnpEnabled ? { upnpEnabled: remote.upnpEnabled } : {}), |
49 | | - apikey: remote.apikey ?? initialState.remote.apikey, |
50 | | - localApiKey: remote.localApiKey ?? initialState.remote.localApiKey, |
51 | | - email: remote.email ?? initialState.remote.email, |
52 | | - username: remote.username ?? initialState.remote.username, |
53 | | - avatar: remote.avatar ?? initialState.remote.avatar, |
54 | | - regWizTime: remote.regWizTime ?? initialState.remote.regWizTime, |
55 | | - idtoken: remote.idtoken ?? initialState.remote.idtoken, |
56 | | - accesstoken: remote.accesstoken ?? initialState.remote.accesstoken, |
57 | | - refreshtoken: |
58 | | - remote.refreshtoken ?? initialState.remote.refreshtoken, |
59 | | - ...(mode === 'memory' |
60 | | - ? { |
61 | | - allowedOrigins: |
62 | | - getAllowedOrigins().join(', ') |
63 | | - } |
64 | | - : {}), |
65 | | - dynamicRemoteAccessType: remote.dynamicRemoteAccessType ?? DynamicRemoteAccessType.DISABLED, |
| 30 | + ...defaultConfig.remote, |
| 31 | + ...config.remote, |
66 | 32 | }, |
67 | | - upc: { |
68 | | - apikey: upc.apikey ?? initialState.upc.apikey, |
69 | | - }, |
70 | | - ...(mode === 'memory' |
71 | | - ? { |
72 | | - connectionStatus: { |
73 | | - minigraph: |
74 | | - connectionStatus.minigraph ?? |
75 | | - initialState.connectionStatus.minigraph, |
76 | | - ...(connectionStatus.upnpStatus |
77 | | - ? { upnpStatus: connectionStatus.upnpStatus } |
78 | | - : {}), |
79 | | - }, |
80 | | - } |
81 | | - : {}), |
82 | | - } as ConfigObject<T>; |
83 | | - return newState; |
| 33 | + } as T extends 'memory' ? MyServersConfigMemory : MyServersConfig; |
| 34 | + |
| 35 | + if (mode === 'memory') { |
| 36 | + (mergedConfig as MyServersConfigMemory).remote.allowedOrigins = getAllowedOrigins().join(', '); |
| 37 | + (mergedConfig as MyServersConfigMemory).connectionStatus = { |
| 38 | + ...(defaultConfig as MyServersConfigMemory).connectionStatus, |
| 39 | + ...(config as MyServersConfigMemory).connectionStatus, |
| 40 | + }; |
| 41 | + } |
| 42 | + |
| 43 | + return schema.parse(mergedConfig) as any; // Narrowing ensures correct typing |
84 | 44 | }; |
85 | 45 |
|
86 | 46 | /** |
87 | | - * Helper function to convert an object into a normalized config file. |
88 | | - * This is used for loading config files and ensure changes have been made before the state is merged. |
| 47 | + * Check if two configurations are equivalent by normalizing them through the Zod schema. |
89 | 48 | */ |
90 | 49 | export const areConfigsEquivalent = ( |
91 | | - newConfigFile: RecursivePartial<MyServersConfig>, |
92 | | - currentConfig: ConfigSliceState |
93 | | -): boolean => |
94 | | - // Enable to view config diffs: logger.debug(getDiff(getWriteableConfig(currentConfig, 'flash'), newConfigFile)); |
95 | | - isEqual(newConfigFile, getWriteableConfig(currentConfig, 'flash')); |
| 50 | + newConfigFile: Partial<MyServersConfigMemory>, // Use Partial here for flexibility |
| 51 | + currentConfig: MyServersConfig |
| 52 | +): boolean => { |
| 53 | + // Parse and validate the new config file using the schema (with default values applied) |
| 54 | + const normalizedNewConfig = MyServersConfigSchema.parse({ |
| 55 | + ...currentConfig, // Use currentConfig as a baseline to fill missing fields |
| 56 | + ...newConfigFile, |
| 57 | + }); |
| 58 | + |
| 59 | + // Get the writeable configuration for the current config |
| 60 | + const normalizedCurrentConfig = getWriteableConfig(currentConfig, 'flash'); |
| 61 | + |
| 62 | + // Compare the normalized configurations |
| 63 | + return isEqual(normalizedNewConfig, normalizedCurrentConfig); |
| 64 | +}; |
0 commit comments