Files
OpenFlare/Wavelet/frontend/lib/services/openflare/update.service.ts
T
2026-06-18 18:05:00 +08:00

136 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 type {InternalAxiosRequestConfig} from 'axios';
import {getApiBaseUrl} from '@/lib/services/core/config';
import {ApiErrorBase} from '@/lib/services/core/errors';
import type {LegacyApiResponse} from './legacy-base.service';
import {LegacyOpenFlareBaseService} from './legacy-base.service';
import type {LatestReleaseInfo, ReleaseChannel, UpgradeStreamSnapshot, UploadedServerBinaryInfo,} from './types';
/**
* OpenFlare 服务端升级 API(遗留 `/api/update/*`)。
*/
export class UpdateService extends LegacyOpenFlareBaseService {
protected static override readonly basePath = '/api/update';
private static getLegacyApiUrl(path: string): string {
const baseURL = getApiBaseUrl();
const fullPath = this.getFullPath(path);
if (!baseURL) {
return fullPath;
}
return `${baseURL.replace(/\/$/, '')}${fullPath}`;
}
static getLatestRelease(channel: ReleaseChannel = 'stable'): Promise<LatestReleaseInfo> {
return this.legacyGet<LatestReleaseInfo>('/latest-release', { channel });
}
static upgradeServer(channel: ReleaseChannel = 'stable'): Promise<LatestReleaseInfo> {
return this.legacyPost<LatestReleaseInfo>('/upgrade', { channel });
}
static uploadServerBinary(
binary: File,
onProgress?: (progress: number) => void,
): Promise<UploadedServerBinaryInfo> {
const formData = new FormData();
formData.append('binary', binary);
if (!onProgress) {
return this.legacyPost<UploadedServerBinaryInfo>('/manual-upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
} as InternalAxiosRequestConfig);
}
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', this.getLegacyApiUrl('/manual-upload'));
xhr.withCredentials = true;
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
onProgress(Math.round((event.loaded / event.total) * 100));
}
});
xhr.addEventListener('load', () => {
let payload: LegacyApiResponse<UploadedServerBinaryInfo> | null = null;
try {
payload = JSON.parse(
xhr.responseText,
) as LegacyApiResponse<UploadedServerBinaryInfo>;
} catch {
payload = null;
}
if (xhr.status < 200 || xhr.status >= 300) {
reject(
new ApiErrorBase(
payload?.message || `请求失败(${xhr.status})`,
undefined,
xhr.status,
),
);
return;
}
if (!payload) {
reject(new ApiErrorBase('响应格式无效', undefined, xhr.status));
return;
}
if (!payload.success) {
reject(new ApiErrorBase(payload.message || '请求失败', undefined, xhr.status));
return;
}
resolve(payload.data);
});
xhr.addEventListener('error', () => {
reject(new ApiErrorBase('上传过程中网络连接中断,请检查网络后重试', undefined, 0));
});
xhr.send(formData);
});
}
static confirmManualServerUpgrade(uploadToken: string): Promise<UploadedServerBinaryInfo> {
return this.legacyPost<UploadedServerBinaryInfo>('/manual-upgrade', {
upload_token: uploadToken,
});
}
static createUpgradeLogsWebSocket(): WebSocket | null {
if (typeof window === 'undefined') {
return null;
}
const apiUrl = this.getLegacyApiUrl('/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);
}
static 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;
}
}
}