initial commit

This commit is contained in:
2025-12-10 22:50:35 +09:00
parent 8cf674c9e5
commit 6b76389942
12 changed files with 393 additions and 493 deletions

View File

@@ -27,3 +27,31 @@ export function getPreviewUpdates() {
return http.get('/api/application/preview');
}
// update server binary
export function updateServer(file: File) {
const formData = new FormData();
formData.append('file', file);
return http.request({
method: 'post',
url: '/api/application/update/server',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
// update web resources
export function updateWeb(file: File) {
const formData = new FormData();
formData.append('file', file);
return http.request({
method: 'post',
url: '/api/application/update/web',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { LoadingOutlined, RocketOutlined, SmileOutlined } from '@ant-design/icons';
import { Button, Divider, Result, Spin } from 'antd';
import { useState } from 'react';
import { LoadingOutlined, RocketOutlined, SmileOutlined, UploadOutlined } from '@ant-design/icons';
import { Button, Divider, Result, Spin, Upload, message } from 'antd';
import { useTranslation } from 'react-i18next';
import semver from 'semver';
@@ -20,11 +20,13 @@ export const Update = ({ setIsLocked }: UpdateProps) => {
const [status, setStatus] = useState<Status>('');
const [currentVersion, setCurrentVersion] = useState('');
const [latestVersion, setLatestVersion] = useState('');
const [updateUrl, setUpdateUrl] = useState('');
const [errMsg, setErrMsg] = useState('');
useEffect(() => {
checkForUpdates();
}, []);
// Auto-check disabled
// useEffect(() => {
// checkForUpdates();
// }, []);
function checkForUpdates() {
if (status === 'loading') return;
@@ -41,6 +43,7 @@ export const Update = ({ setIsLocked }: UpdateProps) => {
setCurrentVersion(rsp.data.current);
setLatestVersion(rsp.data.latest);
setUpdateUrl(rsp.data.update_url);
const isLatest = semver.gte(rsp.data.current, rsp.data.latest);
setStatus(isLatest ? 'latest' : 'outdated');
@@ -75,6 +78,52 @@ export const Update = ({ setIsLocked }: UpdateProps) => {
});
}
const handleServerUpload = (file: File) => {
setIsLocked(true);
message.loading('Uploading server binary...', 0);
api.updateServer(file).then((rsp: any) => {
message.destroy();
if (rsp.code === 0) {
message.success('Server updated successfully. Restarting...');
setTimeout(() => {
setIsLocked(false);
window.location.reload();
}, 5000);
} else {
message.error('Server update failed: ' + rsp.msg);
setIsLocked(false);
}
}).catch(() => {
message.destroy();
message.error('Server update failed');
setIsLocked(false);
});
return false;
};
const handleWebUpload = (file: File) => {
setIsLocked(true);
message.loading('Uploading web resources...', 0);
api.updateWeb(file).then((rsp: any) => {
message.destroy();
if (rsp.code === 0) {
message.success('Web resources updated successfully. Reloading...');
setTimeout(() => {
setIsLocked(false);
window.location.reload();
}, 2000);
} else {
message.error('Web update failed: ' + rsp.msg);
setIsLocked(false);
}
}).catch(() => {
message.destroy();
message.error('Web update failed');
setIsLocked(false);
});
return false;
};
return (
<>
<div className="text-base font-bold">{t('settings.update.title')}</div>
@@ -82,9 +131,23 @@ export const Update = ({ setIsLocked }: UpdateProps) => {
<Preview />
<div className="my-[40px] h-px bg-neutral-500/10" />
<div className="my-[20px] h-px bg-neutral-500/10" />
{updateUrl && (
<div className="mb-4 text-xs text-gray-500">
Update Server: {updateUrl}
</div>
)}
<div className="flex min-h-[400px] flex-col justify-between">
{status === '' && (
<div className="flex justify-center pt-24">
<Button type="primary" onClick={checkForUpdates}>
Check for Updates
</Button>
</div>
)}
{status === 'loading' && (
<div className="flex justify-center pt-24">
<Spin indicator={<LoadingOutlined spin />} size="large" />
@@ -126,9 +189,21 @@ export const Update = ({ setIsLocked }: UpdateProps) => {
/>
)}
{status === 'failed' && <Result subTitle={errMsg} />}
{status === 'failed' && <Result subTitle={errMsg} extra={[<Button onClick={() => setStatus('')}>Retry</Button>]} />}
<div className="flex justify-center">
<div className="mt-8">
<Divider orientation="left">Manual Update</Divider>
<div className="flex space-x-4 justify-center">
<Upload beforeUpload={handleServerUpload} showUploadList={false}>
<Button icon={<UploadOutlined />}>Upload Server (Binary)</Button>
</Upload>
<Upload beforeUpload={handleWebUpload} showUploadList={false}>
<Button icon={<UploadOutlined />}>Upload Web (tar.gz)</Button>
</Upload>
</div>
</div>
<div className="flex justify-center mt-8">
<Button
type="link"
size="small"