using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MiGu.Server.Auth;
using MiGu.Server.Launcher;
namespace MiGu.Server.Controllers;
///
/// 读取 Simple3 地图固定目录下的 JSON 原文,供平台「地图管理」右侧预览。
/// 先向 Simple3 拉取 maps 列表拿到 directory,再读本机同路径文件(与 Simple3 同机部署)。
///
[ApiController]
[Authorize]
[Route("api/maps")]
public class MapsContentController : ControllerBase
{
private readonly IHttpClientFactory _httpFactory;
private readonly InternalTokenStore _internalToken;
private readonly Simple3Options _sl;
private readonly ILogger _log;
public MapsContentController(
IHttpClientFactory httpFactory,
InternalTokenStore internalToken,
IOptions sl,
ILogger log)
{
_httpFactory = httpFactory;
_internalToken = internalToken;
_sl = sl.Value;
_log = log;
}
[HttpGet("{name}/content")]
public async Task GetContent(string name, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(name)
|| name.Contains("..", StringComparison.Ordinal)
|| name.IndexOfAny(['/', '\\', ':', '*', '?', '"', '<', '>', '|']) >= 0)
{
return BadRequest(new { message = "地图名称非法" });
}
try
{
var directory = await FetchMapsDirectoryAsync(ct);
if (string.IsNullOrWhiteSpace(directory))
return StatusCode(503, new { message = "无法从 Simple3 获取地图目录" });
var fileName = name.EndsWith(".json", StringComparison.OrdinalIgnoreCase) ? name : $"{name}.json";
var fullPath = Path.GetFullPath(Path.Combine(directory, fileName));
var root = Path.GetFullPath(directory);
if (!fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase))
return BadRequest(new { message = "路径校验失败" });
if (!System.IO.File.Exists(fullPath))
return NotFound(new { message = $"地图文件不存在:{fileName}" });
var content = await System.IO.File.ReadAllTextAsync(fullPath, ct);
// 不返回 fullPath:避免向前端泄露服务器目录结构。
return Ok(new
{
name,
fileName,
content
});
}
catch (Exception ex)
{
_log.LogWarning(ex, "读取地图 JSON 失败 name={Name}", name);
return StatusCode(500, new { message = $"读取地图 JSON 失败:{ex.Message}" });
}
}
private async Task FetchMapsDirectoryAsync(CancellationToken ct)
{
var client = _httpFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(8);
var url = $"http://127.0.0.1:{_sl.ProjectionPort}/projection/map-edit/maps";
using var msg = new HttpRequestMessage(HttpMethod.Get, url);
if (!string.IsNullOrEmpty(_internalToken.Token))
msg.Headers.TryAddWithoutValidation("X-Platform-Internal-Token", _internalToken.Token);
using var resp = await client.SendAsync(msg, ct);
var body = await resp.Content.ReadAsStringAsync(ct);
if (!resp.IsSuccessStatusCode)
throw new InvalidOperationException($"Simple3 maps 列表返回 {(int)resp.StatusCode}");
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
if (root.TryGetProperty("success", out var ok) && ok.ValueKind == JsonValueKind.False)
{
var message = root.TryGetProperty("message", out var m) ? m.GetString() : "maps 列表失败";
throw new InvalidOperationException(message ?? "maps 列表失败");
}
JsonElement data = root;
if (root.TryGetProperty("data", out var d) && d.ValueKind == JsonValueKind.Object)
data = d;
if (data.TryGetProperty("directory", out var dir) && dir.ValueKind == JsonValueKind.String)
return dir.GetString();
return null;
}
}