feat(plugin): 集成 WASM 插件系统到主服务并修复链路问题
- 新增 erp-plugin crate:插件管理、WASM 运行时、动态表、数据 CRUD - 新增前端插件管理页面(PluginAdmin/PluginCRUDPage)和 API 层 - 新增插件数据迁移(plugins/plugin_entities/plugin_event_subscriptions) - 新增权限补充迁移(为已有租户补充 plugin.admin/plugin.list 权限) - 修复 PluginAdmin 页面 InstallOutlined 图标不存在的崩溃问题 - 修复 settings 唯一索引迁移顺序错误(先去重再建索引) - 更新 wiki 和 CLAUDE.md 反映插件系统集成状态 - 新增 dev.ps1 一键启动脚本
This commit is contained in:
71
apps/web/src/api/pluginData.ts
Normal file
71
apps/web/src/api/pluginData.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import client from './client';
|
||||
|
||||
export interface PluginDataRecord {
|
||||
id: string;
|
||||
data: Record<string, unknown>;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
version?: number;
|
||||
}
|
||||
|
||||
interface PaginatedDataResponse {
|
||||
data: PluginDataRecord[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
total_pages: number;
|
||||
}
|
||||
|
||||
export async function listPluginData(
|
||||
pluginId: string,
|
||||
entity: string,
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
) {
|
||||
const { data } = await client.get<{ success: boolean; data: PaginatedDataResponse }>(
|
||||
`/plugins/${pluginId}/${entity}`,
|
||||
{ params: { page, page_size: pageSize } },
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function getPluginData(pluginId: string, entity: string, id: string) {
|
||||
const { data } = await client.get<{ success: boolean; data: PluginDataRecord }>(
|
||||
`/plugins/${pluginId}/${entity}/${id}`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function createPluginData(
|
||||
pluginId: string,
|
||||
entity: string,
|
||||
recordData: Record<string, unknown>,
|
||||
) {
|
||||
const { data } = await client.post<{ success: boolean; data: PluginDataRecord }>(
|
||||
`/plugins/${pluginId}/${entity}`,
|
||||
{ data: recordData },
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function updatePluginData(
|
||||
pluginId: string,
|
||||
entity: string,
|
||||
id: string,
|
||||
recordData: Record<string, unknown>,
|
||||
version: number,
|
||||
) {
|
||||
const { data } = await client.put<{ success: boolean; data: PluginDataRecord }>(
|
||||
`/plugins/${pluginId}/${entity}/${id}`,
|
||||
{ data: recordData, version },
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function deletePluginData(
|
||||
pluginId: string,
|
||||
entity: string,
|
||||
id: string,
|
||||
) {
|
||||
await client.delete(`/plugins/${pluginId}/${entity}/${id}`);
|
||||
}
|
||||
121
apps/web/src/api/plugins.ts
Normal file
121
apps/web/src/api/plugins.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import client from './client';
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
total_pages: number;
|
||||
}
|
||||
|
||||
export interface PluginEntityInfo {
|
||||
name: string;
|
||||
display_name: string;
|
||||
table_name: string;
|
||||
}
|
||||
|
||||
export interface PluginPermissionInfo {
|
||||
code: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export type PluginStatus = 'uploaded' | 'installed' | 'enabled' | 'running' | 'disabled' | 'uninstalled';
|
||||
|
||||
export interface PluginInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
author?: string;
|
||||
status: PluginStatus;
|
||||
config: Record<string, unknown>;
|
||||
installed_at?: string;
|
||||
enabled_at?: string;
|
||||
entities: PluginEntityInfo[];
|
||||
permissions?: PluginPermissionInfo[];
|
||||
record_version: number;
|
||||
}
|
||||
|
||||
export async function listPlugins(page = 1, pageSize = 20, status?: string) {
|
||||
const { data } = await client.get<{ success: boolean; data: PaginatedResponse<PluginInfo> }>(
|
||||
'/admin/plugins',
|
||||
{ params: { page, page_size: pageSize, status: status || undefined } },
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function getPlugin(id: string) {
|
||||
const { data } = await client.get<{ success: boolean; data: PluginInfo }>(
|
||||
`/admin/plugins/${id}`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function uploadPlugin(wasmFile: File, manifestToml: string) {
|
||||
const formData = new FormData();
|
||||
formData.append('wasm', wasmFile);
|
||||
formData.append('manifest', manifestToml);
|
||||
|
||||
const { data } = await client.post<{ success: boolean; data: PluginInfo }>(
|
||||
'/admin/plugins/upload',
|
||||
formData,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' }, timeout: 60000 },
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function installPlugin(id: string) {
|
||||
const { data } = await client.post<{ success: boolean; data: PluginInfo }>(
|
||||
`/admin/plugins/${id}/install`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function enablePlugin(id: string) {
|
||||
const { data } = await client.post<{ success: boolean; data: PluginInfo }>(
|
||||
`/admin/plugins/${id}/enable`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function disablePlugin(id: string) {
|
||||
const { data } = await client.post<{ success: boolean; data: PluginInfo }>(
|
||||
`/admin/plugins/${id}/disable`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function uninstallPlugin(id: string) {
|
||||
const { data } = await client.post<{ success: boolean; data: PluginInfo }>(
|
||||
`/admin/plugins/${id}/uninstall`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function purgePlugin(id: string) {
|
||||
await client.delete(`/admin/plugins/${id}`);
|
||||
}
|
||||
|
||||
export async function getPluginHealth(id: string) {
|
||||
const { data } = await client.get<{
|
||||
success: boolean;
|
||||
data: { plugin_id: string; status: string; details: Record<string, unknown> };
|
||||
}>(`/admin/plugins/${id}/health`);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function updatePluginConfig(id: string, config: Record<string, unknown>, version: number) {
|
||||
const { data } = await client.put<{ success: boolean; data: PluginInfo }>(
|
||||
`/admin/plugins/${id}/config`,
|
||||
{ config, version },
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
|
||||
export async function getPluginSchema(id: string) {
|
||||
const { data } = await client.get<{ success: boolean; data: Record<string, unknown> }>(
|
||||
`/admin/plugins/${id}/schema`,
|
||||
);
|
||||
return data.data;
|
||||
}
|
||||
Reference in New Issue
Block a user