From b789bf28da38e79ce5aee665b90df7c8861d9fc4 Mon Sep 17 00:00:00 2001 From: Zhu Chenrui Date: Fri, 25 Jul 2025 19:46:29 +0800 Subject: [PATCH 1/9] Update Update.json Signed-off-by: Zhu Chenrui --- Update.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Update.json b/Update.json index 8674920b..e4384e5b 100644 --- a/Update.json +++ b/Update.json @@ -2937,7 +2937,7 @@ ], "Notes": "No release notes were provided for this release." }, - "1.10.0": { + "1.999990.0": { "UpdateDate": 1753443146018, "Prerelease": false, "UpdateContents": [ @@ -2949,4 +2949,4 @@ "Notes": "No release notes were provided for this release." } } -} \ No newline at end of file +} From 6adac8da5181731071e8d7fad87fe9552e93c96f Mon Sep 17 00:00:00 2001 From: Zhu Chenrui Date: Sun, 24 Aug 2025 11:07:02 +0800 Subject: [PATCH 2/9] Parse release notes from comment block (cherry picked from commit c7137ff7122b2307b9ea78a24de240b3bd00c6ab) --- .github/workflows/UpdateToRelease.yml | 4 +++- .github/workflows/UpdateVersion.yml | 4 +++- CONTRIBUTING.md | 1 + Update/UpdateToRelease.js | 10 +++++++++- Update/UpdateVersion.js | 13 ++++++++++++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/UpdateToRelease.yml b/.github/workflows/UpdateToRelease.yml index ba950e34..e401f757 100644 --- a/.github/workflows/UpdateToRelease.yml +++ b/.github/workflows/UpdateToRelease.yml @@ -5,6 +5,7 @@ on: - opened - reopened - synchronize + - edited branches: - master jobs: @@ -24,7 +25,8 @@ jobs: gh pr comment ${{ github.event.pull_request.number }} --body "请向\`dev\`分支提交pull request, 本pull request将被自动关闭" gh pr close ${{ github.event.pull_request.number }} else - node ./Update/UpdateToRelease.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.number }} + node ./Update/UpdateToRelease.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.number }} "$PR_BODY" fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_BODY: ${{ github.event.pull_request.body }} diff --git a/.github/workflows/UpdateVersion.yml b/.github/workflows/UpdateVersion.yml index 8ab253d5..b71fbb1f 100644 --- a/.github/workflows/UpdateVersion.yml +++ b/.github/workflows/UpdateVersion.yml @@ -23,4 +23,6 @@ jobs: private-key: ${{ secrets.APP_PRIVATE_KEY }} - uses: actions/checkout@v5 - name: Update version - run: node ./Update/UpdateVersion.js ${{ steps.generate_token.outputs.token }} ${{ github.event.number }} "${{ github.event.pull_request.title }}" + env: + PR_BODY: ${{ github.event.pull_request.body }} + run: node ./Update/UpdateVersion.js ${{ steps.generate_token.outputs.token }} ${{ github.event.number }} "${{ github.event.pull_request.title }}" "$PR_BODY" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d357e825..0f5c6e14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,3 +18,4 @@ We believe that you must be excited to contribute to our repo, but first, please - Be patient. We are a small team and may not be able to review your PR immediately. - Please be considerate towards the developers and other users when raising issues or presenting pull requests. - Respect our decision(s), and do not be upset or abusive if your submission is not used. +- For release pull requests, include an HTML comment block starting with `` in the PR description. The automation will extract that block into the release notes. diff --git a/Update/UpdateToRelease.js b/Update/UpdateToRelease.js index 37256782..c675452c 100644 --- a/Update/UpdateToRelease.js +++ b/Update/UpdateToRelease.js @@ -3,6 +3,13 @@ import {execSync} from "child_process"; var GithubToken = process.argv[2]; var PRNumber = process.argv[3]; +function extractReleaseNotes(body) { + const match = body + .replace(/\r\n/g, "\n") + .match(//i); + return match ? match[1].trim() : ""; +} +var CurrentNotes = extractReleaseNotes(String(process.argv[4] || "")); process.env.GITHUB_TOKEN = GithubToken; execSync("gh pr checkout " + PRNumber); console.info("PR #" + PRNumber + " has been checked out."); @@ -45,6 +52,7 @@ console.log("Last JSON version : " + LastJSONVersion); console.log("Last PR : " + LastPR); console.log("Last type : " + LastType); console.log("npm version : " + NpmVersion); +console.log("Current notes : " + (CurrentNotes || "No release notes were provided for this release.")); if (LastJSONVersion != LastJSVersion) { console.error("XMOJ.user.js and Update.json have different patch versions."); @@ -67,7 +75,7 @@ JSONObject.UpdateHistory[CurrentVersion] = { "UpdateDate": Date.now(), "Prerelease": false, "UpdateContents": [], - "Notes": "No release notes were provided for this release." + "Notes": CurrentNotes || "No release notes were provided for this release." }; for (var i = Object.keys(JSONObject.UpdateHistory).length - 2; i >= 0; i--) { diff --git a/Update/UpdateVersion.js b/Update/UpdateVersion.js index 1fab8e5e..840ec2b8 100644 --- a/Update/UpdateVersion.js +++ b/Update/UpdateVersion.js @@ -47,6 +47,13 @@ execSync("git config --global user.email \"github-actions[bot]@users.noreply.git execSync("git config --global user.name \"github-actions[bot]\""); var CurrentPR = Number(PRNumber); var CurrentDescription = String(process.argv[4]); +function extractReleaseNotes(body) { + const match = body + .replace(/\r\n/g, "\n") + .match(//i); + return match ? match[1].trim() : ""; +} +var CurrentNotes = extractReleaseNotes(String(process.argv[5] || "")); if (LastJSVersion != NpmVersion) { console.warn("Assuming you manually ran npm version."); } else if (!(LastPR == CurrentPR && NpmVersion == LastJSVersion)) { @@ -58,6 +65,7 @@ var CurrentVersion = execSync("jq -r '.version' package.json").toString().trim() console.log("Current version : " + CurrentVersion); console.log("Current PR : " + CurrentPR); console.log("Current description: " + CurrentDescription); +console.log("Current notes : " + (CurrentNotes || "No release notes were provided for this release.")); var ChangedFileList = execSync("gh pr diff " + CurrentPR + " --name-only").toString().trim().split("\n"); console.log("Changed files : " + ChangedFileList.join(", ")); @@ -67,6 +75,9 @@ if (LastPR == CurrentPR && NpmVersion == LastJSVersion) { console.warn("Warning: PR is the same as last version."); JSONObject.UpdateHistory[LastJSVersion].UpdateDate = Date.now(); JSONObject.UpdateHistory[LastJSVersion].UpdateContents[0].Description = CurrentDescription; + if (CurrentNotes) { + JSONObject.UpdateHistory[LastJSVersion].Notes = CurrentNotes; + } CommitMessage = "Update time and description of " + LastJSVersion; } else if (ChangedFileList.indexOf("XMOJ.user.js") == -1) { console.warn("XMOJ.user.js is not changed, so the version should not be updated."); @@ -79,7 +90,7 @@ if (LastPR == CurrentPR && NpmVersion == LastJSVersion) { "PR": CurrentPR, "Description": CurrentDescription }], - "Notes": "No release notes were provided for this release." + "Notes": CurrentNotes || "No release notes were provided for this release." }; writeFileSync(JSFileName, JSFileContent.replace(/@version(\s+)\d+\.\d+\.\d+/, "@version$1" + CurrentVersion), "utf8"); console.warn("XMOJ.user.js has been updated."); From f86a45eeda019268fdabb67f03000f616a31da95 Mon Sep 17 00:00:00 2001 From: Zhu Chenrui Date: Sun, 24 Aug 2025 11:36:00 +0800 Subject: [PATCH 3/9] Update bug.yml Signed-off-by: Zhu Chenrui (cherry picked from commit 07d7590955a2913e422e500613f140f7619ff364) Update feature.yml Signed-off-by: Zhu Chenrui (cherry picked from commit 1a99430f1edf90e782b984b956dcb78efd6e88db) Update docs.yml Signed-off-by: Zhu Chenrui (cherry picked from commit 6017bcf99acdb37c877633c4f13f9851393fa1af) --- .github/ISSUE_TEMPLATE/bug.yml | 1 + .github/ISSUE_TEMPLATE/docs.yml | 1 + .github/ISSUE_TEMPLATE/feature.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 9e03a313..bf1f6784 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,6 +1,7 @@ name: 综合 Bug 反馈 description: 有功能有问题 labels: bug, needs-triage +type: Bug # assignees: PythonSmall-Q, boomzero, shihongxi title: "[Bug]" body: diff --git a/.github/ISSUE_TEMPLATE/docs.yml b/.github/ISSUE_TEMPLATE/docs.yml index 8152f0a9..982e8c57 100644 --- a/.github/ISSUE_TEMPLATE/docs.yml +++ b/.github/ISSUE_TEMPLATE/docs.yml @@ -1,6 +1,7 @@ name: 帮助文档反馈 description: 帮助文档有问题 labels: docs, needs-triage +type: Task title: "[Docs]" body: - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 9fbae4f5..90d3177d 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,6 +1,7 @@ name: 新功能提案 description: 对已有功能的大幅度修改,或添加一个新内容或选项 labels: enhancement, needs-triage +type: Feature #assignees: PythonSmall-Q, boomzero, shihongxi title: "[Feature Request]" body: From 54d39cf3357e0e94ce95c50559de1a735b272872 Mon Sep 17 00:00:00 2001 From: Shan Wenxiao Date: Sat, 7 Feb 2026 09:14:08 +0800 Subject: [PATCH 4/9] Update GitHub Actions workflow to skip bot triggers Signed-off-by: Shan Wenxiao --- .github/workflows/UpdateToRelease.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/UpdateToRelease.yml b/.github/workflows/UpdateToRelease.yml index e401f757..5f82aa0f 100644 --- a/.github/workflows/UpdateToRelease.yml +++ b/.github/workflows/UpdateToRelease.yml @@ -11,11 +11,13 @@ on: jobs: UpdateToRelease: runs-on: ubuntu-latest + # 添加条件:如果是 bot 触发的则跳过,避免无限循环 + if: github.event.pull_request.user.login != 'github-actions[bot]' permissions: pull-requests: write contents: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Update to release @@ -25,7 +27,7 @@ jobs: gh pr comment ${{ github.event.pull_request.number }} --body "请向\`dev\`分支提交pull request, 本pull request将被自动关闭" gh pr close ${{ github.event.pull_request.number }} else - node ./Update/UpdateToRelease.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.number }} "$PR_BODY" + node ./Update/UpdateToRelease.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.pull_request.number }} "$PR_BODY" fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 27863a74aad62f6ed0dd7d9b2a4da6bfc3ec0270 Mon Sep 17 00:00:00 2001 From: boomzero Date: Wed, 11 Feb 2026 20:59:12 +0800 Subject: [PATCH 5/9] Prevent UpdateVersion from running if last commit was by github-actions[bot] This prevents infinite loops where the bot commits version updates, which triggers the workflow again, causing another commit. Co-Authored-By: Claude Sonnet 4.5 --- Update/UpdateVersion.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Update/UpdateVersion.js b/Update/UpdateVersion.js index 840ec2b8..cd744cd8 100644 --- a/Update/UpdateVersion.js +++ b/Update/UpdateVersion.js @@ -7,6 +7,14 @@ process.env.GITHUB_TOKEN = GithubToken; execSync("gh pr checkout " + PRNumber); console.info("PR #" + PRNumber + " has been checked out."); +// Check if the last commit was made by github-actions[bot] +const lastCommitAuthor = execSync("git log -1 --pretty=format:'%an'").toString().trim(); +console.log("Last commit author: " + lastCommitAuthor); +if (lastCommitAuthor === "github-actions[bot]") { + console.log("Last commit was made by github-actions[bot]. Skipping to prevent infinite loop."); + process.exit(0); +} + const JSONFileName = "./Update.json"; const JSFileName = "./XMOJ.user.js"; var JSONFileContent = readFileSync(JSONFileName, "utf8"); From fab65934b678092f505e2c20c02d2ff4cc707788 Mon Sep 17 00:00:00 2001 From: boomzero Date: Mon, 23 Feb 2026 20:06:47 +0800 Subject: [PATCH 6/9] Allow metadata updates on edited PRs after bot version commit The last-commit-author guard now only exits for non-edited events, so PR title/body changes still update Update.json metadata even when the branch tip is a github-actions[bot] commit. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/UpdateVersion.yml | 2 +- Update/UpdateVersion.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/UpdateVersion.yml b/.github/workflows/UpdateVersion.yml index 74a49224..b8409176 100644 --- a/.github/workflows/UpdateVersion.yml +++ b/.github/workflows/UpdateVersion.yml @@ -26,4 +26,4 @@ jobs: - name: Update version env: PR_BODY: ${{ github.event.pull_request.body }} - run: node ./Update/UpdateVersion.js ${{ steps.generate_token.outputs.token }} ${{ github.event.number }} "${{ github.event.pull_request.title }}" "$PR_BODY" + run: node ./Update/UpdateVersion.js ${{ steps.generate_token.outputs.token }} ${{ github.event.number }} "${{ github.event.pull_request.title }}" "$PR_BODY" "${{ github.event.action }}" diff --git a/Update/UpdateVersion.js b/Update/UpdateVersion.js index cd744cd8..891d2c46 100644 --- a/Update/UpdateVersion.js +++ b/Update/UpdateVersion.js @@ -8,9 +8,14 @@ execSync("gh pr checkout " + PRNumber); console.info("PR #" + PRNumber + " has been checked out."); // Check if the last commit was made by github-actions[bot] +// Only skip for synchronize events (push-triggered) to prevent infinite loops. +// For edited events (PR title/body changes), allow metadata updates even when +// the branch tip is a bot commit. +const eventAction = String(process.argv[6] || ""); const lastCommitAuthor = execSync("git log -1 --pretty=format:'%an'").toString().trim(); console.log("Last commit author: " + lastCommitAuthor); -if (lastCommitAuthor === "github-actions[bot]") { +console.log("Event action : " + eventAction); +if (lastCommitAuthor === "github-actions[bot]" && eventAction !== "edited") { console.log("Last commit was made by github-actions[bot]. Skipping to prevent infinite loop."); process.exit(0); } From 2f7e9d6a45f04f3fe3238f45ba895fe7277c9ecf Mon Sep 17 00:00:00 2001 From: boomzero Date: Mon, 23 Feb 2026 20:06:47 +0800 Subject: [PATCH 7/9] Allow metadata updates on edited PRs after bot version commit Exclude all bot actors (not just github-actions[bot]) from triggering the UpdateVersion workflow, preventing loops from AI code review bots. Allow edited events through the script-level guard so PR title/body changes still update Update.json metadata. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/UpdateVersion.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/UpdateVersion.yml b/.github/workflows/UpdateVersion.yml index b8409176..6112841f 100644 --- a/.github/workflows/UpdateVersion.yml +++ b/.github/workflows/UpdateVersion.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write contents: write runs-on: ubuntu-latest - if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'github-actions[bot]' + if: github.event.pull_request.head.repo.full_name == github.repository && !endsWith(github.actor, '[bot]') steps: - name: Generate a token id: generate_token From 636b037711b51ea3765649bfc8230c836ec77e4e Mon Sep 17 00:00:00 2001 From: Zhu Chenrui Date: Sun, 15 Mar 2026 15:46:04 +0800 Subject: [PATCH 8/9] feat: add experimental webui for xmoj short messages --- README.md | 9 +- index.html | 3 + webui.html | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 webui.html diff --git a/README.md b/README.md index 32387e6b..6f8804c8 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,14 @@ 请参考 [官网介绍](https://www.xmoj-bbs.me) 。 如果您无法打开该网站,请前往[这里](https://scriptcat.org/zh-CN/script-show-page/1500/)安装。 +### 实验功能:短消息 WebUI + +仓库中新增了一个实验页面 `webui.html`,用于在不安装用户脚本时访问短消息相关能力(拉取会话、查看消息、发送消息、粘贴上传图片)。 + +- 登录方式:支持输入用户名/密码后尝试获取 `PHPSESSID`,也支持直接粘贴 `PHPSESSID`。 +- 安全策略:密码仅用于发起登录请求,不会保存;会话信息仅存储在当前标签页的 `sessionStorage`,可一键清除。 +- 维护策略:API 调用格式与现有脚本中的 `RequestAPI` 保持一致,便于后续复用和统一维护。 + ### 贡献 您想为我们的脚本添砖加瓦吗?快加入我们,为小明的OJ用户创造更美好的环境!(具体要求参见Code Of Conduct) @@ -105,4 +113,3 @@ - diff --git a/index.html b/index.html index 268a37b2..a5901bc6 100644 --- a/index.html +++ b/index.html @@ -41,6 +41,9 @@ + diff --git a/webui.html b/webui.html new file mode 100644 index 00000000..4f8e2434 --- /dev/null +++ b/webui.html @@ -0,0 +1,425 @@ + + + + + + XMOJ 短消息 WebUI(实验) + + + + + +
+

XMOJ 短消息 WebUI(实验)

+

为了兼顾安全与可维护性:密码仅用于登录请求,不落盘;会话仅存储在 sessionStorage;支持手动清除会话。

+ +
+
+

1) 登录 / 会话

+
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+
+
+
+ +
+
+
+
+
+

2) 会话列表

