Files
insightflow/chrome-extension/background.js
OpenClaw Bot 95a558acc9 Phase 7 Task 3: 数据安全与合规
- 创建 security_manager.py 安全模块
  - SecurityManager: 安全管理主类
  - 审计日志系统 - 记录所有数据操作
  - 端到端加密 - AES-256-GCM 加密项目数据
  - 数据脱敏 - 支持手机号、邮箱、身份证等敏感信息脱敏
  - 数据访问策略 - 基于用户、角色、IP、时间的访问控制
  - 访问审批流程 - 敏感数据访问需要审批

- 更新 schema.sql 添加安全相关数据库表
  - audit_logs: 审计日志表
  - encryption_configs: 加密配置表
  - masking_rules: 脱敏规则表
  - data_access_policies: 数据访问策略表
  - access_requests: 访问请求表

- 更新 main.py 添加安全相关 API 端点
  - GET /api/v1/audit-logs - 查询审计日志
  - GET /api/v1/audit-logs/stats - 审计统计
  - POST /api/v1/projects/{id}/encryption/enable - 启用加密
  - POST /api/v1/projects/{id}/encryption/disable - 禁用加密
  - POST /api/v1/projects/{id}/encryption/verify - 验证密码
  - GET /api/v1/projects/{id}/encryption - 获取加密配置
  - POST /api/v1/projects/{id}/masking-rules - 创建脱敏规则
  - GET /api/v1/projects/{id}/masking-rules - 获取脱敏规则
  - PUT /api/v1/masking-rules/{id} - 更新脱敏规则
  - DELETE /api/v1/masking-rules/{id} - 删除脱敏规则
  - POST /api/v1/projects/{id}/masking/apply - 应用脱敏
  - POST /api/v1/projects/{id}/access-policies - 创建访问策略
  - GET /api/v1/projects/{id}/access-policies - 获取访问策略
  - POST /api/v1/access-policies/{id}/check - 检查访问权限
  - POST /api/v1/access-requests - 创建访问请求
  - POST /api/v1/access-requests/{id}/approve - 批准访问
  - POST /api/v1/access-requests/{id}/reject - 拒绝访问

- 更新 requirements.txt 添加 cryptography 依赖

- 更新 STATUS.md 和 README.md 记录完成状态
2026-02-23 18:11:11 +08:00

198 lines
5.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
// InsightFlow Chrome Extension - Background Script
// 处理扩展的后台逻辑
chrome.runtime.onInstalled.addListener(() => {
console.log('[InsightFlow] Extension installed');
// 创建右键菜单
chrome.contextMenus.create({
id: 'insightflow-clip-selection',
title: 'Clip selection to InsightFlow',
contexts: ['selection']
});
chrome.contextMenus.create({
id: 'insightflow-clip-page',
title: 'Clip page to InsightFlow',
contexts: ['page']
});
chrome.contextMenus.create({
id: 'insightflow-clip-link',
title: 'Clip link to InsightFlow',
contexts: ['link']
});
});
// 处理右键菜单点击
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === 'insightflow-clip-selection') {
clipSelection(tab);
} else if (info.menuItemId === 'insightflow-clip-page') {
clipPage(tab);
} else if (info.menuItemId === 'insightflow-clip-link') {
clipLink(tab, info.linkUrl);
}
});
// 处理来自 popup 的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'clipPage') {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0]) {
clipPage(tabs[0]).then(sendResponse);
}
});
return true;
} else if (request.action === 'clipSelection') {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0]) {
clipSelection(tabs[0]).then(sendResponse);
}
});
return true;
} else if (request.action === 'openClipper') {
chrome.action.openPopup();
}
});
// 剪辑整个页面
async function clipPage(tab) {
try {
// 向 content script 发送消息提取内容
const response = await chrome.tabs.sendMessage(tab.id, { action: 'extractContent' });
if (response.success) {
// 保存到本地存储
await saveClip(response.data);
return { success: true, message: 'Page clipped successfully' };
}
} catch (error) {
console.error('[InsightFlow] Failed to clip page:', error);
return { success: false, error: error.message };
}
}
// 剪辑选中的内容
async function clipSelection(tab) {
try {
const response = await chrome.tabs.sendMessage(tab.id, { action: 'getSelection' });
if (response.success && response.data) {
const clipData = {
url: tab.url,
title: tab.title,
content: response.data.text,
context: response.data.context,
contentType: 'selection',
extractedAt: new Date().toISOString()
};
await saveClip(clipData);
return { success: true, message: 'Selection clipped successfully' };
} else {
return { success: false, error: 'No text selected' };
}
} catch (error) {
console.error('[InsightFlow] Failed to clip selection:', error);
return { success: false, error: error.message };
}
}
// 剪辑链接
async function clipLink(tab, linkUrl) {
const clipData = {
url: linkUrl,
title: linkUrl,
content: `Link: ${linkUrl}`,
sourceUrl: tab.url,
contentType: 'link',
extractedAt: new Date().toISOString()
};
await saveClip(clipData);
return { success: true, message: 'Link clipped successfully' };
}
// 保存剪辑内容
async function saveClip(data) {
// 获取现有剪辑
const result = await chrome.storage.local.get(['clips']);
const clips = result.clips || [];
// 添加新剪辑
clips.unshift({
id: generateId(),
...data,
synced: false
});
// 只保留最近 100 条
if (clips.length > 100) {
clips.pop();
}
// 保存
await chrome.storage.local.set({ clips });
// 尝试同步到服务器
syncToServer();
}
// 同步到服务器
async function syncToServer() {
const { serverUrl, apiKey } = await chrome.storage.sync.get(['serverUrl', 'apiKey']);
if (!serverUrl || !apiKey) {
console.log('[InsightFlow] Server not configured, skipping sync');
return;
}
const result = await chrome.storage.local.get(['clips']);
const clips = result.clips || [];
const unsyncedClips = clips.filter(c => !c.synced);
if (unsyncedClips.length === 0) return;
for (const clip of unsyncedClips) {
try {
const response = await fetch(`${serverUrl}/api/v1/plugins/chrome/import`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
token: apiKey,
url: clip.url,
title: clip.title,
content: clip.content,
html_content: clip.html || null
})
});
if (response.ok) {
clip.synced = true;
clip.syncedAt = new Date().toISOString();
}
} catch (error) {
console.error('[InsightFlow] Sync failed:', error);
}
}
// 更新存储
await chrome.storage.local.set({ clips });
}
// 生成唯一ID
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// 定时同步每5分钟
chrome.alarms.create('syncClips', { periodInMinutes: 5 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'syncClips') {
syncToServer();
}
});