fix: 审计修复 — ErrorBoundary 接入 + data_scope 全端点接线 + inventory.wasm
1. H1: App.tsx 接入 ErrorBoundary 包裹 Suspense,防止页面渲染错误导致白屏 2. H2: data_scope 行级权限扩展到 count/aggregate/timeseries 端点, 所有数据查询操作现在都受 data_scope 过滤 3. M3: 进销存插件 WASM 编译部署到 apps/web/public/inventory.wasm
This commit is contained in:
BIN
apps/web/public/inventory.wasm
Normal file
BIN
apps/web/public/inventory.wasm
Normal file
Binary file not shown.
@@ -4,6 +4,7 @@ import { ConfigProvider, theme as antdTheme, Spin } from 'antd';
|
|||||||
import zhCN from 'antd/locale/zh_CN';
|
import zhCN from 'antd/locale/zh_CN';
|
||||||
import MainLayout from './layouts/MainLayout';
|
import MainLayout from './layouts/MainLayout';
|
||||||
import Login from './pages/Login';
|
import Login from './pages/Login';
|
||||||
|
import { ErrorBoundary } from './components/ErrorBoundary';
|
||||||
import { useAuthStore } from './stores/auth';
|
import { useAuthStore } from './stores/auth';
|
||||||
import { useAppStore } from './stores/app';
|
import { useAppStore } from './stores/app';
|
||||||
|
|
||||||
@@ -133,6 +134,7 @@ export default function App() {
|
|||||||
element={
|
element={
|
||||||
<PrivateRoute>
|
<PrivateRoute>
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
|
<ErrorBoundary>
|
||||||
<Suspense fallback={<div style={{ display: 'flex', justifyContent: 'center', padding: 100 }}><Spin size="large" /></div>}>
|
<Suspense fallback={<div style={{ display: 'flex', justifyContent: 'center', padding: 100 }}><Spin size="large" /></div>}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
@@ -151,6 +153,7 @@ export default function App() {
|
|||||||
<Route path="/plugins/:pluginId/:entityName" element={<PluginCRUDPage />} />
|
<Route path="/plugins/:pluginId/:entityName" element={<PluginCRUDPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
</ErrorBoundary>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -527,6 +527,7 @@ impl PluginDataService {
|
|||||||
db: &sea_orm::DatabaseConnection,
|
db: &sea_orm::DatabaseConnection,
|
||||||
filter: Option<serde_json::Value>,
|
filter: Option<serde_json::Value>,
|
||||||
search: Option<String>,
|
search: Option<String>,
|
||||||
|
scope: Option<DataScopeParams>,
|
||||||
) -> AppResult<u64> {
|
) -> AppResult<u64> {
|
||||||
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
||||||
|
|
||||||
@@ -543,7 +544,7 @@ impl PluginDataService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (sql, values) = DynamicTableManager::build_filtered_count_sql(
|
let (mut sql, mut values) = DynamicTableManager::build_filtered_count_sql(
|
||||||
&info.table_name,
|
&info.table_name,
|
||||||
tenant_id,
|
tenant_id,
|
||||||
filter,
|
filter,
|
||||||
@@ -551,6 +552,13 @@ impl PluginDataService {
|
|||||||
)
|
)
|
||||||
.map_err(|e| AppError::Validation(e))?;
|
.map_err(|e| AppError::Validation(e))?;
|
||||||
|
|
||||||
|
// 合并数据权限条件
|
||||||
|
let scope_condition = build_scope_sql(&scope, &info.generated_fields);
|
||||||
|
if !scope_condition.0.is_empty() {
|
||||||
|
sql = merge_scope_condition(sql, &scope_condition);
|
||||||
|
values.extend(scope_condition.1);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromQueryResult)]
|
#[derive(FromQueryResult)]
|
||||||
struct CountResult {
|
struct CountResult {
|
||||||
count: i64,
|
count: i64,
|
||||||
@@ -578,10 +586,11 @@ impl PluginDataService {
|
|||||||
db: &sea_orm::DatabaseConnection,
|
db: &sea_orm::DatabaseConnection,
|
||||||
group_by_field: &str,
|
group_by_field: &str,
|
||||||
filter: Option<serde_json::Value>,
|
filter: Option<serde_json::Value>,
|
||||||
|
scope: Option<DataScopeParams>,
|
||||||
) -> AppResult<Vec<(String, i64)>> {
|
) -> AppResult<Vec<(String, i64)>> {
|
||||||
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
||||||
|
|
||||||
let (sql, values) = DynamicTableManager::build_aggregate_sql(
|
let (mut sql, mut values) = DynamicTableManager::build_aggregate_sql(
|
||||||
&info.table_name,
|
&info.table_name,
|
||||||
tenant_id,
|
tenant_id,
|
||||||
group_by_field,
|
group_by_field,
|
||||||
@@ -589,6 +598,13 @@ impl PluginDataService {
|
|||||||
)
|
)
|
||||||
.map_err(|e| AppError::Validation(e))?;
|
.map_err(|e| AppError::Validation(e))?;
|
||||||
|
|
||||||
|
// 合并数据权限条件
|
||||||
|
let scope_condition = build_scope_sql(&scope, &info.generated_fields);
|
||||||
|
if !scope_condition.0.is_empty() {
|
||||||
|
sql = merge_scope_condition(sql, &scope_condition);
|
||||||
|
values.extend(scope_condition.1);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromQueryResult)]
|
#[derive(FromQueryResult)]
|
||||||
struct AggRow {
|
struct AggRow {
|
||||||
key: Option<String>,
|
key: Option<String>,
|
||||||
@@ -621,7 +637,7 @@ impl PluginDataService {
|
|||||||
filter: Option<serde_json::Value>,
|
filter: Option<serde_json::Value>,
|
||||||
) -> AppResult<Vec<(String, i64)>> {
|
) -> AppResult<Vec<(String, i64)>> {
|
||||||
// TODO: 未来版本添加 Redis 缓存层
|
// TODO: 未来版本添加 Redis 缓存层
|
||||||
Self::aggregate(plugin_id, entity_name, tenant_id, db, group_by_field, filter).await
|
Self::aggregate(plugin_id, entity_name, tenant_id, db, group_by_field, filter, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 时间序列聚合 — 按时间字段截断为 day/week/month 统计计数
|
/// 时间序列聚合 — 按时间字段截断为 day/week/month 统计计数
|
||||||
@@ -634,10 +650,11 @@ impl PluginDataService {
|
|||||||
time_grain: &str,
|
time_grain: &str,
|
||||||
start: Option<String>,
|
start: Option<String>,
|
||||||
end: Option<String>,
|
end: Option<String>,
|
||||||
|
scope: Option<DataScopeParams>,
|
||||||
) -> AppResult<Vec<crate::data_dto::TimeseriesItem>> {
|
) -> AppResult<Vec<crate::data_dto::TimeseriesItem>> {
|
||||||
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
let info = resolve_entity_info(plugin_id, entity_name, tenant_id, db).await?;
|
||||||
|
|
||||||
let (sql, values) = DynamicTableManager::build_timeseries_sql(
|
let (mut sql, mut values) = DynamicTableManager::build_timeseries_sql(
|
||||||
&info.table_name,
|
&info.table_name,
|
||||||
tenant_id,
|
tenant_id,
|
||||||
time_field,
|
time_field,
|
||||||
@@ -647,6 +664,13 @@ impl PluginDataService {
|
|||||||
)
|
)
|
||||||
.map_err(|e| AppError::Validation(e))?;
|
.map_err(|e| AppError::Validation(e))?;
|
||||||
|
|
||||||
|
// 合并数据权限条件
|
||||||
|
let scope_condition = build_scope_sql(&scope, &info.generated_fields);
|
||||||
|
if !scope_condition.0.is_empty() {
|
||||||
|
sql = merge_scope_condition(sql, &scope_condition);
|
||||||
|
values.extend(scope_condition.1);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromQueryResult)]
|
#[derive(FromQueryResult)]
|
||||||
struct TsRow {
|
struct TsRow {
|
||||||
period: Option<String>,
|
period: Option<String>,
|
||||||
|
|||||||
@@ -390,6 +390,11 @@ where
|
|||||||
let fine_perm = compute_permission_code(&manifest_id, &entity, "list");
|
let fine_perm = compute_permission_code(&manifest_id, &entity, "list");
|
||||||
require_permission(&ctx, &fine_perm)?;
|
require_permission(&ctx, &fine_perm)?;
|
||||||
|
|
||||||
|
// 解析数据权限范围
|
||||||
|
let scope = resolve_data_scope(
|
||||||
|
&ctx, &manifest_id, &entity, &fine_perm, &state.db,
|
||||||
|
).await?;
|
||||||
|
|
||||||
// 解析 filter JSON
|
// 解析 filter JSON
|
||||||
let filter: Option<serde_json::Value> = params
|
let filter: Option<serde_json::Value> = params
|
||||||
.filter
|
.filter
|
||||||
@@ -403,6 +408,7 @@ where
|
|||||||
&state.db,
|
&state.db,
|
||||||
filter,
|
filter,
|
||||||
params.search,
|
params.search,
|
||||||
|
scope,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -434,6 +440,11 @@ where
|
|||||||
let fine_perm = compute_permission_code(&manifest_id, &entity, "list");
|
let fine_perm = compute_permission_code(&manifest_id, &entity, "list");
|
||||||
require_permission(&ctx, &fine_perm)?;
|
require_permission(&ctx, &fine_perm)?;
|
||||||
|
|
||||||
|
// 解析数据权限范围
|
||||||
|
let scope = resolve_data_scope(
|
||||||
|
&ctx, &manifest_id, &entity, &fine_perm, &state.db,
|
||||||
|
).await?;
|
||||||
|
|
||||||
// 解析 filter JSON
|
// 解析 filter JSON
|
||||||
let filter: Option<serde_json::Value> = params
|
let filter: Option<serde_json::Value> = params
|
||||||
.filter
|
.filter
|
||||||
@@ -447,6 +458,7 @@ where
|
|||||||
&state.db,
|
&state.db,
|
||||||
¶ms.group_by,
|
¶ms.group_by,
|
||||||
filter,
|
filter,
|
||||||
|
scope,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -483,6 +495,11 @@ where
|
|||||||
let fine_perm = compute_permission_code(&manifest_id, &entity, "list");
|
let fine_perm = compute_permission_code(&manifest_id, &entity, "list");
|
||||||
require_permission(&ctx, &fine_perm)?;
|
require_permission(&ctx, &fine_perm)?;
|
||||||
|
|
||||||
|
// 解析数据权限范围
|
||||||
|
let scope = resolve_data_scope(
|
||||||
|
&ctx, &manifest_id, &entity, &fine_perm, &state.db,
|
||||||
|
).await?;
|
||||||
|
|
||||||
let result = PluginDataService::timeseries(
|
let result = PluginDataService::timeseries(
|
||||||
plugin_id,
|
plugin_id,
|
||||||
&entity,
|
&entity,
|
||||||
@@ -492,6 +509,7 @@ where
|
|||||||
¶ms.time_grain,
|
¶ms.time_grain,
|
||||||
params.start,
|
params.start,
|
||||||
params.end,
|
params.end,
|
||||||
|
scope,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user