Initial clean project import

This commit is contained in:
cat-shark
2026-06-19 18:41:41 +08:00
commit a13b804c7a
1306 changed files with 220568 additions and 0 deletions
+222
View File
@@ -0,0 +1,222 @@
import archiver from "archiver";
import axios from "axios";
import fs from "node:fs";
import path from "node:path";
import yauzl from "yauzl";
const getZipFileContent = async (path: string, pathInZip: string) => {
return new Promise((resolve, reject) => {
// console.log('getZipFileContent', path, pathInZip)
yauzl.open(path, {lazyEntries: true}, (err: any, zipfile: any) => {
if (err) {
// console.log('getZipFileContent err', err)
reject(err);
return;
}
zipfile.on("error", function (err: any) {
// console.log('getZipFileContent error', err)
reject(err);
});
zipfile.on("end", function () {
// console.log('getZipFileContent end')
reject("FileNotFound");
});
zipfile.on("entry", function (entry: any) {
// console.log('getZipFileContent entry', entry.fileName)
if (entry.fileName === pathInZip) {
zipfile.openReadStream(entry, function (err: any, readStream: any) {
if (err) {
reject(err);
return;
}
let chunks: any[] = [];
readStream.on("data", function (chunk: any) {
chunks.push(chunk);
});
readStream.on("end", function () {
const bytes = Buffer.concat(chunks);
const text = bytes.toString("utf8");
resolve(text);
});
});
} else {
zipfile.readEntry();
}
});
zipfile.readEntry();
});
});
};
const unzip = async (
zipPath: string,
dest: string,
option?: {
process: (type: "start" | "end", entry: any) => void;
}
) => {
option = Object.assign(
{
process: null,
},
option
);
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, {recursive: true});
}
return new Promise((resolve, reject) => {
// console.log('unzip', zipPath, dest)
yauzl.open(zipPath, {lazyEntries: true}, (err: any, zipfile: any) => {
if (err) {
// console.log('unzip err', err)
reject(err);
return;
}
zipfile.on("error", function (err: any) {
// console.log('unzip error', err)
reject(err);
});
zipfile.on("end", function () {
// console.log('unzip end')
resolve(undefined);
});
zipfile.on("entry", function (entry: any) {
if (option.process) {
option.process("start", entry);
}
// console.log('unzip entry', dest, entry.fileName)
const destPath = dest + "/" + entry.fileName;
if (/\/$/.test(entry.fileName)) {
// console.log('unzip mkdir', destPath)
fs.mkdirSync(destPath, {recursive: true});
zipfile.readEntry();
} else {
const dirname = destPath.replace(/\/[^/]+$/, "");
if (!fs.existsSync(dirname)) {
fs.mkdirSync(dirname, {recursive: true});
}
zipfile.openReadStream(entry, function (err: any, readStream: any) {
if (err) {
reject(err);
return;
}
readStream.on("end", function () {
if (option.process) {
option.process("end", entry);
}
zipfile.readEntry();
});
readStream.pipe(fs.createWriteStream(destPath));
});
}
});
zipfile.readEntry();
});
});
};
const zip = async (
zipPath: string,
sourceDir: string,
option?: {
end?: (archive: any) => Promise<void>;
filter?: (params: { name: string, path: string, fullPath: string, isDir: boolean }) => Promise<boolean>;
}
): Promise<void> => {
option = Object.assign(
{
end: null,
filter: null,
},
option
);
return new Promise((resolve, reject) => {
const output = fs.createWriteStream(zipPath);
const archive = archiver("zip", {
zlib: {level: 9},
});
output.on("close", function () {
resolve(undefined);
});
archive.on("error", function (err: any) {
reject(err);
});
archive.pipe(output);
const addFiles = async (dir: string, relativePath: string = "") => {
const items = fs.readdirSync(path.join(dir, relativePath));
for (const item of items) {
const fullPath = path.join(dir, relativePath, item);
const relPath = path.join(relativePath, item).replace(/\\/g, "/"); // Normalize for zip
const stat = fs.statSync(fullPath);
const isDir = stat.isDirectory();
const shouldInclude = !option.filter || await option.filter({
name: item,
path: relPath,
fullPath,
isDir
});
if (isDir) {
if (shouldInclude) {
await addFiles(dir, relPath);
}
} else {
if (shouldInclude) {
archive.file(fullPath, {name: relPath});
}
}
}
};
addFiles(sourceDir).then(async () => {
if (option.end) {
await option.end(archive);
}
archive.finalize();
}).catch(reject);
});
};
const request = async (option: {
url: string,
method?: "GET" | "POST";
responseType?: "json" | "text" | "arraybuffer";
headers?: any;
data?: any;
}) => {
option = Object.assign({
url: "",
method: "GET",
responseType: "json",
headers: {},
data: null,
}, option);
const response = await axios.request({
url: option.url,
method: option.method,
responseType: option.responseType === "arraybuffer" ? "arraybuffer" : "text",
headers: option.headers,
data: option.data,
});
if (response.status !== 200) {
throw new Error(`Request failed with status code ${response.status}`);
}
if (option.responseType === "json") {
return JSON.parse(response.data);
} else if (option.responseType === "text") {
return response.data;
} else if (option.responseType === "arraybuffer") {
return Buffer.from(response.data);
} else {
return response.data;
}
}
export const Misc = {
getZipFileContent,
unzip,
zip,
request,
};
export default Misc;
+28
View File
@@ -0,0 +1,28 @@
import {ipcMain} from "electron";
import index from "./index";
// 注册所有 IPC 处理器,这样 render.ts 就不需要导入 Node.js 模块
ipcMain.handle("misc:getZipFileContent", async (_, path: string, pathInZip: string) => {
return await index.getZipFileContent(path, pathInZip);
});
ipcMain.handle("misc:unzip", async (_, zipPath: string, dest: string, option?: any) => {
return await index.unzip(zipPath, dest, option);
});
ipcMain.handle("misc:zip", async (_, zipPath: string, sourceDir: string, option?: any) => {
return await index.zip(zipPath, sourceDir, option);
});
ipcMain.handle("misc:request", async (_, requestOption: any) => {
return await index.request(requestOption);
});
export default {
...index,
};
export const MiscMain = {
...index,
};
+32
View File
@@ -0,0 +1,32 @@
import { ipcRenderer } from "electron";
// 🔧 纯 IPC 包装 - 不导入 Node.js 模块(特别是 archiver),防止被打包到渲染进程
export default {
getZipFileContent: async (path: string, pathInZip: string) => {
if (!ipcRenderer) {
throw new Error('IPC 接口不可用');
}
return await ipcRenderer.invoke("misc:getZipFileContent", path, pathInZip);
},
unzip: async (zipPath: string, dest: string, option?: any) => {
if (!ipcRenderer) {
throw new Error('IPC 接口不可用');
}
return await ipcRenderer.invoke("misc:unzip", zipPath, dest, option);
},
zip: async (zipPath: string, sourceDir: string, option?: any) => {
if (!ipcRenderer) {
throw new Error('IPC 接口不可用');
}
return await ipcRenderer.invoke("misc:zip", zipPath, sourceDir, option);
},
request: async (requestOption: any) => {
if (!ipcRenderer) {
throw new Error('IPC 接口不可用');
}
return await ipcRenderer.invoke("misc:request", requestOption);
},
};