mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-02 06:56:36 +08:00
[优化] 添加服务器升级日志的WebSocket流
This commit is contained in:
@@ -8,14 +8,18 @@ import { useAuth } from '@/components/providers/auth-provider';
|
||||
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
||||
import { getPublicStatus } from '@/features/auth/api/public';
|
||||
import {
|
||||
createUpgradeLogsWebSocket,
|
||||
confirmManualServerUpgrade,
|
||||
getLatestRelease,
|
||||
parseUpgradeStreamSnapshot,
|
||||
upgradeServer,
|
||||
uploadServerBinary,
|
||||
} from '@/features/update/api/update';
|
||||
import { VersionUpgradeModal } from '@/features/update/components/version-upgrade-modal';
|
||||
import type {
|
||||
LatestReleaseInfo,
|
||||
ReleaseChannel,
|
||||
UpgradeStreamSnapshot,
|
||||
UploadedServerBinaryInfo,
|
||||
} from '@/features/update/types';
|
||||
import { publicEnv } from '@/lib/env/public-env';
|
||||
@@ -46,6 +50,8 @@ export function DashboardTopbar() {
|
||||
const [uploadedBinary, setUploadedBinary] =
|
||||
useState<UploadedServerBinaryInfo | null>(null);
|
||||
const [uploadProgress, setUploadProgress] = useState(0);
|
||||
const [upgradeStream, setUpgradeStream] =
|
||||
useState<UpgradeStreamSnapshot | null>(null);
|
||||
const menuRef = useRef<HTMLDivElement | null>(null);
|
||||
const isRoot = (user?.role ?? 0) >= 100;
|
||||
const upgradeStatusPollInterval = 3000;
|
||||
@@ -146,6 +152,51 @@ export function DashboardTopbar() {
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVersionModalOpen || !isRoot) {
|
||||
setUpgradeStream(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let closed = false;
|
||||
let reconnectTimer: number | null = null;
|
||||
let socket: WebSocket | null = null;
|
||||
|
||||
const connect = () => {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket = createUpgradeLogsWebSocket();
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
const snapshot = parseUpgradeStreamSnapshot(String(event.data));
|
||||
if (snapshot) {
|
||||
setUpgradeStream(snapshot);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
if (!closed) {
|
||||
reconnectTimer = window.setTimeout(connect, 1500);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
connect();
|
||||
|
||||
return () => {
|
||||
closed = true;
|
||||
if (reconnectTimer !== null) {
|
||||
window.clearTimeout(reconnectTimer);
|
||||
}
|
||||
socket?.close();
|
||||
};
|
||||
}, [isRoot, isVersionModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isUserMenuOpen) {
|
||||
return;
|
||||
@@ -245,6 +296,10 @@ export function DashboardTopbar() {
|
||||
selectedReleaseChannel === 'preview'
|
||||
? previewReleaseQuery.data
|
||||
: stableReleaseQuery.data;
|
||||
const releaseWithStream = mergeReleaseWithUpgradeStream(
|
||||
selectedRelease,
|
||||
upgradeStream,
|
||||
);
|
||||
const selectedReleaseError =
|
||||
selectedReleaseChannel === 'preview'
|
||||
? previewReleaseQuery.error
|
||||
@@ -345,7 +400,7 @@ export function DashboardTopbar() {
|
||||
isOpen={isVersionModalOpen}
|
||||
onClose={() => setIsVersionModalOpen(false)}
|
||||
currentVersion={currentVersion}
|
||||
release={selectedRelease}
|
||||
release={releaseWithStream}
|
||||
selectedChannel={selectedReleaseChannel}
|
||||
uploadedBinary={uploadedBinary}
|
||||
isLoading={
|
||||
@@ -376,3 +431,19 @@ export function DashboardTopbar() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function mergeReleaseWithUpgradeStream(
|
||||
release: LatestReleaseInfo | null | undefined,
|
||||
stream: UpgradeStreamSnapshot | null,
|
||||
) {
|
||||
if (!release || !stream) {
|
||||
return release;
|
||||
}
|
||||
|
||||
return {
|
||||
...release,
|
||||
in_progress: stream.in_progress,
|
||||
upgrade_status: stream.upgrade_status,
|
||||
upgrade_logs: stream.upgrade_logs,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ApiEnvelope } from '@/types/api';
|
||||
import type {
|
||||
LatestReleaseInfo,
|
||||
ReleaseChannel,
|
||||
UpgradeStreamSnapshot,
|
||||
UploadedServerBinaryInfo,
|
||||
} from '@/features/update/types';
|
||||
|
||||
@@ -48,7 +49,9 @@ export function uploadServerBinary(
|
||||
xhr.addEventListener('load', () => {
|
||||
let payload: ApiEnvelope<UploadedServerBinaryInfo> | null = null;
|
||||
try {
|
||||
payload = JSON.parse(xhr.responseText) as ApiEnvelope<UploadedServerBinaryInfo>;
|
||||
payload = JSON.parse(
|
||||
xhr.responseText,
|
||||
) as ApiEnvelope<UploadedServerBinaryInfo>;
|
||||
} catch {
|
||||
payload = null;
|
||||
}
|
||||
@@ -86,3 +89,40 @@ export function confirmManualServerUpgrade(uploadToken: string) {
|
||||
body: JSON.stringify({ upload_token: uploadToken }),
|
||||
});
|
||||
}
|
||||
|
||||
export function createUpgradeLogsWebSocket() {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const apiUrl = getApiUrl('/update/logs/ws');
|
||||
const resolvedUrl = apiUrl.startsWith('http://')
|
||||
? `ws://${apiUrl.slice('http://'.length)}`
|
||||
: apiUrl.startsWith('https://')
|
||||
? `wss://${apiUrl.slice('https://'.length)}`
|
||||
: `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}${apiUrl}`;
|
||||
|
||||
return new WebSocket(resolvedUrl);
|
||||
}
|
||||
|
||||
export function parseUpgradeStreamSnapshot(
|
||||
rawMessage: string,
|
||||
): UpgradeStreamSnapshot | null {
|
||||
try {
|
||||
const parsed = JSON.parse(rawMessage) as Partial<UpgradeStreamSnapshot>;
|
||||
if (
|
||||
typeof parsed.in_progress !== 'boolean' ||
|
||||
typeof parsed.upgrade_status !== 'string' ||
|
||||
!Array.isArray(parsed.upgrade_logs)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
in_progress: parsed.in_progress,
|
||||
upgrade_status: parsed.upgrade_status,
|
||||
upgrade_logs: parsed.upgrade_logs,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ export interface LatestReleaseInfo {
|
||||
upgrade_logs: UpgradeLogItem[];
|
||||
}
|
||||
|
||||
export interface UpgradeStreamSnapshot {
|
||||
in_progress: boolean;
|
||||
upgrade_status: 'idle' | 'running' | 'succeeded' | 'failed' | string;
|
||||
upgrade_logs: UpgradeLogItem[];
|
||||
}
|
||||
|
||||
export interface UploadedServerBinaryInfo {
|
||||
upload_token: string;
|
||||
file_name: string;
|
||||
|
||||
Reference in New Issue
Block a user