Files
NapCatQQ/packages/napcat-webui-frontend/src/pages/dashboard/config/login.tsx
手瓜一十雪 2f8569f30c Add Registry20 GUID management and DPAPI support
Introduce tools and UI to read, write, backup and restore QQ Registry20 GUIDs using Windows DPAPI. Adds a Python CLI (guid_tool.py) for local Registry20 operations and a new TypeScript package napcat-dpapi with native bindings for DPAPI. Implements Registry20Utils in the webui-backend to protect/unprotect Registry20, plus backup/restore/delete helpers. Exposes new backend API handlers and routes (get/set GUID, backups, restore, reset, restart) and integrates frontend GUIDManager component and qq_manager controller methods. Propagates QQ data path via WebUiDataRuntime (setter/getter) and wires it up in framework/shell; updates Vite alias and package.json to include the new dpapi workspace. Includes native addon binaries for win32 x64/arm64 and basic tsconfig/readme/license for the new package.
2026-02-12 17:08:24 +08:00

149 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Input } from '@heroui/input';
import { Button } from '@heroui/button';
import { Divider } from '@heroui/divider';
import { useRequest } from 'ahooks';
import { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import SaveButtons from '@/components/button/save_buttons';
import GUIDManager from '@/components/guid_manager';
import PageLoading from '@/components/page_loading';
import QQManager from '@/controllers/qq_manager';
import ProcessManager from '@/controllers/process_manager';
import { waitForBackendReady } from '@/utils/process_utils';
const LoginConfigCard = () => {
const [isRestarting, setIsRestarting] = useState(false);
const {
data: quickLoginData,
loading: quickLoginLoading,
error: quickLoginError,
refreshAsync: refreshQuickLogin,
} = useRequest(QQManager.getQuickLoginQQ);
const {
control,
handleSubmit: handleOnebotSubmit,
formState: { isSubmitting },
setValue: setOnebotValue,
} = useForm<{
quickLoginQQ: string;
}>({
defaultValues: {
quickLoginQQ: '',
},
});
const reset = () => {
setOnebotValue('quickLoginQQ', quickLoginData ?? '');
};
const onSubmit = handleOnebotSubmit(async (data) => {
try {
await QQManager.setQuickLoginQQ(data.quickLoginQQ);
toast.success('保存成功');
} catch (error) {
const msg = (error as Error).message;
toast.error(`保存失败: ${msg}`);
}
});
const onRefresh = async () => {
try {
await refreshQuickLogin();
toast.success('刷新成功');
} catch (error) {
const msg = (error as Error).message;
toast.error(`刷新失败: ${msg}`);
}
};
const onRestartProcess = async () => {
setIsRestarting(true);
try {
const result = await ProcessManager.restartProcess();
toast.success(result.message || '进程重启请求已发送');
// 轮询探测后端是否恢复
const isReady = await waitForBackendReady(
30000, // 30秒超时
() => {
setIsRestarting(false);
toast.success('进程重启完成');
},
() => {
setIsRestarting(false);
toast.error('后端在 30 秒内未响应,请检查 NapCat 运行日志');
}
);
if (!isReady) {
setIsRestarting(false);
}
} catch (error) {
const msg = (error as Error).message;
toast.error(`进程重启失败: ${msg}`);
setIsRestarting(false);
}
};
useEffect(() => {
reset();
}, [quickLoginData]);
if (quickLoginLoading) return <PageLoading loading />;
return (
<>
<title>OneBot配置 - NapCat WebUI</title>
<div className='flex-shrink-0 w-full'>QQ</div>
<Controller
control={control}
name='quickLoginQQ'
render={({ field }) => (
<Input
{...field}
label='快速登录QQ'
placeholder='请输入QQ号'
isDisabled={!!quickLoginError}
errorMessage={quickLoginError ? '获取快速登录QQ失败' : undefined}
/>
)}
/>
<SaveButtons
onSubmit={onSubmit}
reset={reset}
isSubmitting={isSubmitting || quickLoginLoading}
refresh={onRefresh}
/>
<div className='flex-shrink-0 w-full mt-6 pt-6 border-t border-divider'>
<div className='mb-3 text-sm text-default-600'></div>
<Button
color='warning'
variant='flat'
onPress={onRestartProcess}
isLoading={isRestarting}
isDisabled={isRestarting}
fullWidth
>
{isRestarting ? '正在重启进程...' : '重启进程'}
</Button>
<div className='mt-2 text-xs text-default-500'>
Worker 3
</div>
</div>
<Divider className='mt-6' />
<div className='flex-shrink-0 w-full mt-4'>
<div className='mb-3 text-sm text-default-600'> GUID </div>
<div className='text-xs text-default-400 mb-3'>
GUID Registry20
</div>
<GUIDManager showRestart={false} />
</div>
</>
);
};
export default LoginConfigCard;