refactor(server): config system (#11081)

This commit is contained in:
forehalo
2025-03-27 12:32:28 +00:00
parent 7091111f85
commit 0ea38680fa
274 changed files with 7583 additions and 5841 deletions

View File

@@ -1,75 +1,47 @@
import { Button } from '@affine/admin/components/ui/button';
import { ScrollArea } from '@affine/admin/components/ui/scroll-area';
import { Separator } from '@affine/admin/components/ui/separator';
import type { RuntimeConfigType } from '@affine/graphql';
import { get } from 'lodash-es';
import { CheckIcon } from 'lucide-react';
import type { Dispatch, SetStateAction } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useState } from 'react';
import { Header } from '../header';
import { useNav } from '../nav/context';
import {
ALL_CONFIG,
ALL_CONFIGURABLE_MODULES,
type ConfigDescriptor,
} from './config';
import { ConfigInput } from './config-input';
import { ConfirmChanges } from './confirm-changes';
import { RuntimeSettingRow } from './runtime-setting-row';
import { useGetServerRuntimeConfig } from './use-get-server-runtime-config';
import { useUpdateServerRuntimeConfigs } from './use-update-server-runtime-config';
import {
formatValue,
formatValueForInput,
isEqual,
renderInput,
} from './utils';
export type ModifiedValues = {
id: string;
expiredValue: any;
newValue: any;
};
import { useAppConfig } from './use-app-config';
export function SettingsPage() {
const { trigger } = useUpdateServerRuntimeConfigs();
const { serverRuntimeConfig } = useGetServerRuntimeConfig();
const { appConfig, update, save, updates } = useAppConfig();
const [open, setOpen] = useState(false);
const [configValues, setConfigValues] = useState(
serverRuntimeConfig.reduce(
(acc, config) => {
acc[config.id] = config.value;
return acc;
},
{} as Record<string, any>
)
);
const modifiedValues: ModifiedValues[] = useMemo(() => {
return serverRuntimeConfig
.filter(config => !isEqual(config.value, configValues[config.id]))
.map(config => ({
id: config.id,
key: config.key,
expiredValue: config.value,
newValue: configValues[config.id],
}));
}, [configValues, serverRuntimeConfig]);
const handleSave = useCallback(() => {
// post value example: { "key1": "newValue1","key2": "newValue2"}
const updates: Record<string, any> = {};
modifiedValues.forEach(item => {
if (item.id && item.newValue !== undefined) {
updates[item.id] = item.newValue;
}
});
trigger({ updates });
}, [modifiedValues, trigger]);
const disableSave = modifiedValues.length === 0;
const onOpen = useCallback(() => setOpen(true), [setOpen]);
const onClose = useCallback(() => setOpen(false), [setOpen]);
const onConfirm = useCallback(() => {
const disableSave = Object.keys(updates).length === 0;
const saveChanges = useCallback(() => {
if (disableSave) {
return;
}
handleSave();
onClose();
}, [disableSave, handleSave, onClose]);
save(
Object.entries(updates).map(([key, { to }]) => {
const splitAt = key.indexOf('.');
const [module, field] = [key.slice(0, splitAt), key.slice(splitAt + 1)];
return {
module,
key: field,
value: to,
};
})
);
setOpen(false);
}, [save, disableSave, updates]);
return (
<div className=" h-screen flex-1 flex-col flex">
<Header
@@ -87,101 +59,68 @@ export function SettingsPage() {
</Button>
}
/>
<AdminPanel
configValues={configValues}
setConfigValues={setConfigValues}
/>
<AdminPanel onUpdate={update} appConfig={appConfig} />
<ConfirmChanges
modifiedValues={modifiedValues}
updates={updates}
open={open}
onOpenChange={setOpen}
onClose={onClose}
onConfirm={onConfirm}
onConfirm={saveChanges}
/>
</div>
);
}
export const AdminPanel = ({
setConfigValues,
configValues,
appConfig,
onUpdate,
}: {
setConfigValues: Dispatch<SetStateAction<Record<string, any>>>;
configValues: Record<string, any>;
appConfig: Record<string, any>;
onUpdate: (module: string, field: string, value: any) => void;
}) => {
const { configGroup } = useGetServerRuntimeConfig();
const { currentModule } = useNav();
const handleInputChange = useCallback(
(key: string, value: any, type: RuntimeConfigType) => {
const newValue = formatValueForInput(value, type);
setConfigValues(prevValues => ({
...prevValues,
[key]: newValue,
}));
},
[setConfigValues]
);
return (
<ScrollArea>
<div className="flex flex-col h-full gap-3 py-5 px-6 w-full max-w-[800px] mx-auto">
{configGroup
.filter(group => group.moduleName === currentModule)
.map(group => {
const { moduleName, configs } = group;
return (
<div
className="flex flex-col gap-5"
id={moduleName}
key={moduleName}
>
<div className="text-xl font-semibold">{moduleName}</div>
{configs?.map((config, index) => {
const { id, type, description, updatedAt } = config;
const isValueEqual = isEqual(config.value, configValues[id]);
const formatServerValue = formatValue(config.value);
const formatCurrentValue = formatValue(configValues[id]);
return (
<div key={id} className="flex flex-col gap-10">
{index !== 0 && <Separator />}
<RuntimeSettingRow
key={id}
id={id}
description={description}
lastUpdatedTime={updatedAt}
operation={renderInput(type, configValues[id], value =>
handleInputChange(id, value, type)
)}
>
<div style={{ opacity: isValueEqual ? 0 : 1 }}>
<span
className="line-through"
style={{
color: 'rgba(198, 34, 34, 1)',
backgroundColor: 'rgba(254, 213, 213, 1)',
}}
>
{formatServerValue}
</span>{' '}
=&gt;{' '}
<span
style={{
color: 'rgba(20, 147, 67, 1)',
backgroundColor: 'rgba(225, 250, 177, 1)',
}}
>
{formatCurrentValue}
</span>
</div>
</RuntimeSettingRow>
</div>
);
})}
</div>
);
})}
{ALL_CONFIGURABLE_MODULES.filter(
module => module === currentModule
).map(module => {
const fields = Object.keys(ALL_CONFIG[module]);
return (
<div
className="flex flex-col gap-5"
id={`config-module-${module}`}
key={module}
>
<div className="text-xl font-semibold">{module}</div>
{fields.map((field, index) => {
// @ts-expect-error allow
const { desc, type } = ALL_CONFIG[module][
field
] as ConfigDescriptor;
return (
<div key={field} className="flex flex-col gap-10">
{index !== 0 && <Separator />}
<RuntimeSettingRow
key={field}
id={field}
description={desc}
>
<ConfigInput
module={module}
field={field}
type={type}
defaultValue={get(appConfig[module], field)}
onChange={onUpdate}
/>
</RuntimeSettingRow>
</div>
);
})}
</div>
);
})}
</div>
</ScrollArea>
);