feat: 迁入 MiGu.Server、平台前端与车辆列表 reflection 回退
从 Simple-FR 拆出 Platform.Server 并重命名为 MiGu.Server;frontends 源码与构建脚本迁入本仓库。地图监控在 projection/cars 失败或为空时回退 reflection 车辆列表;Simple 仓库已移除旧 Platform.Server。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MiGu.Server.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 运维白名单网关(占位)。真实落地时按架构 §5.1 + §10.5:
|
||||
/// - 校验 scope=RCSMonitor 是否允许该 op;
|
||||
/// - YARP 转发到 SimpleLite /api/ops/*;
|
||||
/// - 写 OpsAuditLog(simple_main.db) + 写 OpsLogs(platform.db)。
|
||||
///
|
||||
/// AR-4: 全 class 加 [Authorize] —— 至少要求登录,再按 op 白名单 + JWT ops claim 双校验。
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/sl/ops")]
|
||||
public class OpsController : ControllerBase
|
||||
{
|
||||
public record ExecuteRequest(string OpCode, string TargetId, string? Reason, string? IdempotencyKey);
|
||||
public record ExecuteResponse(bool Ok, string AuditId, string? Message);
|
||||
public record AuditEntry(string Id, DateTimeOffset Ts, string User, string Scope, string OpCode, string Target, string Result, string? Message);
|
||||
|
||||
private static readonly ConcurrentQueue<AuditEntry> Audits = new();
|
||||
private static long _seq = 0;
|
||||
|
||||
private static readonly HashSet<string> Whitelist = new(StringComparer.Ordinal)
|
||||
{
|
||||
"ops.car.pause", "ops.car.resume", "ops.car.gohome", "ops.car.resetSession",
|
||||
"ops.car.manualCharge", "ops.task.pause", "ops.task.cancel", "ops.task.reassign",
|
||||
"ops.task.boostPriority", "monitor.note.write"
|
||||
};
|
||||
|
||||
[HttpPost("execute")]
|
||||
public ActionResult<ExecuteResponse> Execute([FromBody] ExecuteRequest req)
|
||||
{
|
||||
if (!Whitelist.Contains(req.OpCode))
|
||||
return BadRequest(new { message = $"非白名单 op:{req.OpCode}" });
|
||||
|
||||
// AR-4: JWT ops claim 二次校验 —— JWT 颁发时已写入用户被授权的 ops 列表(空格分隔),
|
||||
// 这里要求 (op 在白名单) AND (op 在用户 ops claim);admin 的 "*" 会被特判通过。
|
||||
var opsClaim = User.FindFirst("ops")?.Value ?? "";
|
||||
var userOps = opsClaim.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (!userOps.Contains("*") && !userOps.Contains(req.OpCode))
|
||||
return StatusCode(403, new { message = $"当前账号无权执行 {req.OpCode}" });
|
||||
|
||||
var user = User.Identity?.Name ?? User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous";
|
||||
var scope = User.FindFirst("scope")?.Value ?? "unknown";
|
||||
|
||||
var id = $"A{Interlocked.Increment(ref _seq):D6}";
|
||||
var entry = new AuditEntry(id, DateTimeOffset.UtcNow, user, scope,
|
||||
req.OpCode, req.TargetId, "ok", req.Reason);
|
||||
Audits.Enqueue(entry);
|
||||
while (Audits.Count > 200 && Audits.TryDequeue(out _)) { }
|
||||
return Ok(new ExecuteResponse(true, id, null));
|
||||
}
|
||||
|
||||
[HttpGet("audits")]
|
||||
public IActionResult Audits200()
|
||||
{
|
||||
return Ok(Audits.Reverse());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user