+ +
+
+
+
+
+
+
+
+
+

3) 对话内容

+ +
+
请先选择一个会话。
+
+
+
+
+
+

4) 发送消息 / 图片

+
+ +
+
+ + +
+
+
+
+
+
+ + + + From b7c77e6262cadba6f92b2f80be9ce64470f8586b Mon Sep 17 00:00:00 2001 From: Zhu Chenrui Date: Sun, 15 Mar 2026 15:58:12 +0800 Subject: [PATCH 9/9] fix: add proxy mode for webui login and api calls --- README.md | 6 +- tools/webui-proxy.mjs | 146 +++++++++++++++++++++++ webui.html | 268 +++++++++++++++++++----------------------- 3 files changed, 269 insertions(+), 151 deletions(-) create mode 100644 tools/webui-proxy.mjs diff --git a/README.md b/README.md index 6f8804c8..2dfc6b00 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,10 @@ 仓库中新增了一个实验页面 `webui.html`,用于在不安装用户脚本时访问短消息相关能力(拉取会话、查看消息、发送消息、粘贴上传图片)。 -- 登录方式:支持输入用户名/密码后尝试获取 `PHPSESSID`,也支持直接粘贴 `PHPSESSID`。 -- 安全策略:密码仅用于发起登录请求,不会保存;会话信息仅存储在当前标签页的 `sessionStorage`,可一键清除。 +- 推荐启动代理模式:`node tools/webui-proxy.mjs`,然后访问 `http://127.0.0.1:8787/webui.html`。 +- 代理模式下支持直接输入用户名/密码登录并自动获取 `PHPSESSID`;同时也支持手动粘贴 `PHPSESSID`。 +- 直连模式(直接打开页面)受浏览器跨域限制,账号密码登录可能不可用,此时建议改用代理模式。 +- 安全策略:密码仅用于发起登录请求,不会在前端存储;会话信息仅存储在当前标签页的 `sessionStorage`,可一键清除。 - 维护策略:API 调用格式与现有脚本中的 `RequestAPI` 保持一致,便于后续复用和统一维护。 ### 贡献 diff --git a/tools/webui-proxy.mjs b/tools/webui-proxy.mjs new file mode 100644 index 00000000..f198a926 --- /dev/null +++ b/tools/webui-proxy.mjs @@ -0,0 +1,146 @@ +#!/usr/bin/env node +import { createServer } from 'node:http'; +import { readFile } from 'node:fs/promises'; +import { createHash } from 'node:crypto'; +import { extname, join, normalize } from 'node:path'; + +const HOST = process.env.HOST || '0.0.0.0'; +const PORT = Number(process.env.PORT || 8787); +const ROOT = process.cwd(); + +const MIME = { + '.html': 'text/html; charset=utf-8', + '.js': 'application/javascript; charset=utf-8', + '.mjs': 'application/javascript; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.svg': 'image/svg+xml', + '.ico': 'image/x-icon' +}; + +const MAX_BODY = 15 * 1024 * 1024; + +function json(res, code, payload) { + res.writeHead(code, { 'Content-Type': 'application/json; charset=utf-8' }); + res.end(JSON.stringify(payload)); +} + +function parseCookie(setCookie = '') { + const match = setCookie.match(/PHPSESSID=([^;]+)/i); + return match ? match[1] : ''; +} + +function md5(input) { + return createHash('md5').update(input).digest('hex'); +} + +async function readBody(req) { + const chunks = []; + let size = 0; + for await (const chunk of req) { + size += chunk.length; + if (size > MAX_BODY) throw new Error('请求体过大'); + chunks.push(chunk); + } + if (chunks.length === 0) return {}; + const raw = Buffer.concat(chunks).toString('utf8'); + return raw ? JSON.parse(raw) : {}; +} + +async function handleLogin(req, res) { + const body = await readBody(req); + const username = String(body.username || '').trim(); + const password = String(body.password || ''); + if (!username || !password) { + return json(res, 400, { success: false, message: '用户名或密码不能为空。' }); + } + + const form = new URLSearchParams(); + form.set('user_id', username); + form.set('password', md5(password)); + + const response = await fetch('https://www.xmoj.tech/login.php', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: form.toString(), + redirect: 'manual' + }); + + const text = await response.text(); + const setCookie = response.headers.get('set-cookie') || ''; + const sessionId = parseCookie(setCookie); + + if (!sessionId) { + const maybeSuccess = text.includes('history.go(-2);'); + return json(res, 401, { + success: false, + message: maybeSuccess ? '登录结果无法解析,请重试。' : '用户名或密码错误。' + }); + } + + return json(res, 200, { + success: true, + username, + sessionId + }); +} + +async function handleApiProxy(req, res, action) { + const body = await readBody(req); + const response = await fetch(`https://api.xmoj-bbs.me/${action}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }); + const text = await response.text(); + res.writeHead(response.status, { 'Content-Type': 'application/json; charset=utf-8' }); + res.end(text); +} + +async function serveStatic(req, res, pathname) { + const clean = normalize(pathname).replace(/^\/+/, ''); + const file = clean === '' ? 'index.html' : clean; + if (file.includes('..')) { + res.writeHead(403); + res.end('Forbidden'); + return; + } + const fullPath = join(ROOT, file); + try { + const data = await readFile(fullPath); + const type = MIME[extname(fullPath)] || 'application/octet-stream'; + res.writeHead(200, { 'Content-Type': type }); + res.end(data); + } catch { + res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); + res.end('Not Found'); + } +} + +createServer(async (req, res) => { + try { + const url = new URL(req.url || '/', `http://${req.headers.host}`); + const pathname = url.pathname; + + if (pathname === '/__webui/health') { + return json(res, 200, { success: true, mode: 'proxy' }); + } + if (pathname === '/__webui/login' && req.method === 'POST') { + return await handleLogin(req, res); + } + if (pathname.startsWith('/__webui/api/') && req.method === 'POST') { + const action = pathname.substring('/__webui/api/'.length); + if (!action) return json(res, 400, { success: false, message: '缺少 action' }); + return await handleApiProxy(req, res, action); + } + + return await serveStatic(req, res, pathname); + } catch (error) { + return json(res, 500, { success: false, message: error.message || 'Internal error' }); + } +}).listen(PORT, HOST, () => { + console.log(`[webui-proxy] http://${HOST}:${PORT}`); +}); diff --git a/webui.html b/webui.html index 4f8e2434..de0d7de4 100644 --- a/webui.html +++ b/webui.html @@ -22,7 +22,8 @@

XMOJ 短消息 WebUI(实验)

-

为了兼顾安全与可维护性:密码仅用于登录请求,不落盘;会话仅存储在 sessionStorage;支持手动清除会话。

+

密码仅用于单次登录请求,不落盘;会话仅保存在当前标签页的 sessionStorage,可手动清除。

+
正在检测运行模式...
@@ -37,11 +38,11 @@

1) 登录 / 会话

- +
- +
@@ -91,16 +92,17 @@

4) 发送消息 / 图片