import { promises as fs } from 'fs';
import path from 'path';
export interface Task {
id: number;
title: string;
project?: string;
due?: string;
created_at: string;
completed_at?: string;
}
export class TaskService {
private tasksFile: string;
constructor() {
this.tasksFile = process.env.TASKS_FILE || './tasks.md';
}
async createTask(title: string, project?: string, due?: string): Promise<Task> {
try {
console.log(`✅ Создание задачи: ${title}`);
const tasks = await this.loadTasks();
const newTask: Task = {
id: this.getNextId(tasks),
title,
project,
due,
created_at: new Date().toISOString(),
};
tasks.push(newTask);
await this.saveTasks(tasks);
console.log(`✅ Задача создана: ${title} (ID: ${newTask.id})`);
return newTask;
} catch (error) {
console.error('Ошибка создания задачи:', error);
throw new Error(`Ошибка создания задачи: ${error}`);
}
}
async listTasks(project?: string, status?: string): Promise<Task[]> {
try {
console.log(`📋 Список задач (проект: ${project || 'все'}, статус: ${status || 'все'})`);
const tasks = await this.loadTasks();
let filteredTasks = tasks;
// Фильтр по проекту
if (project) {
filteredTasks = filteredTasks.filter(task => task.project === project);
}
// Фильтр по статусу
if (status === 'open') {
filteredTasks = filteredTasks.filter(task => !task.completed_at);
} else if (status === 'completed') {
filteredTasks = filteredTasks.filter(task => task.completed_at);
}
console.log(`✅ Найдено задач: ${filteredTasks.length}`);
return filteredTasks;
} catch (error) {
console.error('Ошибка получения списка задач:', error);
throw new Error(`Ошибка получения списка задач: ${error}`);
}
}
async completeTask(taskId: number): Promise<Task> {
try {
console.log(`✅ Завершение задачи: ${taskId}`);
const tasks = await this.loadTasks();
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex === -1) {
throw new Error(`Задача с ID ${taskId} не найдена`);
}
tasks[taskIndex].completed_at = new Date().toISOString();
await this.saveTasks(tasks);
console.log(`✅ Задача завершена: ${tasks[taskIndex].title}`);
return tasks[taskIndex];
} catch (error) {
console.error('Ошибка завершения задачи:', error);
throw new Error(`Ошибка завершения задачи: ${error}`);
}
}
async updateTask(taskId: number, updates: Partial<Task>): Promise<Task> {
try {
console.log(`✏️ Обновление задачи: ${taskId}`);
const tasks = await this.loadTasks();
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex === -1) {
throw new Error(`Задача с ID ${taskId} не найдена`);
}
// Обновляем только разрешенные поля
const allowedUpdates = ['title', 'project', 'due'];
for (const [key, value] of Object.entries(updates)) {
if (allowedUpdates.includes(key) && value !== undefined) {
(tasks[taskIndex] as any)[key] = value;
}
}
await this.saveTasks(tasks);
console.log(`✅ Задача обновлена: ${tasks[taskIndex].title}`);
return tasks[taskIndex];
} catch (error) {
console.error('Ошибка обновления задачи:', error);
throw new Error(`Ошибка обновления задачи: ${error}`);
}
}
async deleteTask(taskId: number): Promise<void> {
try {
console.log(`🗑️ Удаление задачи: ${taskId}`);
const tasks = await this.loadTasks();
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex === -1) {
throw new Error(`Задача с ID ${taskId} не найдена`);
}
tasks.splice(taskIndex, 1);
await this.saveTasks(tasks);
console.log(`✅ Задача удалена`);
} catch (error) {
console.error('Ошибка удаления задачи:', error);
throw new Error(`Ошибка удаления задачи: ${error}`);
}
}
private async loadTasks(): Promise<Task[]> {
try {
const content = await fs.readFile(this.tasksFile, 'utf-8');
return this.parseTasksFromMarkdown(content);
} catch (error) {
// Если файл не существует, возвращаем пустой массив
if ((error as any).code === 'ENOENT') {
return [];
}
throw error;
}
}
private async saveTasks(tasks: Task[]): Promise<void> {
const content = this.generateMarkdownFromTasks(tasks);
await fs.writeFile(this.tasksFile, content, 'utf-8');
}
private parseTasksFromMarkdown(content: string): Task[] {
const tasks: Task[] = [];
const lines = content.split('\n');
let currentTask: Partial<Task> = {};
for (const line of lines) {
if (line.startsWith('## ')) {
// Сохраняем предыдущую задачу
if (currentTask.title) {
tasks.push(currentTask as Task);
}
// Начинаем новую задачу
currentTask = {
title: line.substring(3).trim(),
created_at: new Date().toISOString(),
};
} else if (line.startsWith('- **Проект:**')) {
currentTask.project = line.split('**Проект:**')[1].trim();
} else if (line.startsWith('- **Срок:**')) {
currentTask.due = line.split('**Срок:**')[1].trim();
} else if (line.startsWith('- **Статус:** Завершено')) {
currentTask.completed_at = new Date().toISOString();
}
}
// Добавляем последнюю задачу
if (currentTask.title) {
tasks.push(currentTask as Task);
}
// Назначаем ID
tasks.forEach((task, index) => {
task.id = index + 1;
});
return tasks;
}
private generateMarkdownFromTasks(tasks: Task[]): string {
if (tasks.length === 0) {
return '# Задачи\n\nНет активных задач.';
}
const lines = ['# Задачи\n'];
for (const task of tasks) {
lines.push(`## ${task.title}`);
if (task.project) {
lines.push(`- **Проект:** ${task.project}`);
}
if (task.due) {
lines.push(`- **Срок:** ${task.due}`);
}
if (task.completed_at) {
lines.push(`- **Статус:** Завершено`);
}
lines.push('');
}
return lines.join('\n');
}
private getNextId(tasks: Task[]): number {
if (tasks.length === 0) return 1;
return Math.max(...tasks.map(task => task.id)) + 1;
}
}