initial commit
This commit is contained in:
@@ -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'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user