This commit is contained in:
Baobhan Sith
2025-04-14 22:51:05 +08:00
parent 286492fc63
commit a974b8b1d9
49 changed files with 13954 additions and 0 deletions
@@ -0,0 +1,116 @@
import { Request, Response } from 'express';
import { Statement } from 'sqlite3'; // 引入 Statement 类型
import { getDb } from '../database';
import { encrypt } from '../utils/crypto'; // 引入加密函数
const db = getDb();
// 连接数据结构 (仅用于类型提示,不包含敏感信息)
interface ConnectionInfo {
id: number;
name: string;
host: string;
port: number;
username: string;
auth_method: 'password'; // MVP 仅支持密码
created_at: number;
updated_at: number;
last_connected_at: number | null;
}
/**
* 创建新连接 (POST /api/v1/connections)
*/
export const createConnection = async (req: Request, res: Response): Promise<void> => {
const { name, host, port = 22, username, password } = req.body;
const auth_method = 'password'; // MVP 强制为 password
const userId = req.session.userId; // 从会话获取用户 ID
// 输入验证 (基础)
if (!name || !host || !username || !password) {
res.status(400).json({ message: '缺少必要的连接信息 (name, host, username, password)。' });
return;
}
if (typeof port !== 'number' || port <= 0 || port > 65535) {
res.status(400).json({ message: '端口号无效。' });
return;
}
try {
// 加密密码
const encryptedPassword = encrypt(password);
const now = Math.floor(Date.now() / 1000); // 当前 Unix 时间戳 (秒)
// 插入数据库
const result = await new Promise<{ lastID: number }>((resolve, reject) => {
const stmt = db.prepare(
`INSERT INTO connections (name, host, port, username, auth_method, encrypted_password, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
);
// 注意:这里没有存储 userId,因为 MVP 只有一个用户。如果未来支持多用户,需要添加 user_id 字段。
// 使用 function 关键字以保留正确的 this 上下文,并为 err 和 this 添加类型注解
stmt.run(name, host, port, username, auth_method, encryptedPassword, now, now, function (this: Statement, err: Error | null) {
if (err) {
console.error('插入连接时出错:', err.message);
return reject(new Error('创建连接失败'));
}
// this.lastID 包含新插入行的 ID
// 使用类型断言 (as any) 来解决 TS 类型检查问题
resolve({ lastID: (this as any).lastID });
});
stmt.finalize(); // 完成语句执行
});
// 返回成功响应
res.status(201).json({
message: '连接创建成功。',
connection: {
id: result.lastID,
name, host, port, username, auth_method,
created_at: now, updated_at: now, last_connected_at: null
}
});
} catch (error) {
console.error('创建连接时发生错误:', error);
res.status(500).json({ message: '创建连接时发生内部服务器错误。' });
}
};
/**
* 获取连接列表 (GET /api/v1/connections)
*/
export const getConnections = async (req: Request, res: Response): Promise<void> => {
const userId = req.session.userId; // 虽然 MVP 只有一个用户,但保留以备将来使用
try {
// 查询数据库,排除敏感字段 encrypted_password
// 注意:如果未来支持多用户,需要添加 WHERE user_id = ? 条件
const connections = await new Promise<ConnectionInfo[]>((resolve, reject) => {
db.all(
`SELECT id, name, host, port, username, auth_method, created_at, updated_at, last_connected_at
FROM connections
ORDER BY name ASC`, // 按名称排序
(err, rows: ConnectionInfo[]) => {
if (err) {
console.error('查询连接列表时出错:', err.message);
return reject(new Error('获取连接列表失败'));
}
resolve(rows);
}
);
});
res.status(200).json(connections);
} catch (error) {
console.error('获取连接列表时发生错误:', error);
res.status(500).json({ message: '获取连接列表时发生内部服务器错误。' });
}
};
// 其他控制器函数的占位符
// export const getConnectionById = ...
// export const updateConnection = ...
// export const deleteConnection = ...
// export const testConnection = ...
@@ -0,0 +1,22 @@
import { Router } from 'express';
import { isAuthenticated } from '../auth/auth.middleware'; // 引入认证中间件
import { createConnection, getConnections } from './connections.controller';
const router = Router();
// 应用认证中间件到所有 /connections 路由
router.use(isAuthenticated); // 恢复认证检查
// GET /api/v1/connections - 获取连接列表
router.get('/', getConnections);
// POST /api/v1/connections - 创建新连接
router.post('/', createConnection);
// 未来可以添加其他路由,如获取单个连接、更新、删除、测试连接等
// router.get('/:id', getConnectionById);
// router.put('/:id', updateConnection);
// router.delete('/:id', deleteConnection);
// router.post('/:id/test', testConnection);
export default router;