Skip to main content
Glama
SQLiteProjectStorage.js9.2 kB
/** * SQLite Project Storage - 프로젝트 데이터베이스 저장소 클래스 * 기존 TaskStorage 패턴을 따라 sqlite3/sqlite 사용 */ import sqlite3 from 'sqlite3'; import { open } from 'sqlite'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export class SQLiteProjectStorage { constructor() { // dashboard/src/lib/server/ -> project root (4 levels up) this.dbPath = path.resolve(__dirname, '../../../../data/workflow.db'); this.db = null; } async getDatabase() { if (!this.db) { this.db = await open({ filename: this.dbPath, driver: sqlite3.Database }); await this.db.exec('PRAGMA foreign_keys = ON'); } return this.db; } /** * 데이터베이스 초기화 */ async initialize() { try { await this.getDatabase(); console.log(`✅ SQLiteProjectStorage initialized: ${this.dbPath}`); } catch (error) { console.error('SQLiteProjectStorage 초기화 실패:', error); throw error; } } /** * 프로젝트 저장 * @param {Object} project - 저장할 프로젝트 데이터 */ async saveProject(project) { const db = await this.getDatabase(); try { const tagsJson = Array.isArray(project.tags) ? JSON.stringify(project.tags) : project.tags; await db.run(` INSERT OR REPLACE INTO projects ( id, name, description, status, priority, start_date, end_date, created_at, updated_at, created_by, manager, tags, progress, notes ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ project.id, project.name, project.description, project.status, project.priority, project.start_date, project.end_date, project.created_at, project.updated_at, project.created_by, project.manager, tagsJson, project.progress, project.notes ]); return project; } catch (error) { console.error('프로젝트 저장 실패:', error); throw error; } } /** * 프로젝트 조회 * @param {string} projectId - 프로젝트 ID * @returns {Object|null} 프로젝트 데이터 */ async getProject(projectId) { const db = await this.getDatabase(); try { const row = await db.get('SELECT * FROM projects WHERE id = ?', [projectId]); if (row) { return this.formatProjectData(row); } return null; } catch (error) { console.error('프로젝트 조회 실패:', error); throw error; } } /** * 전체 프로젝트 목록 조회 * @param {string} sortBy - 정렬 기준 * @returns {Array} 프로젝트 목록 */ async listAllProjects(sortBy = 'updated_desc') { const db = await this.getDatabase(); try { let orderClause = 'ORDER BY updated_at DESC'; switch (sortBy) { case 'created_desc': orderClause = 'ORDER BY created_at DESC'; break; case 'created_asc': orderClause = 'ORDER BY created_at ASC'; break; case 'updated_asc': orderClause = 'ORDER BY updated_at ASC'; break; case 'name_asc': orderClause = 'ORDER BY name ASC'; break; case 'name_desc': orderClause = 'ORDER BY name DESC'; break; case 'priority': orderClause = `ORDER BY CASE priority WHEN 'High' THEN 1 WHEN 'Medium' THEN 2 WHEN 'Low' THEN 3 ELSE 4 END ASC, updated_at DESC`; break; case 'status': orderClause = `ORDER BY CASE status WHEN 'active' THEN 1 WHEN 'planning' THEN 2 WHEN 'on_hold' THEN 3 WHEN 'completed' THEN 4 ELSE 5 END ASC, updated_at DESC`; break; default: orderClause = 'ORDER BY updated_at DESC'; } const rows = await db.all(`SELECT * FROM projects ${orderClause}`); return rows.map(row => this.formatProjectData(row)); } catch (error) { console.error('프로젝트 목록 조회 실패:', error); throw error; } } /** * 상태별 프로젝트 목록 조회 * @param {string} status - 필터링할 상태 * @returns {Array} 프로젝트 목록 */ async listProjectsByStatus(status) { if (!this.db) throw new Error('Database not initialized'); try { const stmt = this.db.prepare(` SELECT * FROM projects WHERE status = ? ORDER BY updated_at DESC `); const rows = stmt.all(status); return rows.map(row => this.formatProjectData(row)); } catch (error) { console.error('상태별 프로젝트 조회 실패:', error); throw error; } } /** * 프로젝트 삭제 * @param {string} projectId - 프로젝트 ID */ async deleteProject(projectId) { const db = await this.getDatabase(); try { const result = await db.run('DELETE FROM projects WHERE id = ?', [projectId]); if (result.changes === 0) { throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`); } return { success: true, deletedId: projectId }; } catch (error) { console.error('프로젝트 삭제 실패:', error); throw error; } } /** * 프로젝트 진행률 업데이트 * @param {string} projectId - 프로젝트 ID * @param {number} progress - 진행률 (0-100) */ async updateProjectProgress(projectId, progress) { if (!this.db) throw new Error('Database not initialized'); try { const stmt = this.db.prepare(` UPDATE projects SET progress = ?, updated_at = ? WHERE id = ? `); const result = stmt.run(progress, new Date().toISOString(), projectId); if (result.changes === 0) { throw new Error(`프로젝트를 찾을 수 없습니다: ${projectId}`); } return { success: true, projectId, progress }; } catch (error) { console.error('프로젝트 진행률 업데이트 실패:', error); throw error; } } /** * 프로젝트 통계 조회 * @returns {Object} 프로젝트 전체 통계 */ async getProjectStatistics() { if (!this.db) throw new Error('Database not initialized'); try { const stmt = this.db.prepare(` SELECT COUNT(*) as total, SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active, SUM(CASE WHEN status = 'planning' THEN 1 ELSE 0 END) as planning, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN status = 'on_hold' THEN 1 ELSE 0 END) as on_hold, AVG(progress) as avg_progress FROM projects `); const stats = stmt.get(); return { total: stats.total || 0, active: stats.active || 0, planning: stats.planning || 0, completed: stats.completed || 0, on_hold: stats.on_hold || 0, avgProgress: Math.round(stats.avg_progress || 0) }; } catch (error) { console.error('프로젝트 통계 조회 실패:', error); throw error; } } /** * 프로젝트 검색 * @param {string} searchTerm - 검색어 * @returns {Array} 검색 결과 */ async searchProjects(searchTerm) { if (!this.db) throw new Error('Database not initialized'); try { const stmt = this.db.prepare(` SELECT * FROM projects WHERE name LIKE ? OR description LIKE ? OR manager LIKE ? ORDER BY updated_at DESC `); const searchPattern = `%${searchTerm}%`; const rows = stmt.all(searchPattern, searchPattern, searchPattern); return rows.map(row => this.formatProjectData(row)); } catch (error) { console.error('프로젝트 검색 실패:', error); throw error; } } /** * 데이터베이스 행을 프로젝트 객체로 변환 * @param {Object} row - 데이터베이스 행 * @returns {Object} 프로젝트 객체 */ formatProjectData(row) { try { return { id: row.id, name: row.name, description: row.description || '', status: row.status, priority: row.priority, start_date: row.start_date, end_date: row.end_date, created_at: row.created_at, updated_at: row.updated_at, created_by: row.created_by, manager: row.manager || '', tags: row.tags ? JSON.parse(row.tags) : [], progress: row.progress || 0, notes: row.notes || '' }; } catch (error) { console.error('프로젝트 데이터 포맷 실패:', error); return row; } } /** * 데이터베이스 연결 종료 */ close() { if (this.db) { this.db.close(); this.db = null; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/foswmine/workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server