@gftdcojp/gftd-cli
Version:
GFTD Schema Management CLI - Drizzle-like schema management for Kafka and ksqlDB
389 lines • 14.4 kB
JavaScript
"use strict";
/**
* DNS Manager - Local DNS Resolution Management
* Automatically manages /etc/hosts file for development domains
*/
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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DnsManager = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("node:path"));
const os = __importStar(require("node:os"));
const node_child_process_1 = require("node:child_process");
const node_util_1 = require("node:util");
const chalk_1 = __importDefault(require("chalk"));
const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
class DnsManager {
constructor(domain = 'gftd.ai.dev') {
this.domain = domain;
this.marker = `# GFTD CLI - ${domain}`;
// OS別のhostsファイルパス
this.hostsPath = os.platform() === 'win32'
? 'C:\\Windows\\System32\\drivers\\etc\\hosts'
: '/etc/hosts';
this.backupPath = path.join(os.homedir(), '.gftd', 'hosts.backup');
}
/**
* 管理者権限チェック
*/
async checkPermissions() {
try {
await fs.access(this.hostsPath, fs.constants.W_OK);
return true;
}
catch {
return false;
}
}
/**
* sudoで実行可能かチェック
*/
async checkSudoAccess() {
try {
await execAsync('sudo -n true');
return true;
}
catch {
return false;
}
}
/**
* hostsファイルのバックアップ作成
*/
async createBackup() {
try {
await fs.ensureDir(path.dirname(this.backupPath));
await fs.copy(this.hostsPath, this.backupPath);
console.log(chalk_1.default.green('✅ Hosts file backed up'));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to create backup:'), error);
throw error;
}
}
/**
* hostsファイルを読み込み
*/
async readHosts() {
try {
return await fs.readFile(this.hostsPath, 'utf-8');
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to read hosts file:'), error);
throw error;
}
}
/**
* hostsファイルに書き込み
*/
async writeHosts(content) {
try {
// 管理者権限が必要な場合はsudoを使用
if (!(await this.checkPermissions())) {
if (await this.checkSudoAccess()) {
const tempPath = path.join(os.tmpdir(), 'gftd-hosts-temp');
await fs.writeFile(tempPath, content);
await execAsync(`sudo mv "${tempPath}" "${this.hostsPath}"`);
}
else {
throw new Error('Administrator privileges required to modify hosts file');
}
}
else {
await fs.writeFile(this.hostsPath, content);
}
console.log(chalk_1.default.green('✅ Hosts file updated'));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to write hosts file:'), error);
throw error;
}
}
/**
* GFTD管理のエントリを取得
*/
async getManagedEntries() {
const content = await this.readHosts();
const entries = [];
const lines = content.split('\n');
let inGftdSection = false;
for (const line of lines) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith(this.marker)) {
inGftdSection = true;
continue;
}
if (inGftdSection) {
if (trimmedLine.startsWith('# END GFTD')) {
break;
}
if (trimmedLine && !trimmedLine.startsWith('#')) {
const parts = trimmedLine.split(/\s+/);
if (parts.length >= 2) {
entries.push({
ip: parts[0],
hostname: parts[1],
comment: parts.slice(2).join(' ')
});
}
}
}
}
return entries;
}
/**
* DNSエントリを追加
*/
async addEntry(hostname, ip = '127.0.0.1', comment) {
try {
const content = await this.readHosts();
const entries = await this.getManagedEntries();
// 既存エントリをチェック
const existingEntry = entries.find(e => e.hostname === hostname);
if (existingEntry) {
console.log(chalk_1.default.yellow(`⚠️ Entry for ${hostname} already exists`));
return;
}
// バックアップ作成
await this.createBackup();
// 新しいエントリを追加
const newEntry = { ip, hostname, comment };
const updatedContent = this.insertEntry(content, newEntry);
await this.writeHosts(updatedContent);
console.log(chalk_1.default.green(`✅ Added DNS entry: ${hostname} -> ${ip}`));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to add DNS entry:'), error);
throw error;
}
}
/**
* DNSエントリを削除
*/
async removeEntry(hostname) {
try {
const content = await this.readHosts();
const entries = await this.getManagedEntries();
const entryToRemove = entries.find(e => e.hostname === hostname);
if (!entryToRemove) {
console.log(chalk_1.default.yellow(`⚠️ Entry for ${hostname} not found`));
return;
}
// バックアップ作成
await this.createBackup();
// エントリを削除
const updatedContent = this.removeEntryFromContent(content, hostname);
await this.writeHosts(updatedContent);
console.log(chalk_1.default.green(`✅ Removed DNS entry: ${hostname}`));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to remove DNS entry:'), error);
throw error;
}
}
/**
* 全てのGFTD管理エントリを削除
*/
async removeAllEntries() {
try {
const content = await this.readHosts();
// バックアップ作成
await this.createBackup();
// GFTD管理セクションを削除
const updatedContent = this.removeGftdSection(content);
await this.writeHosts(updatedContent);
console.log(chalk_1.default.green('✅ Removed all GFTD DNS entries'));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to remove DNS entries:'), error);
throw error;
}
}
/**
* プロジェクト用のDNSエントリを追加
*/
async addProjectEntry(subdomain, projectName) {
const hostname = `${subdomain}.${this.domain}`;
const comment = `# ${projectName}`;
await this.addEntry(hostname, '127.0.0.1', comment);
}
/**
* プロジェクト用のDNSエントリを削除
*/
async removeProjectEntry(subdomain) {
const hostname = `${subdomain}.${this.domain}`;
await this.removeEntry(hostname);
}
/**
* 管理状況を表示
*/
async showStatus() {
try {
const entries = await this.getManagedEntries();
console.log(chalk_1.default.blue('🌐 DNS Management Status:'));
console.log('');
if (entries.length === 0) {
console.log(chalk_1.default.gray(' No DNS entries managed by GFTD'));
return;
}
console.log(chalk_1.default.gray(' Managed DNS entries:'));
entries.forEach(entry => {
console.log(` ${chalk_1.default.green(entry.hostname)} -> ${entry.ip} ${chalk_1.default.gray(entry.comment || '')}`);
});
console.log('');
console.log(chalk_1.default.gray(` Hosts file: ${this.hostsPath}`));
console.log(chalk_1.default.gray(` Backup: ${this.backupPath}`));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to show DNS status:'), error);
}
}
/**
* DNS設定の初期化
*/
async initialize() {
try {
// 権限チェック
if (!(await this.checkPermissions()) && !(await this.checkSudoAccess())) {
console.log(chalk_1.default.yellow('⚠️ Administrator privileges required for DNS management'));
console.log(chalk_1.default.gray(' DNS entries will need to be added manually'));
return;
}
// hostsファイルの確認
const content = await this.readHosts();
if (!content.includes(this.marker)) {
console.log(chalk_1.default.blue('🔧 Initializing DNS management...'));
const updatedContent = this.initializeGftdSection(content);
await this.writeHosts(updatedContent);
console.log(chalk_1.default.green('✅ DNS management initialized'));
}
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to initialize DNS management:'), error);
throw error;
}
}
/**
* バックアップから復元
*/
async restoreBackup() {
try {
if (!(await fs.pathExists(this.backupPath))) {
console.log(chalk_1.default.yellow('⚠️ No backup found'));
return;
}
await fs.copy(this.backupPath, this.hostsPath);
console.log(chalk_1.default.green('✅ Hosts file restored from backup'));
}
catch (error) {
console.error(chalk_1.default.red('❌ Failed to restore backup:'), error);
throw error;
}
}
// Private helper methods
insertEntry(content, entry) {
const lines = content.split('\n');
let insertIndex = -1;
// GFTD管理セクションを探す
for (let i = 0; i < lines.length; i++) {
if (lines[i].trim().startsWith(this.marker)) {
// セクションの終了を探す
for (let j = i + 1; j < lines.length; j++) {
if (lines[j].trim().startsWith('# END GFTD')) {
insertIndex = j;
break;
}
}
break;
}
}
// セクションが見つからない場合は作成
if (insertIndex === -1) {
lines.push('');
lines.push(this.marker);
lines.push('# END GFTD');
insertIndex = lines.length - 1;
}
// エントリを挿入
const entryLine = `${entry.ip} ${entry.hostname} ${entry.comment || ''}`.trim();
lines.splice(insertIndex, 0, entryLine);
return lines.join('\n');
}
removeEntryFromContent(content, hostname) {
const lines = content.split('\n');
const filteredLines = lines.filter(line => {
const trimmedLine = line.trim();
return !trimmedLine.includes(hostname) || trimmedLine.startsWith('#');
});
return filteredLines.join('\n');
}
removeGftdSection(content) {
const lines = content.split('\n');
const filteredLines = [];
let inGftdSection = false;
for (const line of lines) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith(this.marker)) {
inGftdSection = true;
continue;
}
if (inGftdSection && trimmedLine.startsWith('# END GFTD')) {
inGftdSection = false;
continue;
}
if (!inGftdSection) {
filteredLines.push(line);
}
}
return filteredLines.join('\n');
}
initializeGftdSection(content) {
const lines = content.split('\n');
// 末尾に空行がない場合は追加
if (lines[lines.length - 1].trim() !== '') {
lines.push('');
}
lines.push(this.marker);
lines.push('# Automatically managed by GFTD CLI');
lines.push('# Do not edit this section manually');
lines.push('# END GFTD');
return lines.join('\n');
}
}
exports.DnsManager = DnsManager;
//# sourceMappingURL=dns-manager.js.map