feat(remote): 完善文件传输功能及WebSocket支持
This commit is contained in:
@@ -262,6 +262,124 @@ ipcMain.handle("clipboard-write-text", async (event, text) => {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("remote-fetch-drives", async (_event, serverHost, port, password) => {
|
||||
try {
|
||||
let url = `http://${serverHost}:${port}/api/files/drives`;
|
||||
if (password) {
|
||||
url += `?password=${encodeURIComponent(password)}`;
|
||||
}
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch drives: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const items = data.items || [];
|
||||
return {
|
||||
success: true,
|
||||
data: items.map((item) => ({
|
||||
name: item.name,
|
||||
path: item.name,
|
||||
type: item.isDirectory ? "dir" : "file",
|
||||
size: item.size,
|
||||
modified: ""
|
||||
}))
|
||||
};
|
||||
} catch (error) {
|
||||
log2.error("Remote fetch drives failed:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("remote-fetch-files", async (_event, serverHost, port, filePath, password) => {
|
||||
try {
|
||||
let url = `http://${serverHost}:${port}/api/files/browse?path=${encodeURIComponent(filePath)}&allowSystem=true`;
|
||||
if (password) {
|
||||
url += `&password=${encodeURIComponent(password)}`;
|
||||
}
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch files: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const items = data.items || [];
|
||||
return {
|
||||
success: true,
|
||||
data: items.map((item) => ({
|
||||
name: item.name,
|
||||
path: data.currentPath ? `${data.currentPath}/${item.name}` : item.name,
|
||||
type: item.isDirectory ? "dir" : "file",
|
||||
size: item.size,
|
||||
modified: item.modified?.toString()
|
||||
}))
|
||||
};
|
||||
} catch (error) {
|
||||
log2.error("Remote fetch files failed:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("remote-upload-file", async (_event, serverHost, port, filePath, remotePath, password) => {
|
||||
try {
|
||||
const win = electronState.getMainWindow();
|
||||
if (!win) {
|
||||
throw new Error("No window found");
|
||||
}
|
||||
const fullPath = path2.resolve(filePath);
|
||||
if (!fs2.existsSync(fullPath)) {
|
||||
throw new Error("File not found");
|
||||
}
|
||||
const fileBuffer = fs2.readFileSync(fullPath);
|
||||
const fileName = path2.basename(fullPath);
|
||||
let url = `http://${serverHost}:${port}/api/files/upload`;
|
||||
if (password) {
|
||||
url += `?password=${encodeURIComponent(password)}`;
|
||||
}
|
||||
const formData = new FormData();
|
||||
const blob = new Blob([fileBuffer]);
|
||||
formData.append("file", blob, fileName);
|
||||
if (remotePath) {
|
||||
formData.append("path", remotePath);
|
||||
}
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Upload failed: ${response.statusText}`);
|
||||
}
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
log2.error("Remote upload failed:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("remote-download-file", async (_event, serverHost, port, fileName, remotePath, password) => {
|
||||
try {
|
||||
const win = electronState.getMainWindow();
|
||||
if (!win) {
|
||||
throw new Error("No window found");
|
||||
}
|
||||
let url = `http://${serverHost}:${port}/api/files/${encodeURIComponent(fileName)}`;
|
||||
if (password) {
|
||||
url += `?password=${encodeURIComponent(password)}`;
|
||||
}
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Download failed: ${response.statusText}`);
|
||||
}
|
||||
const buffer = await response.arrayBuffer();
|
||||
const { filePath } = await dialog2.showSaveDialog(win, {
|
||||
title: "\u4FDD\u5B58\u6587\u4EF6",
|
||||
defaultPath: fileName
|
||||
});
|
||||
if (!filePath) {
|
||||
return { success: false, canceled: true };
|
||||
}
|
||||
fs2.writeFileSync(filePath, Buffer.from(buffer));
|
||||
return { success: true, filePath };
|
||||
} catch (error) {
|
||||
log2.error("Remote download failed:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
async function startServer() {
|
||||
if (electronState.isDevelopment()) {
|
||||
log2.info("In dev mode, assuming external servers are running.");
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -19,6 +19,10 @@ import_electron.contextBridge.exposeInMainWorld("electronAPI", {
|
||||
return () => import_electron.ipcRenderer.removeListener("remote-clipboard-auto-sync", handler);
|
||||
},
|
||||
clipboardReadText: () => import_electron.ipcRenderer.invoke("clipboard-read-text"),
|
||||
clipboardWriteText: (text) => import_electron.ipcRenderer.invoke("clipboard-write-text", text)
|
||||
clipboardWriteText: (text) => import_electron.ipcRenderer.invoke("clipboard-write-text", text),
|
||||
remoteFetchDrives: (serverHost, port, password) => import_electron.ipcRenderer.invoke("remote-fetch-drives", serverHost, port, password),
|
||||
remoteFetchFiles: (serverHost, port, filePath, password) => import_electron.ipcRenderer.invoke("remote-fetch-files", serverHost, port, filePath, password),
|
||||
remoteUploadFile: (serverHost, port, filePath, remotePath, password) => import_electron.ipcRenderer.invoke("remote-upload-file", serverHost, port, filePath, remotePath, password),
|
||||
remoteDownloadFile: (serverHost, port, fileName, remotePath, password) => import_electron.ipcRenderer.invoke("remote-download-file", serverHost, port, fileName, remotePath, password)
|
||||
});
|
||||
//# sourceMappingURL=preload.cjs.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sources":["../electron/preload.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron'\n\nconsole.log('--- PRELOAD SCRIPT LOADED SUCCESSFULLY ---')\n\ncontextBridge.exposeInMainWorld('electronAPI', {\n exportPDF: (title: string, htmlContent?: string) => ipcRenderer.invoke('export-pdf', title, htmlContent),\n selectHtmlFile: () => ipcRenderer.invoke('select-html-file'),\n updateTitlebarButtons: (symbolColor: string) => ipcRenderer.invoke('update-titlebar-buttons', symbolColor),\n onRemoteClipboardSyncToRemote: (callback: () => void) => {\n ipcRenderer.on('remote-clipboard-sync-to-remote', callback);\n return () => ipcRenderer.removeListener('remote-clipboard-sync-to-remote', callback);\n },\n onRemoteClipboardSyncFromRemote: (callback: () => void) => {\n ipcRenderer.on('remote-clipboard-sync-from-remote', callback);\n return () => ipcRenderer.removeListener('remote-clipboard-sync-from-remote', callback);\n },\n onRemoteClipboardAutoSync: (callback: (text: string) => void) => {\n const handler = (_event: Electron.IpcRendererEvent, text: string) => callback(text);\n ipcRenderer.on('remote-clipboard-auto-sync', handler);\n return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);\n },\n clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),\n clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),\n})\n"],"mappings":";AAAA,sBAA2C;AAE3C,QAAQ,IAAI,4CAA4C;AAExD,8BAAc,kBAAkB,eAAe;AAAA,EAC7C,WAAW,CAAC,OAAe,gBAAyB,4BAAY,OAAO,cAAc,OAAO,WAAW;AAAA,EACvG,gBAAgB,MAAM,4BAAY,OAAO,kBAAkB;AAAA,EAC3D,uBAAuB,CAAC,gBAAwB,4BAAY,OAAO,2BAA2B,WAAW;AAAA,EACzG,+BAA+B,CAAC,aAAyB;AACvD,gCAAY,GAAG,mCAAmC,QAAQ;AAC1D,WAAO,MAAM,4BAAY,eAAe,mCAAmC,QAAQ;AAAA,EACrF;AAAA,EACA,iCAAiC,CAAC,aAAyB;AACzD,gCAAY,GAAG,qCAAqC,QAAQ;AAC5D,WAAO,MAAM,4BAAY,eAAe,qCAAqC,QAAQ;AAAA,EACvF;AAAA,EACA,2BAA2B,CAAC,aAAqC;AAC/D,UAAM,UAAU,CAAC,QAAmC,SAAiB,SAAS,IAAI;AAClF,gCAAY,GAAG,8BAA8B,OAAO;AACpD,WAAO,MAAM,4BAAY,eAAe,8BAA8B,OAAO;AAAA,EAC/E;AAAA,EACA,mBAAmB,MAAM,4BAAY,OAAO,qBAAqB;AAAA,EACjE,oBAAoB,CAAC,SAAiB,4BAAY,OAAO,wBAAwB,IAAI;AACvF,CAAC;","names":[]}
|
||||
{"version":3,"sources":["../electron/preload.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron'\r\n\r\nconsole.log('--- PRELOAD SCRIPT LOADED SUCCESSFULLY ---')\r\n\r\ncontextBridge.exposeInMainWorld('electronAPI', {\r\n exportPDF: (title: string, htmlContent?: string) => ipcRenderer.invoke('export-pdf', title, htmlContent),\r\n selectHtmlFile: () => ipcRenderer.invoke('select-html-file'),\r\n updateTitlebarButtons: (symbolColor: string) => ipcRenderer.invoke('update-titlebar-buttons', symbolColor),\r\n onRemoteClipboardSyncToRemote: (callback: () => void) => {\r\n ipcRenderer.on('remote-clipboard-sync-to-remote', callback);\r\n return () => ipcRenderer.removeListener('remote-clipboard-sync-to-remote', callback);\r\n },\r\n onRemoteClipboardSyncFromRemote: (callback: () => void) => {\r\n ipcRenderer.on('remote-clipboard-sync-from-remote', callback);\r\n return () => ipcRenderer.removeListener('remote-clipboard-sync-from-remote', callback);\r\n },\r\n onRemoteClipboardAutoSync: (callback: (text: string) => void) => {\r\n const handler = (_event: Electron.IpcRendererEvent, text: string) => callback(text);\r\n ipcRenderer.on('remote-clipboard-auto-sync', handler);\r\n return () => ipcRenderer.removeListener('remote-clipboard-auto-sync', handler);\r\n },\r\n clipboardReadText: () => ipcRenderer.invoke('clipboard-read-text'),\r\n clipboardWriteText: (text: string) => ipcRenderer.invoke('clipboard-write-text', text),\r\n remoteFetchDrives: (serverHost: string, port: number, password?: string) => \r\n ipcRenderer.invoke('remote-fetch-drives', serverHost, port, password),\r\n remoteFetchFiles: (serverHost: string, port: number, filePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-fetch-files', serverHost, port, filePath, password),\r\n remoteUploadFile: (serverHost: string, port: number, filePath: string, remotePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-upload-file', serverHost, port, filePath, remotePath, password),\r\n remoteDownloadFile: (serverHost: string, port: number, fileName: string, remotePath: string, password?: string) => \r\n ipcRenderer.invoke('remote-download-file', serverHost, port, fileName, remotePath, password),\r\n})\r\n"],"mappings":";AAAA,sBAA2C;AAE3C,QAAQ,IAAI,4CAA4C;AAExD,8BAAc,kBAAkB,eAAe;AAAA,EAC7C,WAAW,CAAC,OAAe,gBAAyB,4BAAY,OAAO,cAAc,OAAO,WAAW;AAAA,EACvG,gBAAgB,MAAM,4BAAY,OAAO,kBAAkB;AAAA,EAC3D,uBAAuB,CAAC,gBAAwB,4BAAY,OAAO,2BAA2B,WAAW;AAAA,EACzG,+BAA+B,CAAC,aAAyB;AACvD,gCAAY,GAAG,mCAAmC,QAAQ;AAC1D,WAAO,MAAM,4BAAY,eAAe,mCAAmC,QAAQ;AAAA,EACrF;AAAA,EACA,iCAAiC,CAAC,aAAyB;AACzD,gCAAY,GAAG,qCAAqC,QAAQ;AAC5D,WAAO,MAAM,4BAAY,eAAe,qCAAqC,QAAQ;AAAA,EACvF;AAAA,EACA,2BAA2B,CAAC,aAAqC;AAC/D,UAAM,UAAU,CAAC,QAAmC,SAAiB,SAAS,IAAI;AAClF,gCAAY,GAAG,8BAA8B,OAAO;AACpD,WAAO,MAAM,4BAAY,eAAe,8BAA8B,OAAO;AAAA,EAC/E;AAAA,EACA,mBAAmB,MAAM,4BAAY,OAAO,qBAAqB;AAAA,EACjE,oBAAoB,CAAC,SAAiB,4BAAY,OAAO,wBAAwB,IAAI;AAAA,EACrF,mBAAmB,CAAC,YAAoB,MAAc,aACpD,4BAAY,OAAO,uBAAuB,YAAY,MAAM,QAAQ;AAAA,EACtE,kBAAkB,CAAC,YAAoB,MAAc,UAAkB,aACrE,4BAAY,OAAO,sBAAsB,YAAY,MAAM,UAAU,QAAQ;AAAA,EAC/E,kBAAkB,CAAC,YAAoB,MAAc,UAAkB,YAAoB,aACzF,4BAAY,OAAO,sBAAsB,YAAY,MAAM,UAAU,YAAY,QAAQ;AAAA,EAC3F,oBAAoB,CAAC,YAAoB,MAAc,UAAkB,YAAoB,aAC3F,4BAAY,OAAO,wBAAwB,YAAY,MAAM,UAAU,YAAY,QAAQ;AAC/F,CAAC;","names":[]}
|
||||
Reference in New Issue
Block a user