mirror of
https://github.com/whyour/qinglong.git
synced 2026-10-16 22:35:32 +08:00
feat: add script version history (#3085)
* feat: add script version history * fix: validate canonical save-as paths before backup * fix: stack script history footer on small screens
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# OpenAPI 全量参考 / Complete OpenAPI reference
|
||||
|
||||
CLI 对应当前 develop 的 143 条有效路由。通过 `ql api routes --json` 查看方法、路径、对应命令、位置参数、请求体与上传字段;CLI 测试与 back/api 路由逐项核对。GET configs/:file、scripts/:file、logs/:file 已返回 410,改用 detail 命令,不再暴露旧入口。旧版面板可能没有新路由,以服务端响应为准。
|
||||
CLI 对应本版本后端的 146 条有效路由。通过 `ql api routes --json` 查看方法、路径、对应命令、位置参数、请求体与上传字段;CLI 测试与 back/api 路由逐项核对。GET configs/:file、scripts/:file、logs/:file 已返回 410,改用 detail 命令,不再暴露旧入口。旧版面板可能没有新路由,以服务端响应为准。
|
||||
|
||||
The catalogue covers 143 active routes in the current develop backend. `ql api routes --json` lists methods, paths, commands, positional parameters, bodies and upload fields. A route-coverage test detects drift. Three retired GET :file routes return 410; use detail-based commands. Older panel versions may not implement newer routes.
|
||||
The catalogue covers 146 active routes in the current backend. `ql api routes --json` lists methods, paths, commands, positional parameters, bodies and upload fields. A route-coverage test detects drift. Three retired GET :file routes return 410; use detail-based commands. Older panel versions may not implement newer routes.
|
||||
|
||||
## 认证与权限 / Authentication and permissions
|
||||
|
||||
@@ -46,6 +46,9 @@ ql env create --data @envs.json --json
|
||||
ql env update 8 --data '{"name":"EXAMPLE","value":"value"}' --json
|
||||
ql script create --file ./demo.js --data '{"filename":"demo.js","path":""}' --json
|
||||
ql script get --query '{"file":"demo.js","path":""}' --json
|
||||
ql script history-list --query '{"filename":"demo.js","path":""}' --json
|
||||
ql script history-detail --query '{"filename":"demo.js","path":"","id":"VERSION_UUID"}' --json
|
||||
ql script history-restore --data '{"filename":"demo.js","path":"","id":"VERSION_UUID","expectedHash":"CURRENT_SHA256"}' --json
|
||||
ql config save --data '{"name":"example.sh","content":"# example"}' --json
|
||||
ql config get --query '{"path":"example.sh"}' --json
|
||||
ql log download --data '{"filename":"example.log","path":"example"}' --output ./example.log --json
|
||||
@@ -133,6 +136,9 @@ System/user operations below execute remotely through the panel API. They do not
|
||||
| `ql config save ` | POST | `configs/save` | --data, --query |
|
||||
| `ql script list ` | GET | `scripts` | --query |
|
||||
| `ql script get ` | GET | `scripts/detail` | --query |
|
||||
| `ql script history-list ` | GET | `scripts/history` | --query |
|
||||
| `ql script history-detail ` | GET | `scripts/history/detail` | --query |
|
||||
| `ql script history-restore ` | PUT | `scripts/history/restore` | --data, --query |
|
||||
| `ql script create ` | POST | `scripts` | --data, --file (file), --query |
|
||||
| `ql script update ` | PUT | `scripts` | --data, --file (file), --query |
|
||||
| `ql script delete ` | DELETE | `scripts` | --data, --query |
|
||||
|
||||
@@ -275,6 +275,18 @@ export const openOperations: readonly OpenOperation[] = [
|
||||
{ name: 'config save', method: 'POST', path: 'configs/save', body: 'json' },
|
||||
{ name: 'script list', method: 'GET', path: 'scripts' },
|
||||
{ name: 'script get', method: 'GET', path: 'scripts/detail' },
|
||||
{ name: 'script history-list', method: 'GET', path: 'scripts/history' },
|
||||
{
|
||||
name: 'script history-detail',
|
||||
method: 'GET',
|
||||
path: 'scripts/history/detail',
|
||||
},
|
||||
{
|
||||
name: 'script history-restore',
|
||||
method: 'PUT',
|
||||
path: 'scripts/history/restore',
|
||||
body: 'json',
|
||||
},
|
||||
{
|
||||
name: 'script create',
|
||||
method: 'POST',
|
||||
|
||||
@@ -58,6 +58,31 @@ test('dashboard record sends execution statistics and rejects a missing body bef
|
||||
assert.equal(f.requests.length, 1);
|
||||
});
|
||||
|
||||
test('script history commands send exact query parameters and restore payloads', async t => {
|
||||
const f = await fixture(t);
|
||||
const identity = { filename: '中文 script.js', path: 'nested folder' };
|
||||
const version = { ...identity, id: '32ec8b22-2c48-4bcf-85d4-b394198417ba' };
|
||||
for (const [command, endpoint, query] of [
|
||||
['history-list', 'scripts/history', identity],
|
||||
['history-detail', 'scripts/history/detail', version],
|
||||
]) {
|
||||
success(await f.run(['script', command, '--query', JSON.stringify(query)]));
|
||||
const req = f.requests.at(-1), url = new URL(req.url, 'http://localhost');
|
||||
assert.equal(req.method, 'GET');
|
||||
assert.equal(url.pathname, '/panel/open/' + endpoint);
|
||||
for (const [key, value] of Object.entries(query)) assert.equal(url.searchParams.get(key), value);
|
||||
assert.equal(req.body, '');
|
||||
}
|
||||
const payload = { ...version, expectedHash: 'a'.repeat(64) };
|
||||
success(await f.run(['script', 'history-restore', '--data', '-'], JSON.stringify(payload)));
|
||||
assert.equal(f.requests.at(-1).method, 'PUT');
|
||||
assert.equal(f.requests.at(-1).url, '/panel/open/scripts/history/restore');
|
||||
assert.deepEqual(JSON.parse(f.requests.at(-1).body), payload);
|
||||
const count = f.requests.length;
|
||||
assert.equal((await f.run(['script', 'history-restore'])).code, 2);
|
||||
assert.equal(f.requests.length, count, 'missing restore payload must not issue a request');
|
||||
});
|
||||
|
||||
test('OpenAPI catalogue covers every active registered backend route; retired 410 routes are explicit', async () => {
|
||||
const root = path.resolve(__dirname, '../../../back/api');
|
||||
const expected = [];
|
||||
|
||||
Reference in New Issue
Block a user