@cloudglab/yapi-cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +131 -0
- package/dist/bin/yapi.d.ts +2 -0
- package/dist/bin/yapi.js +8 -0
- package/dist/bin/yapi.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +203 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/changelog.d.ts +17 -0
- package/dist/core/changelog.js +117 -0
- package/dist/core/changelog.js.map +1 -0
- package/dist/core/cli-output.d.ts +13 -0
- package/dist/core/cli-output.js +65 -0
- package/dist/core/cli-output.js.map +1 -0
- package/dist/core/cli-registry.d.ts +38 -0
- package/dist/core/cli-registry.js +189 -0
- package/dist/core/cli-registry.js.map +1 -0
- package/dist/core/completion.d.ts +3 -0
- package/dist/core/completion.js +94 -0
- package/dist/core/completion.js.map +1 -0
- package/dist/core/doctor.d.ts +13 -0
- package/dist/core/doctor.js +124 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/errors.d.ts +38 -0
- package/dist/core/errors.js +74 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/output.d.ts +27 -0
- package/dist/core/output.js +208 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/update-probe.d.ts +14 -0
- package/dist/core/update-probe.js +67 -0
- package/dist/core/update-probe.js.map +1 -0
- package/dist/core/write-guard.d.ts +25 -0
- package/dist/core/write-guard.js +36 -0
- package/dist/core/write-guard.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +13 -0
- package/dist/install.js +201 -0
- package/dist/install.js.map +1 -0
- package/dist/services/yapi/api.d.ts +696 -0
- package/dist/services/yapi/api.js +700 -0
- package/dist/services/yapi/api.js.map +1 -0
- package/dist/services/yapi/auth.d.ts +37 -0
- package/dist/services/yapi/auth.js +143 -0
- package/dist/services/yapi/auth.js.map +1 -0
- package/dist/services/yapi/authCache.d.ts +25 -0
- package/dist/services/yapi/authCache.js +80 -0
- package/dist/services/yapi/authCache.js.map +1 -0
- package/dist/services/yapi/cache.d.ts +27 -0
- package/dist/services/yapi/cache.js +88 -0
- package/dist/services/yapi/cache.js.map +1 -0
- package/dist/services/yapi/config.d.ts +12 -0
- package/dist/services/yapi/config.js +66 -0
- package/dist/services/yapi/config.js.map +1 -0
- package/dist/services/yapi/index.d.ts +6 -0
- package/dist/services/yapi/index.js +6 -0
- package/dist/services/yapi/index.js.map +1 -0
- package/dist/services/yapi/logger.d.ts +6 -0
- package/dist/services/yapi/logger.js +36 -0
- package/dist/services/yapi/logger.js.map +1 -0
- package/dist/services/yapi/types.d.ts +176 -0
- package/dist/services/yapi/types.js +2 -0
- package/dist/services/yapi/types.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/shared.d.ts +33 -0
- package/dist/tools/shared.js +46 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/yapi/docs-sync.d.ts +29 -0
- package/dist/tools/yapi/docs-sync.js +243 -0
- package/dist/tools/yapi/docs-sync.js.map +1 -0
- package/dist/tools/yapi/index.d.ts +6 -0
- package/dist/tools/yapi/index.js +7 -0
- package/dist/tools/yapi/index.js.map +1 -0
- package/dist/tools/yapi/register-auth.d.ts +2 -0
- package/dist/tools/yapi/register-auth.js +182 -0
- package/dist/tools/yapi/register-auth.js.map +1 -0
- package/dist/tools/yapi/register-group.d.ts +2 -0
- package/dist/tools/yapi/register-group.js +178 -0
- package/dist/tools/yapi/register-group.js.map +1 -0
- package/dist/tools/yapi/register-interface.d.ts +2 -0
- package/dist/tools/yapi/register-interface.js +359 -0
- package/dist/tools/yapi/register-interface.js.map +1 -0
- package/dist/tools/yapi/register-project.d.ts +2 -0
- package/dist/tools/yapi/register-project.js +300 -0
- package/dist/tools/yapi/register-project.js.map +1 -0
- package/dist/tools/yapi/register-test.d.ts +2 -0
- package/dist/tools/yapi/register-test.js +267 -0
- package/dist/tools/yapi/register-test.js.map +1 -0
- package/dist/tools/yapi/register-util.d.ts +2 -0
- package/dist/tools/yapi/register-util.js +565 -0
- package/dist/tools/yapi/register-util.js.map +1 -0
- package/dist/tools/yapi/register.d.ts +6 -0
- package/dist/tools/yapi/register.js +19 -0
- package/dist/tools/yapi/register.js.map +1 -0
- package/dist/tools/yapi/utils.d.ts +11 -0
- package/dist/tools/yapi/utils.js +28 -0
- package/dist/tools/yapi/utils.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +38 -0
- package/skills/yapi-cli/SKILL.md +126 -0
- package/skills/yapi-cli/reference/auth.md +106 -0
- package/skills/yapi-cli/reference/cli.md +60 -0
- package/skills/yapi-cli/reference/commands.md +152 -0
- package/skills/yapi-cli/reference/group.md +77 -0
- package/skills/yapi-cli/reference/install.md +82 -0
- package/skills/yapi-cli/reference/interface.md +185 -0
- package/skills/yapi-cli/reference/overview.md +52 -0
- package/skills/yapi-cli/reference/project.md +115 -0
- package/skills/yapi-cli/reference/test.md +130 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
import { pbkdf2Sync } from 'node:crypto';
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
// ==================== 密码加密 ====================
|
|
4
|
+
/**
|
|
5
|
+
* PBKDF2 密码加密(YApi LDAP 模式用)。
|
|
6
|
+
* YApi 服务端使用固定的 salt 和迭代次数。
|
|
7
|
+
*/
|
|
8
|
+
export function encryptPassword(password, salt) {
|
|
9
|
+
return pbkdf2Sync(password, salt, 100, 64, 'sha512').toString('hex');
|
|
10
|
+
}
|
|
11
|
+
// ==================== 错误类型 ====================
|
|
12
|
+
export class YApiError extends Error {
|
|
13
|
+
constructor(message, code, statusCode) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.statusCode = statusCode;
|
|
17
|
+
this.name = 'YApiError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class YApiAuthError extends YApiError {
|
|
21
|
+
constructor(message = '认证失败,请重新登录') {
|
|
22
|
+
super(message, 'AUTH_FAILED', 401);
|
|
23
|
+
this.name = 'YApiAuthError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class YApiNetworkError extends YApiError {
|
|
27
|
+
constructor(cause) {
|
|
28
|
+
super(`网络错误: ${cause.message}`, 'NETWORK_ERROR');
|
|
29
|
+
this.name = 'YApiNetworkError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class YApiTimeoutError extends YApiError {
|
|
33
|
+
constructor(url) {
|
|
34
|
+
super(`请求超时: ${url}`, 'TIMEOUT');
|
|
35
|
+
this.name = 'YApiTimeoutError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ==================== 重试配置 ====================
|
|
39
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
40
|
+
const MAX_RETRIES = 3;
|
|
41
|
+
const RETRYABLE_STATUS_CODES = new Set([408, 429, 502, 503, 504]);
|
|
42
|
+
const RETRYABLE_ERROR_MESSAGES = ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'socket hang up', 'network'];
|
|
43
|
+
function isRetryable(error) {
|
|
44
|
+
if (error instanceof YApiTimeoutError)
|
|
45
|
+
return true;
|
|
46
|
+
if (error instanceof YApiNetworkError)
|
|
47
|
+
return true;
|
|
48
|
+
if (error instanceof YApiError && error.statusCode && RETRYABLE_STATUS_CODES.has(error.statusCode))
|
|
49
|
+
return true;
|
|
50
|
+
if (error instanceof Error && RETRYABLE_ERROR_MESSAGES.some((msg) => error.message.toLowerCase().includes(msg.toLowerCase())))
|
|
51
|
+
return true;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
function sleep(ms) {
|
|
55
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
56
|
+
}
|
|
57
|
+
/** YApi API 客户端 */
|
|
58
|
+
export class YApiService {
|
|
59
|
+
constructor(serverUrl, getHeaders, getToken, timeoutMs = DEFAULT_TIMEOUT_MS, maxRetries = MAX_RETRIES) {
|
|
60
|
+
this.serverUrl = serverUrl;
|
|
61
|
+
this.getHeaders = getHeaders;
|
|
62
|
+
this.getToken = getToken;
|
|
63
|
+
this.timeoutMs = timeoutMs;
|
|
64
|
+
this.maxRetries = maxRetries;
|
|
65
|
+
this.requestId = 0;
|
|
66
|
+
}
|
|
67
|
+
/** 发送 API 请求(带重试和超时) */
|
|
68
|
+
async request(method, path, body) {
|
|
69
|
+
this.requestId += 1;
|
|
70
|
+
const requestId = this.requestId;
|
|
71
|
+
const url = `${this.serverUrl.replace(/\/+$/, '')}/api${path}`;
|
|
72
|
+
const headers = {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
...this.getHeaders(),
|
|
75
|
+
};
|
|
76
|
+
const token = this.getToken();
|
|
77
|
+
if (token) {
|
|
78
|
+
headers['Authorization'] = token;
|
|
79
|
+
}
|
|
80
|
+
let lastError;
|
|
81
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
logger.info(`[${requestId}] ${method} ${url}${attempt > 1 ? ` (重试 ${attempt - 1}/${this.maxRetries - 1})` : ''}`);
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
86
|
+
let response;
|
|
87
|
+
try {
|
|
88
|
+
response = await fetch(url, {
|
|
89
|
+
method,
|
|
90
|
+
headers,
|
|
91
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
92
|
+
signal: controller.signal,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
clearTimeout(timeoutId);
|
|
97
|
+
}
|
|
98
|
+
const text = await response.text();
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
const error = new YApiError(`HTTP ${response.status}: ${text.slice(0, 200)}`, 'HTTP_ERROR', response.status);
|
|
101
|
+
if (response.status === 401) {
|
|
102
|
+
throw new YApiAuthError();
|
|
103
|
+
}
|
|
104
|
+
if (isRetryable(error) && attempt < this.maxRetries) {
|
|
105
|
+
lastError = error;
|
|
106
|
+
await sleep(Math.min(1000 * 2 ** attempt, 8000));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
let result;
|
|
112
|
+
try {
|
|
113
|
+
result = JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new YApiError(`JSON 解析失败: ${text.slice(0, 200)}`, 'PARSE_ERROR');
|
|
117
|
+
}
|
|
118
|
+
if (result.errcode !== 0) {
|
|
119
|
+
const apiError = new YApiError(`API 错误: ${result.errmsg}`, `API_ERROR_${result.errcode}`);
|
|
120
|
+
// errcode 402 表示登录过期
|
|
121
|
+
if (result.errcode === 402) {
|
|
122
|
+
throw new YApiAuthError('登录已过期,请重新登录');
|
|
123
|
+
}
|
|
124
|
+
// 服务端错误可能可重试
|
|
125
|
+
if (result.errcode >= 500 && attempt < this.maxRetries) {
|
|
126
|
+
lastError = apiError;
|
|
127
|
+
await sleep(Math.min(1000 * 2 ** attempt, 8000));
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
throw apiError;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof YApiAuthError) {
|
|
136
|
+
throw error; // 认证错误不重试
|
|
137
|
+
}
|
|
138
|
+
if (error instanceof YApiError) {
|
|
139
|
+
if (isRetryable(error) && attempt < this.maxRetries) {
|
|
140
|
+
lastError = error;
|
|
141
|
+
await sleep(Math.min(1000 * 2 ** attempt, 8000));
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
// fetch 抛出的原生错误(网络中断、超时等)
|
|
147
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
148
|
+
const timeoutError = new YApiTimeoutError(url);
|
|
149
|
+
if (attempt < this.maxRetries) {
|
|
150
|
+
lastError = timeoutError;
|
|
151
|
+
await sleep(Math.min(1000 * 2 ** attempt, 8000));
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
throw timeoutError;
|
|
155
|
+
}
|
|
156
|
+
const networkError = new YApiNetworkError(error instanceof Error ? error : new Error(String(error)));
|
|
157
|
+
if (attempt < this.maxRetries) {
|
|
158
|
+
lastError = networkError;
|
|
159
|
+
await sleep(Math.min(1000 * 2 ** attempt, 8000));
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
throw networkError;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// 所有重试都失败
|
|
166
|
+
throw lastError ?? new YApiError('请求失败(已达最大重试次数)', 'MAX_RETRIES');
|
|
167
|
+
}
|
|
168
|
+
/** GET 请求 */
|
|
169
|
+
async get(path, params) {
|
|
170
|
+
let queryString = '';
|
|
171
|
+
if (params) {
|
|
172
|
+
const entries = Object.entries(params)
|
|
173
|
+
.filter(([, v]) => v !== undefined)
|
|
174
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
175
|
+
if (entries.length > 0) {
|
|
176
|
+
queryString = `?${entries.join('&')}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return this.request('GET', `${path}${queryString}`);
|
|
180
|
+
}
|
|
181
|
+
/** POST 请求 */
|
|
182
|
+
async post(path, body) {
|
|
183
|
+
return this.request('POST', path, body);
|
|
184
|
+
}
|
|
185
|
+
/** PUT 请求 */
|
|
186
|
+
async put(path, body) {
|
|
187
|
+
return this.request('PUT', path, body);
|
|
188
|
+
}
|
|
189
|
+
/** DELETE 请求 */
|
|
190
|
+
async del(path, params) {
|
|
191
|
+
let queryString = '';
|
|
192
|
+
if (params) {
|
|
193
|
+
const entries = Object.entries(params)
|
|
194
|
+
.filter(([, v]) => v !== undefined)
|
|
195
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
196
|
+
if (entries.length > 0)
|
|
197
|
+
queryString = `?${entries.join('&')}`;
|
|
198
|
+
}
|
|
199
|
+
return this.request('DELETE', `${path}${queryString}`);
|
|
200
|
+
}
|
|
201
|
+
/** HEAD 请求 */
|
|
202
|
+
async head(path, params) {
|
|
203
|
+
let queryString = '';
|
|
204
|
+
if (params) {
|
|
205
|
+
const entries = Object.entries(params)
|
|
206
|
+
.filter(([, v]) => v !== undefined)
|
|
207
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
208
|
+
if (entries.length > 0)
|
|
209
|
+
queryString = `?${entries.join('&')}`;
|
|
210
|
+
}
|
|
211
|
+
return this.request('HEAD', `${path}${queryString}`);
|
|
212
|
+
}
|
|
213
|
+
/** OPTIONS 请求 */
|
|
214
|
+
async options(path, body) {
|
|
215
|
+
return this.request('OPTIONS', path, body);
|
|
216
|
+
}
|
|
217
|
+
/** PATCH 请求 */
|
|
218
|
+
async patch(path, body) {
|
|
219
|
+
return this.request('PATCH', path, body);
|
|
220
|
+
}
|
|
221
|
+
// ==================== 认证相关 ====================
|
|
222
|
+
/** 密码登录 */
|
|
223
|
+
async loginWithPassword(email, password) {
|
|
224
|
+
return this.post('/user/login', { email, password });
|
|
225
|
+
}
|
|
226
|
+
/** LDAP 登录(密码已加密) */
|
|
227
|
+
async loginByLdap(email, encryptedPassword) {
|
|
228
|
+
const url = `${this.serverUrl.replace(/\/+$/, '')}/api/user/login_by_ldap`;
|
|
229
|
+
const response = await fetch(url, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
...this.getHeaders(),
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify({ email, password: encryptedPassword }),
|
|
236
|
+
});
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const text = await response.text();
|
|
239
|
+
throw new YApiError(`LDAP 登录失败 (${response.status}): ${text.slice(0, 200)}`);
|
|
240
|
+
}
|
|
241
|
+
const result = await response.json();
|
|
242
|
+
if (result.errcode !== 0) {
|
|
243
|
+
throw new YApiError(`LDAP 登录失败: ${result.errmsg}`);
|
|
244
|
+
}
|
|
245
|
+
// 从 Set-Cookie 头提取 _yapi_token 和 _yapi_uid
|
|
246
|
+
const setCookie = response.headers.get('set-cookie') || '';
|
|
247
|
+
const tokenMatch = setCookie.match(/_yapi_token=([^;]+)/);
|
|
248
|
+
const uidMatch = setCookie.match(/_yapi_uid=([^;]+)/);
|
|
249
|
+
const token = tokenMatch?.[1];
|
|
250
|
+
const uid = uidMatch?.[1] || '';
|
|
251
|
+
if (!token) {
|
|
252
|
+
throw new YApiError('LDAP 登录成功但未获取到 _yapi_token');
|
|
253
|
+
}
|
|
254
|
+
return { token, uid, username: result.data?.username || email };
|
|
255
|
+
}
|
|
256
|
+
/** 获取登录状态 */
|
|
257
|
+
async getLoginStatus() {
|
|
258
|
+
return this.get('/user/status');
|
|
259
|
+
}
|
|
260
|
+
/** 登出 */
|
|
261
|
+
async userLogout() {
|
|
262
|
+
return this.get('/user/logout');
|
|
263
|
+
}
|
|
264
|
+
/** 获取用户信息 */
|
|
265
|
+
async getUserInfo() {
|
|
266
|
+
return this.get('/user/info');
|
|
267
|
+
}
|
|
268
|
+
// ==================== 搜索 ====================
|
|
269
|
+
/** 搜索接口(YApi 实际通过 GET /interface/list?q=xxx 实现) */
|
|
270
|
+
async searchInterfaces(params) {
|
|
271
|
+
return this.get('/interface/list', params);
|
|
272
|
+
}
|
|
273
|
+
// ==================== 分组相关 ====================
|
|
274
|
+
/** 获取分组列表 */
|
|
275
|
+
async listGroups() {
|
|
276
|
+
return this.get('/group/list');
|
|
277
|
+
}
|
|
278
|
+
/** 获取分组详情 */
|
|
279
|
+
async getGroup(groupId) {
|
|
280
|
+
return this.get('/group/get', { id: groupId });
|
|
281
|
+
}
|
|
282
|
+
/** 获取我的分组 */
|
|
283
|
+
async getMyGroup() {
|
|
284
|
+
return this.get('/group/get_mygroup');
|
|
285
|
+
}
|
|
286
|
+
/** 创建分组 */
|
|
287
|
+
async groupAdd(data) {
|
|
288
|
+
return this.post('/group/add', data);
|
|
289
|
+
}
|
|
290
|
+
/** 更新分组 */
|
|
291
|
+
async groupUp(data) {
|
|
292
|
+
return this.post('/group/up', data);
|
|
293
|
+
}
|
|
294
|
+
/** 删除分组 */
|
|
295
|
+
async groupDel(groupId) {
|
|
296
|
+
return this.post('/group/del', { id: groupId });
|
|
297
|
+
}
|
|
298
|
+
/** 添加分组成员 */
|
|
299
|
+
async groupAddMember(data) {
|
|
300
|
+
return this.post('/group/add_member', data);
|
|
301
|
+
}
|
|
302
|
+
/** 修改分组成员角色 */
|
|
303
|
+
async groupChangeMemberRole(data) {
|
|
304
|
+
return this.post('/group/change_member_role', data);
|
|
305
|
+
}
|
|
306
|
+
/** 删除分组成员 */
|
|
307
|
+
async groupDelMember(data) {
|
|
308
|
+
return this.post('/group/del_member', data);
|
|
309
|
+
}
|
|
310
|
+
// ==================== 项目相关 ====================
|
|
311
|
+
/** 获取项目列表 */
|
|
312
|
+
async listProjects(params = {}) {
|
|
313
|
+
return this.get('/project/list', params);
|
|
314
|
+
}
|
|
315
|
+
/** 获取项目详情 */
|
|
316
|
+
async getProject(projectId) {
|
|
317
|
+
return this.get('/project/get', { project_id: projectId });
|
|
318
|
+
}
|
|
319
|
+
/** 通过 token 获取项目信息 */
|
|
320
|
+
async getProjectByToken(token) {
|
|
321
|
+
return this.get('/project/get_by_token', { token });
|
|
322
|
+
}
|
|
323
|
+
/** 创建项目 */
|
|
324
|
+
async createProject(data) {
|
|
325
|
+
return this.post('/project/add', data);
|
|
326
|
+
}
|
|
327
|
+
/** 更新项目 */
|
|
328
|
+
async updateProject(data) {
|
|
329
|
+
return this.post('/project/up', data);
|
|
330
|
+
}
|
|
331
|
+
/** 搜索项目 */
|
|
332
|
+
async searchProjects(query) {
|
|
333
|
+
return this.get('/project/search', { q: query });
|
|
334
|
+
}
|
|
335
|
+
/** 更新项目环境 */
|
|
336
|
+
async updateProjectEnv(data) {
|
|
337
|
+
return this.post('/project/up_env', data);
|
|
338
|
+
}
|
|
339
|
+
/** 更新项目标签 */
|
|
340
|
+
async updateProjectTag(data) {
|
|
341
|
+
return this.post('/project/up_tag', data);
|
|
342
|
+
}
|
|
343
|
+
/** 更新项目 Token(重新生成) */
|
|
344
|
+
async updateProjectToken(projectId) {
|
|
345
|
+
return this.get('/project/update_token', { project_id: projectId });
|
|
346
|
+
}
|
|
347
|
+
/** 检查项目名是否可用 */
|
|
348
|
+
async checkProjectName(name, groupId) {
|
|
349
|
+
return this.get('/project/check_project_name', { name, group_id: groupId });
|
|
350
|
+
}
|
|
351
|
+
/** 复制项目 */
|
|
352
|
+
async copyProject(projectId, name) {
|
|
353
|
+
return this.post('/project/copy', { project_id: projectId, name });
|
|
354
|
+
}
|
|
355
|
+
/** 获取 Swagger URL */
|
|
356
|
+
async getProjectSwaggerUrl(projectId) {
|
|
357
|
+
return this.get('/project/swagger_url', { project_id: projectId });
|
|
358
|
+
}
|
|
359
|
+
/** 添加项目成员 */
|
|
360
|
+
async addProjectMember(data) {
|
|
361
|
+
return this.post('/project/add_member', data);
|
|
362
|
+
}
|
|
363
|
+
/** 更新项目设置(upset 覆盖全部字段) */
|
|
364
|
+
async upSetProject(data) {
|
|
365
|
+
return this.post('/project/upset', data);
|
|
366
|
+
}
|
|
367
|
+
/** 删除项目 */
|
|
368
|
+
async deleteProject(projectId) {
|
|
369
|
+
return this.post('/project/del', { id: projectId });
|
|
370
|
+
}
|
|
371
|
+
/** 删除项目成员 */
|
|
372
|
+
async delProjectMember(data) {
|
|
373
|
+
return this.post('/project/del_member', data);
|
|
374
|
+
}
|
|
375
|
+
/** 修改项目成员角色 */
|
|
376
|
+
async changeProjectMemberRole(data) {
|
|
377
|
+
return this.post('/project/change_member_role', data);
|
|
378
|
+
}
|
|
379
|
+
/** 修改成员邮件通知 */
|
|
380
|
+
async changeProjectMemberEmailNotice(data) {
|
|
381
|
+
return this.post('/project/change_member_email_notice', data);
|
|
382
|
+
}
|
|
383
|
+
// ==================== 分类/目录相关 ====================
|
|
384
|
+
/** 获取分类(目录)列表 */
|
|
385
|
+
async listCategories(projectId) {
|
|
386
|
+
return this.get('/interface/getCatMenu', { project_id: projectId });
|
|
387
|
+
}
|
|
388
|
+
/** 获取分类详情 */
|
|
389
|
+
async getCategory(catId) {
|
|
390
|
+
return this.get('/interface/list_cat', { catid: catId });
|
|
391
|
+
}
|
|
392
|
+
/** 添加分类 */
|
|
393
|
+
async addCategory(data) {
|
|
394
|
+
return this.post('/interface/add_cat', data);
|
|
395
|
+
}
|
|
396
|
+
/** 更新分类 */
|
|
397
|
+
async updateCategory(data) {
|
|
398
|
+
return this.post('/interface/up_cat', data);
|
|
399
|
+
}
|
|
400
|
+
/** 删除分类 */
|
|
401
|
+
async deleteCategory(catId) {
|
|
402
|
+
return this.post('/interface/del_cat', { catid: catId });
|
|
403
|
+
}
|
|
404
|
+
// ==================== 接口相关 ====================
|
|
405
|
+
/** 获取接口列表 */
|
|
406
|
+
async listInterfaces(params = {}) {
|
|
407
|
+
return this.get('/interface/list', params);
|
|
408
|
+
}
|
|
409
|
+
/** 获取接口详情 */
|
|
410
|
+
async getInterfaceDetail(interfaceId) {
|
|
411
|
+
return this.get('/interface/get', { id: interfaceId });
|
|
412
|
+
}
|
|
413
|
+
/** 创建接口 */
|
|
414
|
+
async createInterface(data) {
|
|
415
|
+
return this.post('/interface/add', data);
|
|
416
|
+
}
|
|
417
|
+
/** 更新接口 */
|
|
418
|
+
async updateInterface(data) {
|
|
419
|
+
return this.put('/interface/up', data);
|
|
420
|
+
}
|
|
421
|
+
/** 删除接口 */
|
|
422
|
+
async deleteInterface(interfaceId) {
|
|
423
|
+
return this.post('/interface/del', { id: interfaceId });
|
|
424
|
+
}
|
|
425
|
+
/** 批量删除接口 */
|
|
426
|
+
async batchDeleteInterfaces(ids) {
|
|
427
|
+
return this.post('/interface/del_batch', { ids });
|
|
428
|
+
}
|
|
429
|
+
/** 获取接口列表(按分类) */
|
|
430
|
+
async listInterfacesByCategory(catId) {
|
|
431
|
+
return this.get('/interface/list_cat', { catid: catId });
|
|
432
|
+
}
|
|
433
|
+
/** 保存接口(若 id 存在则更新,否则创建) */
|
|
434
|
+
async saveInterface(data) {
|
|
435
|
+
return this.post('/interface/save', data);
|
|
436
|
+
}
|
|
437
|
+
/** 获取接口列表(公开项目) */
|
|
438
|
+
async listPublicInterfaces(params) {
|
|
439
|
+
return this.get('/interface/list_open', params);
|
|
440
|
+
}
|
|
441
|
+
/** schema 转 JSON */
|
|
442
|
+
async schemaToJson(schema) {
|
|
443
|
+
return this.post('/interface/schema2json', { schema });
|
|
444
|
+
}
|
|
445
|
+
/** 获取接口列表(菜单模式) */
|
|
446
|
+
async listInterfacesByMenu(projectId) {
|
|
447
|
+
return this.get('/interface/list_menu', { project_id: projectId });
|
|
448
|
+
}
|
|
449
|
+
/** 获取自定义字段 */
|
|
450
|
+
async getCustomField(projectId) {
|
|
451
|
+
return this.get('/interface/get_custom_field', { project_id: projectId });
|
|
452
|
+
}
|
|
453
|
+
/** 更新接口排序 */
|
|
454
|
+
async upInterfaceIndex(data) {
|
|
455
|
+
return this.post('/interface/up_index', data);
|
|
456
|
+
}
|
|
457
|
+
/** 更新分类排序 */
|
|
458
|
+
async upCatIndex(data) {
|
|
459
|
+
return this.post('/interface/up_cat_index', data);
|
|
460
|
+
}
|
|
461
|
+
/** 导入接口(swagger 等格式) */
|
|
462
|
+
async interUpload(data) {
|
|
463
|
+
return this.post('/interface/interUpload', data);
|
|
464
|
+
}
|
|
465
|
+
/** 下载浏览器扩展 CRX */
|
|
466
|
+
async downloadCrx() {
|
|
467
|
+
return this.get('/interface/download_crx');
|
|
468
|
+
}
|
|
469
|
+
// ==================== 日志相关 ====================
|
|
470
|
+
/** 获取接口变更日志 */
|
|
471
|
+
async listInterfaceLogs(params) {
|
|
472
|
+
return this.get('/log/list', params);
|
|
473
|
+
}
|
|
474
|
+
/** 获取日志列表(按更新时间排序) */
|
|
475
|
+
async listLogsByUpdate(params) {
|
|
476
|
+
return this.post('/log/list_by_update', params);
|
|
477
|
+
}
|
|
478
|
+
// ==================== 收藏/关注相关 ====================
|
|
479
|
+
/** 获取关注的项目列表 */
|
|
480
|
+
async listFollowedProjects() {
|
|
481
|
+
return this.get('/follow/list');
|
|
482
|
+
}
|
|
483
|
+
/** 关注项目 */
|
|
484
|
+
async followProject(projectId) {
|
|
485
|
+
return this.post('/follow/add', { project_id: projectId });
|
|
486
|
+
}
|
|
487
|
+
/** 取消关注项目 */
|
|
488
|
+
async unfollowProject(projectId) {
|
|
489
|
+
return this.post('/follow/del', { project_id: projectId });
|
|
490
|
+
}
|
|
491
|
+
// ==================== Token 管理 ====================
|
|
492
|
+
/** 获取项目 token */
|
|
493
|
+
async getProjectToken(projectId) {
|
|
494
|
+
return this.get('/project/token', { project_id: projectId });
|
|
495
|
+
}
|
|
496
|
+
// ==================== 环境相关 ====================
|
|
497
|
+
/** 获取项目环境列表 */
|
|
498
|
+
async listProjectEnvs(projectId) {
|
|
499
|
+
return this.get('/project/get_env', { project_id: projectId });
|
|
500
|
+
}
|
|
501
|
+
// ==================== 成员相关 ====================
|
|
502
|
+
/** 获取项目成员列表 */
|
|
503
|
+
async listProjectMembers(projectId) {
|
|
504
|
+
return this.get('/project/get_member_list', { project_id: projectId });
|
|
505
|
+
}
|
|
506
|
+
/** 获取分组成员列表 */
|
|
507
|
+
async listGroupMembers(groupId) {
|
|
508
|
+
return this.get('/group/get_member_list', { group_id: groupId });
|
|
509
|
+
}
|
|
510
|
+
// ==================== 用户相关 ====================
|
|
511
|
+
/** 获取用户列表(需要管理员权限) */
|
|
512
|
+
async listUsers(params = {}) {
|
|
513
|
+
return this.get('/user/list', params);
|
|
514
|
+
}
|
|
515
|
+
/** 搜索用户 */
|
|
516
|
+
async searchUsers(query) {
|
|
517
|
+
return this.get('/user/search', { q: query });
|
|
518
|
+
}
|
|
519
|
+
/** 查找用户 by ID */
|
|
520
|
+
async findUserById(uid) {
|
|
521
|
+
return this.get('/user/find', { id: uid });
|
|
522
|
+
}
|
|
523
|
+
/** 更新用户 */
|
|
524
|
+
async updateUser(data) {
|
|
525
|
+
return this.post('/user/update', data);
|
|
526
|
+
}
|
|
527
|
+
/** 修改密码 */
|
|
528
|
+
async changePassword(data) {
|
|
529
|
+
return this.post('/user/change_password', data);
|
|
530
|
+
}
|
|
531
|
+
/** 获取用户项目列表 */
|
|
532
|
+
async listUserProjects() {
|
|
533
|
+
return this.get('/user/project');
|
|
534
|
+
}
|
|
535
|
+
/** 标记用户学习教程 */
|
|
536
|
+
async upStudy() {
|
|
537
|
+
return this.get('/user/up_study');
|
|
538
|
+
}
|
|
539
|
+
/** 用户注册(需要管理员权限) */
|
|
540
|
+
async userRegister(data) {
|
|
541
|
+
return this.post('/user/reg', data);
|
|
542
|
+
}
|
|
543
|
+
/** 删除用户(需要管理员权限) */
|
|
544
|
+
async userDelete(uid) {
|
|
545
|
+
return this.post('/user/del', { id: uid });
|
|
546
|
+
}
|
|
547
|
+
/** Token 登录(静默认证,不需要交互) */
|
|
548
|
+
async loginByToken(token) {
|
|
549
|
+
return this.request('POST', '/user/login_by_token', { token });
|
|
550
|
+
}
|
|
551
|
+
/** 获取用户头像 URL */
|
|
552
|
+
async getUserAvatar(uid) {
|
|
553
|
+
return this.get('/user/avatar', { uid });
|
|
554
|
+
}
|
|
555
|
+
/** 上传头像 */
|
|
556
|
+
async uploadUserAvatar(data) {
|
|
557
|
+
return this.post('/user/upload_avatar', data);
|
|
558
|
+
}
|
|
559
|
+
// ==================== 集合相关(col) ====================
|
|
560
|
+
/** 获取项目测试集合列表 */
|
|
561
|
+
async listCollections(projectId) {
|
|
562
|
+
return this.get('/col/list', { project_id: projectId });
|
|
563
|
+
}
|
|
564
|
+
/** 创建测试集合 */
|
|
565
|
+
async createCollection(data) {
|
|
566
|
+
return this.post('/col/add_col', data);
|
|
567
|
+
}
|
|
568
|
+
/** 获取集合测试用例列表 */
|
|
569
|
+
async listCollectionCases(colId) {
|
|
570
|
+
return this.get('/col/case_list', { col_id: colId });
|
|
571
|
+
}
|
|
572
|
+
/** 添加测试用例 */
|
|
573
|
+
async addTestCase(data) {
|
|
574
|
+
return this.post('/col/add_case', data);
|
|
575
|
+
}
|
|
576
|
+
/** 更新测试用例 */
|
|
577
|
+
async updateTestCase(data) {
|
|
578
|
+
return this.post('/col/up_case', data);
|
|
579
|
+
}
|
|
580
|
+
/** 获取测试用例详情 */
|
|
581
|
+
async getTestCase(caseId) {
|
|
582
|
+
return this.get('/col/case', { id: caseId });
|
|
583
|
+
}
|
|
584
|
+
/** 更新测试集合 */
|
|
585
|
+
async updateCollection(data) {
|
|
586
|
+
return this.post('/col/up_col', data);
|
|
587
|
+
}
|
|
588
|
+
/** 删除测试集合 */
|
|
589
|
+
async deleteCollection(colId) {
|
|
590
|
+
return this.get('/col/del_col', { col_id: colId });
|
|
591
|
+
}
|
|
592
|
+
/** 删除测试用例 */
|
|
593
|
+
async deleteTestCase(caseId) {
|
|
594
|
+
return this.get('/col/del_case', { case_id: caseId });
|
|
595
|
+
}
|
|
596
|
+
/** 批量添加测试用例 */
|
|
597
|
+
async addTestCaseList(data) {
|
|
598
|
+
return this.post('/col/add_case_list', data);
|
|
599
|
+
}
|
|
600
|
+
/** 克隆用例列表 */
|
|
601
|
+
async cloneTestCaseList(data) {
|
|
602
|
+
return this.post('/col/clone_case_list', data);
|
|
603
|
+
}
|
|
604
|
+
/** 运行测试用例脚本 */
|
|
605
|
+
async runTestCaseScript(data) {
|
|
606
|
+
return this.post('/col/run_script', data);
|
|
607
|
+
}
|
|
608
|
+
/** 获取测试用例环境变量列表 */
|
|
609
|
+
async getTestCaseEnvList(caseId) {
|
|
610
|
+
return this.get('/col/case_env_list', { case_id: caseId });
|
|
611
|
+
}
|
|
612
|
+
/** 获取用例列表(支持变量参数) */
|
|
613
|
+
async getCaseListByVariableParams(colId) {
|
|
614
|
+
return this.get('/col/case_list_by_var_params', { col_id: colId });
|
|
615
|
+
}
|
|
616
|
+
/** 更新用例排序 */
|
|
617
|
+
async upCaseIndex(data) {
|
|
618
|
+
return this.post('/col/up_case_index', data);
|
|
619
|
+
}
|
|
620
|
+
/** 更新集合排序 */
|
|
621
|
+
async upColIndex(data) {
|
|
622
|
+
return this.post('/col/up_col_index', data);
|
|
623
|
+
}
|
|
624
|
+
// ==================== 导出相关 ====================
|
|
625
|
+
/** 导出项目 JSON 格式 */
|
|
626
|
+
async exportProjectJson(projectId) {
|
|
627
|
+
return this.get('/export/json', { project_id: projectId });
|
|
628
|
+
}
|
|
629
|
+
/** 导出项目 Swagger 格式 */
|
|
630
|
+
async exportProjectSwagger(projectId) {
|
|
631
|
+
return this.get('/export/swagger', { project_id: projectId });
|
|
632
|
+
}
|
|
633
|
+
/** 导出项目 OpenAPI 3.0 格式 */
|
|
634
|
+
async exportProjectOpenAPI3(projectId) {
|
|
635
|
+
return this.get('/export/openapi3', { project_id: projectId });
|
|
636
|
+
}
|
|
637
|
+
// ==================== 数据导入相关 ====================
|
|
638
|
+
/** 导入数据到项目 */
|
|
639
|
+
async importData(data) {
|
|
640
|
+
return this.post('/open/import_data', data);
|
|
641
|
+
}
|
|
642
|
+
/** 获取开放接口数据(Open API) */
|
|
643
|
+
async projectInterfaceData(params) {
|
|
644
|
+
return this.get('/open/project_interface_data', params);
|
|
645
|
+
}
|
|
646
|
+
/** 运行自动测试(Open API) */
|
|
647
|
+
async runAutoTest(params) {
|
|
648
|
+
return this.get('/open/run_auto_test', params);
|
|
649
|
+
}
|
|
650
|
+
// ==================== 测试相关(/test/) ====================
|
|
651
|
+
/** 测试 POST 请求 */
|
|
652
|
+
async testPost(url, body) {
|
|
653
|
+
return this.post('/test/post', { url, body });
|
|
654
|
+
}
|
|
655
|
+
/** 测试 GET 请求 */
|
|
656
|
+
async testGet(url) {
|
|
657
|
+
return this.get('/test/get', { url });
|
|
658
|
+
}
|
|
659
|
+
/** 测试 PUT 请求 */
|
|
660
|
+
async testPut(url, body) {
|
|
661
|
+
return this.put('/test/put', { url, body });
|
|
662
|
+
}
|
|
663
|
+
/** 测试 DELETE 请求 */
|
|
664
|
+
async testDelete(url) {
|
|
665
|
+
return this.del('/test/delete', { url });
|
|
666
|
+
}
|
|
667
|
+
/** 测试 HEAD 请求 */
|
|
668
|
+
async testHead(url) {
|
|
669
|
+
return this.head('/test/head', { url });
|
|
670
|
+
}
|
|
671
|
+
/** 测试 OPTIONS 请求 */
|
|
672
|
+
async testOptions(url) {
|
|
673
|
+
return this.options('/test/options', { url });
|
|
674
|
+
}
|
|
675
|
+
/** 测试 PATCH 请求 */
|
|
676
|
+
async testPatch(url, body) {
|
|
677
|
+
return this.patch('/test/patch', { url, body });
|
|
678
|
+
}
|
|
679
|
+
/** 测试文件上传 */
|
|
680
|
+
async testFilesUpload(data) {
|
|
681
|
+
return this.post('/test/files/upload', data);
|
|
682
|
+
}
|
|
683
|
+
/** 测试单文件上传 */
|
|
684
|
+
async testSingleUpload(data) {
|
|
685
|
+
return this.post('/test/single/upload', data);
|
|
686
|
+
}
|
|
687
|
+
/** 测试 HTTP 状态码 */
|
|
688
|
+
async testHttpCode(data) {
|
|
689
|
+
return this.post('/test/http/code', data);
|
|
690
|
+
}
|
|
691
|
+
/** 测试原始请求 */
|
|
692
|
+
async testRaw(data) {
|
|
693
|
+
return this.post('/test/raw', data);
|
|
694
|
+
}
|
|
695
|
+
/** 获取测试响应 */
|
|
696
|
+
async testResponse(url) {
|
|
697
|
+
return this.get('/test/response', { url });
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
//# sourceMappingURL=api.js.map
|