feat(M2): FR-E6 链接气泡+图片入库——LinkBubbleMenu(编辑/移除href);ImageView 节点视图(相对路径转 vaultasset:// 协议展示);assetPickImage/assetWrite IPC + .attachments 入库;剪贴板粘贴图片入库;工具栏 链接/图片/网图 按钮;CSP 放行 vaultasset:;editor-link-image E2E 3 项
This commit is contained in:
@@ -1,13 +1,19 @@
|
||||
// main 进程入口:创建窗口 + 初始化库 + 注册 IPC + 迁移检测
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
import { app, BrowserWindow, ipcMain, protocol, net } from 'electron';
|
||||
import nodePath from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { VaultService } from './services/vault-service.js';
|
||||
import { registerIpc } from './ipc/handlers.js';
|
||||
import { MigrationService } from './services/migration-service.js';
|
||||
import { IPC } from '@hunter/shared';
|
||||
|
||||
const __dirname = nodePath.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// FR-E6:注册 vaultasset:// 协议——渲染层用相对路径展示库内本地图片(须在 app ready 前注册)
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'vaultasset', privileges: { standard: true, secure: true, supportFetchAPI: true, stream: true } },
|
||||
]);
|
||||
|
||||
let win: BrowserWindow | null = null;
|
||||
|
||||
function createWindow() {
|
||||
@@ -34,6 +40,20 @@ function createWindow() {
|
||||
app.whenReady().then(async () => {
|
||||
const vault = new VaultService();
|
||||
await vault.init();
|
||||
// FR-E6:vaultasset:// 协议处理器——安全提供库内文件给渲染层(限定在库根内)
|
||||
protocol.handle('vaultasset', async (req) => {
|
||||
const root = vault.fileService?.getRoot();
|
||||
if (!root) return new Response('no vault', { status: 503 });
|
||||
const rel = decodeURIComponent(new URL(req.url).pathname).replace(/^\/+/, '');
|
||||
const abs = nodePath.resolve(root, rel);
|
||||
const relFromRoot = nodePath.relative(root, abs);
|
||||
if (relFromRoot.startsWith('..') || nodePath.isAbsolute(relFromRoot)) return new Response('forbidden', { status: 403 });
|
||||
try {
|
||||
return await net.fetch(pathToFileURL(abs).toString());
|
||||
} catch {
|
||||
return new Response('not found', { status: 404 });
|
||||
}
|
||||
});
|
||||
// 旧版数据迁移检测(FR-D1/D2/D3):幂等,无旧数据则跳过
|
||||
const migrator = new MigrationService(app.getPath('userData'));
|
||||
const oldDataDir = nodePath.join(app.getPath('userData'), 'legacy-data');
|
||||
|
||||
Reference in New Issue
Block a user