重构(前端): 持久化快速命令排序和密码切换
添加持久化排序字段并重新排序快速命令和标签的端点,更新前端以支持手动拖放排序,并为连接和凭据表单添加密码可见性切换。此外,将 SSH 连接测试作为连接列表中的默认操作,并刷新相关模块文档和更改日志。
This commit is contained in:
@@ -2,202 +2,202 @@ import { Request, Response } from 'express';
|
||||
import * as QuickCommandsService from './quick-commands.service';
|
||||
import { QuickCommandSortBy } from './quick-commands.service';
|
||||
|
||||
/**
|
||||
* 处理添加新快捷指令的请求
|
||||
*/
|
||||
const isNumberArray = (value: unknown): value is number[] =>
|
||||
Array.isArray(value) && value.every((item) => typeof item === 'number' && Number.isFinite(item));
|
||||
|
||||
export const addQuickCommand = async (req: Request, res: Response): Promise<void> => {
|
||||
// 从请求体中解构出 name, command, 以及可选的 tagIds 和 variables
|
||||
const { name, command, tagIds, variables } = req.body;
|
||||
|
||||
// --- 基本验证 ---
|
||||
if (!command || typeof command !== 'string' || command.trim().length === 0) {
|
||||
res.status(400).json({ message: '指令内容不能为空' });
|
||||
return;
|
||||
}
|
||||
// 名称可以是 null 或 string
|
||||
|
||||
if (name !== null && typeof name !== 'string') {
|
||||
res.status(400).json({ message: '名称必须是字符串或 null' });
|
||||
return;
|
||||
}
|
||||
// 验证 tagIds (如果提供的话)
|
||||
if (tagIds !== undefined && (!Array.isArray(tagIds) || !tagIds.every(id => typeof id === 'number'))) {
|
||||
res.status(400).json({ message: 'tagIds 必须是一个数字数组' });
|
||||
res.status(400).json({ message: '名称必须是字符串或 null' });
|
||||
return;
|
||||
}
|
||||
// 验证 variables (如果提供的话)
|
||||
|
||||
if (tagIds !== undefined && !isNumberArray(tagIds)) {
|
||||
res.status(400).json({ message: 'tagIds 必须是数字数组' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (variables !== undefined && (typeof variables !== 'object' || variables === null || Array.isArray(variables))) {
|
||||
res.status(400).json({ message: 'variables 必须是一个对象' });
|
||||
res.status(400).json({ message: 'variables 必须是对象' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 将 tagIds 和 variables 传递给 Service 层
|
||||
const newId = await QuickCommandsService.addQuickCommand(name, command, tagIds, variables);
|
||||
// 尝试获取新创建的带标签的指令信息返回
|
||||
const newCommand = await QuickCommandsService.getQuickCommandById(newId);
|
||||
|
||||
if (newCommand) {
|
||||
res.status(201).json({ message: '快捷指令已添加', command: newCommand });
|
||||
} else {
|
||||
console.error(`[Controller] 添加快捷指令后未能找到 ID: ${newId}`);
|
||||
res.status(201).json({ message: '快捷指令已添加,但无法检索新记录', id: newId });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json({ message: '快捷指令已添加,但无法检索新记录', id: newId });
|
||||
} catch (error: any) {
|
||||
console.error('[Controller] 添加快捷指令失败:', error.message);
|
||||
console.error('[QuickCommandsController] 添加快捷指令失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法添加快捷指令' });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理获取所有快捷指令的请求 (支持排序)
|
||||
*/
|
||||
export const getAllQuickCommands = async (req: Request, res: Response): Promise<void> => {
|
||||
const sortBy = req.query.sortBy as QuickCommandSortBy | undefined;
|
||||
// 验证 sortBy 参数
|
||||
const validSortBy: QuickCommandSortBy = (sortBy === 'name' || sortBy === 'usage_count') ? sortBy : 'name';
|
||||
const validSortBy: QuickCommandSortBy =
|
||||
sortBy === 'name' || sortBy === 'usage_count' || sortBy === 'manual' ? sortBy : 'manual';
|
||||
|
||||
try {
|
||||
const commands = await QuickCommandsService.getAllQuickCommands(validSortBy);
|
||||
res.status(200).json(commands);
|
||||
} catch (error: any) {
|
||||
console.error('获取快捷指令控制器出错:', error);
|
||||
console.error('[QuickCommandsController] 获取快捷指令失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法获取快捷指令' });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理更新快捷指令的请求
|
||||
*/
|
||||
export const updateQuickCommand = async (req: Request, res: Response): Promise<void> => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
// 从请求体中解构出 name, command, 以及可选的 tagIds 和 variables
|
||||
const id = Number.parseInt(req.params.id, 10);
|
||||
const { name, command, tagIds, variables } = req.body;
|
||||
|
||||
// --- 基本验证 ---
|
||||
if (isNaN(id)) {
|
||||
if (Number.isNaN(id)) {
|
||||
res.status(400).json({ message: '无效的 ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!command || typeof command !== 'string' || command.trim().length === 0) {
|
||||
res.status(400).json({ message: '指令内容不能为空' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (name !== null && typeof name !== 'string') {
|
||||
res.status(400).json({ message: '名称必须是字符串或 null' });
|
||||
return;
|
||||
}
|
||||
// 验证 tagIds (如果提供的话)
|
||||
// 注意: tagIds 为 undefined 表示不更新标签,空数组 [] 表示清除所有标签
|
||||
if (tagIds !== undefined && (!Array.isArray(tagIds) || !tagIds.every(id => typeof id === 'number'))) {
|
||||
res.status(400).json({ message: 'tagIds 必须是一个数字数组' });
|
||||
res.status(400).json({ message: '名称必须是字符串或 null' });
|
||||
return;
|
||||
}
|
||||
// 验证 variables (如果提供的话)
|
||||
// undefined 表示不更新 variables, null 或对象表示要更新
|
||||
if (variables !== undefined && variables !== null && (typeof variables !== 'object' || Array.isArray(variables))) {
|
||||
|
||||
if (tagIds !== undefined && !isNumberArray(tagIds)) {
|
||||
res.status(400).json({ message: 'tagIds 必须是数字数组' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
variables !== undefined &&
|
||||
variables !== null &&
|
||||
(typeof variables !== 'object' || Array.isArray(variables))
|
||||
) {
|
||||
res.status(400).json({ message: 'variables 必须是对象或 null' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 将 tagIds 和 variables 传递给 Service 层
|
||||
const success = await QuickCommandsService.updateQuickCommand(id, name, command, tagIds, variables);
|
||||
if (success) {
|
||||
// 尝试获取更新后的带标签的指令信息返回
|
||||
const updatedCommand = await QuickCommandsService.getQuickCommandById(id);
|
||||
if (updatedCommand) {
|
||||
res.status(200).json({ message: '快捷指令已更新', command: updatedCommand });
|
||||
} else {
|
||||
console.error(`[Controller] 更新快捷指令后未能找到 ID: ${id}`);
|
||||
res.status(200).json({ message: '快捷指令已更新,但无法检索更新后的记录' });
|
||||
}
|
||||
} else {
|
||||
// 检查指令是否真的不存在
|
||||
if (!success) {
|
||||
const commandExists = await QuickCommandsService.getQuickCommandById(id);
|
||||
if (!commandExists) {
|
||||
res.status(404).json({ message: '未找到要更新的快捷指令' });
|
||||
} else {
|
||||
console.error(`[Controller] 更新快捷指令 ${id} 失败,但指令存在。`);
|
||||
res.status(500).json({ message: '更新快捷指令时发生未知错误' });
|
||||
}
|
||||
res.status(commandExists ? 500 : 404).json({
|
||||
message: commandExists ? '更新快捷指令时发生未知错误' : '未找到要更新的快捷指令',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedCommand = await QuickCommandsService.getQuickCommandById(id);
|
||||
if (updatedCommand) {
|
||||
res.status(200).json({ message: '快捷指令已更新', command: updatedCommand });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json({ message: '快捷指令已更新,但无法检索更新后的记录' });
|
||||
} catch (error: any) {
|
||||
console.error('更新快捷指令控制器出错:', error);
|
||||
console.error('[QuickCommandsController] 更新快捷指令失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法更新快捷指令' });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理删除快捷指令的请求
|
||||
*/
|
||||
export const deleteQuickCommand = async (req: Request, res: Response): Promise<void> => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
|
||||
if (isNaN(id)) {
|
||||
const id = Number.parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) {
|
||||
res.status(400).json({ message: '无效的 ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await QuickCommandsService.deleteQuickCommand(id);
|
||||
if (success) {
|
||||
res.status(200).json({ message: '快捷指令已删除' });
|
||||
} else {
|
||||
res.status(404).json({ message: '未找到要删除的快捷指令' });
|
||||
}
|
||||
res.status(success ? 200 : 404).json({
|
||||
message: success ? '快捷指令已删除' : '未找到要删除的快捷指令',
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('删除快捷指令控制器出错:', error);
|
||||
console.error('[QuickCommandsController] 删除快捷指令失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法删除快捷指令' });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理增加快捷指令使用次数的请求
|
||||
*/
|
||||
export const incrementUsage = async (req: Request, res: Response): Promise<void> => {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
|
||||
if (isNaN(id)) {
|
||||
const id = Number.parseInt(req.params.id, 10);
|
||||
if (Number.isNaN(id)) {
|
||||
res.status(400).json({ message: '无效的 ID' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await QuickCommandsService.incrementUsageCount(id);
|
||||
if (success) {
|
||||
res.status(200).json({ message: '使用次数已增加' });
|
||||
} else {
|
||||
// 即使没找到也可能返回成功,避免不必要的错误提示
|
||||
console.warn(`尝试增加不存在的快捷指令 (ID: ${id}) 的使用次数`);
|
||||
res.status(200).json({ message: '使用次数已记录 (或指令不存在)' });
|
||||
}
|
||||
res.status(200).json({
|
||||
message: success ? '使用次数已增加' : '使用次数已记录(或指令不存在)',
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('增加快捷指令使用次数控制器出错:', error);
|
||||
console.error('[QuickCommandsController] 增加快捷指令使用次数失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法增加使用次数' });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量将标签分配给多个快捷指令
|
||||
*/
|
||||
export const assignTagToCommands = async (req: Request, res: Response): Promise<void> => { // Add : Promise<void>
|
||||
export const assignTagToCommands = async (req: Request, res: Response): Promise<void> => {
|
||||
const { commandIds, tagId } = req.body;
|
||||
|
||||
// 基本验证
|
||||
if (!Array.isArray(commandIds) || commandIds.length === 0 || typeof tagId !== 'number') {
|
||||
res.status(400).json({ success: false, message: '请求体必须包含 commandIds (非空数组) 和 tagId (数字)。' });
|
||||
return; // Use return without value to exit early
|
||||
if (!isNumberArray(commandIds) || commandIds.length === 0 || typeof tagId !== 'number') {
|
||||
res.status(400).json({ success: false, message: '请求体必须包含 commandIds 和 tagId' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用 Service 函数处理批量分配
|
||||
console.log(`[Controller] assignTagToCommands: Received commandIds: ${JSON.stringify(commandIds)}, tagId: ${tagId}`);
|
||||
await QuickCommandsService.assignTagToCommands(commandIds, tagId);
|
||||
res.status(200).json({ success: true, message: `标签 ${tagId} 已成功尝试关联到 ${commandIds.length} 个指令。` });
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: `标签 ${tagId} 已成功关联到 ${commandIds.length} 个指令`,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('[Controller] 批量分配标签时出错:', error.message);
|
||||
// 根据错误类型返回不同的状态码可能更好,但这里简化处理
|
||||
res.status(500).json({ success: false, message: error.message || '批量分配标签时发生内部服务器错误。' });
|
||||
// No return needed here, error handling completes the response
|
||||
console.error('[QuickCommandsController] 批量分配标签失败:', error.message);
|
||||
res.status(500).json({ success: false, message: error.message || '批量分配标签失败' });
|
||||
}
|
||||
};
|
||||
|
||||
export const reorderQuickCommands = async (req: Request, res: Response): Promise<void> => {
|
||||
const { commandIds } = req.body;
|
||||
if (!isNumberArray(commandIds) || commandIds.length === 0) {
|
||||
res.status(400).json({ message: 'commandIds 必须是非空数字数组' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await QuickCommandsService.reorderQuickCommands(commandIds);
|
||||
res.status(200).json({ message: '快捷指令顺序已更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[QuickCommandsController] 更新快捷指令顺序失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法更新快捷指令顺序' });
|
||||
}
|
||||
};
|
||||
|
||||
export const reorderCommandsByTag = async (req: Request, res: Response): Promise<void> => {
|
||||
const { tagId, commandIds } = req.body;
|
||||
if (typeof tagId !== 'number' || !isNumberArray(commandIds) || commandIds.length === 0) {
|
||||
res.status(400).json({ message: 'tagId 和 commandIds 必须有效' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await QuickCommandsService.reorderCommandsByTag(tagId, commandIds);
|
||||
res.status(200).json({ message: '标签内快捷指令顺序已更新' });
|
||||
} catch (error: any) {
|
||||
console.error('[QuickCommandsController] 更新标签内快捷指令顺序失败:', error.message);
|
||||
res.status(500).json({ message: error.message || '无法更新标签内快捷指令顺序' });
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user