dnssweeper-cli
Version:
CLI tool for detecting and analyzing unused DNS records
272 lines • 10.5 kB
JavaScript
;
/**
* 出力フォーマッター
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.printAnalysisSummary = printAnalysisSummary;
exports.printAnalysisTable = printAnalysisTable;
exports.formatAsJSON = formatAsJSON;
exports.formatAsCSV = formatAsCSV;
exports.formatAsDetailedCSV = formatAsDetailedCSV;
exports.showProgress = showProgress;
const chalk_1 = __importDefault(require("chalk"));
const riskAnalyzer_1 = require("../analyzers/riskAnalyzer");
const messages_1 = require("./messages");
/**
* 分析サマリーをコンソールに出力
* @param summary - 分析サマリー
* @param verbose - 詳細出力フラグ
* @param language - 言語設定
*/
function printAnalysisSummary(summary, verbose = false, language = 'ja') {
const messages = (0, messages_1.getMessages)(language);
console.log(chalk_1.default.blue(`\n${messages.analysis.summary}`));
console.log(chalk_1.default.gray('='.repeat(50)));
// 基本情報
console.log(`${chalk_1.default.white(messages.analysis.totalRecords)} ${chalk_1.default.yellow(summary.totalRecords)}`);
const timeUnit = language === 'ja' ? '秒' : ' seconds';
console.log(`${chalk_1.default.white(messages.analysis.processingTime)} ${chalk_1.default.green(summary.processingTime.toFixed(2))}${timeUnit}`);
// リスク分布
console.log(`\n${messages.analysis.riskDistribution}`);
const { riskDistribution } = summary;
for (const [level, count] of Object.entries(riskDistribution)) {
const { color, symbol } = (0, riskAnalyzer_1.getRiskLevelColor)(level);
const label = messages.riskLevels[level];
const percentage = summary.totalRecords > 0
? Math.round((count / summary.totalRecords) * 100)
: 0;
const colorFunc = chalk_1.default[color];
console.log(` ${symbol} ${label}: ${colorFunc(count)} (${percentage}%)`);
}
// 高リスクレコード数の強調表示
const highRiskCount = riskDistribution.critical + riskDistribution.high;
if (highRiskCount > 0) {
const itemUnit = language === 'ja' ? '件' : ' items';
console.log(`\n⚠️ ${chalk_1.default.red(messages.analysis.deleteRecommended)} ${chalk_1.default.redBright(highRiskCount)}${itemUnit}`);
}
else {
console.log(`\n✅ ${chalk_1.default.green(messages.analysis.noHighRisk)}`);
}
// 詳細情報(verboseモード)
if (verbose && summary.topRiskyRecords.length > 0) {
console.log(`\n${messages.analysis.topRiskyRecords}`);
for (let i = 0; i < Math.min(5, summary.topRiskyRecords.length); i++) {
const result = summary.topRiskyRecords[i];
const { symbol } = (0, riskAnalyzer_1.getRiskLevelColor)(result.riskLevel);
const label = messages.riskLevels[result.riskLevel];
const scoreLabel = language === 'ja' ? 'スコア' : 'Score';
console.log(` ${i + 1}. ${symbol} ${result.record.name} (${label}, ${scoreLabel}: ${result.riskScore})`);
}
}
}
/**
* 分析結果の詳細をテーブル形式で出力
* @param results - 分析結果の配列
* @param limit - 表示件数の制限
* @param minRiskLevel - 最小リスクレベル
* @param language - 言語設定
*/
function printAnalysisTable(results, limit = 20, minRiskLevel = 'low', language = 'ja') {
const messages = (0, messages_1.getMessages)(language);
// フィルタリング
const filteredResults = filterResultsByRiskLevel(results, minRiskLevel);
const displayResults = filteredResults.slice(0, limit);
if (displayResults.length === 0) {
const noResultsMsg = language === 'ja' ? '表示する結果がありません' : 'No results to display';
console.log(chalk_1.default.gray(noResultsMsg));
return;
}
console.log(chalk_1.default.blue(`\n${messages.analysis.detailedResults}`));
console.log(chalk_1.default.gray('='.repeat(80)));
// ヘッダー
const headers = language === 'ja'
? `${'No'.padEnd(4)} ${'リスク'.padEnd(8)} ${'スコア'.padEnd(6)} ${'レコード名'.padEnd(30)} ${'タイプ'.padEnd(8)}`
: `${'No'.padEnd(4)} ${'Risk'.padEnd(12)} ${'Score'.padEnd(6)} ${'Record Name'.padEnd(30)} ${'Type'.padEnd(8)}`;
console.log(chalk_1.default.white(headers));
console.log(chalk_1.default.gray('-'.repeat(80)));
// データ行
displayResults.forEach((result, index) => {
const { symbol } = (0, riskAnalyzer_1.getRiskLevelColor)(result.riskLevel);
const localizedLabel = messages.riskLevels[result.riskLevel];
const no = (index + 1).toString().padEnd(4);
const risk = `${symbol}${localizedLabel}`.padEnd(language === 'ja' ? 8 : 12);
const score = result.riskScore.toString().padEnd(6);
const name = result.record.name.padEnd(30);
const type = result.record.type.padEnd(8);
console.log(`${no} ${risk} ${score} ${name} ${type}`);
});
// フッター
if (filteredResults.length > limit) {
const moreMsg = language === 'ja'
? `... 他${filteredResults.length - limit}件(--verboseで全件表示)`
: `... ${filteredResults.length - limit} more (use --verbose to show all)`;
console.log(chalk_1.default.gray(`\n${moreMsg}`));
}
}
/**
* 分析結果をJSON形式で出力
* @param results - 分析結果の配列
* @param summary - 分析サマリー
* @returns JSON文字列
*/
function formatAsJSON(results, summary) {
const output = {
summary,
results: results.map(result => ({
record: result.record,
analysis: {
riskScore: result.riskScore,
riskLevel: result.riskLevel,
matchedPatterns: result.matchedPatterns,
reasons: result.reasons,
},
})),
};
return JSON.stringify(output, null, 2);
}
/**
* 分析結果をCSV形式で出力
* @param results - 分析結果の配列
* @returns CSV文字列
*/
function formatAsCSV(results) {
const headers = [
'Name',
'Type',
'Content',
'TTL',
'Proxied',
'Created',
'Modified',
'RiskScore',
'RiskLevel',
'MatchedPatterns',
'Reasons',
];
const rows = results.map(result => [
result.record.name,
result.record.type,
result.record.content,
result.record.ttl,
result.record.proxied,
result.record.created,
result.record.modified,
result.riskScore,
result.riskLevel,
result.matchedPatterns.join(';'),
result.reasons.join(';'),
]);
return [headers, ...rows].map(row => row.join(',')).join('\n');
}
/**
* 分析結果を詳細CSVファイル形式で出力(運用フロー対応)
* @param results - 分析結果の配列
* @param language - 言語設定
* @returns 詳細CSV文字列
*/
function formatAsDetailedCSV(results, language = 'ja') {
const messages = (0, messages_1.getMessages)(language);
// 多言語対応ヘッダー
const headers = language === 'ja'
? [
'ドメイン名',
'レコードタイプ',
'コンテンツ',
'TTL',
'プロキシ設定',
'作成日',
'最終更新日',
'リスクスコア',
'リスクレベル',
'検出パターン',
'判定理由',
'使用状況確認',
'削除判断',
'削除実行日',
'備考',
]
: [
'Domain Name',
'Record Type',
'Content',
'TTL',
'Proxied',
'Created',
'Last Modified',
'Risk Score',
'Risk Level',
'Detected Patterns',
'Reasons',
'Usage Check',
'Delete Decision',
'Deletion Date',
'Notes',
];
const rows = results.map(result => [
`"${result.record.name}"`,
result.record.type,
`"${result.record.content}"`,
result.record.ttl,
result.record.proxied ? 'YES' : 'NO',
result.record.created,
result.record.modified,
result.riskScore,
messages.riskLevels[result.riskLevel],
`"${result.matchedPatterns.join('; ')}"`,
`"${result.reasons.join('; ')}"`,
'', // 使用状況確認(手動入力用)
'', // 削除判断(手動入力用)
'', // 削除実行日(手動入力用)
'', // 備考(手動入力用)
]);
// ヘッダーコメント追加
const headerComment = language === 'ja'
? '# DNSweeper分析結果 - 月次DNS棚卸し用'
: '# DNSweeper Analysis Results - Monthly DNS Audit';
const usageComment = language === 'ja'
? '# 使用方法: 1.Cloudflare UIで使用状況確認 2.削除判断記入 3.削除実行'
: '# Usage: 1.Check usage in Cloudflare UI 2.Fill deletion decision 3.Execute deletion';
return [
headerComment,
usageComment,
headers.join(','),
...rows.map(row => row.join(',')),
].join('\n');
}
/**
* リスクレベルでフィルタリング
*/
function filterResultsByRiskLevel(results, minRiskLevel) {
const riskLevelOrder = [
'safe',
'low',
'medium',
'high',
'critical',
];
const minIndex = riskLevelOrder.indexOf(minRiskLevel);
return results.filter(result => {
const resultIndex = riskLevelOrder.indexOf(result.riskLevel);
return resultIndex >= minIndex;
});
}
/**
* 進捗バーの表示
* @param current - 現在の進捗
* @param total - 総数
* @param message - メッセージ
*/
function showProgress(current, total, message) {
const percentage = Math.round((current / total) * 100);
const barLength = 30;
const filledLength = Math.round((current / total) * barLength);
const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
process.stdout.write(`\r${message} [${bar}] ${percentage}% (${current}/${total})`);
if (current === total) {
process.stdout.write('\n');
}
}
//# sourceMappingURL=formatter.js.map