"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectionService = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const crypto = __importStar(require("crypto"));
const data_dir_1 = require("../utils/data-dir");
/**
 * SSH 连接管理服务
 * 负责管理 SSH 连接配置的 CRUD，数据存储在本地 JSON 文件中
 */
class ConnectionService {
    /**
     * 连接配置文件路径
     * 优先使用环境变量 AICMD_DATA_DIR，否则使用用户主目录下的 .aicmd 目录
     */
    configPath;
    constructor() {
        this.configPath = (0, data_dir_1.getDataPath)('connections.json');
    }
    /**
     * 初始化服务，创建配置目录
     */
    async init() {
        const configDir = path.dirname(this.configPath);
        if (!fs.existsSync(configDir)) {
            fs.mkdirSync(configDir, { recursive: true });
        }
    }
    /**
     * 获取所有 SSH 连接配置
     */
    async getAllConnections() {
        try {
            if (!fs.existsSync(this.configPath)) {
                return [];
            }
            const data = fs.readFileSync(this.configPath, 'utf8');
            return JSON.parse(data);
        }
        catch (error) {
            console.error('读取连接配置失败:', error);
            return [];
        }
    }
    /**
     * 根据 ID 获取 SSH 连接配置
     */
    async getConnectionById(id) {
        const connections = await this.getAllConnections();
        return connections.find(conn => conn.id === id) || null;
    }
    /**
     * 添加 SSH 连接配置
     */
    async addConnection(connection) {
        const connections = await this.getAllConnections();
        // 检查名称是否重复
        if (connections.find(conn => conn.name === connection.name)) {
            throw new Error('连接名称已存在');
        }
        // 生成 ID 并设置时间戳
        connection.id = this.generateId();
        connection.createdAt = new Date();
        connection.updatedAt = new Date();
        connection.enabled = connection.enabled !== undefined ? connection.enabled : true;
        connections.push(connection);
        await this.saveConnections(connections);
        return connection;
    }
    /**
     * 更新 SSH 连接配置
     */
    async updateConnection(id, updates) {
        const connections = await this.getAllConnections();
        const index = connections.findIndex(conn => conn.id === id);
        if (index === -1) {
            throw new Error('连接配置不存在');
        }
        // 检查名称重复
        if (updates.name && connections.find((conn, idx) => conn.name === updates.name && idx !== index)) {
            throw new Error('连接名称已存在');
        }
        connections[index] = { ...connections[index], ...updates, updatedAt: new Date() };
        await this.saveConnections(connections);
        return connections[index];
    }
    /**
     * 删除 SSH 连接配置
     */
    async deleteConnection(id) {
        const connections = await this.getAllConnections();
        const filteredConnections = connections.filter(conn => conn.id !== id);
        if (filteredConnections.length === connections.length) {
            throw new Error('连接配置不存在');
        }
        await this.saveConnections(filteredConnections);
    }
    /**
     * 测试 SSH 连接
     */
    async testConnection(connection) {
        try {
            const { Client } = require('ssh2');
            return new Promise((resolve) => {
                const conn = new Client();
                const timeout = setTimeout(() => {
                    conn.end();
                    resolve(false);
                }, 10000);
                conn.on('ready', () => {
                    clearTimeout(timeout);
                    conn.end();
                    resolve(true);
                }).on('error', (err) => {
                    clearTimeout(timeout);
                    console.error('SSH 连接测试失败:', err.message);
                    resolve(false);
                }).connect(this.getSSHConfig(connection));
            });
        }
        catch (error) {
            console.error('SSH 连接测试异常:', error);
            return false;
        }
    }
    /**
     * 从连接实体提取 ssh2 连接配置
     */
    getSSHConfig(connection) {
        const config = {
            host: connection.host,
            port: connection.port || 22,
            username: connection.username,
            readyTimeout: 30000,
            keepaliveInterval: 10000,
            keepaliveCountMax: 3,
        };
        if (connection.authType === 'privateKey' && connection.privateKey) {
            config.privateKey = this.convertToPEM(connection.privateKey);
            if (connection.passphrase) {
                config.passphrase = connection.passphrase;
            }
        }
        else if (connection.password) {
            config.password = connection.password;
        }
        else {
            // 没有配置密码和证书，尝试使用本机 SSH 密钥
            const localKey = this.getLocalSSHKey();
            if (localKey) {
                config.privateKey = localKey;
            }
        }
        return { ...config, ...connection.options };
    }
    /**
     * 获取本机 SSH 密钥（自动扫描 ~/.ssh 目录下的私钥文件）
     */
    getLocalSSHKey() {
        const sshDir = path.join(os.homedir(), '.ssh');
        try {
            if (!fs.existsSync(sshDir)) {
                console.warn(`[SSH] SSH 目录不存在: ${sshDir}`);
                return null;
            }
            // 读取 ~/.ssh 目录下所有文件
            const files = fs.readdirSync(sshDir);
            // 过滤出可能的私钥文件（排除 .pub、known_hosts、config、authorized_keys 等）
            const privateKeyFiles = files.filter(f => !f.endsWith('.pub') &&
                !['known_hosts', 'known_hosts.old', 'config', 'authorized_keys', 'authorized_keys2'].includes(f) &&
                !f.startsWith('.'));
            if (privateKeyFiles.length === 0) {
                console.warn('[SSH] ~/.ssh 目录下未找到私钥文件');
                return null;
            }
            // 尝试每个私钥文件
            for (const keyFile of privateKeyFiles) {
                const keyPath = path.join(sshDir, keyFile);
                try {
                    const stats = fs.statSync(keyPath);
                    if (!stats.isFile())
                        continue;
                    const key = fs.readFileSync(keyPath, 'utf8');
                    // 简单验证是否是有效的私钥文件（包含 BEGIN ... PRIVATE KEY）
                    if (key.includes('BEGIN') && key.includes('PRIVATE KEY')) {
                        console.log(`[SSH] 使用本机密钥: ${keyPath}`);
                        return this.convertToPEM(key);
                    }
                }
                catch (e) {
                    console.warn(`[SSH] 读取密钥文件失败 ${keyPath}:`, e.message);
                }
            }
            console.warn('[SSH] 未找到可用的本机 SSH 私钥');
            return null;
        }
        catch (e) {
            console.error(`[SSH] 扫描 SSH 目录失败:`, e.message);
            return null;
        }
    }
    /**
     * 将 OpenSSH 格式的私钥转换为 PEM 格式
     * ssh2 库不支持 OpenSSH 格式，需要转换
     */
    convertToPEM(privateKey) {
        // 如果已经是 PEM 格式，直接返回
        if (privateKey.includes('BEGIN RSA PRIVATE KEY') || privateKey.includes('BEGIN PRIVATE KEY')) {
            return privateKey;
        }
        // 尝试转换 OpenSSH 格式为 PEM 格式
        try {
            const keyObject = crypto.createPrivateKey({
                key: privateKey,
                format: 'openssh',
            });
            return keyObject.export({ type: 'pkcs8', format: 'pem' }).toString();
        }
        catch (e) {
            console.warn('转换私钥格式失败，使用原始私钥:', e.message);
            return privateKey;
        }
    }
    /**
     * 保存连接配置到文件
     */
    async saveConnections(connections) {
        try {
            const dir = path.dirname(this.configPath);
            if (!fs.existsSync(dir)) {
                fs.mkdirSync(dir, { recursive: true });
            }
            fs.writeFileSync(this.configPath, JSON.stringify(connections, null, 2), 'utf8');
        }
        catch (error) {
            console.error('保存连接配置失败:', error);
            throw error;
        }
    }
    /**
     * 生成唯一 ID
     */
    generateId() {
        return Date.now().toString(36) + Math.random().toString(36).substr(2);
    }
}
exports.ConnectionService = ConnectionService;
//# sourceMappingURL=connection.service.js.map