gitrecon
Version:
This CLI tool is designed to retrieve exposed email addresses from a GitHub user’s public activity and to identify a GitHub username associated with a given email address. It is intended strictly for educational use and ethical security research. Please u
417 lines (341 loc) • 14.6 kB
JavaScript
// Progress tracking and monitoring - progress logic
const ColorUtils = require('../utils/colors');
class ProgressTracker {
constructor() {
this.tasks = new Map();
this.currentTask = null;
this.startTime = null;
this.stats = {
totalTasks: 0,
completedTasks: 0,
failedTasks: 0,
totalItems: 0,
processedItems: 0
};
}
// Start tracking a new task
startTask(taskId, description, totalItems = 0) {
const task = {
id: taskId,
description,
totalItems,
processedItems: 0,
startTime: Date.now(),
status: 'running',
errors: [],
subTasks: new Map()
};
this.tasks.set(taskId, task);
this.currentTask = taskId;
this.stats.totalTasks++;
this.stats.totalItems += totalItems;
if (!this.startTime) {
this.startTime = Date.now();
}
console.log(ColorUtils.green(`Starting: ${description}`));
if (totalItems > 0) {
this.displayProgress(taskId);
}
return task;
}
// Update task progress - repository scanning progress
updateProgress(taskId, processedItems = null, currentItem = null) {
const task = this.tasks.get(taskId);
if (!task) return;
if (processedItems !== null) {
const increment = processedItems - task.processedItems;
task.processedItems = processedItems;
this.stats.processedItems += increment;
} else {
task.processedItems++;
this.stats.processedItems++;
}
if (currentItem) {
task.currentItem = currentItem;
}
this.displayProgress(taskId);
}
// Display progress bar - progress display logic
displayProgress(taskId) {
const task = this.tasks.get(taskId);
if (!task || task.totalItems === 0) return;
const percentage = Math.round((task.processedItems / task.totalItems) * 100);
const progressBarLength = 30;
const filledLength = Math.round((progressBarLength * task.processedItems) / task.totalItems);
const progressBar = '█'.repeat(filledLength) + '░'.repeat(progressBarLength - filledLength);
let progressText = `\r${ColorUtils.green(`[${progressBar}] ${percentage}%`)}`;
progressText += ` ${ColorUtils.cyan(`${task.processedItems}/${task.totalItems}`)}`;
if (task.currentItem) {
const itemDisplay = task.currentItem.length > 30 ?
task.currentItem.substring(0, 27) + '...' :
task.currentItem;
progressText += ` - ${ColorUtils.yellow(itemDisplay)}`;
}
process.stdout.write(progressText);
// Add newline when complete
if (task.processedItems >= task.totalItems) {
process.stdout.write('\n');
}
}
// Clear current progress line - orijinal koddan
clearProgress() {
process.stdout.write('\r' + ' '.repeat(100) + '\r');
}
// Add subtask
addSubTask(parentTaskId, subTaskId, description, totalItems = 0) {
const parentTask = this.tasks.get(parentTaskId);
if (!parentTask) return;
const subTask = {
id: subTaskId,
description,
totalItems,
processedItems: 0,
startTime: Date.now(),
status: 'running',
errors: []
};
parentTask.subTasks.set(subTaskId, subTask);
return subTask;
}
// Update subtask progress
updateSubTask(parentTaskId, subTaskId, processedItems = null, currentItem = null) {
const parentTask = this.tasks.get(parentTaskId);
if (!parentTask) return;
const subTask = parentTask.subTasks.get(subTaskId);
if (!subTask) return;
if (processedItems !== null) {
subTask.processedItems = processedItems;
} else {
subTask.processedItems++;
}
if (currentItem) {
subTask.currentItem = currentItem;
}
// Update parent task display with subtask info
const subTaskProgress = subTask.totalItems > 0 ?
`${Math.round((subTask.processedItems / subTask.totalItems) * 100)}%` :
`${subTask.processedItems}`;
this.displaySubTaskProgress(parentTaskId, subTaskId, subTaskProgress);
}
// Display subtask progress
displaySubTaskProgress(parentTaskId, subTaskId, progress) {
const parentTask = this.tasks.get(parentTaskId);
const subTask = parentTask?.subTasks.get(subTaskId);
if (!subTask) return;
const indent = ' ';
let progressText = `\r${indent}${ColorUtils.cyan('└─')} ${subTask.description}: ${ColorUtils.yellow(progress)}`;
if (subTask.currentItem) {
progressText += ` - ${ColorUtils.dim(subTask.currentItem)}`;
}
process.stdout.write(progressText);
}
// Complete a task
completeTask(taskId, success = true, message = null) {
const task = this.tasks.get(taskId);
if (!task) return;
this.clearProgress();
task.status = success ? 'completed' : 'failed';
task.endTime = Date.now();
task.duration = task.endTime - task.startTime;
if (success) {
this.stats.completedTasks++;
console.log(ColorUtils.green(`✓ Completed: ${task.description}`));
} else {
this.stats.failedTasks++;
console.log(ColorUtils.red(`✗ Failed: ${task.description}`));
}
if (message) {
console.log(ColorUtils.dim(` ${message}`));
}
// Show duration for longer tasks
if (task.duration > 5000) {
console.log(ColorUtils.dim(` Duration: ${this.formatDuration(task.duration)}`));
}
if (taskId === this.currentTask) {
this.currentTask = null;
}
}
// Add error to task
addError(taskId, error, item = null) {
const task = this.tasks.get(taskId);
if (!task) return;
const errorEntry = {
message: error.message || error,
item,
timestamp: Date.now()
};
task.errors.push(errorEntry);
console.log(ColorUtils.red(`Error in ${task.description}: ${errorEntry.message}`));
if (item) {
console.log(ColorUtils.dim(` Item: ${item}`));
}
}
// Estimate remaining time - ETA calculation concepts
estimateRemainingTime(taskId) {
const task = this.tasks.get(taskId);
if (!task || task.processedItems === 0 || task.totalItems === 0) return null;
const elapsed = Date.now() - task.startTime;
const rate = task.processedItems / elapsed; // items per millisecond
const remaining = task.totalItems - task.processedItems;
return remaining / rate;
}
// Format duration
formatDuration(milliseconds) {
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (hours > 0) {
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
} else if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`;
} else {
return `${seconds}s`;
}
}
// Display overall progress summary
displaySummary() {
console.log(ColorUtils.bright('\n=== PROGRESS SUMMARY ==='));
const totalElapsed = this.startTime ? Date.now() - this.startTime : 0;
console.log(`Total Duration: ${ColorUtils.cyan(this.formatDuration(totalElapsed))}`);
console.log(`Tasks: ${ColorUtils.green(this.stats.completedTasks)} completed, ${ColorUtils.red(this.stats.failedTasks)} failed, ${ColorUtils.yellow(this.stats.totalTasks)} total`);
console.log(`Items: ${ColorUtils.cyan(this.stats.processedItems)}/${this.stats.totalItems} processed`);
// Show task details
if (this.tasks.size > 0) {
console.log(ColorUtils.bright('\nTask Details:'));
this.tasks.forEach(task => {
const statusIcon = task.status === 'completed' ? ColorUtils.green('✓') :
task.status === 'failed' ? ColorUtils.red('✗') :
ColorUtils.yellow('⧗');
const duration = task.endTime ? this.formatDuration(task.duration) : 'running';
const progress = task.totalItems > 0 ?
`${task.processedItems}/${task.totalItems}` :
`${task.processedItems} items`;
console.log(` ${statusIcon} ${task.description} - ${progress} (${duration})`);
// Show errors
if (task.errors.length > 0) {
console.log(ColorUtils.red(` ${task.errors.length} errors`));
}
// Show subtasks
if (task.subTasks.size > 0) {
task.subTasks.forEach(subTask => {
const subStatus = subTask.status === 'completed' ? ColorUtils.green('✓') : ColorUtils.yellow('⧗');
const subProgress = subTask.totalItems > 0 ?
`${subTask.processedItems}/${subTask.totalItems}` :
`${subTask.processedItems} items`;
console.log(` ${subStatus} ${subTask.description} - ${subProgress}`);
});
}
});
}
// Calculate success rate
if (this.stats.totalTasks > 0) {
const successRate = Math.round((this.stats.completedTasks / this.stats.totalTasks) * 100);
console.log(`\nSuccess Rate: ${ColorUtils.cyan(successRate + '%')}`);
}
}
// Get current status
getStatus() {
const currentTask = this.currentTask ? this.tasks.get(this.currentTask) : null;
const totalElapsed = this.startTime ? Date.now() - this.startTime : 0;
return {
isRunning: !!this.currentTask,
currentTask: currentTask ? {
id: currentTask.id,
description: currentTask.description,
progress: currentTask.totalItems > 0 ?
Math.round((currentTask.processedItems / currentTask.totalItems) * 100) : 0,
processedItems: currentTask.processedItems,
totalItems: currentTask.totalItems,
currentItem: currentTask.currentItem,
estimatedTimeRemaining: this.estimateRemainingTime(currentTask.id)
} : null,
stats: { ...this.stats },
totalElapsed,
tasks: Array.from(this.tasks.values()).map(task => ({
id: task.id,
description: task.description,
status: task.status,
processedItems: task.processedItems,
totalItems: task.totalItems,
duration: task.endTime ? task.duration : Date.now() - task.startTime,
errors: task.errors.length
}))
};
}
// Export progress data for analysis
exportData() {
return {
summary: {
totalDuration: this.startTime ? Date.now() - this.startTime : 0,
stats: { ...this.stats }
},
tasks: Array.from(this.tasks.entries()).map(([id, task]) => ({
id,
description: task.description,
status: task.status,
startTime: task.startTime,
endTime: task.endTime,
duration: task.duration,
totalItems: task.totalItems,
processedItems: task.processedItems,
errors: task.errors,
subTasks: Array.from(task.subTasks.values())
}))
};
}
// Reset all progress tracking
reset() {
this.clearProgress();
this.tasks.clear();
this.currentTask = null;
this.startTime = null;
this.stats = {
totalTasks: 0,
completedTasks: 0,
failedTasks: 0,
totalItems: 0,
processedItems: 0
};
}
// Pause current task
pauseTask(taskId) {
const task = this.tasks.get(taskId);
if (!task) return;
task.status = 'paused';
task.pausedAt = Date.now();
console.log(ColorUtils.yellow(`⏸ Paused: ${task.description}`));
}
// Resume paused task
resumeTask(taskId) {
const task = this.tasks.get(taskId);
if (!task || task.status !== 'paused') return;
const pauseDuration = Date.now() - task.pausedAt;
task.startTime += pauseDuration; // Adjust start time to exclude pause duration
task.status = 'running';
delete task.pausedAt;
console.log(ColorUtils.green(`▶ Resumed: ${task.description}`));
}
// Create progress bar string for external use
createProgressBar(current, total, length = 20) {
const percentage = Math.round((current / total) * 100);
const filledLength = Math.round((length * current) / total);
return {
bar: '█'.repeat(filledLength) + '░'.repeat(length - filledLength),
percentage,
text: `[${this.createProgressBar(current, total, length).bar}] ${percentage}% (${current}/${total})`
};
}
// Set custom progress display format
setProgressFormat(taskId, formatter) {
const task = this.tasks.get(taskId);
if (!task) return;
task.customFormatter = formatter;
}
// Cleanup resources
cleanup() {
this.clearProgress();
this.tasks.clear();
this.currentTask = null;
}
}
module.exports = ProgressTracker;