From ec239b6baebe819ce822cc703579a12f4b5058cb Mon Sep 17 00:00:00 2001 From: BaizhouziYou <111175934+BaizhouziYou@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:25:26 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=90=88=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20CI=20=E5=B9=B6=E6=95=B4=E7=90=86=E4=B8=AD=E6=96=87=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并构建、数据库集成和中文检查工作流,将中文要求调整为非阻断建议。 取消过期运行,复用编译缓存,减少重复检查和压缩开销。 补充 AI 辅助贡献规范,统一中文说明并完善首页文档导航。 --- .github/scripts/chinese-advice.mjs | 58 +++++++++ .github/scripts/chinese-advice.test.mjs | 30 +++++ .github/workflows/ci.yml | 105 +++++++++++++-- .github/workflows/commit-language.yml | 44 ------- .github/workflows/database-integration.yml | 71 ---------- AGENTS.md | 3 +- CONTRIBUTING.md | 21 ++- README.md | 90 ++++++++----- build.mjs | 2 +- dev/README.md | 121 +++++++++++++----- dev/web/README.md | 35 ++++- ...23\346\236\204\346\243\200\346\237\245.md" | 73 +++++++++++ 12 files changed, 446 insertions(+), 207 deletions(-) create mode 100644 .github/scripts/chinese-advice.mjs create mode 100644 .github/scripts/chinese-advice.test.mjs delete mode 100644 .github/workflows/commit-language.yml delete mode 100644 .github/workflows/database-integration.yml create mode 100644 "docs/\351\241\271\347\233\256\347\273\223\346\236\204\346\243\200\346\237\245.md" diff --git a/.github/scripts/chinese-advice.mjs b/.github/scripts/chinese-advice.mjs new file mode 100644 index 0000000..56b56da --- /dev/null +++ b/.github/scripts/chinese-advice.mjs @@ -0,0 +1,58 @@ +import { execFileSync } from 'node:child_process' +import { appendFileSync, readFileSync } from 'node:fs' +import { pathToFileURL } from 'node:url' + +export function pullRequestAdvice({ title = '', body = '' }) { + const advice = [] + if (!/\p{Script=Han}/u.test(title)) { + advice.push('PR 标题未包含中文,建议补充中文标题,方便维护者阅读。') + } + const description = (body || '') + .replace(/|$)/g, '') + .replace(/^\s*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^\s*\1\s*$/gm, '') + .replace(/`[^`\n]*`/g, '') + .replace(/^\s*#{1,6}\s+.*$/gm, '') + if (!/\p{Script=Han}/u.test(description)) { + advice.push('PR 正文未包含中文说明,建议补充中文的问题说明、改动内容和验证结果。') + } + return advice +} + +export function commitAdvice(log) { + return log.split('\n').filter(Boolean).flatMap(line => { + const [sha, title] = line.split('\0') + return /\p{Script=Han}/u.test(title || '') + ? [] + : [`提交 ${sha} 的标题未包含中文,建议补充中文摘要。`] + }) +} + +function git(...args) { + return execFileSync('git', args, { encoding: 'utf8' }).trimEnd() +} + +function main() { + const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8')) + const advice = event.pull_request ? pullRequestAdvice(event.pull_request) : [] + const base = process.env.BASE_SHA + const head = process.env.HEAD_SHA + let revisions + if (!base || /^0+$/.test(base)) { + // 新分支不追溯其他远程分支已有的提交。 + const others = git('for-each-ref', '--format=%(objectname)', 'refs/remotes/origin/') + .split('\n').filter(sha => sha && sha !== head) + revisions = others.length ? [head, '--not', ...others] : ['-1', head] + } else { + revisions = [`${base}..${head}`] + } + advice.push(...commitAdvice(git('log', '--format=%h%x00%s', ...revisions))) + // 使用固定提示和 Git 生成的提交哈希,避免把 PR 原文作为工作流指令输出。 + for (const message of advice) console.log(`::warning title=中文描述建议::${message}`) + const summary = advice.length + ? advice.map(message => `- ${message}`).join('\n') + : '本次检查未发现需要补充中文的项目。' + appendFileSync(process.env.GITHUB_STEP_SUMMARY, + `## 中文描述建议\n\n${summary}\n\n以上仅为建议,不因缺少中文而阻断合并。\n`) +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) main() diff --git a/.github/scripts/chinese-advice.test.mjs b/.github/scripts/chinese-advice.test.mjs new file mode 100644 index 0000000..0c70fb4 --- /dev/null +++ b/.github/scripts/chinese-advice.test.mjs @@ -0,0 +1,30 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { commitAdvice, pullRequestAdvice } from './chinese-advice.mjs' + +test('标题和正文分别提醒,允许中英文混用', () => { + assert.equal(pullRequestAdvice({ title: 'Fix login', body: 'Fix a bug' }).length, 2) + assert.equal(pullRequestAdvice({ title: '修复登录', body: 'Fix a bug' }).length, 1) + assert.equal(pullRequestAdvice({ title: 'Fix login', body: '修复登录失败' }).length, 1) + assert.deepEqual(pullRequestAdvice({ title: 'Fix 登录', body: '修复 WebUI 登录' }), []) +}) + +test('空正文和仅有模板标题、注释或代码的正文仍给出建议', () => { + for (const body of [null, '', '## 改动说明\n\nFix login', + '```text\n中文\n```', '~~~text\n中文\n~~~', '`中文`']) { + assert.equal(pullRequestAdvice({ title: '修复登录', body }).length, 1) + } +}) + +test('识别繁体和扩展汉字', () => { + assert.deepEqual(pullRequestAdvice({ title: '修復登入', body: '𠀀' }), []) +}) + +test('仅提醒缺少中文的提交,不回显不可信的标题', () => { + const log = 'abc123\0修复登录\ndef456\0Fix login\nghi789\0::error::bad' + assert.deepEqual(commitAdvice(log), [ + '提交 def456 的标题未包含中文,建议补充中文摘要。', + '提交 ghi789 的标题未包含中文,建议补充中文摘要。', + ]) + assert.deepEqual(commitAdvice(''), []) +}) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a82651..66444d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,14 +1,46 @@ -name: Build and Test +name: 构建、测试与贡献建议 on: pull_request: + types: [opened, synchronize, reopened, edited] push: + branches: [main] + tags: ['**'] workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: contents: read +env: + # 与便携包编译参数保持一致,复用测试和构建缓存。 + CGO_ENABLED: '0' + GOFLAGS: -trimpath + jobs: + commit-language: + name: 中文描述建议 + if: github.event_name == 'pull_request' || (github.event_name == 'push' && !github.event.deleted) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: 验证建议规则 + run: node --test .github/scripts/chinese-advice.test.mjs + - name: 提供中文描述建议 + env: + BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} + HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: node .github/scripts/chinese-advice.mjs + go: strategy: matrix: @@ -22,7 +54,7 @@ jobs: cache-dependency-path: dev/go.sum - name: Test working-directory: dev - run: go test ./... + run: go test -vet=off ./... - name: Vet working-directory: dev run: go vet ./... @@ -41,19 +73,66 @@ jobs: release/*.zip release/*.tar.gz if-no-files-found: error + compression-level: 0 - web: + mysql: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: ["8.4.11", "9.6.0"] + services: + mysql: + image: mysql:${{ matrix.version }} + env: + MYSQL_ROOT_PASSWORD: root-test-password + MYSQL_DATABASE: easybot_test + MYSQL_USER: easybot + MYSQL_PASSWORD: easybot-test-password + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot-test-password" + --health-interval=3s + --health-timeout=5s + --health-retries=40 steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-go@v5 with: - node-version: 22 - cache: npm - cache-dependency-path: dev/web/package-lock.json - - name: Install dependencies - working-directory: dev/web - run: npm ci - - name: Build - working-directory: dev/web - run: npm run build + go-version-file: dev/go.mod + cache-dependency-path: dev/go.sum + - name: Test against MySQL + working-directory: dev + env: + EASYBOT_TEST_DATABASE_DSN: mysql://easybot:easybot-test-password@127.0.0.1:3306/easybot_test + # 其他包由跨平台任务覆盖;外部数据库测试每次连接新测试库执行。 + run: go test -count=1 -vet=off ./internal/store + + postgres: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:18.6 + env: + POSTGRES_DB: easybot_test + POSTGRES_USER: easybot + POSTGRES_PASSWORD: easybot-test-password + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -h 127.0.0.1 -U easybot -d easybot_test" + --health-interval=3s + --health-timeout=5s + --health-retries=40 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: dev/go.mod + cache-dependency-path: dev/go.sum + - name: Test against PostgreSQL + working-directory: dev + env: + EASYBOT_TEST_DATABASE_DSN: postgres://easybot:easybot-test-password@127.0.0.1:5432/easybot_test?sslmode=disable + run: go test -count=1 -vet=off ./internal/store diff --git a/.github/workflows/commit-language.yml b/.github/workflows/commit-language.yml deleted file mode 100644 index 4556521..0000000 --- a/.github/workflows/commit-language.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: 中文提交检查 - -on: - pull_request: - types: [opened, synchronize, reopened, edited] - push: - -permissions: - contents: read - -jobs: - commit-language: - name: 提交标题使用中文 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: 检查新增提交 - env: - BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} - HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - run: | - python3 - <<'PY' - import os - import re - import subprocess - - base, head = os.environ['BASE_SHA'], os.environ['HEAD_SHA'] - if not base or set(base) == {'0'}: - # 新分支只检查不在其他远程分支上的提交,避免追溯检查历史。 - others = subprocess.check_output(['git', 'for-each-ref', '--format=%(objectname)', 'refs/remotes/origin/'], text=True).splitlines() - others = [sha for sha in others if sha != head] - revisions = [head, '--not', *others] if others else ['-1', head] - else: - revisions = [f'{base}..{head}'] - commits = subprocess.check_output(['git', 'log', '--format=%h %s', *revisions], text=True).splitlines() - invalid = [line for line in commits if not re.search(r'[\u4e00-\u9fff]', line.partition(' ')[2])] - if invalid: - print('以下提交标题必须改为中文:\n' + '\n'.join(invalid)) - raise SystemExit(1) - print(f'已检查 {len(commits)} 个新增提交。') - PY diff --git a/.github/workflows/database-integration.yml b/.github/workflows/database-integration.yml deleted file mode 100644 index 2a80aea..0000000 --- a/.github/workflows/database-integration.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Database Integration - -on: - pull_request: - push: - workflow_dispatch: - -permissions: - contents: read - -jobs: - mysql: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - version: ["8.4.11", "9.6.0"] - services: - mysql: - image: mysql:${{ matrix.version }} - env: - MYSQL_ROOT_PASSWORD: root-test-password - MYSQL_DATABASE: easybot_test - MYSQL_USER: easybot - MYSQL_PASSWORD: easybot-test-password - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot-test-password" - --health-interval=10s - --health-timeout=5s - --health-retries=12 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: dev/go.mod - cache-dependency-path: dev/go.sum - - name: Test against MySQL - working-directory: dev - env: - EASYBOT_TEST_DATABASE_DSN: mysql://easybot:easybot-test-password@127.0.0.1:3306/easybot_test - run: go test ./... - - postgres: - runs-on: ubuntu-latest - services: - postgres: - image: postgres:18.6 - env: - POSTGRES_DB: easybot_test - POSTGRES_USER: easybot - POSTGRES_PASSWORD: easybot-test-password - ports: - - 5432:5432 - options: >- - --health-cmd="pg_isready -U easybot -d easybot_test" - --health-interval=10s - --health-timeout=5s - --health-retries=12 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version-file: dev/go.mod - cache-dependency-path: dev/go.sum - - name: Test against PostgreSQL - working-directory: dev - env: - EASYBOT_TEST_DATABASE_DSN: postgres://easybot:easybot-test-password@127.0.0.1:5432/easybot_test?sslmode=disable - run: go test ./... diff --git a/AGENTS.md b/AGENTS.md index 6ced2d7..654d346 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,8 @@ 本文件适用于整个 Core 仓库及后续开发代理。 - 项目目标:在已获授权的范围内复刻闭源 EasyBot 的功能和使用流程;以可验证的协议、行为和测试为依据。 -- 中文交流、中文开发文档、中文 commit 标题及正文。技术名、路径、类型标记可以保留英文,例如 `fix(web): 修复管理员登录失败`。 +- 建议使用中文交流、编写开发文档及 PR、commit 标题和正文。技术名、路径、类型标记可以保留英文,例如 `fix(web): 修复管理员登录失败`。中文 CI 仅提供提醒,不因缺少中文阻断合并。 +- 使用 AI 辅助贡献时,在 PR 中披露工具及用途;贡献者须理解并审阅改动,用自己的话说明问题、行为和验证结果,提交 PR 前完成人工测试。详细要求见 CONTRIBUTING.md;AI 披露及人工验证不纳入自动检测。 - 不得改写既有历史以追溯修改英文提交。 - 反编译参考仓库位于同组织的 EasyBot-Recovered。本地建议与 Core 并列;只用于功能、协议和接口对照。不得直接把私有参考源码或 DLL 提交到 Core。 - 不把配置 CRUD、页面入口或模拟器通过描述为真实运行时已完成。状态说明必须区分已实现、模拟验证、真实环境验证和待实现。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8a92f3..bf61c58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,21 @@ # 贡献规范 -## 中文提交规范 +## 中文交流建议 -所有后续提交的标题和正文使用中文,技术术语、代码标识和类型前缀可保留英文。推荐格式:`类型(范围): 中文摘要`,例如 `feat(bridge): 增加连接身份校验`。正文说明原因、行为变化与验证结果。合并和回滚提交同样使用中文;不追溯重写历史英文提交。 +建议 PR 和 commit 的标题、正文使用中文,方便维护者阅读;允许中英文混用,技术术语、代码标识和类型前缀可保留英文。推荐格式:`类型(范围): 中文摘要`,例如 `feat(bridge): 增加连接身份校验`。正文说明原因、行为变化与验证结果。不追溯重写历史英文提交。 -CI 的中文提交检查校验每个新增提交标题是否含汉字;它只能做基本格式检查,正文语言和语义仍由人工评审。要强制所有贡献者遵守中文提交规范,需要在 GitHub 分支保护中将该检查设置为必需,并限制直接推送与绕过权限。 +CI 在 PR 标题、正文说明或新增 commit 标题不含汉字时,通过警告及运行摘要建议补充中文,检查仍然通过。正文检测忽略 Markdown 标题、HTML 注释和代码,避免把模板提示当作实际说明。它只做字符检查,不判断语言质量;构建、测试和静态检查失败仍正常报告失败。 + +## AI 辅助贡献 + +参考 [GeyserMC 的 AI 使用政策](https://github.com/GeyserMC/.github/blob/master/AI_POLICY.md),本仓库采用以下协作要求: + +- 使用 AI 辅助代码、测试或文档时,在 PR 正文中披露工具及用途,一句话即可,例如“使用 Codex 辅助调整 CI,已人工审阅配置并验证中文提醒逻辑”。 +- 贡献者须理解并审阅所有改动,能够解释实现及验证结果,并对提交负责。PR 说明和评审回复应使用自己的话,不直接粘贴未经核实的 AI 回答。允许翻译和拼写检查;翻译时请附上原文,方便核对。 +- 无论是否使用 AI,提交 PR 前都须完成人工测试,写清测试方式、结果及未验证部分。文档或 CI 改动可采用人工审阅和相关配置、脚本验证;不得把仅由 AI 声称通过的检查写成实测结果。 +- PR 聚焦相关改动,不夹带大量无关格式修改或生成内容。不要提交 AI 生成的图片、视频或音频等媒体素材。 + +以上由贡献者披露、维护者评审,不加入 CI 自动检测,也不使用 AI 内容识别器。 ## 开发与验证 @@ -12,7 +23,9 @@ CI 的中文提交检查校验每个新增提交标题是否含汉字;它只 后端在 `dev` 执行 `go test ./...` 和 `go vet ./...`;前端在 `dev/web` 执行 `npm ci` 和 `npm run build`。涉及 MySQL/PostgreSQL 时使用空的专用测试库执行集成检查。Windows 启动改动需要验证从其他工作目录启动 exe,并在验证后停止服务。 -PR 使用中文说明问题、最终行为和验证结果。为非平凡修复保留能暴露原问题的测试;不要把默认跳过的外部数据库测试描述为已验证通过。 +CI 在 PR、主分支推送、标签推送或手动触发时运行;普通开发分支请创建 PR 或手动运行。同一 PR 或分支的新运行会取消旧运行。跨平台任务完整执行 Go 测试和独立的 `go vet`,数据库任务只执行 `go test -count=1 -vet=off ./internal/store`,保证每次实际访问测试库。CI 使用与便携包一致的 `CGO_ENABLED=0` 和 `-trimpath` 编译参数,复用测试、静态检查与打包的编译缓存。 + +建议 PR 使用中文说明问题、最终行为和验证结果。为非平凡修复保留能暴露原问题的测试;不要把默认跳过的外部数据库测试描述为已验证通过。 ## 复刻依据与边界 diff --git a/README.md b/README.md index 21308c0..8aec585 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,87 @@ -# EasyBotCE Core +# EasybotCE Core -EasybotCE 的主程序。当前重建项目由 Go HTTP API 和 Vue 3 管理台组成,使用 SQLite 默认存储,并提供 MySQL/PostgreSQL 存储实现与集成测试目标。 +EasybotCE 的主程序,使用 Go 提供管理 API、Vue 3 提供网页管理台,默认使用 SQLite 保存数据。便携包包含程序和页面资源,解压后即可运行。 -## 项目结构 +## 文档导航 -- `dev/`: Go 服务、Vue 3 + TypeScript + Tailwind CSS v4 管理台和开发说明。 -- `PLAN.md`: 原始重建规划。 -- `REMAINING_PLAN.md`: 后续版本产品范围、验收标准和工作包。 -- `.github/workflows/`: 本地构建及数据库集成测试。 +- 开发:[开发说明](dev/README.md) · [网页管理台说明](dev/web/README.md) · [项目结构检查](docs/项目结构检查.md) +- 规划:[原始重建规划](PLAN.md) · [后续开发计划](REMAINING_PLAN.md) +- 协作:[贡献规范](CONTRIBUTING.md) · [协作约定](AGENTS.md) · [署名说明](AUTHORS.md) · [许可证](LICENSE) -当前运行时已覆盖管理员初始化/认证、管理台概览、配置 CRUD、持久化运行日志及 OneBot 11 正向 WebSocket 连接;OneBot 模拟器验证已完成,真实账号仍待验收。Minecraft 网络桥接和消息同步尚未实现。MySQL/PostgreSQL 是否正式支持以对应 CI 集成测试通过为准。 +## 当前进度 -## License and attribution +| 功能 | 状态 | +| --- | --- | +| 管理台、配置管理、登录认证、持久化运行日志 | 已实现 | +| OneBot 11 正向 WebSocket 适配器 | 已实现连接、消息收发接口、状态管理与断线重连;已通过协议模拟器验证,真实账号仍待验收 | +| MySQL、PostgreSQL 存储 | 已提供实现和集成测试;具体版本以对应 CI 验证结果为准 | +| Minecraft 桥接、消息转发、账号绑定 | 尚未实现 | +| JavaScript 插件执行、AI 对话、图片模板渲染 | 尚未实现;已有配置入口不代表运行能力可用 | -This repository is licensed under [CC BY-NC 4.0 (`CC-BY-NC-4.0`)](https://creativecommons.org/licenses/by-nc/4.0/). Commercial use is not permitted by that license. When sharing or adapting this project, credit EasybotCE and link to this repository and the license, identify changes, and retain existing author, copyright, and license notices. See [`AUTHORS.md`](AUTHORS.md). +后续范围和验收标准见 [开发计划](REMAINING_PLAN.md)。 -## 运行 +## 快速开始 -Windows 用户下载便携压缩包,解压到可写目录后双击 `easybotce.exe`,浏览器打开 `http://127.0.0.1:5001`。默认本机回环监听直接进入管理台,无需注册或登录。包内已包含页面资源,默认使用 SQLite,无需安装 Go、Node.js 或数据库服务器,也无需启动脚本。运行期间保持程序窗口打开。 +1. 从 [发布版本](https://github.com/EasybotCE/Core/releases) 下载适合当前系统的便携压缩包。 +2. 解压到可写目录,保留包内的 `web` 目录。 +3. Windows 双击 `easybotce.exe`;Linux 在解压目录运行 `./easybotce`,缺少执行权限时先执行 `chmod +x easybotce`。 +4. 浏览器打开 `http://127.0.0.1:5001`,运行期间保持程序窗口或终端打开。 -非本地监听使用原版式 Token 登录,不需要用户名或独立 API Key。首次自动生成 12 位登录 Token,仅在控制台显示一次;`token.txt` 保存原版格式的 SHA-256 哈希,后续沿用。非本地地址可直接使用 HTTP,HTTPS 可选;公网传输 Token 时建议使用 HTTPS。 +默认仅监听本机回环地址,无需注册或登录。普通用户不需要安装 Go、Node.js、数据库服务器,也不需要额外启动前端服务。 -保留包内的 `web` 目录。首次启动自动创建 `appdata` 数据目录;升级前停止程序、备份并保留 `appdata`,再替换程序和页面资源。已有数据库可通过 `EASYBOT_SQLITE_PATH` 指定。 +Windows 双击启动失败时会显示错误原因,按回车后关闭窗口;从已有终端或自动化任务启动时,程序直接返回错误码。 -Windows 双击启动失败时,窗口会保留并显示原因,按回车后关闭。从已有终端或自动化环境运行时直接返回错误码。 +## 配置与登录 -## 启动配置与原版迁移 +便携包自带 `appsettings.json`;文件缺失时首次启动会生成默认配置。修改配置后需重启,程序不会重写已有配置文件。 -便携包自带 `appsettings.json`,缺失时首次启动自动生成。修改后重启生效,程序不会重写已有配置。沿用原版字段: +| 配置项 | 用途与默认值 | +| --- | --- | +| `Kestrel.Endpoints.web_api.Url` | 管理台及 API 地址,默认 `http://127.0.0.1:5001` | +| `ServerOptions.Host`、`ServerOptions.Port` | 桥接地址,默认 `127.0.0.1:26991`;桥接运行时尚未实现,当前不监听 | +| `Database.SQLitePath` | SQLite 数据库路径,默认 `appdata/easybot.db` | +| `Serilog.MinimumLevel.Default` | 控制台日志级别 | +| `AllowedHosts` | 允许的请求主机名,支持 `*`、分号分隔的主机名及 `*.example.com` | -- `Kestrel.Endpoints.web_api.Url`:WebUI 地址,CE 默认 `http://127.0.0.1:5001`。 -- `ServerOptions.Host`、`ServerOptions.Port`:Bridge 地址,CE 默认 `127.0.0.1:26991`;运行时尚未实现。 -- `Serilog.MinimumLevel.Default`:控制台日志级别。 -- `AllowedHosts`:允许的 HTTP Host,支持 `*`、分号分隔的主机名及 `*.example.com`。 +改为非本地地址监听后,管理台使用登录令牌(Token)认证,无需用户名或独立 API Key。非本地模式缺少 `token.txt` 时,程序会自动生成 12 位令牌并在控制台显示一次;`token.txt` 只保存 SHA-256 哈希,之后继续使用原令牌。 -迁移启动配置时,将原版 `appsettings.json` 直接复制到 CE 的 exe 旁边即可,现有字段及端口按原值读取。已有原版 `token.txt` 也可一并复制,继续使用同一个登录 Token。你本机原版使用的 5000/26990 会保留;同时运行原版时请改成不同端口。CE 新增配置缺失时使用默认值,原版数据库需要单独迁移,不能直接替换 CE 数据库。 +忘记令牌时,先停止程序,再删除 `token.txt`,重启进入非本地模式后生成新令牌,旧登录会话随之失效。非本地监听支持 HTTP;通过公网传输凭据时建议使用 HTTPS。 -忘记 Token 时,先停止程序并删除 `token.txt`,非本地模式重启后会生成新的 Token,并使旧登录会话失效。升级请保留自己的 `appsettings.json`、`token.txt` 和 `appdata`。旧 CE 用户名/密码登录由 Token 登录替代,业务数据保留。 +`EASYBOT_CONFIG` 可指定配置文件,相关环境变量优先于 JSON 配置。完整配置项、HTTPS 示例和数据库接入方式见 [开发说明](dev/README.md),配置模板见 [appsettings.example.json](dev/internal/config/appsettings.example.json)。 -`EASYBOT_CONFIG` 可指定其他配置文件,原有环境变量优先于 JSON 配置。开发构建默认生成 `dev/appsettings.json`;exe 旁已有配置时优先读取它。配置示例见 [appsettings.example.json](dev/internal/config/appsettings.example.json)。 +## 升级与原版迁移 -## 开发 +升级便携包前先停止程序,备份并保留 `appsettings.json`、`token.txt` 和 `appdata`,再替换程序与 `web` 页面资源,避免用包内模板覆盖现有配置。已有 CE 数据库也可通过 `EASYBOT_SQLITE_PATH` 指定路径继续使用。 -环境要求:Go 1.24+、Node.js 20.19+。 +从原版迁移时,可将 `appsettings.json` 和已有的 `token.txt` 复制到 CE 程序旁。原有配置字段和端口按原值读取,CE 新增配置项缺失时使用默认值;同时运行两套程序时需避免端口冲突。原版 `token.txt` 是二进制哈希文件,不能用明文令牌替代。 -运行端口:`127.0.0.1:5001` 同时提供 WebUI 和管理 API;`26991` 用于 Bridge 与插件/模组通信(运行时尚未实现,当前不监听)。构建后的程序不需要独立前端服务器。WebUI 地址可通过 `EASYBOT_ADDR` 覆盖。 +复制启动配置不会导入原版数据库、适配器或插件数据,原版数据库需要单独迁移,不能直接替换 CE 数据库。旧版 CE 的用户名、密码登录已由令牌登录替代,已有业务数据保留。 -贡献者与开发代理须遵守 [贡献规范](CONTRIBUTING.md) 和 [协作约定](AGENTS.md):所有后续 commit 使用中文。 +## 源码开发 -在仓库根目录执行一条命令,完成依赖下载、前端构建、Go 编译和便携包打包: +开发环境要求:Go 1.24+、Node.js 20.19+。在仓库根目录执行: ```powershell node build.mjs ``` -Windows 输出 `release/easybotce-windows-amd64.zip`,Linux 输出对应平台的 `.tar.gz`(程序名为 `easybotce`);CI 生成的便携包可从运行记录的 Artifacts 下载。本地编译程序同时保存在 `dev/bin`。 +脚本自动安装前端依赖、构建页面、编译程序并生成便携包:Windows 输出 `release/easybotce-windows-amd64.zip`,Linux 输出对应平台的 `.tar.gz`;本地编译程序同时保存在 `dev/bin`。 + +手动运行、热更新和测试命令见 [开发说明](dev/README.md),前端开发见 [管理台说明](dev/web/README.md)。CI 包含跨平台测试、静态检查、便携包构建和数据库集成测试。 + +贡献者和开发代理须遵守 [贡献规范](CONTRIBUTING.md) 与 [协作约定](AGENTS.md)。建议使用中文说明 PR 和提交;中文 CI 仅提醒,不阻断合并。使用 AI 辅助贡献时须披露工具及用途,详细要求见贡献规范。 + +## 项目结构 + +| 路径 | 内容 | +| --- | --- | +| `dev/cmd/easybot/` | Go 程序入口 | +| `dev/internal/` | 配置、认证、存储、适配器和管理 API | +| `dev/web/` | Vue 3、TypeScript、Tailwind CSS v4 管理台 | +| `.github/workflows/` | CI 工作流 | +| `PLAN.md` | 原始重建规划 | +| `REMAINING_PLAN.md` | 后续功能范围与验收标准 | -浏览器打开 `http://127.0.0.1:5001`。可选热更新开发方式见开发说明,日常运行无需启动 Vite。 +## 许可证与署名 -运行方式、配置项和安全说明见 [`dev/README.md`](dev/README.md)。 +本仓库使用 [CC BY-NC 4.0(`CC-BY-NC-4.0`)](https://creativecommons.org/licenses/by-nc/4.0/) 许可证,该许可证不允许商业使用。分享或改编时,请注明 EasybotCE 及本仓库来源,附上许可证链接、标明改动,并保留已有作者、版权和许可声明。详见 [署名说明](AUTHORS.md)。 diff --git a/build.mjs b/build.mjs index 1d5541e..6475ece 100644 --- a/build.mjs +++ b/build.mjs @@ -23,7 +23,7 @@ function run(command, args, cwd) { } } -run('npm', ['ci', '--no-audit', '--no-fund'], join(dev, 'web')) +run('npm', ['ci', '--prefer-offline', '--no-audit', '--no-fund'], join(dev, 'web')) run('npm', ['run', 'build'], join(dev, 'web')) mkdirSync(join(dev, 'bin'), { recursive: true }) const target = env.GOOS || (process.platform === 'win32' ? 'windows' : process.platform) diff --git a/dev/README.md b/dev/README.md index fa48ccd..0b67b95 100644 --- a/dev/README.md +++ b/dev/README.md @@ -1,12 +1,12 @@ -# EasyBot development rebuild +# EasybotCE 开发说明 -Development rebuild using Go for the HTTP/API service and Vue 3, TypeScript, Vite, and Tailwind CSS v4 for the web console. The service uses SQLite by default and accepts MySQL or PostgreSQL URLs through `EASYBOT_DATABASE`. +`dev/` 包含 Go 管理服务和 Vue 3 管理台。默认使用 SQLite,也可通过配置接入 MySQL 或 PostgreSQL。普通用户的便携包使用方法见 [项目首页](../README.md)。 -## Run locally +## 构建与本地运行 -Requirements: Go 1.24+ and Node.js 20.19+. +开发环境要求:Go 1.24+、Node.js 20.19+。在仓库根目录执行 `node build.mjs`,即可安装依赖、构建页面、编译程序并在 `release/` 中生成便携包。 -从仓库根目录执行 `node build.mjs`,自动安装前端依赖、构建页面、编译程序并生成 `release` 下的便携压缩包。普通用户解压包后直接运行程序,无需安装开发环境。以下命令用于手动开发调试。 +手动调试时,从本目录 `dev/` 执行以下 PowerShell 命令: ```powershell cd web @@ -17,21 +17,57 @@ $env:EASYBOT_CONFIG = Join-Path (Get-Location) 'appsettings.json' go run ./cmd/easybot ``` -Open `http://127.0.0.1:5001`. The Go process serves both the API and the built UI from `web/dist`. +浏览器打开 `http://127.0.0.1:5001`。Go 进程同时提供 API 和 `web/dist` 中构建好的页面;日常运行不需要启动 Vite。 -正常运行只使用 WebUI/API 5001 和 Bridge 26991 两个端口。前端执行 `npm run build` 后由 Go 直接提供页面,不启动 Vite。需要热更新时才在 `web` 目录另开终端执行 `npm run dev`,它将 `/api` 代理到 5001,其临时端口不是产品部署端口。Bridge 当前仅保留端口约定,尚无监听器、握手或协议兼容实现。不得把 WebUI/API 或 OneBot 适配器端口当作 Bridge 端口。 +需要前端热更新时,在 `dev/web/` 另开终端执行 `npm run dev`,并保持 Go 服务运行。Vite 将 `/api` 请求代理到 `http://127.0.0.1:5001`,其临时端口只用于开发。 -### Windows executable +| 服务 | 默认地址 | 状态 | +| --- | --- | --- | +| 网页管理台与管理 API | `127.0.0.1:5001` | 由 Go 进程监听 | +| Minecraft 桥接(Bridge) | `127.0.0.1:26991` | 仅保留配置约定,尚无监听、握手或协议兼容实现 | -发布便携包包含 `easybotce.exe`、`appsettings.json`、`web/dist`、许可证和使用说明。解压到可写目录后双击 exe,浏览器打开配置中的地址,默认 `http://127.0.0.1:5001`,保持控制台窗口打开。保留包内目录结构,数据库自动创建在程序旁的 `appdata` 中;不需要额外启动脚本。开发构建 `dev/bin/easybotce.exe` 默认读取 `dev/appsettings.json`;exe 旁已有配置时优先读取。 +### 可执行程序与数据位置 -## appsettings.json +便携包包含可执行程序、`appsettings.json`、`web/dist`、许可证和使用说明。解压到可写目录后即可运行,默认数据保存在同目录的 `appdata/`,需保留包内目录结构。 -可直接复制原版配置到 exe 旁;读取后不会重写,CE 默认值只补充缺失的配置项。支持原版的 `Kestrel.Endpoints`(一个 HTTP/HTTPS 端点)、`ServerOptions`、`Serilog.MinimumLevel`(字符串或带 Default 的对象)和 `AllowedHosts`。Bridge 的配置会读取并显示,但当前不建立桥接监听;HeartbeatInterval 保留给桥接运行时。当前配置文件使用标准 JSON,可带 UTF-8 BOM。 +开发构建的 `dev/bin/easybotce.exe` 默认读取 `dev/appsettings.json`;程序旁已有配置时优先读取。`go run` 使用临时可执行文件,因此应按上面的示例通过 `EASYBOT_CONFIG` 指定开发配置。 -CE 扩展项:`Database.Url` 设置 MySQL/PostgreSQL DSN,`Database.SQLitePath` 设置 SQLite 文件。JSON 中的相对文件路径基于配置文件目录;环境变量中的相对路径仍基于启动目录。原版配置没有 Database 扩展项时,开发用 bin 布局继续使用父目录的 appdata。登录凭据统一使用 token.txt;旧 Security.ApiKey 和 EASYBOT_API_KEY 不再参与认证。 +Windows 双击启动失败时,程序显示错误并等待回车关闭;从已有终端或无控制台任务运行时不暂停,返回非零退出码。 -HTTPS 配置示例(PEM 文件,相对路径基于配置文件): +## 启动配置 + +配置文件使用标准 JSON,支持 UTF-8 BOM。缺失时自动生成默认文件;已有文件始终只读,CE 默认值只补充缺失的配置项。模板见 [appsettings.example.json](internal/config/appsettings.example.json)。 + +| 配置项 | 说明 | +| --- | --- | +| `Kestrel.Endpoints` | 配置一个 HTTP 或 HTTPS 端点;默认管理地址为 `http://127.0.0.1:5001` | +| `ServerOptions.Host`、`ServerOptions.Port` | 桥接配置,默认 `127.0.0.1:26991`;当前只读取,不建立监听 | +| `ServerOptions.HeartbeatInterval` | 为桥接运行时保留的配置 | +| `Serilog.MinimumLevel` | 支持日志级别字符串,或包含 `Default` 的对象 | +| `AllowedHosts` | 允许的请求主机名;支持 `*`、分号分隔的主机名和通配子域名 | +| `Database.Url` | CE 扩展项,用于配置 MySQL 或 PostgreSQL 连接地址 | +| `Database.SQLitePath` | CE 扩展项,默认 `appdata/easybot.db` | + +JSON 中的相对文件路径基于配置文件目录解析;环境变量中的相对路径基于启动目录解析。原版配置缺少 `Database.SQLitePath` 且配置文件放在开发用 `bin/` 目录时,数据仍保存在其父目录的 `appdata/` 中。 + +### 环境变量 + +相关环境变量优先于 JSON 配置: + +| 环境变量 | 用途 | +| --- | --- | +| `EASYBOT_CONFIG` | 指定配置文件 | +| `EASYBOT_ADDR` | 覆盖管理服务监听地址,例如 `127.0.0.1:5001` | +| `EASYBOT_SQLITE_PATH` | 覆盖 SQLite 数据库路径,可指向已有 CE 数据库 | +| `EASYBOT_DATABASE` | 覆盖外部数据库连接地址 | +| `EASYBOT_TLS_CERT`、`EASYBOT_TLS_KEY` | 指定 PEM 证书及私钥文件 | +| `EASYBOT_ALLOWED_HOSTS` | 覆盖允许的请求主机名 | + +MySQL 连接格式为 `mysql://user:password@host:3306/database`;PostgreSQL 连接格式为 `postgres://user:password@host:5432/database?sslmode=disable`。后者示例关闭了数据库连接的 TLS,实际连接参数应与数据库部署方式一致。 + +### HTTPS 示例 + +本地与非本地监听均支持 HTTP。选择 HTTPS 时需提供有效的 PEM 证书和私钥,也可使用反向代理终止 TLS;当前不支持带密码的 PFX。通过公网传输凭据时建议使用 HTTPS。 ```json { @@ -39,44 +75,59 @@ HTTPS 配置示例(PEM 文件,相对路径基于配置文件): "Endpoints": { "web_api": { "Url": "https://0.0.0.0:5001", - "Certificate": { "Path": "cert.pem", "KeyPath": "key.pem" } + "Certificate": { + "Path": "cert.pem", + "KeyPath": "key.pem" + } } } } } ``` -原版 token.txt 是 SHA-256 原始 32 字节文件,可直接复制,不能手写明文。CE 优先读取 exe 旁的 token.txt,否则读取配置目录中的同名文件。缺失时仅在非本地模式自动生成 12 位 Token 并在控制台显示一次。更换或删除 Token 文件后重启会清除旧会话;旧 CE 用户名/密码登录由 Token 登录替代。 +示例中的证书相对路径基于配置文件目录解析。 + +## 登录与访问控制 + +认证模式由实际监听地址决定:仅绑定回环地址(如 `127.0.0.1`、`::1`)时,可在本机直接进入管理台,无需注册或登录;请求主机名、来源和连接地址仍须符合本地访问条件。绑定非本地地址时,即使从本机访问也需要登录令牌(Token)。 -复制原版启动配置不会导入原版数据库、适配器和插件数据。升级便携包时请保留自己的 appsettings.json、token.txt 与 appdata,不要用包内模板覆盖。 +程序优先读取可执行文件旁的 `token.txt`,否则读取配置目录中的同名文件。该文件保存 SHA-256 原始 32 字节哈希,可直接复制原版文件,不能手写明文令牌。文件缺失时,仅在非本地模式自动生成 12 位令牌,并在控制台显示一次;后续重启继续使用原令牌,首次凭据输出不受日志级别过滤。 -## Database and access +忘记令牌时,停止程序并删除 `token.txt`,再重启进入非本地模式。更换或删除令牌文件后,旧会话失效。旧版 CE 用户名、密码登录已由令牌登录替代,旧 `Security.ApiKey` 和 `EASYBOT_API_KEY` 不再参与认证。 -- 默认数据库由 appsettings.json 的 Database.SQLitePath 指定为 `appdata/easybot.db`。位置不依赖前端资源是否存在或启动时的工作目录。 -- `EASYBOT_SQLITE_PATH` 可覆盖 JSON 中的路径,相对路径按启动目录解析。`go run` 使用临时可执行文件,因此按上面的命令指定开发配置文件;已有 CE 数据库应设置为其实际路径继续使用。 -- MySQL: `EASYBOT_DATABASE=mysql://user:password@host:3306/database` -- PostgreSQL: `EASYBOT_DATABASE=postgres://user:password@host:5432/database?sslmode=disable` -- Database CI is configured for MySQL 8.4.11 (LTS), MySQL 9.6.0 (Innovation), and PostgreSQL 18.6. These versions are validation targets, not a support claim until the corresponding CI runs pass. -- Listen address: `EASYBOT_ADDR=127.0.0.1:5001` -- 本地与非本地监听均可使用 HTTP,不需要独立 API Key。非本地管理 API 通过 Token 登录后获得的会话 Cookie 访问。选择 HTTPS 时必须提供有效证书与私钥;可以使用反向代理终止 TLS。公网传输凭据建议使用 HTTPS。健康检查和静态页面仍公开。 -- Windows 双击启动失败时保留独立控制台,显示错误并等待回车关闭。已有终端或无控制台任务不暂停,返回非零退出码;配置错误、数据库错误和端口占用都经过同一失败处理。 -- 认证模式按实际监听地址决定:仅绑定回环地址(127.0.0.0/8、::1 等)时,本机直接进入管理台,无需注册或登录;Host、Origin 和连接来源仍需匹配本地访问。绑定非本地地址时,即使从本机访问也需要登录。 -- Token 文件保存 SHA-256 哈希,不保存明文;自动生成的 Token 重启不变,首次凭据输出不受日志级别过滤。浏览器会话使用 HttpOnly、SameSite=Strict Cookie,有效期 7 天。注销和更换 Token 都会使对应旧会话失效。 -- Login is rate-limited to 10 attempts per remote IP in each 5-minute window. The API never returns password hashes or session tokens in JSON responses. +非本地管理 API 使用登录后取得的会话 Cookie;Cookie 设置为 `HttpOnly`、`SameSite=Strict`,有效期为 7 天。每个远端 IP 在 5 分钟内最多尝试登录 10 次,注销和更换令牌会使对应旧会话失效。API 不在 JSON 响应中返回密码哈希或会话令牌;健康检查和静态页面仍可公开访问。 -## Current implementation +## 升级与迁移 -The dashboard and persistent management API cover adapter configurations, server/player/account/group records, allow/deny lists, command definitions, AI provider settings, image-template and JavaScript-plugin catalogs, and instance settings. CRUD data is stored in one typed record table behind versioned startup migrations; adapters are exposed at the compatibility routes `/api/adapter/*`. The first active adapter runtime is OneBot 11 over forward WebSocket, with Bearer authentication, identity handshake, group/private message event normalization, send/group-list calls, lifecycle controls, status and capabilities APIs, and bounded reconnect backoff. Enabled OneBot 11 configurations are restored at startup. Adapter credentials are omitted from adapter API responses and blank edits preserve a saved token. Runtime events are kept in a bounded in-memory tail and written through a bounded asynchronous queue to the `runtime_logs` table. The authenticated `/api/logs` endpoint supports level, component, UTC time range, keyword, and offset pagination filters; `/api/logs/status` reports the current retention policy and write failure counters. The log page controls persistent storage, retention days, and the maximum row count; cleanup runs hourly. Log messages redact common token, password, DSN, and verification-code forms; message bodies are not included by default. CRUD configuration does not imply the corresponding runtime is installed. +升级前停止程序,备份并保留 `appsettings.json`、`token.txt` 与 `appdata/`,再替换程序及页面资源,避免用包内模板覆盖现有配置。 -Real OneBot 11 account verification, Milky, Telegram and OpenQQ runtimes, game-server Bridge plugins, message forwarding and account-binding workflows, JavaScript plugin execution, AI chat execution, browser-based image rendering/template marketplace, PlaceholderAPI/statistics evaluation, and platform installers/Docker packaging remain outstanding. The API and UI expose only the implemented OneBot 11 adapter; unsupported platform types are rejected instead of being saved as runnable configurations. The OneBot protocol simulator covers authentication, identity handshake, event receipt, sending, and reconnect; no real test bot is configured, and platform rate-limit behavior has not been validated. The original NativeAOT executable cannot be reliably reconstructed from the available decompilation; the separate EasyBot-Recovered repository is used as behavioral reference for its WebUI API and module names. +可将原版 `appsettings.json` 和 `token.txt` 复制到 CE 程序旁,已有字段及端口按原值读取,缺失的 CE 配置项使用默认值。同时运行两套程序时应避免端口冲突。复制启动配置不会导入原版数据库、适配器或插件数据,原版数据库不能直接替换 CE 数据库。 -## Verify +## 已实现与待验证范围 + +管理台和持久化 API 已覆盖适配器、服务器、玩家、账号、群组、允许与拒绝名单、命令定义、AI 服务配置、图片模板目录、JavaScript 插件目录和实例设置。配置数据通过统一记录表保存,启动时执行带版本的数据库迁移;存在配置入口不代表相应运行时已经实现。 + +OneBot 11 是当前唯一可运行的适配器,使用正向 WebSocket,支持 Bearer 认证、身份握手、群聊及私聊事件归一化、消息发送和群列表调用、生命周期控制、状态及能力查询、有限退避重连。程序启动时恢复已启用的配置,API 不返回适配器凭据,编辑时留空会保留已保存的令牌。不支持的平台类型会被拒绝,避免保存成可运行配置。 + +运行日志保留有界内存缓冲,并通过有界异步队列写入 `runtime_logs`。`/api/logs` 支持日志级别、组件、UTC 时间范围、关键词和分页筛选;`/api/logs/status` 提供保留策略和写入失败计数。日志页面可设置持久化开关、保留天数和最大条数,每小时清理一次。日志会遮蔽常见令牌、密码、数据库连接地址及验证码,默认不记录消息正文。 + +以下能力仍待完成或验证: + +- **真实环境验证**:OneBot 11 已通过协议模拟器的认证、握手、事件接收、发送和重连测试;真实账号及平台限流行为尚未验证。 +- **尚未实现**:Milky、Telegram、OpenQQ 适配器运行时,Minecraft 桥接及配套插件,消息转发和账号绑定,JavaScript 插件执行、AI 对话、浏览器图片渲染、模板市场、PlaceholderAPI 与统计求值、平台安装器及 Docker 打包。 +- **参考边界**:原版 NativeAOT 可执行文件无法通过现有反编译资料可靠重建,独立的 EasyBot-Recovered 仓库仅作为管理 API、模块名称和行为的对照依据。 + +## 测试与协作 + +从 `dev/` 目录执行: ```powershell +go test ./... +go vet ./... cd web npm run build -cd .. -go test ./... ``` -To run the shared store contract against a disposable external database, set `EASYBOT_TEST_DATABASE_DSN` and run `go test ./...`. The contract creates test users and records and expects an empty database; do not point it at production data. Without the variable, the external database test is skipped. +外部数据库测试使用 `EASYBOT_TEST_DATABASE_DSN` 指定专用空测试库,再执行 `go test -count=1 -vet=off ./internal/store`。测试会创建用户和记录,不得指向生产数据库;未设置该变量时,外部数据库测试会跳过。当前 CI 验证目标为 MySQL 8.4.11、MySQL 9.6.0 和 PostgreSQL 18.6,具体版本以对应集成测试通过为准。 + +贡献要求见 [贡献规范](../CONTRIBUTING.md) 和 [协作约定](../AGENTS.md)。建议使用中文说明 PR 和提交;中文 CI 仅提醒,不阻断合并。AI 辅助贡献须披露工具及用途,并由贡献者人工审阅和验证,相关要求不纳入自动检测。 diff --git a/dev/web/README.md b/dev/web/README.md index 8b49fac..6236b2f 100644 --- a/dev/web/README.md +++ b/dev/web/README.md @@ -1,11 +1,34 @@ -# CE 管理台 +# EasybotCE 网页管理台 -Vue 3 + TypeScript + Tailwind CSS v4,使用 Vite 构建。依赖 Node.js 20.19+。 +管理台使用 Vue 3、TypeScript 和 Tailwind CSS v4,由 Vite 构建。开发环境要求 Node.js 20.19+。 -上述依赖仅用于源码开发。普通用户下载便携包,解压后直接双击 exe 即可。开发者在仓库根目录执行 `node build.mjs` 可一次完成构建与打包。 +普通用户直接运行便携包中的程序即可,无需安装前端依赖或额外启动网页服务。下载与运行方式见 [项目首页](../../README.md)。 -日常运行在本目录执行 `npm ci`、`npm run build`,然后按 `dev/README.md` 的说明启动 Go 服务(`go run` 时通过 EASYBOT_CONFIG 指定开发配置),或双击已编译的 exe,访问 appsettings.json 中配置的地址,默认 `http://127.0.0.1:5001`。页面和管理 API 由同一个 Go 进程提供,无需单独启动前端服务器。 +## 构建页面 -需要开发热更新时才执行 `npm run dev`,另开终端运行 Go 服务,使用 Vite 输出的临时地址;Vite 将 `/api` 代理到 5001。前端临时端口不属于产品的部署端口。 +在本目录 `dev/web/` 执行: -Bridge 使用独立的 26991 端口;运行时尚未实现。贡献要求见根目录 CONTRIBUTING.md,所有后续提交使用中文。 +```powershell +npm ci +npm run build +``` + +页面输出到 `dist/`。Go 进程同时提供管理 API 和构建后的页面,默认地址为 `http://127.0.0.1:5001`。启动 Go 服务的命令及 `EASYBOT_CONFIG` 配置见 [开发说明](../README.md)。 + +需要完整便携包时,在仓库根目录执行 `node build.mjs`,脚本会完成依赖安装、前端构建、Go 编译及打包。 + +## 热更新开发 + +保持 Go 服务运行,在本目录另开终端执行: + +```powershell +npm run dev +``` + +打开 Vite 输出的开发地址。`/api` 请求会代理到 `http://127.0.0.1:5001`;这个临时端口仅用于开发,不属于产品部署端口。 + +Minecraft 桥接(Bridge)预留 `26991` 端口,运行时尚未实现,当前不监听。管理台页面与 OneBot 适配器均不能替代桥接运行时。 + +## 贡献要求 + +请阅读 [贡献规范](../../CONTRIBUTING.md) 和 [协作约定](../../AGENTS.md)。建议使用中文说明 PR 和提交,中文 CI 仅提醒,不阻断合并;AI 辅助贡献须披露工具及用途,由贡献者人工审阅和验证,相关要求不纳入自动检测。 diff --git "a/docs/\351\241\271\347\233\256\347\273\223\346\236\204\346\243\200\346\237\245.md" "b/docs/\351\241\271\347\233\256\347\273\223\346\236\204\346\243\200\346\237\245.md" new file mode 100644 index 0000000..76c8516 --- /dev/null +++ "b/docs/\351\241\271\347\233\256\347\273\223\346\236\204\346\243\200\346\237\245.md" @@ -0,0 +1,73 @@ +# CE 项目结构与原版功能对照 + +检查日期:2026-11-07。定位:已获授权复刻闭源 EasyBot 的功能和使用流程;CE 是 Go/Vue 独立实现。按单进程、少量管理人员和多个机器人/游戏服连接评估。本次是结构与功能边界检查,不是完整安全审计或协议兼容认证。 + +## 参考依据 + +同组织私有仓库 `EasybotCE/EasyBot-Recovered` 已拉到 Core 的同级目录。本次参考 `linux` 分支、提交 `b3f00bc`。 + +- `decompiled/linux/README.md`:原版 2.2.0-dev.2 发布包恢复记录;34 个产品模块。原版 NativeAOT 主程序无法可靠恢复,WebUI 反编译项目也不能直接重复构建。 +- `decompiled/linux/EasyBot.WebUI/EasyBot.New.WebUI.Resources.appsettings.json`:恢复的原版 WebUI 默认端口 26990。 +- `decompiled/linux/Modules/EasyBot.Bridge.decompiled.cs`:原版 Bridge 默认端口 8080,注册 `/bridge` WebSocket 路径,包含会话、心跳、识别及请求回调等类型。 +- `decompiled/linux/EasyBot.WebUI/EasyBot/New/WebUI/Controllers/`:原版管理 API 及功能入口。 +- 参考仓库的 `src/EasyBot` 是另外编写的 .NET 脚手架,不能当作原版实现证据。 + +私有参考文件继续保留在独立仓库,不并入公开 Core。恢复源码只证明可观察到的功能、接口与行为;缺失代码不推断为已知。 + +## 当前结构 + +```text +Core/ +├── README.md、LICENSE、AUTHORS.md +├── AGENTS.md、CONTRIBUTING.md 协作与中文提交规范(本次新增) +├── PLAN.md、REMAINING_PLAN.md 产品规划与待实现功能 +├── docs/ 检查与设计文档 +├── .github/workflows/ Go、前端、数据库与提交语言检查 +└── dev/ + ├── go.mod、go.sum + ├── cmd/easybot/main.go 进程入口、监听、关闭与运行时装配 + ├── internal/ + │ ├── config/ 监听安全校验 + │ ├── store/ 三种数据库、迁移、认证、配置与日志 + │ ├── runtime/ 内存日志、持久化写入与脱敏 + │ ├── adapter/onebot11/ OneBot 11 连接与生命周期 + │ └── web/ HTTP 路由、认证、管理 API 和静态文件 + └── web/ Vue 3、TypeScript、Vite、Tailwind CSS + ├── src/App.vue 页面、请求、状态和表单集中于此 + └── dist/ 构建生成,由 Go 进程提供(忽略提交) +``` + +现有 `cmd/internal` 模式、模块锁文件、测试、许可证和 CI 已构成可继续开发的骨架。`dev/` 是当前正式实现,名称本身不是错误,现阶段无需为了套目录模板整体搬迁。运行数据库、构建文件、node_modules 和反编译产物已有忽略规则。 + +## 原版与 CE 功能差距 + +| 原版恢复证据 | CE 当前情况 | 下一步验收 | +| --- | --- | --- | +| WebUI Controllers、登录服务 | 管理员初始化、登录、配置 CRUD、运行日志已实现 | Windows/Linux 可重复构建与运行 | +| Adapter.Onebot11/Milky/Telegram/OpenQQ 模块 | OneBot 11 正向 WebSocket 已实现,模拟器已覆盖;其他协议未实现 | 真实账号收发、群发现、限流与重连 | +| Bridge 模块、ServerController | 服务器记录可配置,Bridge 协议与插件未实现 | 身份认证、版本协商、心跳、聊天事件、命令回执 | +| Core、Model、Whitelist 模块 | 玩家、名单、命令、同步规则等可保存配置,未形成消息与绑定闭环 | 群服双向消息、防回环、绑定和权限验证 | +| Ai.OpenAi、AiChatOs、AiController | 有 AI 配置目录,无 AI 对话执行链路 | 超时、限流、上下文与真实调用 | +| ImageTemplate、Skin、NbtLib、MineRenderer | 有模板/皮肤等目录入口,无对应渲染与物品预览链路 | 资源限制、模板渲染和真实预览 | +| Scripting、V8Plugin、Plugin.Api、LGraph.Runtime | 有插件目录,无脚本隔离与执行运行时 | 权限、生命周期、资源上限和故障隔离 | +| DataPersistence、Statistics 模块 | SQLite 默认存储及 MySQL/PostgreSQL 实现;业务多用通用 records 表 | 独立领域约束、迁移和统计口径 | + +## 检查发现及处理顺序 + +1. **必须优先:首个群服消息闭环尚未完成。** 原版服务器页有实际会话与能力查询,CE 目前只有服务器配置,不能把记录数当在线数。先实现 26991 Bridge 与一个真实插件端,再做消息路由、回执及防回环;否则管理台仍无法完成复刻目标的核心任务。 +2. **应修复:规划与实现描述有漂移。** 根说明及 PLAN 中仍有适配器尚未实现等早期表述,实际 OneBot 11 运行时已存在;开发说明里的旧本地反编译路径也不成立。本次修正根说明与参考路径,后续逐项按“配置、模拟、真实验证”更新验收表,避免按页面数量计算完成度。 +3. **应修复:业务模型仍缺领域约束。** `store.Record.Detail` 是通用 JSON 字符串,`Save` 主要校验类型与名称;玩家绑定唯一性、路由引用和命令权限目前不能依靠该模型完整保证。进入对应运行时开发时增加具体校验、数据库约束与事务测试,不提前新建空模块。 +4. **应修复:前端和 HTTP 层职责集中。** `App.vue` 集中导航、登录、所有管理页面、请求及轮询;`web.go` 集中路由、认证与多个业务处理器。并行增加 Bridge、绑定和路由时会频繁修改同文件。按实际功能增量抽取 API 客户端、页面及后端处理器,保留单体架构,不以行数机械拆文件。 +5. **应修复:交付验证缺口。** 原 CI 仅 Linux Go 测试/静态检查与前端构建,未持续覆盖 Windows 可执行程序构建。本次扩展 Windows/Linux Go 测试、vet 与构建。仍需后续补齐把前端与 exe 一起交付的发布包流程;当前 exe 依赖 `web/dist`,不能当作单文件发布版。 +6. **已补基础规则:所有开发者使用中文提交。** 新增贡献规范、AGENTS 和提交标题检查;既有英文历史保持不变。CI 只能判断标题包含汉字,不能理解是否中文表达。GitHub 必需检查与分支保护需要在远端设置后才能强制所有贡献者遵守中文提交规范。 + +## 端口与启动约定 + +- CE 正常运行:5001 同时提供 WebUI 和管理 API;26991 用于 Bridge。对照恢复的原版默认值 26990/8080,二者不重合;用户自行修改过的原版端口不在此结论范围内。 +- Bridge 尚未实现,因此目前仅实际监听 5001,不占用 26991。 +- 日常运行先构建前端,随后启动 Go/exe;前端由同一进程提供。Vite 仅为可选热更新工具,它的临时端口不属于产品部署端口。 +- `EASYBOT_ADDR` 继续支持显式覆盖;外部监听仍遵循既有密钥和 TLS 校验。 + +## 检查边界 + +已检查项目目录、入口、构建/数据库 CI、依赖、HTTP 认证与存储代表路径、OneBot 管理器、前端集中结构及原版模块/服务器控制器/Bridge 配置。未逐行审查全部反编译代码;未做真实 QQ/Minecraft 联调、外部数据库实测、完整安全审计、Linux 本机执行或跨版本插件兼容认证。下一开发优先级是 Bridge 协议与真实游戏端闭环,不是目录重排。