Files
AIclinicalresearch/backend/recover-code-from-cursor-db.js
HaHafeng 5523ef36ea feat(admin): Complete Phase 3.5.1-3.5.4 Prompt Management System (83%)
Summary:
- Implement Prompt management infrastructure and core services
- Build admin portal frontend with light theme
- Integrate CodeMirror 6 editor for non-technical users

Phase 3.5.1: Infrastructure Setup
- Create capability_schema for Prompt storage
- Add prompt_templates and prompt_versions tables
- Add prompt:view/edit/debug/publish permissions
- Migrate RVW prompts to database (RVW_EDITORIAL, RVW_METHODOLOGY)

Phase 3.5.2: PromptService Core
- Implement gray preview logic (DRAFT for debuggers, ACTIVE for users)
- Module-level debug control (setDebugMode)
- Handlebars template rendering
- Variable extraction and validation (extractVariables, validateVariables)
- Three-level disaster recovery (database -> cache -> hardcoded fallback)

Phase 3.5.3: Management API
- 8 RESTful endpoints (/api/admin/prompts/*)
- Permission control (PROMPT_ENGINEER can edit, SUPER_ADMIN can publish)

Phase 3.5.4: Frontend Management UI
- Build admin portal architecture (AdminLayout, OrgLayout)
- Add route system (/admin/*, /org/*)
- Implement PromptListPage (filter, search, debug switch)
- Implement PromptEditor (CodeMirror 6 simplified for clinical users)
- Implement PromptEditorPage (edit, save, publish, test, version history)

Technical Details:
- Backend: 6 files, ~2044 lines (prompt.service.ts 596 lines)
- Frontend: 9 files, ~1735 lines (PromptEditorPage.tsx 399 lines)
- CodeMirror 6: Line numbers, auto-wrap, variable highlight, search, undo/redo
- Chinese-friendly: 15px font, 1.8 line-height, system fonts

Next Step: Phase 3.5.5 - Integrate RVW module with PromptService

Tested: Backend API tests passed (8/8), Frontend pending user testing
Status: Ready for Phase 3.5.5 RVW integration
2026-01-11 21:25:16 +08:00

232 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 从Cursor的SQLite数据库中恢复代码历史
*
* 使用方法:
* 1. cd backend
* 2. npm install better-sqlite3
* 3. node recover-code-from-cursor-db.js
*/
const Database = require('better-sqlite3');
const fs = require('fs');
const path = require('path');
// Cursor SQLite数据库路径
const DB_PATH = path.join(
process.env.APPDATA || process.env.HOME,
'Cursor/User/workspaceStorage/d5e3431d02cbaa0109f69d72300733da/state.vscdb'
);
// 输出目录
const OUTPUT_DIR = path.join(__dirname, 'cursor-history-recovery');
console.log('🔍 正在读取Cursor历史数据库...');
console.log('📂 数据库路径:', DB_PATH);
if (!fs.existsSync(DB_PATH)) {
console.error('❌ 数据库文件不存在!');
process.exit(1);
}
// 创建输出目录
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
try {
// 打开数据库(只读模式)
const db = new Database(DB_PATH, { readonly: true, fileMustExist: true });
console.log('✅ 数据库打开成功!');
// 1. 查看表结构
console.log('\n📊 数据库表列表:');
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
console.log(tables.map(t => ` - ${t.name}`).join('\n'));
// 2. 查询ItemTable表结构
if (tables.some(t => t.name === 'ItemTable')) {
console.log('\n📋 ItemTable 表结构:');
const columns = db.prepare("PRAGMA table_info(ItemTable)").all();
console.log(columns.map(c => ` - ${c.name} (${c.type})`).join('\n'));
// 3. 查询所有key了解有哪些类型的数据
console.log('\n🔑 ItemTable 中的所有key类型');
const keys = db.prepare("SELECT DISTINCT key FROM ItemTable").all();
console.log(keys.map(k => ` - ${k.key}`).join('\n'));
// 4. 查找聊天历史相关的key
console.log('\n💬 查找聊天/Composer历史记录...');
const chatKeys = [
'workbench.panel.chat',
'composer',
'chat',
'workbench.panel.aichat',
'aiPanel'
];
let foundCount = 0;
for (const keyPattern of chatKeys) {
const rows = db.prepare(
`SELECT key, value FROM ItemTable WHERE key LIKE ?`
).all(`%${keyPattern}%`);
if (rows.length > 0) {
console.log(`\n✅ 找到 ${rows.length} 条与 "${keyPattern}" 相关的记录`);
rows.forEach((row, index) => {
foundCount++;
const filename = `${keyPattern.replace(/[^a-z0-9]/gi, '_')}_${index + 1}.json`;
const filepath = path.join(OUTPUT_DIR, filename);
// 保存原始JSON
fs.writeFileSync(filepath, row.value);
console.log(` 📄 已保存: ${filename} (${(row.value.length / 1024).toFixed(2)} KB)`);
// 尝试解析JSON并提取代码
try {
const data = JSON.parse(row.value);
// 提取可能的代码片段
const codeBlocks = extractCodeBlocks(data);
if (codeBlocks.length > 0) {
const codeFilename = `${keyPattern.replace(/[^a-z0-9]/gi, '_')}_${index + 1}_code.txt`;
const codeFilepath = path.join(OUTPUT_DIR, codeFilename);
fs.writeFileSync(codeFilepath, codeBlocks.join('\n\n' + '='.repeat(80) + '\n\n'));
console.log(` 📝 提取了 ${codeBlocks.length} 个代码块: ${codeFilename}`);
}
} catch (err) {
console.log(` ⚠️ JSON解析失败: ${err.message}`);
}
});
}
}
if (foundCount === 0) {
console.log('\n⚠ 未找到聊天历史记录,尝试提取所有数据...');
// 导出所有ItemTable数据
const allRows = db.prepare("SELECT key, value FROM ItemTable").all();
console.log(`\n📦 共有 ${allRows.length} 条记录,正在导出...`);
const allDataFile = path.join(OUTPUT_DIR, 'all_itemtable_data.json');
fs.writeFileSync(allDataFile, JSON.stringify(allRows, null, 2));
console.log(`✅ 已导出所有数据到: all_itemtable_data.json (${(fs.statSync(allDataFile).size / 1024 / 1024).toFixed(2)} MB)`);
}
} else {
console.log('\n❌ ItemTable 表不存在!');
}
db.close();
console.log(`\n✅ 恢复完成!所有文件保存在: ${OUTPUT_DIR}`);
console.log('\n💡 下一步:');
console.log(' 1. 检查 cursor-history-recovery 文件夹');
console.log(' 2. 打开 .json 文件查找DC模块相关的代码');
console.log(' 3. 查找关键词DualModelExtractionService, HealthCheckService, ExtractionController');
} catch (error) {
console.error('❌ 错误:', error.message);
console.error(error.stack);
process.exit(1);
}
/**
* 从JSON数据中递归提取代码块
*/
function extractCodeBlocks(obj, blocks = []) {
if (typeof obj === 'string') {
// 查找代码块模式
const codePatterns = [
/```[\s\S]*?```/g, // Markdown代码块
/export\s+(const|function|class)\s+\w+/g, // TypeScript导出
/interface\s+\w+/g, // TypeScript接口
/async\s+function\s+\w+/g, // 异步函数
];
codePatterns.forEach(pattern => {
const matches = obj.match(pattern);
if (matches) {
blocks.push(...matches);
}
});
// 如果包含关键代码关键词,保存整段
const keywords = [
'DualModelExtractionService',
'HealthCheckService',
'TemplateService',
'ConflictDetectionService',
'ExtractionController',
'dc_extraction_tasks',
'dc_health_checks'
];
if (keywords.some(kw => obj.includes(kw))) {
blocks.push(obj);
}
} else if (Array.isArray(obj)) {
obj.forEach(item => extractCodeBlocks(item, blocks));
} else if (obj && typeof obj === 'object') {
Object.values(obj).forEach(value => extractCodeBlocks(value, blocks));
}
return blocks;
}