本地 RAG 知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1004 lines
25 KiB

/**
* 统一 API 请求层
* 封装所有后端接口调用,组件只需调用函数处理业务逻辑
* P1: 所有请求自动附加 Bearer Token,401 时触发未登录事件
*/
import { API_BASE, authHeaders, clearTokens } from './utils.js'
// ==================== 通用请求 ====================
/**
* 处理 401 未授权响应
*/
function handleUnauthorized() {
clearTokens()
window.dispatchEvent(new Event('auth:unauthorized'))
}
/**
* GET 请求,返回 JSON
*/
async function getJSON(path) {
const res = await fetch(API_BASE + path, { headers: authHeaders() })
if (res.status === 401) { handleUnauthorized(); throw new Error('401') }
if (res.status === 403) throw new Error('403')
return res.json()
}
/**
* POST JSON 请求,返回 JSON
*/
async function postJSON(path, body) {
const res = await fetch(API_BASE + path, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders() },
body: JSON.stringify(body)
})
const json = await res.json().catch(() => ({ message: res.statusText || `HTTP ${res.status}` }))
if (res.status === 401) { handleUnauthorized(); throw new Error(json.message || '未登录或登录已过期') }
if (res.status === 403) throw new Error(json.message || '权限不足')
if (!res.ok) throw new Error(json.message || `HTTP ${res.status}`)
return json
}
/**
* DELETE 请求 + JSON body,返回 JSON(用于批量删除等场景)
*/
async function deleteJSONWithBody(path, body) {
const res = await fetch(API_BASE + path, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json', ...authHeaders() },
body: JSON.stringify(body)
})
if (res.status === 401) { handleUnauthorized(); throw new Error('401') }
return res.json()
}
/**
* PUT 请求 + JSON body,返回 JSON(用于批量操作等场景)
*/
async function putJSONWithBody(path, body) {
const res = await fetch(API_BASE + path, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', ...authHeaders() },
body: JSON.stringify(body)
})
if (res.status === 401) { handleUnauthorized(); throw new Error('401') }
return res.json()
}
/**
* DELETE 请求,返回 JSON(无 body)
*/
async function deleteJSON(path) {
const res = await fetch(API_BASE + path, { method: 'DELETE', headers: authHeaders() })
if (res.status === 401) { handleUnauthorized(); throw new Error('401') }
return res.json()
}
/**
* PUT 请求,返回 JSON(参数走 query string)
*/
async function putJSON(path, params) {
let url = API_BASE + path
if (params) {
const qs = Object.entries(params)
.filter(([, v]) => v !== undefined && v !== null && v !== '')
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
.join('&')
if (qs) url += (url.includes('?') ? '&' : '?') + qs
}
const res = await fetch(url, { method: 'PUT', headers: authHeaders() })
if (res.status === 401) { handleUnauthorized(); throw new Error('401') }
return res.json()
}
/**
* POST FormData 请求,返回 JSON
*/
async function postForm(path, formData) {
const res = await fetch(API_BASE + path, {
method: 'POST',
headers: authHeaders(),
body: formData
})
if (res.status === 401) { handleUnauthorized(); throw new Error('401') }
return res.json()
}
// ==================== AI 对话 ====================
/**
* 同步对话
* @param {string} message 用户消息(原样发送,不做包装)
* @param {string} chatId 会话ID
* @param {string} [roleId] 客服角色ID,命中后由服务端套用该角色人设
*/
export function chatSync(message, chatId, roleId, accountId) {
let url = API_BASE + `/ai/assistant_app/chat/sync?message=${encodeURIComponent(message)}&chatId=${encodeURIComponent(chatId)}`
if (roleId) url += `&roleId=${encodeURIComponent(roleId)}`
if (accountId) url += `&accountId=${encodeURIComponent(accountId)}`
return fetch(url).then(res => res.text())
}
/**
* RAG 同步对话
* 知识库范围由服务端依据 roleId 强制限定,前端无需(也无法)跨域指定分类。
* @param {string} message 用户消息
* @param {string} chatId 会话ID
* @param {string} strategy 查询重写策略
* @param {string} [roleId] 客服角色ID
*/
export function chatRagSync(message, chatId, strategy, roleId, accountId) {
let url = API_BASE + `/ai/assistant_app/chat/rag/sync?message=${encodeURIComponent(message)}&chatId=${encodeURIComponent(chatId)}&rewriteStrategy=${encodeURIComponent(strategy)}`
if (roleId) url += `&roleId=${encodeURIComponent(roleId)}`
if (accountId) url += `&accountId=${encodeURIComponent(accountId)}`
return fetch(url).then(res => res.text())
}
/**
* 获取普通 SSE 流式对话 URL
* @param {string} message
* @param {string} chatId
* @param {'sse'|'sse2'|'sse3'} mode
* @param {string} [roleId]
* @returns {string}
*/
export function chatSSEUrl(message, chatId, mode, roleId, accountId) {
const pathMap = {
sse: '/ai/assistant_app/chat/sse',
sse2: '/ai/assistant_app/chat/server_sent_event',
sse3: '/ai/assistant_app/chat/sse_emitter'
}
let url = API_BASE + `${pathMap[mode]}?message=${encodeURIComponent(message)}&chatId=${encodeURIComponent(chatId)}`
if (roleId) url += `&roleId=${encodeURIComponent(roleId)}`
if (accountId) url += `&accountId=${encodeURIComponent(accountId)}`
return url
}
/**
* 获取 RAG 流式对话 URL(知识库范围由服务端依据 roleId 强制限定)
* @param {string} message
* @param {string} chatId
* @param {string} strategy
* @param {string} [roleId]
* @returns {string}
*/
export function chatRagSSEUrl(message, chatId, strategy, roleId, accountId) {
let url = API_BASE + `/ai/assistant_app/chat/rag/sse?message=${encodeURIComponent(message)}&chatId=${encodeURIComponent(chatId)}&rewriteStrategy=${encodeURIComponent(strategy)}`
if (roleId) url += `&roleId=${encodeURIComponent(roleId)}`
if (accountId) url += `&accountId=${encodeURIComponent(accountId)}`
return url
}
/**
* 获取本次 RAG 回答命中的知识库片段(引用来源)
*/
export function ragSources(message, chatId, strategy, roleId, accountId) {
let path = `/ai/assistant_app/rag/sources?message=${encodeURIComponent(message)}&chatId=${encodeURIComponent(chatId)}&rewriteStrategy=${encodeURIComponent(strategy)}`
if (roleId) path += `&roleId=${encodeURIComponent(roleId)}`
if (accountId) path += `&accountId=${encodeURIComponent(accountId)}`
return getJSON(path)
}
export function getRoleList() {
return getJSON('/role/list')
}
/** 管理用:列出全部角色(含已停用) */
export function getAllRoles() {
return getJSON('/role/all')
}
/** 新增角色 */
export function createRole(role) {
return postJSON('/role', role)
}
/** 编辑角色基本信息 */
export function updateRole(roleId, role) {
return putJSONWithBody(`/role/${roleId}`, role)
}
/** 删除角色(逻辑删除) */
export function deleteRole(roleId) {
return deleteJSON(`/role/${roleId}`)
}
export function updateRoleCategories(roleId, categoryIds) {
return putJSONWithBody(`/role/${roleId}/categories`, { categoryIds })
}
export function getAccountList() {
return getJSON('/account/list')
}
export function getAllAccounts() {
return getJSON('/account/all')
}
export function createAccount(account) {
return postJSON('/account', account)
}
export function updateAccount(accountId, account) {
return putJSONWithBody(`/account/${accountId}`, account)
}
export function deleteAccount(accountId) {
return deleteJSON(`/account/${accountId}`)
}
// ==================== 文档管理 ====================
/**
* 文档列表(分页 + 过滤 + 关键词搜索 + 标签筛选)
*/
export function listDocuments(page = 1, size = 10, categoryId, status, keyword, tag) {
let path = `/document/list?page=${page}&size=${size}`
if (categoryId) path += `&categoryId=${categoryId}`
if (status) path += `&status=${status}`
if (keyword) path += `&keyword=${encodeURIComponent(keyword)}`
if (tag) path += `&tag=${encodeURIComponent(tag)}`
return getJSON(path)
}
/**
* 文档详情
*/
export function getDocumentDetail(id) {
return getJSON(`/document/${id}`)
}
/**
* 文档分块列表
*/
export function getDocumentChunks(id) {
return getJSON(`/document/${id}/chunks`)
}
/**
* 删除文档
*/
export function deleteDocument(id) {
return deleteJSON(`/document/${id}`)
}
/**
* 更新文档元信息
*/
export function updateDocument(id, title, categoryId, tags) {
return putJSON(`/document/${id}`, { title, categoryId, tags })
}
/**
* 重新处理文档
*/
export function reprocessDocument(id) {
return putJSON(`/document/${id}/reprocess`)
}
/**
* 批量删除文档
*/
export function batchDeleteDocuments(ids) {
return postJSON('/document/batch/delete', { ids })
}
/**
* 批量重新处理文档
*/
export function batchReprocessDocuments(ids) {
return postJSON('/document/batch/reprocess', { ids })
}
/**
* P1-2.1: 切换文档启用/禁用状态
*/
export function toggleDocument(id) {
return putJSON(`/document/${id}/toggle`)
}
/**
* P1-2.1: 批量启用/禁用文档
*/
export function batchToggleDocuments(ids, enabled) {
return postJSON('/document/batch/toggle', { ids, enabled })
}
/**
* P1-2.2: 批量移动文档分类
*/
export function batchMoveDocuments(ids, categoryId) {
return postJSON('/document/batch/move', { ids, categoryId })
}
/**
* P1-2.3: 获取标签列表(聚合去重,含使用次数)
*/
export function getTagList() {
return getJSON('/tag/list')
}
/**
* P1-2.5: 更新单个分块内容
*/
export function updateChunk(docId, chunkIndex, content) {
return putJSONWithBody(`/document/${docId}/chunk/${chunkIndex}`, { content })
}
/**
* P1-2.5: 删除单个分块
*/
export function deleteChunk(docId, chunkIndex) {
return deleteJSON(`/document/${docId}/chunk/${chunkIndex}`)
}
/**
* 语义搜索
*/
export function searchDocuments(query, topK, similarityThreshold, categoryId, categoryIds, searchMode) {
const body = { query, topK, similarityThreshold }
if (categoryIds && categoryIds.length) body.categoryIds = categoryIds
if (categoryId) body.categoryId = categoryId
if (searchMode) body.searchMode = searchMode
return postJSON('/document/search', body)
}
// ==================== 统计 ====================
/**
* 知识库统计
*/
export function getStats() {
return getJSON('/document/stats')
}
// ==================== 上传 ====================
/**
* 上传文件(带进度回调)
* 使用 XMLHttpRequest 替代 fetch,支持监听上传进度
*
* @param {string} path 请求路径
* @param {FormData} formData 表单数据
* @param {function(number): void} onProgress 进度回调,参数为 0-100 的百分比
* @returns {Promise<object>}
*/
function postFormWithProgress(path, formData, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('POST', API_BASE + path)
// 附加认证头
const token = localStorage.getItem('sb_token')
if (token) xhr.setRequestHeader('Authorization', 'Bearer ' + token)
// 上传进度监听
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable && onProgress) {
onProgress(Math.round((e.loaded / e.total) * 100))
}
})
xhr.addEventListener('load', () => {
if (xhr.status === 401) {
handleUnauthorized()
reject(new Error('401'))
return
}
try {
resolve(JSON.parse(xhr.responseText))
} catch (e) {
reject(new Error('响应解析失败'))
}
})
xhr.addEventListener('error', () => reject(new Error('网络错误')))
xhr.addEventListener('abort', () => reject(new Error('上传已取消')))
xhr.send(formData)
})
}
/**
* 上传普通文件(带进度)
*/
export function uploadFile(formData, onProgress) {
return postFormWithProgress('/upload/file', formData, onProgress)
}
/**
* 上传普通文件(无进度,兼容旧调用)
*/
export function uploadFileSimple(formData) {
return postForm('/upload/file', formData)
}
/**
* 上传文本内容
*/
export function uploadString(content, title, categoryId, tags) {
let url = `/upload/string?title=${encodeURIComponent(title)}`
if (categoryId) url += `&categoryId=${categoryId}`
if (tags) url += `&tags=${encodeURIComponent(tags)}`
return fetch(API_BASE + url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain', ...authHeaders() },
body: content
}).then(res => res.json())
}
/**
* 上传 Markdown(带进度)
*/
export function uploadMarkdown(formData, onProgress) {
return postFormWithProgress('/upload/markdown', formData, onProgress)
}
/**
* 上传 JSON(基本方式,带进度)
*/
export function uploadJsonBasic(formData, onProgress) {
return postFormWithProgress('/upload/json/basic', formData, onProgress)
}
/**
* 上传 JSON(按字段提取,带进度)
*/
export function uploadJsonFields(formData, fields, onProgress) {
return postFormWithProgress(`/upload/json/fields?fields=${encodeURIComponent(fields)}`, formData, onProgress)
}
/**
* 上传 JSON(按指针拆分,带进度)
*/
export function uploadJsonPointer(formData, pointer, onProgress) {
return postFormWithProgress(`/upload/json/pointer?pointer=${encodeURIComponent(pointer)}`, formData, onProgress)
}
// ==================== 分类 ====================
/**
* 分类树
*/
export function getCategoryTree() {
return getJSON('/category/tree')
}
/**
* 分类列表
*/
export function getCategoryList() {
return getJSON('/category/list')
}
/**
* 创建分类
*/
export function createCategory(data) {
return postJSON('/category', data)
}
/**
* 更新分类
*/
export function updateCategory(id, data) {
return putJSONWithBody(`/category/${id}`, data)
}
/**
* 删除分类
*/
export function deleteCategory(id) {
return deleteJSON(`/category/${id}`)
}
// ==================== 会话管理 ====================
/**
* 会话列表(分页)
*/
export function listConversations(page = 1, size = 10, keyword, accountId, roleId) {
let path = `/conversation/list?page=${page}&size=${size}`
if (keyword) path += `&keyword=${encodeURIComponent(keyword)}`
if (accountId) path += `&accountId=${encodeURIComponent(accountId)}`
if (roleId) path += `&roleId=${encodeURIComponent(roleId)}`
return getJSON(path)
}
/**
* 会话详情
*/
export function getConversationDetail(conversationId) {
return getJSON(`/conversation/${conversationId}`)
}
/**
* 会话消息列表
*/
export function getConversationMessages(conversationId) {
return getJSON(`/conversation/${conversationId}/messages`)
}
/**
* 删除会话
*/
export function deleteConversation(conversationId) {
return deleteJSON(`/conversation/${conversationId}`)
}
/**
* 导出会话记录
*/
export function exportConversation(conversationId) {
return fetch(API_BASE + `/conversation/${conversationId}/export`, { headers: authHeaders() })
.then(res => res.text())
}
/**
* 会话统计
*/
export function getConversationStats() {
return getJSON('/conversation/stats')
}
// ==================== Model config management ====================
export function listModelConfigs(page = 1, size = 10, appType) {
let path = `/model-config/list?page=${page}&size=${size}`
if (appType) path += `&appType=${encodeURIComponent(appType)}`
return getJSON(path)
}
export function getModelConfigDetail(id) {
return getJSON(`/model-config/${id}`)
}
export function getActiveModelConfig(appType) {
return getJSON(`/model-config/active/${encodeURIComponent(appType)}`)
}
export function createModelConfig(data) {
return postJSON('/model-config', data)
}
export function updateModelConfig(id, data) {
return putJSONWithBody(`/model-config/${id}`, data)
}
export function activateModelConfig(id) {
return putJSON(`/model-config/${id}/activate`)
}
export function deleteModelConfig(id) {
return deleteJSON(`/model-config/${id}`)
}
// ==================== F1: 连接测试 ====================
/**
* 测试模型配置连接可用性
*/
export function testModelConfig(id) {
return postJSON(`/model-config/${id}/test`, {})
}
// ==================== F2: 配置复制 ====================
/**
* 复制模型配置
*/
export function duplicateModelConfig(id) {
return postJSON(`/model-config/${id}/duplicate`, {})
}
// ==================== F4: 健康状态 ====================
/**
* 获取所有配置的健康状态
*/
export function getHealthStatus() {
return getJSON('/model-config/health')
}
/**
* 手动触发单个配置的健康检查
*/
export function manualHealthCheck(id) {
return postJSON(`/model-config/${id}/health-check`, {})
}
// ==================== F6: 优先级管理 ====================
/**
* 更新配置优先级
*/
export function updateModelConfigPriority(id, priority) {
return putJSONWithBody(`/model-config/${id}/priority`, { priority })
}
/**
* 停用配置(从 Fallback 链中移除)
*/
export function deactivateModelConfig(id) {
return putJSON(`/model-config/${id}/deactivate`)
}
// ==================== F7: 配置导入/导出 ====================
/**
* 导出所有模型配置(API Key 始终脱敏)
*/
export function exportModelConfigs() {
return getJSON('/model-config/export')
}
/**
* 批量导入模型配置
*/
export function importModelConfigs(configs, onConflict = 'skip') {
return postJSON('/model-config/import', { configs, onConflict })
}
/**
* Truncate a conversation from the given user turn onward.
*/
export function truncateConversation(conversationId, userTurn) {
return postJSON(`/conversation/${conversationId}/truncate`, { userTurn })
}
// ==================== P0-002: 用户反馈 ====================
/**
* 提交/修改消息反馈(upsert 语义)
* @param {Object} feedback - { messageId, conversationId, feedbackType, reasonCategory, reasonComment }
*/
export function submitFeedback(feedback) {
return postJSON('/feedback', feedback)
}
/**
* 获取反馈统计
*/
export function getFeedbackStats(startDate, endDate) {
let path = '/feedback/stats'
const params = []
if (startDate) params.push(`startDate=${encodeURIComponent(startDate)}`)
if (endDate) params.push(`endDate=${encodeURIComponent(endDate)}`)
if (params.length) path += '?' + params.join('&')
return getJSON(path)
}
/**
* 按会话查询反馈
*/
export function getFeedbackByConversation(conversationId) {
return getJSON(`/feedback/by-conversation/${encodeURIComponent(conversationId)}`)
}
/**
* 批量查询消息反馈状态(供回显)
*/
export function getFeedbackBatch(messageIds) {
return getJSON(`/feedback/batch?messageIds=${encodeURIComponent(messageIds.join(','))}`)
}
// ==================== P0-004: 敏感词管理 ====================
/**
* 敏感词分页列表
*/
export function listSensitiveWords(page = 1, size = 20, keyword, category) {
let path = `/sensitive-word/list?page=${page}&size=${size}`
if (keyword) path += `&keyword=${encodeURIComponent(keyword)}`
if (category) path += `&category=${encodeURIComponent(category)}`
return getJSON(path)
}
/**
* 新增敏感词
*/
export function createSensitiveWord(data) {
return postJSON('/sensitive-word', data)
}
/**
* 修改敏感词
*/
export function updateSensitiveWord(id, data) {
return putJSONWithBody(`/sensitive-word/${id}`, data)
}
/**
* 删除敏感词
*/
export function deleteSensitiveWord(id) {
return deleteJSON(`/sensitive-word/${id}`)
}
/**
* 批量导入敏感词
*/
export function batchImportSensitiveWords(data) {
return postJSON('/sensitive-word/batch-import', data)
}
/**
* 查询审计日志
*/
export function listAuditLogs(page = 1, size = 20, sessionId) {
let path = `/sensitive-word/audit-log?page=${page}&size=${size}`
if (sessionId) path += `&sessionId=${encodeURIComponent(sessionId)}`
return getJSON(path)
}
// ==================== P0-003: FAQ 管理 ====================
/**
* FAQ 分页列表
*/
export function listFaqs(page = 1, size = 20, keyword, categoryId, status) {
let path = `/faq/list?page=${page}&size=${size}`
if (keyword) path += `&keyword=${encodeURIComponent(keyword)}`
if (categoryId) path += `&categoryId=${categoryId}`
if (status) path += `&status=${encodeURIComponent(status)}`
return getJSON(path)
}
/**
* 新增 FAQ
*/
export function createFaq(data) {
return postJSON('/faq', data)
}
/**
* 修改 FAQ
*/
export function updateFaq(id, data) {
return putJSONWithBody(`/faq/${id}`, data)
}
/**
* 删除 FAQ
*/
export function deleteFaq(id) {
return deleteJSON(`/faq/${id}`)
}
/**
* 启用/禁用 FAQ
*/
export function toggleFaqStatus(id, status) {
return putJSON(`/faq/${id}/toggle`, { status })
}
/**
* 批量导入 FAQ
*/
export function batchImportFaqs(data) {
return postJSON('/faq/batch-import', data)
}
/**
* 导出所有 FAQ
*/
export function exportFaqs() {
return getJSON('/faq/export')
}
/**
* FAQ 匹配统计
*/
export function getFaqStats() {
return getJSON('/faq/stats')
}
// ==================== P1-001: 认证管理 ====================
/**
* 用户登录
*/
export function login(username, password) {
return postJSON('/auth/login', { username, password })
}
/**
* 刷新 Token
*/
export function refreshToken(refreshToken) {
return postJSON('/auth/refresh', { refreshToken })
}
/**
* 获取当前登录用户信息
*/
export function getCurrentUser() {
return getJSON('/auth/me')
}
// ==================== P1-001: 系统用户管理 ====================
/**
* 系统用户分页列表
*/
export function listSysUsers(page = 1, size = 20, keyword, enabled) {
let path = `/sys-user/list?page=${page}&size=${size}`
if (keyword) path += `&keyword=${encodeURIComponent(keyword)}`
if (enabled !== undefined && enabled !== null) path += `&enabled=${enabled}`
return getJSON(path)
}
/**
* 获取用户详情
*/
export function getSysUserDetail(id) {
return getJSON(`/sys-user/${id}`)
}
/**
* 创建系统用户
*/
export function createSysUser(data) {
return postJSON('/sys-user', data)
}
/**
* 更新系统用户
*/
export function updateSysUser(id, data) {
return putJSONWithBody(`/sys-user/${id}`, data)
}
/**
* 启用/禁用系统用户
*/
export function toggleSysUser(id, enabled) {
return putJSON(`/sys-user/${id}/toggle`, { enabled })
}
/**
* 分配角色
*/
export function assignSysUserRoles(id, roleIds) {
return putJSONWithBody(`/sys-user/${id}/roles`, { roleIds })
}
/**
* 修改密码
*/
export function changeSysUserPassword(id, newPassword) {
return putJSONWithBody(`/sys-user/${id}/password`, { newPassword })
}
/**
* 获取所有角色(分配角色下拉用)
*/
export function getAllSysRoles() {
return getJSON('/sys-user/roles')
}
/**
* 获取系统角色列表
*/
export function listSysRoles() {
return getJSON('/sys-role/list')
}
/**
* 创建系统角色
*/
export function createSysRole(data) {
return postJSON('/sys-role', data)
}
/**
* 更新系统角色
*/
export function updateSysRole(id, data) {
return putJSONWithBody(`/sys-role/${id}`, data)
}
// ==================== P1-002: 运营看板 ====================
/**
* 看板概览(今日实时指标)
*/
export function getDashboardOverview() {
return getJSON('/dashboard/overview')
}
/**
* 趋势数据(7/30天)
*/
export function getDashboardTrend(days = 7) {
return getJSON(`/dashboard/trend?days=${days}`)
}
/**
* 知识库分析(命中TOP-10 + 未命中问题)
*/
export function getDashboardKnowledge() {
return getJSON('/dashboard/knowledge-analysis')
}
/**
* 自定义时间范围数据
*/
export function getDashboardCustom(startDate, endDate) {
return getJSON(`/dashboard/custom?startDate=${encodeURIComponent(startDate)}&endDate=${encodeURIComponent(endDate)}`)
}
// ==================== P1-003: API Key 管理 ====================
/**
* API Key 列表
*/
export function listApiKeys(page = 1, size = 20) {
return getJSON(`/api-key/list?page=${page}&size=${size}`)
}
/**
* 创建 API Key
*/
export function createApiKey(data) {
return postJSON('/api-key', data)
}
/**
* 吊销 API Key
*/
export function revokeApiKey(id) {
return putJSON(`/api-key/${id}/revoke`)
}
/**
* 启用 API Key
*/
export function enableApiKey(id) {
return putJSON(`/api-key/${id}/enable`)
}
/**
* 删除 API Key
*/
export function deleteApiKey(id) {
return deleteJSON(`/api-key/${id}`)
}
// ==================== P1-003: Webhook 管理 ====================
/**
* Webhook 列表
*/
export function listWebhooks(page = 1, size = 20) {
return getJSON(`/webhook/list?page=${page}&size=${size}`)
}
/**
* 创建 Webhook
*/
export function createWebhook(data) {
return postJSON('/webhook', data)
}
/**
* 更新 Webhook
*/
export function updateWebhook(id, data) {
return putJSONWithBody(`/webhook/${id}`, data)
}
/**
* 删除 Webhook
*/
export function deleteWebhook(id) {
return deleteJSON(`/webhook/${id}`)
}
/**
* 测试 Webhook 推送
*/
export function testWebhook(id) {
return postJSON(`/webhook/${id}/test`, {})
}