refactor(dashboard): migrate plugin-api HTTP functions to ApiResponse pattern
- Migrate 14 HTTP functions in plugin-api.ts to return ApiResponse<T> - fetchPluginList, checkGitStatus, getMaimaiVersion - getInstalledPlugins, installPlugin, uninstallPlugin, updatePlugin - getPluginConfigSchema, getPluginConfig, getPluginConfigRaw - updatePluginConfig, updatePluginConfigRaw, resetPluginConfig, togglePlugin - Update 3 caller files to handle ApiResponse pattern: - plugins.tsx: 5 function calls updated - plugin-config.tsx: 5 function calls updated - plugin-detail.tsx: 5 function calls updated - All callers now check .success before accessing .data - Preserve WebSocket and utility functions unchanged - Build verification: npm run build succeeds with 0 errors
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import { fetchWithAuth, getAuthHeaders } from '@/lib/fetch-with-auth'
|
import type { ApiResponse } from '@/types/api'
|
||||||
import type { PluginInfo } from '@/types/plugin'
|
import type { PluginInfo } from '@/types/plugin'
|
||||||
|
|
||||||
|
import { fetchWithAuth, getAuthHeaders } from '@/lib/fetch-with-auth'
|
||||||
|
import { parseResponse } from './api-helpers'
|
||||||
import { createReconnectingWebSocket } from './ws-utils'
|
import { createReconnectingWebSocket } from './ws-utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,12 +108,9 @@ interface PluginApiResponse {
|
|||||||
/**
|
/**
|
||||||
* 从远程获取插件列表(通过后端代理避免 CORS)
|
* 从远程获取插件列表(通过后端代理避免 CORS)
|
||||||
*/
|
*/
|
||||||
export async function fetchPluginList(): Promise<PluginInfo[]> {
|
export async function fetchPluginList(): Promise<ApiResponse<PluginInfo[]>> {
|
||||||
try {
|
|
||||||
// 通过后端 API 获取 Raw 文件
|
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/fetch-raw', {
|
const response = await fetchWithAuth('/api/webui/plugins/fetch-raw', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
owner: PLUGIN_REPO_OWNER,
|
owner: PLUGIN_REPO_OWNER,
|
||||||
repo: PLUGIN_REPO_NAME,
|
repo: PLUGIN_REPO_NAME,
|
||||||
@@ -120,23 +119,24 @@ export async function fetchPluginList(): Promise<PluginInfo[]> {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<{ success: boolean; data: string; error?: string }>(response)
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
|
if (!apiResult.success) {
|
||||||
|
return apiResult
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
const result = apiResult.data
|
||||||
|
|
||||||
// 检查后端返回的结果
|
|
||||||
if (!result.success || !result.data) {
|
if (!result.success || !result.data) {
|
||||||
throw new Error(result.error || '获取插件列表失败')
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result.error || '获取插件列表失败'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: PluginApiResponse[] = JSON.parse(result.data)
|
const data: PluginApiResponse[] = JSON.parse(result.data)
|
||||||
|
|
||||||
// 转换为 PluginInfo 格式,并过滤掉无效数据
|
|
||||||
const pluginList = data
|
const pluginList = data
|
||||||
.filter(item => {
|
.filter(item => {
|
||||||
// 验证必需字段
|
|
||||||
if (!item?.id || !item?.manifest) {
|
if (!item?.id || !item?.manifest) {
|
||||||
console.warn('跳过无效插件数据:', item)
|
console.warn('跳过无效插件数据:', item)
|
||||||
return false
|
return false
|
||||||
@@ -164,7 +164,6 @@ export async function fetchPluginList(): Promise<PluginInfo[]> {
|
|||||||
default_locale: item.manifest.default_locale || 'zh-CN',
|
default_locale: item.manifest.default_locale || 'zh-CN',
|
||||||
locales_path: item.manifest.locales_path,
|
locales_path: item.manifest.locales_path,
|
||||||
},
|
},
|
||||||
// 默认值,这些信息可能需要从其他 API 获取
|
|
||||||
downloads: 0,
|
downloads: 0,
|
||||||
rating: 0,
|
rating: 0,
|
||||||
review_count: 0,
|
review_count: 0,
|
||||||
@@ -173,57 +172,54 @@ export async function fetchPluginList(): Promise<PluginInfo[]> {
|
|||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return pluginList
|
return {
|
||||||
} catch (error) {
|
success: true,
|
||||||
console.error('Failed to fetch plugin list:', error)
|
data: pluginList
|
||||||
throw error
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查本机 Git 安装状态
|
* 检查本机 Git 安装状态
|
||||||
*/
|
*/
|
||||||
export async function checkGitStatus(): Promise<GitStatus> {
|
export async function checkGitStatus(): Promise<ApiResponse<GitStatus>> {
|
||||||
try {
|
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/git-status')
|
const response = await fetchWithAuth('/api/webui/plugins/git-status')
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<GitStatus>(response)
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
if (!apiResult.success) {
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to check Git status:', error)
|
|
||||||
// 返回未安装状态
|
|
||||||
return {
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
installed: false,
|
installed: false,
|
||||||
error: '无法检测 Git 安装状态'
|
error: '无法检测 Git 安装状态'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiResult
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取麦麦版本信息
|
* 获取麦麦版本信息
|
||||||
*/
|
*/
|
||||||
export async function getMaimaiVersion(): Promise<MaimaiVersion> {
|
export async function getMaimaiVersion(): Promise<ApiResponse<MaimaiVersion>> {
|
||||||
try {
|
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/version')
|
const response = await fetchWithAuth('/api/webui/plugins/version')
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<MaimaiVersion>(response)
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
if (!apiResult.success) {
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to get Maimai version:', error)
|
|
||||||
// 返回默认版本
|
|
||||||
return {
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
version: '0.0.0',
|
version: '0.0.0',
|
||||||
version_major: 0,
|
version_major: 0,
|
||||||
version_minor: 0,
|
version_minor: 0,
|
||||||
version_patch: 0
|
version_patch: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiResult
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -318,26 +314,31 @@ export async function connectPluginProgressWebSocket(
|
|||||||
/**
|
/**
|
||||||
* 获取已安装插件列表
|
* 获取已安装插件列表
|
||||||
*/
|
*/
|
||||||
export async function getInstalledPlugins(): Promise<InstalledPlugin[]> {
|
export async function getInstalledPlugins(): Promise<ApiResponse<InstalledPlugin[]>> {
|
||||||
try {
|
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/installed', {
|
const response = await fetchWithAuth('/api/webui/plugins/installed', {
|
||||||
headers: getAuthHeaders()
|
headers: getAuthHeaders()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<{ success: boolean; plugins?: InstalledPlugin[]; message?: string }>(response)
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
|
if (!apiResult.success) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
const result = apiResult.data
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.message || '获取已安装插件列表失败')
|
return {
|
||||||
|
success: true,
|
||||||
|
data: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.plugins || []
|
return {
|
||||||
} catch (error) {
|
success: true,
|
||||||
console.error('Failed to get installed plugins:', error)
|
data: result.plugins || []
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,10 +364,9 @@ export function getInstalledPluginVersion(pluginId: string, installedPlugins: In
|
|||||||
/**
|
/**
|
||||||
* 安装插件
|
* 安装插件
|
||||||
*/
|
*/
|
||||||
export async function installPlugin(pluginId: string, repositoryUrl: string, branch: string = 'main'): Promise<{ success: boolean; message: string }> {
|
export async function installPlugin(pluginId: string, repositoryUrl: string, branch: string = 'main'): Promise<ApiResponse<{ success: boolean; message: string }>> {
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/install', {
|
const response = await fetchWithAuth('/api/webui/plugins/install', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
plugin_id: pluginId,
|
plugin_id: pluginId,
|
||||||
repository_url: repositoryUrl,
|
repository_url: repositoryUrl,
|
||||||
@@ -374,41 +374,29 @@ export async function installPlugin(pluginId: string, repositoryUrl: string, bra
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; message: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '安装失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卸载插件
|
* 卸载插件
|
||||||
*/
|
*/
|
||||||
export async function uninstallPlugin(pluginId: string): Promise<{ success: boolean; message: string }> {
|
export async function uninstallPlugin(pluginId: string): Promise<ApiResponse<{ success: boolean; message: string }>> {
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/uninstall', {
|
const response = await fetchWithAuth('/api/webui/plugins/uninstall', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
plugin_id: pluginId
|
plugin_id: pluginId
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; message: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '卸载失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新插件
|
* 更新插件
|
||||||
*/
|
*/
|
||||||
export async function updatePlugin(pluginId: string, repositoryUrl: string, branch: string = 'main'): Promise<{ success: boolean; message: string; old_version: string; new_version: string }> {
|
export async function updatePlugin(pluginId: string, repositoryUrl: string, branch: string = 'main'): Promise<ApiResponse<{ success: boolean; message: string; old_version: string; new_version: string }>> {
|
||||||
const response = await fetchWithAuth('/api/webui/plugins/update', {
|
const response = await fetchWithAuth('/api/webui/plugins/update', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
plugin_id: pluginId,
|
plugin_id: pluginId,
|
||||||
repository_url: repositoryUrl,
|
repository_url: repositoryUrl,
|
||||||
@@ -416,12 +404,7 @@ export async function updatePlugin(pluginId: string, repositoryUrl: string, bran
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; message: string; old_version: string; new_version: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '更新失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -525,82 +508,85 @@ export interface PluginConfigSchema {
|
|||||||
/**
|
/**
|
||||||
* 获取插件配置 Schema
|
* 获取插件配置 Schema
|
||||||
*/
|
*/
|
||||||
export async function getPluginConfigSchema(pluginId: string): Promise<PluginConfigSchema> {
|
export async function getPluginConfigSchema(pluginId: string): Promise<ApiResponse<PluginConfigSchema>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/schema`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/schema`, {
|
||||||
headers: getAuthHeaders()
|
headers: getAuthHeaders()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<{ success: boolean; schema?: PluginConfigSchema; message?: string }>(response)
|
||||||
const text = await response.text()
|
|
||||||
try {
|
if (!apiResult.success) {
|
||||||
const error = JSON.parse(text)
|
return apiResult
|
||||||
throw new Error(error.detail || '获取配置 Schema 失败')
|
}
|
||||||
} catch {
|
|
||||||
throw new Error(`获取配置 Schema 失败 (${response.status})`)
|
const result = apiResult.data
|
||||||
|
if (!result.success || !result.schema) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result.message || '获取配置 Schema 失败'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
return {
|
||||||
|
success: true,
|
||||||
if (!result.success) {
|
data: result.schema
|
||||||
throw new Error(result.message || '获取配置 Schema 失败')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.schema
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取插件当前配置值
|
* 获取插件当前配置值
|
||||||
*/
|
*/
|
||||||
export async function getPluginConfig(pluginId: string): Promise<Record<string, unknown>> {
|
export async function getPluginConfig(pluginId: string): Promise<ApiResponse<Record<string, unknown>>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}`, {
|
||||||
headers: getAuthHeaders()
|
headers: getAuthHeaders()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<{ success: boolean; config?: Record<string, unknown>; message?: string }>(response)
|
||||||
const text = await response.text()
|
|
||||||
try {
|
if (!apiResult.success) {
|
||||||
const error = JSON.parse(text)
|
return apiResult
|
||||||
throw new Error(error.detail || '获取配置失败')
|
}
|
||||||
} catch {
|
|
||||||
throw new Error(`获取配置失败 (${response.status})`)
|
const result = apiResult.data
|
||||||
|
if (!result.success || !result.config) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result.message || '获取配置失败'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
return {
|
||||||
|
success: true,
|
||||||
if (!result.success) {
|
data: result.config
|
||||||
throw new Error(result.message || '获取配置失败')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取插件原始 TOML 配置
|
* 获取插件原始 TOML 配置
|
||||||
*/
|
*/
|
||||||
export async function getPluginConfigRaw(pluginId: string): Promise<string> {
|
export async function getPluginConfigRaw(pluginId: string): Promise<ApiResponse<string>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/raw`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/raw`, {
|
||||||
headers: getAuthHeaders()
|
headers: getAuthHeaders()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
const apiResult = await parseResponse<{ success: boolean; config?: string; message?: string }>(response)
|
||||||
const text = await response.text()
|
|
||||||
try {
|
if (!apiResult.success) {
|
||||||
const error = JSON.parse(text)
|
return apiResult
|
||||||
throw new Error(error.detail || '获取配置失败')
|
}
|
||||||
} catch {
|
|
||||||
throw new Error(`获取配置失败 (${response.status})`)
|
const result = apiResult.data
|
||||||
|
if (!result.success || !result.config) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result.message || '获取配置失败'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
return {
|
||||||
|
success: true,
|
||||||
if (!result.success) {
|
data: result.config
|
||||||
throw new Error(result.message || '获取配置失败')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -609,19 +595,14 @@ export async function getPluginConfigRaw(pluginId: string): Promise<string> {
|
|||||||
export async function updatePluginConfig(
|
export async function updatePluginConfig(
|
||||||
pluginId: string,
|
pluginId: string,
|
||||||
config: Record<string, unknown>
|
config: Record<string, unknown>
|
||||||
): Promise<{ success: boolean; message: string; note?: string }> {
|
): Promise<ApiResponse<{ success: boolean; message: string; note?: string }>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: getAuthHeaders(),
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ config })
|
body: JSON.stringify({ config })
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; message: string; note?: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '保存配置失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -630,19 +611,14 @@ export async function updatePluginConfig(
|
|||||||
export async function updatePluginConfigRaw(
|
export async function updatePluginConfigRaw(
|
||||||
pluginId: string,
|
pluginId: string,
|
||||||
configToml: string
|
configToml: string
|
||||||
): Promise<{ success: boolean; message: string; note?: string }> {
|
): Promise<ApiResponse<{ success: boolean; message: string; note?: string }>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/raw`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/raw`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: getAuthHeaders(),
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ config: configToml })
|
body: JSON.stringify({ config: configToml })
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; message: string; note?: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '保存配置失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -650,18 +626,13 @@ export async function updatePluginConfigRaw(
|
|||||||
*/
|
*/
|
||||||
export async function resetPluginConfig(
|
export async function resetPluginConfig(
|
||||||
pluginId: string
|
pluginId: string
|
||||||
): Promise<{ success: boolean; message: string; backup?: string }> {
|
): Promise<ApiResponse<{ success: boolean; message: string; backup?: string }>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/reset`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/reset`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getAuthHeaders()
|
headers: getAuthHeaders()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; message: string; backup?: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '重置配置失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -669,16 +640,11 @@ export async function resetPluginConfig(
|
|||||||
*/
|
*/
|
||||||
export async function togglePlugin(
|
export async function togglePlugin(
|
||||||
pluginId: string
|
pluginId: string
|
||||||
): Promise<{ success: boolean; enabled: boolean; message: string; note?: string }> {
|
): Promise<ApiResponse<{ success: boolean; enabled: boolean; message: string; note?: string }>> {
|
||||||
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/toggle`, {
|
const response = await fetchWithAuth(`/api/webui/plugins/config/${pluginId}/toggle`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getAuthHeaders()
|
headers: getAuthHeaders()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
return await parseResponse<{ success: boolean; enabled: boolean; message: string; note?: string }>(response)
|
||||||
const error = await response.json()
|
|
||||||
throw new Error(error.detail || '切换状态失败')
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,16 +341,44 @@ function PluginConfigEditor({ plugin, onBack }: PluginConfigEditorProps) {
|
|||||||
const loadConfig = useCallback(async () => {
|
const loadConfig = useCallback(async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const [schemaData, configData, rawConfigData] = await Promise.all([
|
const [schemaResult, configResult, rawResult] = await Promise.all([
|
||||||
getPluginConfigSchema(plugin.id),
|
getPluginConfigSchema(plugin.id),
|
||||||
getPluginConfig(plugin.id),
|
getPluginConfig(plugin.id),
|
||||||
getPluginConfigRaw(plugin.id)
|
getPluginConfigRaw(plugin.id)
|
||||||
])
|
])
|
||||||
setSchema(schemaData)
|
|
||||||
setConfig(configData)
|
if (!schemaResult.success) {
|
||||||
setOriginalConfig(JSON.parse(JSON.stringify(configData)))
|
toast({
|
||||||
setSourceCode(rawConfigData)
|
title: '加载配置架构失败',
|
||||||
setOriginalSourceCode(rawConfigData)
|
description: schemaResult.error,
|
||||||
|
variant: 'destructive'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载配置数据失败',
|
||||||
|
description: configResult.error,
|
||||||
|
variant: 'destructive'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rawResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载原始配置失败',
|
||||||
|
description: rawResult.error,
|
||||||
|
variant: 'destructive'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setSchema(schemaResult.data)
|
||||||
|
setConfig(configResult.data)
|
||||||
|
setOriginalConfig(JSON.parse(JSON.stringify(configResult.data)))
|
||||||
|
setSourceCode(rawResult.data)
|
||||||
|
setOriginalSourceCode(rawResult.data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: '加载配置失败',
|
title: '加载配置失败',
|
||||||
@@ -433,7 +461,15 @@ function PluginConfigEditor({ plugin, onBack }: PluginConfigEditorProps) {
|
|||||||
// 重置配置
|
// 重置配置
|
||||||
const handleReset = async () => {
|
const handleReset = async () => {
|
||||||
try {
|
try {
|
||||||
await resetPluginConfig(plugin.id)
|
const resetResult = await resetPluginConfig(plugin.id)
|
||||||
|
if (!resetResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '重置失败',
|
||||||
|
description: resetResult.error,
|
||||||
|
variant: 'destructive'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
toast({
|
toast({
|
||||||
title: '配置已重置',
|
title: '配置已重置',
|
||||||
description: '下次加载插件时将使用默认配置'
|
description: '下次加载插件时将使用默认配置'
|
||||||
@@ -452,10 +488,18 @@ function PluginConfigEditor({ plugin, onBack }: PluginConfigEditorProps) {
|
|||||||
// 切换启用状态
|
// 切换启用状态
|
||||||
const handleToggle = async () => {
|
const handleToggle = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await togglePlugin(plugin.id)
|
const toggleResult = await togglePlugin(plugin.id)
|
||||||
|
if (!toggleResult.success) {
|
||||||
toast({
|
toast({
|
||||||
title: result.message,
|
title: '切换失败',
|
||||||
description: result.note
|
description: toggleResult.error,
|
||||||
|
variant: 'destructive'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toast({
|
||||||
|
title: toggleResult.data.message,
|
||||||
|
description: toggleResult.data.note
|
||||||
})
|
})
|
||||||
loadConfig()
|
loadConfig()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -723,8 +767,16 @@ function PluginConfigPageContent() {
|
|||||||
const loadPlugins = async () => {
|
const loadPlugins = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const data = await getInstalledPlugins()
|
const installedResult = await getInstalledPlugins()
|
||||||
setPlugins(data)
|
if (!installedResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '加载插件列表失败',
|
||||||
|
description: installedResult.error,
|
||||||
|
variant: 'destructive'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setPlugins(installedResult.data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: '加载插件列表失败',
|
title: '加载插件列表失败',
|
||||||
|
|||||||
@@ -131,10 +131,37 @@ export function PluginDetailPage() {
|
|||||||
getInstalledPlugins(),
|
getInstalledPlugins(),
|
||||||
])
|
])
|
||||||
|
|
||||||
setGitStatus(gitStatusResult)
|
if (!gitStatusResult.success) {
|
||||||
setMaimaiVersion(versionResult)
|
toast({
|
||||||
setIsInstalled(checkPluginInstalled(search.pluginId, installedPlugins))
|
title: 'Git 状态检查失败',
|
||||||
setInstalledVersion(getInstalledPluginVersion(search.pluginId, installedPlugins))
|
description: gitStatusResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setGitStatus(gitStatusResult.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!versionResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '版本获取失败',
|
||||||
|
description: versionResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setMaimaiVersion(versionResult.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!installedPlugins.success) {
|
||||||
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedPlugins.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsInstalled(checkPluginInstalled(search.pluginId, installedPlugins.data))
|
||||||
|
setInstalledVersion(getInstalledPluginVersion(search.pluginId, installedPlugins.data))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : '加载失败')
|
setError(err instanceof Error ? err.message : '加载失败')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -243,7 +270,16 @@ export function PluginDetailPage() {
|
|||||||
try {
|
try {
|
||||||
setOperating(true)
|
setOperating(true)
|
||||||
|
|
||||||
await installPlugin(plugin.id, plugin.manifest.repository_url || '', 'main')
|
const installResult = await installPlugin(plugin.id, plugin.manifest.repository_url || '', 'main')
|
||||||
|
|
||||||
|
if (!installResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '安装失败',
|
||||||
|
description: installResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 记录下载统计
|
// 记录下载统计
|
||||||
recordPluginDownload(plugin.id).catch((err) => {
|
recordPluginDownload(plugin.id).catch((err) => {
|
||||||
@@ -256,9 +292,17 @@ export function PluginDetailPage() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 重新加载安装状态
|
// 重新加载安装状态
|
||||||
const installedPlugins = await getInstalledPlugins()
|
const installedPluginsResult = await getInstalledPlugins()
|
||||||
setIsInstalled(checkPluginInstalled(plugin.id, installedPlugins))
|
if (!installedPluginsResult.success) {
|
||||||
setInstalledVersion(getInstalledPluginVersion(plugin.id, installedPlugins))
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedPluginsResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsInstalled(checkPluginInstalled(plugin.id, installedPluginsResult.data))
|
||||||
|
setInstalledVersion(getInstalledPluginVersion(plugin.id, installedPluginsResult.data))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: '安装失败',
|
title: '安装失败',
|
||||||
@@ -277,7 +321,16 @@ export function PluginDetailPage() {
|
|||||||
try {
|
try {
|
||||||
setOperating(true)
|
setOperating(true)
|
||||||
|
|
||||||
await uninstallPlugin(plugin.id)
|
const uninstallResult = await uninstallPlugin(plugin.id)
|
||||||
|
|
||||||
|
if (!uninstallResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '卸载失败',
|
||||||
|
description: uninstallResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: '卸载成功',
|
title: '卸载成功',
|
||||||
@@ -285,9 +338,17 @@ export function PluginDetailPage() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 重新加载安装状态
|
// 重新加载安装状态
|
||||||
const installedPlugins = await getInstalledPlugins()
|
const installedPluginsResult = await getInstalledPlugins()
|
||||||
setIsInstalled(checkPluginInstalled(plugin.id, installedPlugins))
|
if (!installedPluginsResult.success) {
|
||||||
setInstalledVersion(getInstalledPluginVersion(plugin.id, installedPlugins))
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedPluginsResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsInstalled(checkPluginInstalled(plugin.id, installedPluginsResult.data))
|
||||||
|
setInstalledVersion(getInstalledPluginVersion(plugin.id, installedPluginsResult.data))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: '卸载失败',
|
title: '卸载失败',
|
||||||
@@ -306,17 +367,34 @@ export function PluginDetailPage() {
|
|||||||
try {
|
try {
|
||||||
setOperating(true)
|
setOperating(true)
|
||||||
|
|
||||||
const result = await updatePlugin(plugin.id, plugin.manifest.repository_url || '', 'main')
|
const updateResult = await updatePlugin(plugin.id, plugin.manifest.repository_url || '', 'main')
|
||||||
|
|
||||||
|
if (!updateResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '更新失败',
|
||||||
|
description: updateResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: '更新成功',
|
title: '更新成功',
|
||||||
description: `${plugin.manifest.name} 已从 ${result.old_version} 更新到 ${result.new_version}`,
|
description: `${plugin.manifest.name} 已从 ${updateResult.data.old_version} 更新到 ${updateResult.data.new_version}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重新加载安装状态
|
// 重新加载安装状态
|
||||||
const installedPlugins = await getInstalledPlugins()
|
const installedPluginsResult = await getInstalledPlugins()
|
||||||
setIsInstalled(checkPluginInstalled(plugin.id, installedPlugins))
|
if (!installedPluginsResult.success) {
|
||||||
setInstalledVersion(getInstalledPluginVersion(plugin.id, installedPlugins))
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedPluginsResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsInstalled(checkPluginInstalled(plugin.id, installedPluginsResult.data))
|
||||||
|
setInstalledVersion(getInstalledPluginVersion(plugin.id, installedPluginsResult.data))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: '更新失败',
|
title: '更新失败',
|
||||||
|
|||||||
@@ -180,34 +180,71 @@ function PluginsPageContent() {
|
|||||||
|
|
||||||
// 3. 检查 Git 状态
|
// 3. 检查 Git 状态
|
||||||
if (!isUnmounted) {
|
if (!isUnmounted) {
|
||||||
const status = await checkGitStatus()
|
const statusResult = await checkGitStatus()
|
||||||
setGitStatus(status)
|
if (!statusResult.success) {
|
||||||
|
|
||||||
if (!status.installed) {
|
|
||||||
toast({
|
toast({
|
||||||
title: 'Git 未安装',
|
title: 'Git 状态检查失败',
|
||||||
description: status.error || '请先安装 Git 才能使用插件安装功能',
|
description: statusResult.error,
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
})
|
})
|
||||||
|
setGitStatus({ installed: false, error: statusResult.error })
|
||||||
|
} else {
|
||||||
|
setGitStatus(statusResult.data)
|
||||||
|
|
||||||
|
if (!statusResult.data.installed) {
|
||||||
|
toast({
|
||||||
|
title: 'Git 未安装',
|
||||||
|
description: statusResult.data.error || '请先安装 Git 才能使用插件安装功能',
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 获取麦麦版本
|
// 4. 获取麦麦版本
|
||||||
if (!isUnmounted) {
|
if (!isUnmounted) {
|
||||||
const version = await getMaimaiVersion()
|
const versionResult = await getMaimaiVersion()
|
||||||
setMaimaiVersion(version)
|
if (!versionResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '版本获取失败',
|
||||||
|
description: versionResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setMaimaiVersion(versionResult.data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 加载插件列表(包含已安装信息)
|
// 5. 加载插件列表(包含已安装信息)
|
||||||
if (!isUnmounted) {
|
if (!isUnmounted) {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
const data = await fetchPluginList()
|
const apiResult = await fetchPluginList()
|
||||||
|
if (!apiResult.success) {
|
||||||
|
if (!isUnmounted) {
|
||||||
|
setError(apiResult.error)
|
||||||
|
toast({
|
||||||
|
title: '加载失败',
|
||||||
|
description: apiResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = apiResult.data
|
||||||
|
|
||||||
if (!isUnmounted) {
|
if (!isUnmounted) {
|
||||||
// 获取已安装插件列表
|
// 获取已安装插件列表
|
||||||
const installed = await getInstalledPlugins()
|
const installedResult = await getInstalledPlugins()
|
||||||
|
if (!installedResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const installed = installedResult.data
|
||||||
setInstalledPlugins(installed)
|
setInstalledPlugins(installed)
|
||||||
|
|
||||||
// 将已安装信息合并到插件数据中
|
// 将已安装信息合并到插件数据中
|
||||||
@@ -261,16 +298,6 @@ function PluginsPageContent() {
|
|||||||
// 6. 加载所有插件的统计数据
|
// 6. 加载所有插件的统计数据
|
||||||
loadPluginStats(mergedData)
|
loadPluginStats(mergedData)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
if (!isUnmounted) {
|
|
||||||
const errorMessage = err instanceof Error ? err.message : '加载插件列表失败'
|
|
||||||
setError(errorMessage)
|
|
||||||
toast({
|
|
||||||
title: '加载失败',
|
|
||||||
description: errorMessage,
|
|
||||||
variant: 'destructive',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
if (!isUnmounted) {
|
if (!isUnmounted) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -463,12 +490,21 @@ function PluginsPageContent() {
|
|||||||
try {
|
try {
|
||||||
setInstallDialogOpen(false)
|
setInstallDialogOpen(false)
|
||||||
|
|
||||||
await installPlugin(
|
const installResult = await installPlugin(
|
||||||
installingPlugin.id,
|
installingPlugin.id,
|
||||||
installingPlugin.manifest.repository_url || '',
|
installingPlugin.manifest.repository_url || '',
|
||||||
branch
|
branch
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!installResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '安装失败',
|
||||||
|
description: installResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 记录下载统计
|
// 记录下载统计
|
||||||
recordPluginDownload(installingPlugin.id).catch(err => {
|
recordPluginDownload(installingPlugin.id).catch(err => {
|
||||||
console.warn('Failed to record download:', err)
|
console.warn('Failed to record download:', err)
|
||||||
@@ -480,7 +516,16 @@ function PluginsPageContent() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 重新加载已安装插件列表
|
// 重新加载已安装插件列表
|
||||||
const installed = await getInstalledPlugins()
|
const installedResult = await getInstalledPlugins()
|
||||||
|
if (!installedResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const installed = installedResult.data
|
||||||
setInstalledPlugins(installed)
|
setInstalledPlugins(installed)
|
||||||
|
|
||||||
// 重新合并已安装信息到插件列表
|
// 重新合并已安装信息到插件列表
|
||||||
@@ -513,7 +558,16 @@ function PluginsPageContent() {
|
|||||||
// 卸载插件处理
|
// 卸载插件处理
|
||||||
const handleUninstall = async (plugin: PluginInfo) => {
|
const handleUninstall = async (plugin: PluginInfo) => {
|
||||||
try {
|
try {
|
||||||
await uninstallPlugin(plugin.id)
|
const uninstallResult = await uninstallPlugin(plugin.id)
|
||||||
|
|
||||||
|
if (!uninstallResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '卸载失败',
|
||||||
|
description: uninstallResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: '卸载成功',
|
title: '卸载成功',
|
||||||
@@ -521,7 +575,16 @@ function PluginsPageContent() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 重新加载已安装插件列表
|
// 重新加载已安装插件列表
|
||||||
const installed = await getInstalledPlugins()
|
const installedResult = await getInstalledPlugins()
|
||||||
|
if (!installedResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const installed = installedResult.data
|
||||||
setInstalledPlugins(installed)
|
setInstalledPlugins(installed)
|
||||||
|
|
||||||
// 重新合并已安装信息到插件列表
|
// 重新合并已安装信息到插件列表
|
||||||
@@ -561,19 +624,37 @@ function PluginsPageContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await updatePlugin(
|
const updateResult = await updatePlugin(
|
||||||
plugin.id,
|
plugin.id,
|
||||||
plugin.manifest.repository_url || '',
|
plugin.manifest.repository_url || '',
|
||||||
'main'
|
'main'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!updateResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '更新失败',
|
||||||
|
description: updateResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: '更新成功',
|
title: '更新成功',
|
||||||
description: `${plugin.manifest.name} 已从 ${result.old_version} 更新到 ${result.new_version}`,
|
description: `${plugin.manifest.name} 已从 ${updateResult.data.old_version} 更新到 ${updateResult.data.new_version}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重新加载已安装插件列表
|
// 重新加载已安装插件列表
|
||||||
const installed = await getInstalledPlugins()
|
const installedResult = await getInstalledPlugins()
|
||||||
|
if (!installedResult.success) {
|
||||||
|
toast({
|
||||||
|
title: '获取已安装插件失败',
|
||||||
|
description: installedResult.error,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const installed = installedResult.data
|
||||||
setInstalledPlugins(installed)
|
setInstalledPlugins(installed)
|
||||||
|
|
||||||
// 重新合并已安装信息到插件列表
|
// 重新合并已安装信息到插件列表
|
||||||
|
|||||||
Reference in New Issue
Block a user