fix(platform): 代码审查整改——反代按域拆分授权、根除探测副作用与死代码清理
- YARP: map-edit/ai-config 全方法、reflection 写方法挂 PlatformScope, reflection/selection 单独放行(运营端 3D 高亮),堵住运营账号直达地图编辑/反射调用 - goto-site 探测改用不存在的 car/-1(消除健康检查真实派车风险)并加 60s 缓存 - Config PUT 按 scope 收紧:RCSMonitor 仅可写 ops 节;wizard 写操作与 simplelite/restart-for-update 限 PlatformScope;/api/health 去除虚假端口表 - 修复 wms 模块菜单裁剪失效(admin-config-location → admin-config-facility) - vrHost 默认 location.hostname:8223(新增 utils/vrender.ts),远程访问 3D 视口可用 - /status 页改接真实 /api/health* 诊断;uploadAsset 移除矛盾 multipart 头; mapsApi.merge 对齐 save 的 409 冲突处理;JWT 验签参数改启动期 DI 一次性配置 - 清理死代码:ProjectionController、DataTablePro、useClipboard、CadToolbarView、 AppShell 未用导入;lint 脚本替换为 typecheck;日志窗口 List 改 Queue
This commit is contained in:
@@ -6,7 +6,7 @@ using MiGu.Server.Configs;
|
||||
namespace MiGu.Server.Controllers;
|
||||
|
||||
// AR-4: 全 class 加 [Authorize] —— 替代会话21 点名的「ConfigController 无鉴权 PUT 任意 section」漏洞。
|
||||
// GET (List/Get) 只要登录就放;PUT 强制 PlatformScope,避免运营人员误改业务配置。
|
||||
// GET (List/Get) 只要登录就放;PUT 按 scope 收紧:Platform 任意节,RCSMonitor 仅 ops 白名单。
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/config")]
|
||||
@@ -47,15 +47,24 @@ public class ConfigController : ControllerBase
|
||||
});
|
||||
}
|
||||
|
||||
// 配置中心页面已有 PermissionGuard;此处仅要求登录即可保存,避免 RCSMonitor scope
|
||||
// 账号在特殊场景下无法写入 ops.monitor(地图监控动作)备份字段。
|
||||
/// <summary>RCSMonitor scope 允许写入的 section 白名单(地图监控动作备份等运营自有配置)。</summary>
|
||||
private static readonly string[] MonitorWritableSections = { "ops" };
|
||||
|
||||
// Platform scope 可写任意 section;RCSMonitor 仅允许写 ops(保留运营端
|
||||
// 「地图监控动作 ops.monitor 备份」既有功能),其余 section(routing/auth/system 等)一律 403。
|
||||
[HttpPut("{section}")]
|
||||
[Authorize]
|
||||
public IActionResult Put(string section, [FromBody] JsonElement payload)
|
||||
{
|
||||
if (!ConfigStore.AllSections.Contains(section, StringComparer.OrdinalIgnoreCase))
|
||||
return NotFound(new { message = $"未知 section: {section}" });
|
||||
|
||||
var scope = User.FindFirst("scope")?.Value;
|
||||
if (!string.Equals(scope, "Platform", StringComparison.OrdinalIgnoreCase)
|
||||
&& !MonitorWritableSections.Contains(section, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return StatusCode(403, new { message = $"当前账号无权修改配置节 {section}(需要 Platform 管理端权限)" });
|
||||
}
|
||||
|
||||
var env = _store.Put(section, payload);
|
||||
return Ok(new
|
||||
{
|
||||
|
||||
@@ -13,39 +13,33 @@ public class HealthController : ControllerBase
|
||||
|
||||
public HealthController(SimpleLiteLauncher launcher) => _launcher = launcher;
|
||||
|
||||
/// <summary>匿名存活探针:仅返回进程级状态,不暴露端口拓扑等部署细节。</summary>
|
||||
[HttpGet]
|
||||
public IActionResult Get()
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
status = "ok",
|
||||
mode = "WebEnabled",
|
||||
startTime = StartTime,
|
||||
uptimeSec = (long)(DateTimeOffset.UtcNow - StartTime).TotalSeconds,
|
||||
ports = new
|
||||
{
|
||||
webApi = 7001,
|
||||
webSocket = 7002,
|
||||
platform = 8080,
|
||||
vrender = 8223,
|
||||
vehicle = 8222
|
||||
},
|
||||
architecture = "v1.5"
|
||||
uptimeSec = (long)(DateTimeOffset.UtcNow - StartTime).TotalSeconds
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SimpleLite 拉起配置诊断:查看当前 ExecutablePath、解析结果、端口是否已有服务。
|
||||
/// 配置位置:<c>MiGu.Server/appsettings.json</c> → <c>SimpleLite</c> 节点。
|
||||
/// 含服务器本地路径等敏感信息,要求登录。
|
||||
/// </summary>
|
||||
[HttpGet("simplelite")]
|
||||
[Authorize]
|
||||
public IActionResult GetSimpleLiteDiagnostics() => Ok(_launcher.GetDiagnostics());
|
||||
|
||||
/// <summary>
|
||||
/// 关闭 SimpleLite、同步最新 DLL、重新拉起。用于「前往站点」API 缺失时一键更新。
|
||||
/// 会终止本机全部 SimpleLite 进程并重启,仅 Platform 管理端可调。
|
||||
/// </summary>
|
||||
[HttpPost("simplelite/restart-for-update")]
|
||||
[Authorize]
|
||||
[Authorize(Policy = "PlatformScope")]
|
||||
public IActionResult RestartSimpleLiteForUpdate([FromQuery] string launchMode = "webonly")
|
||||
{
|
||||
var result = _launcher.RestartForUpdate(launchMode);
|
||||
|
||||
@@ -747,9 +747,9 @@ public sealed class LogsController : ControllerBase
|
||||
}
|
||||
b.Latest = e.Content;
|
||||
b.LatestTime = e.Time;
|
||||
b.Recent.Add(e);
|
||||
// 仅保留最近 maxPerTag 条,避免高频标签把内存撑爆。
|
||||
if (b.Recent.Count > maxPerTag) b.Recent.RemoveAt(0);
|
||||
b.Recent.Enqueue(e);
|
||||
// 仅保留最近 maxPerTag 条,避免高频标签把内存撑爆(Queue 头部出队 O(1))。
|
||||
if (b.Recent.Count > maxPerTag) b.Recent.Dequeue();
|
||||
}
|
||||
|
||||
private static object ToBookDto(Book b) => new
|
||||
@@ -831,6 +831,6 @@ public sealed class LogsController : ControllerBase
|
||||
public DateTime? LastTime { get; set; }
|
||||
public string Latest { get; set; } = "";
|
||||
public DateTime? LatestTime { get; set; }
|
||||
public List<LogEntry> Recent { get; } = new();
|
||||
public Queue<LogEntry> Recent { get; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +59,11 @@ public class MapsContentController : ControllerBase
|
||||
return NotFound(new { message = $"地图文件不存在:{fileName}" });
|
||||
|
||||
var content = await System.IO.File.ReadAllTextAsync(fullPath, ct);
|
||||
// 不返回 fullPath:避免向前端泄露服务器目录结构。
|
||||
return Ok(new
|
||||
{
|
||||
name,
|
||||
fileName,
|
||||
path = fullPath,
|
||||
content
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MiGu.Server.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 投影 API 占位:真实落地时由 YARP 反代到 SimpleLite WebAPI 的 /api/projection/* 路径。
|
||||
/// 本地 Mock 数据仅用于无 SimpleLite 运行时的开发联调。
|
||||
///
|
||||
/// AR-4: 全 class 加 [Authorize] —— 任何登录用户都能读 mock 投影数据;未登录直接 401。
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/projection")]
|
||||
public class ProjectionController : ControllerBase
|
||||
{
|
||||
[HttpGet("sites")]
|
||||
public IActionResult Sites() => Ok(new[]
|
||||
{
|
||||
new { id = "S001", name = "A 区-入库点", x = 1000, y = 2000 },
|
||||
new { id = "S002", name = "A 区-出库点", x = 3000, y = 2000 },
|
||||
new { id = "S003", name = "B 区-缓存区", x = 5000, y = 2000 }
|
||||
});
|
||||
|
||||
[HttpGet("tracks")]
|
||||
public IActionResult Tracks() => Ok(new[]
|
||||
{
|
||||
new { id = "T001", kind = "line", fromSiteId = "S001", toSiteId = "S002" },
|
||||
new { id = "T002", kind = "line", fromSiteId = "S002", toSiteId = "S003" }
|
||||
});
|
||||
|
||||
[HttpGet("cars")]
|
||||
public IActionResult Cars() => Ok(new[]
|
||||
{
|
||||
new { id = "C01", name = "AGV-001", state = "running", batterySoc = 0.86 },
|
||||
new { id = "C02", name = "AGV-002", state = "idle", batterySoc = 0.42 }
|
||||
});
|
||||
|
||||
[HttpGet("missions")]
|
||||
public IActionResult Missions() => Ok(new[]
|
||||
{
|
||||
new { id = "M01", name = "A 区送料 #1", status = "running", priority = 50 },
|
||||
new { id = "M02", name = "A→B 缓存搬运", status = "queued", priority = 60 }
|
||||
});
|
||||
}
|
||||
@@ -12,8 +12,8 @@ namespace MiGu.Server.Controllers;
|
||||
/// <list type="bullet">
|
||||
/// <item><c>GET /api/wizard/options</c>:可选项目录(导航方式 / 模块 / 业务场景模板)。</item>
|
||||
/// <item><c>GET /api/wizard/profile</c>:回显当前部署画像(含由导航选型推导的激活场景 id)。</item>
|
||||
/// <item><c>PUT /api/wizard/profile</c>:保存并置 <c>Configured=true</c>。</item>
|
||||
/// <item><c>POST /api/wizard/reset</c>:把 <c>Configured</c> 置回 false 以重新引导(保留草稿)。</item>
|
||||
/// <item><c>PUT /api/wizard/profile</c>:保存并置 <c>Configured=true</c>(仅 Platform scope)。</item>
|
||||
/// <item><c>POST /api/wizard/reset</c>:把 <c>Configured</c> 置回 false 以重新引导(仅 Platform scope)。</item>
|
||||
/// </list>
|
||||
/// 说明:保存时即把选型固化为单一事实来源 <c>deployment</c> section,并同步联动 Launcher ——
|
||||
/// <see cref="SaveProfile"/> 调 <c>WriteActiveScenes</c> 写 <c>plugins/active-scenes.json</c> / 透传 <c>--scenes</c>,
|
||||
@@ -64,6 +64,7 @@ public class WizardController : ControllerBase
|
||||
}
|
||||
|
||||
[HttpPut("profile")]
|
||||
[Authorize(Policy = "PlatformScope")]
|
||||
public IActionResult SaveProfile([FromBody] SaveWizardRequest req)
|
||||
{
|
||||
if (req == null)
|
||||
@@ -92,6 +93,7 @@ public class WizardController : ControllerBase
|
||||
}
|
||||
|
||||
[HttpPost("reset")]
|
||||
[Authorize(Policy = "PlatformScope")]
|
||||
public IActionResult Reset()
|
||||
{
|
||||
var reset = _store.GetDeployment() with { Configured = false };
|
||||
|
||||
Reference in New Issue
Block a user