database.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import pg from 'pg';
  2. import dotenv from 'dotenv';
  3. import { readFileSync } from 'fs';
  4. import { fileURLToPath } from 'url';
  5. import { dirname, join } from 'path';
  6. import { info, error } from './logger.js';
  7. dotenv.config();
  8. const { Pool } = pg;
  9. let pool = null;
  10. /**
  11. * 从task-manager独立配置文件中读取数据库URI
  12. */
  13. function getDatabaseUrlFromConfig() {
  14. try {
  15. // 获取当前文件的目录
  16. const __filename = fileURLToPath(import.meta.url);
  17. const __dirname = dirname(__filename);
  18. // 配置文件路径
  19. const configPath = join(__dirname, 'config.json');
  20. // 读取配置文件
  21. const configFile = readFileSync(configPath, 'utf-8');
  22. const config = JSON.parse(configFile);
  23. // 获取数据库URI
  24. const databaseUrl = config?.database?.uri;
  25. if (databaseUrl) {
  26. info('Database URL loaded from task-manager config.json: ' + databaseUrl.replace(/:[^:@]+@/, ':****@'));
  27. return databaseUrl;
  28. } else {
  29. throw new Error('database.uri not found in config.json');
  30. }
  31. } catch (err) {
  32. error('Error reading config.json: ' + err.message);
  33. // 如果读取失败,回退到环境变量
  34. return null;
  35. }
  36. }
  37. /**
  38. * 初始化数据库连接池
  39. */
  40. function initPool() {
  41. if (pool) {
  42. return pool;
  43. }
  44. // 优先从task-manager独立配置文件读取
  45. let databaseUrl = getDatabaseUrlFromConfig();
  46. // 如果配置文件读取失败,尝试从环境变量读取
  47. if (!databaseUrl) {
  48. databaseUrl = process.env.DATABASE_URL || process.env.SQLALCHEMY_DATABASE_URI;
  49. if (databaseUrl) {
  50. info('Database URL loaded from environment variable');
  51. }
  52. }
  53. if (!databaseUrl) {
  54. throw new Error('Database URL not found. Please check mcp-servers/task-manager/config.json or set DATABASE_URL environment variable');
  55. }
  56. // 记录实际使用的数据库URI(隐藏密码)
  57. const maskedUrl = databaseUrl.replace(/:[^:@]+@/, ':****@');
  58. info('Initializing database pool with URI: ' + maskedUrl);
  59. pool = new Pool({
  60. connectionString: databaseUrl,
  61. max: 10,
  62. idleTimeoutMillis: 30000,
  63. connectionTimeoutMillis: 2000,
  64. });
  65. pool.on('error', (err) => {
  66. error('Unexpected error on idle client: ' + err.message);
  67. });
  68. return pool;
  69. }
  70. /**
  71. * 获取数据库连接池
  72. */
  73. export function getPool() {
  74. if (!pool) {
  75. return initPool();
  76. }
  77. return pool;
  78. }
  79. /**
  80. * 获取所有待处理的任务
  81. */
  82. export async function getPendingTasks() {
  83. info('[Task Manager] Starting to read task_list table...');
  84. const startTime = Date.now();
  85. const client = await getPool().connect();
  86. try {
  87. const result = await client.query(
  88. `SELECT task_id, task_name, task_description, status, code_name, code_path,
  89. create_time, update_time, create_by
  90. FROM task_list
  91. WHERE status = 'pending'
  92. ORDER BY create_time ASC`
  93. );
  94. const taskCount = result.rows.length;
  95. const duration = Date.now() - startTime;
  96. info(`[Task Manager] Finished reading task_list table. Found ${taskCount} pending task(s) in ${duration}ms`);
  97. return result.rows;
  98. } finally {
  99. client.release();
  100. }
  101. }
  102. /**
  103. * 根据ID获取任务
  104. */
  105. export async function getTaskById(taskId) {
  106. const client = await getPool().connect();
  107. try {
  108. const result = await client.query(
  109. `SELECT task_id, task_name, task_description, status, code_name, code_path,
  110. create_time, update_time, create_by
  111. FROM task_list
  112. WHERE task_id = $1`,
  113. [taskId]
  114. );
  115. return result.rows[0] || null;
  116. } finally {
  117. client.release();
  118. }
  119. }
  120. /**
  121. * 更新任务状态
  122. */
  123. export async function updateTaskStatus(taskId, status, codeName = null, codePath = null) {
  124. const client = await getPool().connect();
  125. try {
  126. const updates = ['status = $1', 'update_time = CURRENT_TIMESTAMP'];
  127. const values = [status];
  128. let paramIndex = 1;
  129. if (codeName !== null) {
  130. paramIndex++;
  131. updates.push(`code_name = $${paramIndex}`);
  132. values.push(codeName);
  133. }
  134. if (codePath !== null) {
  135. paramIndex++;
  136. updates.push(`code_path = $${paramIndex}`);
  137. values.push(codePath);
  138. }
  139. // taskId is always the last parameter
  140. paramIndex++;
  141. values.push(taskId);
  142. const query = `
  143. UPDATE task_list
  144. SET ${updates.join(', ')}
  145. WHERE task_id = $${paramIndex}
  146. RETURNING task_id, task_name, status, update_time
  147. `;
  148. const result = await client.query(query, values);
  149. return result.rows[0];
  150. } finally {
  151. client.release();
  152. }
  153. }
  154. /**
  155. * 创建新任务
  156. */
  157. export async function createTask(taskName, taskDescription, createBy) {
  158. const client = await getPool().connect();
  159. try {
  160. const result = await client.query(
  161. `INSERT INTO task_list (task_name, task_description, status, create_by)
  162. VALUES ($1, $2, 'pending', $3)
  163. RETURNING task_id, task_name, task_description, status, create_time, create_by`,
  164. [taskName, taskDescription, createBy]
  165. );
  166. return result.rows[0];
  167. } finally {
  168. client.release();
  169. }
  170. }
  171. /**
  172. * 获取所有任务(用于调试)
  173. */
  174. export async function getAllTasks(limit = 100) {
  175. const client = await getPool().connect();
  176. try {
  177. const result = await client.query(
  178. `SELECT task_id, task_name, task_description, status, code_name, code_path,
  179. create_time, update_time, create_by
  180. FROM task_list
  181. ORDER BY create_time DESC
  182. LIMIT $1`,
  183. [limit]
  184. );
  185. return result.rows;
  186. } finally {
  187. client.release();
  188. }
  189. }
  190. /**
  191. * 关闭数据库连接池
  192. */
  193. export async function closePool() {
  194. if (pool) {
  195. await pool.end();
  196. pool = null;
  197. }
  198. }