Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 7, 2025

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.

Original prompt

好的,这个改动可以分成三块:配置设计、后端逻辑调整、文档说明。

下面直接给出新的规则和需要修改的点。


一、配置规则(新的行为)

新增/统一成类似配置(示例):

playground:
  enabled: false       # 默认 false:演练场关闭
  password: ""         # 可选;开启时如果为空则公开访问

行为逻辑:

  1. enabled = false(默认)

    • /playground 页面不可访问(前端路由跳转到提示页或 404)。
    • Playground 相关 API(如 /v2/playground/**)直接拒绝(例如 403/404)。
  2. enabled = true 且有 password

    • 需要密码登录才能使用 playground。
    • 前端显示密码输入框,后端校验通过后在 session/token 里标记已授权。
  3. enabled = true 且没有配置密码(空字符串 / null)

    • playground 公开访问,不需要输入密码。
    • 出于安全考虑,在文档里明确提示
      • 一旦在公网环境开启,务必设置密码。

二、后端逻辑需要调整的点

1. 去掉“默认有密码”的逻辑

之前的思路是:

  • 默认必须密码访问;public 为 true 才开放。

现在改为:

  • enabled 控制是否开启;
  • password 是否为空控制是否需要输入密码。

伪代码调整:

public class PlaygroundConfig {
    private boolean enabled;  // 默认 false
    private String password;  // 可选
}

访问检查

所有 playground 相关接口(以及 /playground 页面)在进入时做:

private void ensurePlaygroundEnabled() {
    if (!playgroundConfig.isEnabled()) {
        throw new NotFoundException("playground disabled"); // 或 403
    }
}

private boolean hasPassword() {
    return StringUtils.isNotBlank(playgroundConfig.getPassword());
}

private void ensurePlaygroundAuthed(HttpServletRequest request) {
    ensurePlaygroundEnabled();
    if (!hasPassword()) {
        // 启用但无密码 => 公开模式,不做登录校验
        return;
    }
    Boolean authed = (Boolean) request.getSession().getAttribute("playgroundAuthed");
    if (authed == null || !authed) {
        throw new UnauthorizedException("playground not authorized");
    }
}

登录接口逻辑

POST /v2/playground/login

@PostMapping("/v2/playground/login")
public Response login(@RequestBody PlaygroundLoginRequest req, HttpServletRequest request) {
    if (!playgroundConfig.isEnabled()) {
        return Response.fail("Playground 未开启");
    }

    if (!hasPassword()) {
        // 开启但无密码:直接视为公开,不需要登录
        request.getSession(true).setAttribute("playgroundAuthed", true);
        return Response.ok();
    }

    if (StringUtils.equals(req.getPassword(), playgroundConfig.getPassword())) {
        request.getSession(true).setAttribute("playgroundAuthed", true);
        return Response.ok();
    } else {
        return Response.fail("密码错误");
    }
}

状态接口(给前端用)

GET /v2/playground/status 返回类似结构:

{
  "enabled": true,
  "needPassword": true,
  "authed": false
}

实现逻辑:

@GetMapping("/v2/playground/status")
public Response status(HttpServletRequest request) {
    boolean enabled = playgroundConfig.isEnabled();
    boolean needPassword = enabled && hasPassword();
    boolean authed = false;
    if (enabled) {
        if (!needPassword) {
            authed = true;
        } else {
            Boolean s = (Boolean) request.getSession().getAttribute("playgroundAuthed");
            authed = s != null && s;
        }
    }
    Map<String, Object> data = new HashMap<>();
    data.put("enabled", enabled);
    data.put("needPassword", needPassword);
    data.put("authed", authed);
    return Response.ok(data);
}

三、前端 Playground 行为调整

前端 Playground.vue 逻辑调整为:

  1. 进入页面时先调用 /v2/playground/status
    • enabled = false:提示“Playground 未开启”并不加载编辑器。
    • enabled = true & needPassword = true & !authed:显示密码输入框。
    • 其它情况:直接 initPlayground() 加载 TS 编译器、编辑器等。

伪代码:

data() {
  return {
    statusLoading: true,
    enabled: false,
    needPassword: false,
    authed: false,
    password: '',
    authError: '',
    // 之前的 loading/进度条状态照旧
  };
},
async mounted() {
  await this.fetchStatus();
  this.statusLoading = false;
  if (!this.enabled) {
    // 显示“未开启”的提示,直接 return
    return;
  }
  if (!this.needPassword || this.authed) {
    this.initPlayground();
  }
},
methods: {
  async fetchStatus() {
    const res = await playgroundApi.getStatus();
    this.enabled = res.data.enabled;
    this.needPassword = res.data.needPassword;
    this.authed = res.data.authed;
  },
  async submitPassword() {
    this.authError = '';
    try {
      const res = await playgroundApi.login({ password: this.password });
      if (res.success) {
        this.authed = true;
        this.initPlayground();
      } else {
        this.authError = res.message || '密码错误';
      }
    } catch {
      this.authError = '请求失败';
    }
  }
}

模板:

  • 如果 !enabled:显示“Playground 未开启,由管理员在配置中启用后方可使用”;
  • 如果 enabled && needPassword && !authed:显示密码输入框;
  • 如果 enabled && (!needPassword || authed):显示原有 playground 内容(带加载动画)。

四、文档改动建议

在你现在已有的 TS 演练场文档(TYPESCRIPT_PLAYGROUND_GUIDE.md 或单独的配置文档)中增加一节「访问控制 / 安全」。

示例文案要点(简化版):

访问控制与安全

配置示例:

playground:
  enabled: false          # 是否开启 Playground,默认关闭
  password: ""            # 访问密码,可选。仅在 enabled=true 时生效

行为说明:

  • enabled = false(默认)

    • Playground 功能关闭。
    • /playground 页面和相关 API 不可访问。
  • enabled = truepassword 非空

    • 访问 /playground 时需要输入密码。
    • 密码正确后本次会话内保持已登录状态。
  • enabled = truepassword 为空

    • Playground 对所有访问者开放,无需密码。
    • 适用于本地开发或内网环境。

**...


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI assigned Copilot and qaiu Dec 7, 2025
@qaiu qaiu closed this Dec 7, 2025
Copilot AI requested a review from qaiu December 7, 2025 05:33
@qaiu qaiu deleted the copilot/add-playground-configurations branch December 7, 2025 06:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants