完成动态端口+打包
This commit is contained in:
parent
029b5de84c
commit
165fce6016
File diff suppressed because one or more lines are too long
@ -22,6 +22,15 @@ files:
|
||||
|
||||
asar: true
|
||||
|
||||
extraResources:
|
||||
- from: data
|
||||
to: data
|
||||
filter:
|
||||
- "**/*"
|
||||
- "!db2.sqlite"
|
||||
- "!logs/**"
|
||||
- "!oss/**"
|
||||
|
||||
win:
|
||||
target:
|
||||
- target: nsis
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"scripts": {
|
||||
"dev": "nodemon --inspect --exec tsx src/app.ts",
|
||||
"dev:gui": "electronmon -r tsx scripts/main.ts",
|
||||
"dev:gui-vite": "cross-env VITE_DEV=1 electronmon -r tsx scripts/main.ts",
|
||||
"lint": "tsc --noEmit",
|
||||
"build": "cross-env NODE_ENV=prod tsx scripts/build.ts",
|
||||
"pack": "electron-builder --dir",
|
||||
@ -88,4 +89,4 @@
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@ const defaultPort = 10588;
|
||||
function initializeData(): void {
|
||||
const srcDir = path.join(process.resourcesPath, "data");
|
||||
const destDir = path.join(app.getPath("userData"), "data");
|
||||
if (fs.existsSync(destDir)) return;
|
||||
copyDirRecursive(srcDir, destDir);
|
||||
}
|
||||
|
||||
@ -34,20 +35,12 @@ function getNodeModulesPaths(): string[] {
|
||||
const paths: string[] = [];
|
||||
if (app.isPackaged) {
|
||||
// external 依赖(原生模块)在 unpacked 目录
|
||||
const unpackedNodeModules = path.join(
|
||||
process.resourcesPath,
|
||||
"app.asar.unpacked",
|
||||
"node_modules"
|
||||
);
|
||||
const unpackedNodeModules = path.join(process.resourcesPath, "app.asar.unpacked", "node_modules");
|
||||
if (fs.existsSync(unpackedNodeModules)) {
|
||||
paths.push(unpackedNodeModules);
|
||||
}
|
||||
// 普通依赖在 asar 内
|
||||
const asarNodeModules = path.join(
|
||||
process.resourcesPath,
|
||||
"app.asar",
|
||||
"node_modules"
|
||||
);
|
||||
const asarNodeModules = path.join(process.resourcesPath, "app.asar", "node_modules");
|
||||
paths.push(asarNodeModules);
|
||||
} else {
|
||||
paths.push(path.join(process.cwd(), "node_modules"));
|
||||
@ -89,10 +82,16 @@ function createMainWindow(port: any): void {
|
||||
show: true,
|
||||
autoHideMenuBar: true,
|
||||
});
|
||||
// 开发环境和生产环境使用不同的路径
|
||||
win.webContents.on("did-start-loading", () => {
|
||||
void win.webContents.executeJavaScript(`window.$electron = true; window.$port = ${port};`);
|
||||
});
|
||||
const isDev = process.env.NODE_ENV === "dev" || !app.isPackaged;
|
||||
const htmlPath = isDev ? path.join(process.cwd(), "data", "web", "index.html") : path.join(app.getPath("userData"), "data", "web", "index.html");
|
||||
void win.loadFile(htmlPath);
|
||||
if (process.env.VITE_DEV) {
|
||||
void win.loadURL("http://localhost:50188");
|
||||
} else {
|
||||
const htmlPath = isDev ? path.join(process.cwd(), "data", "web", "index.html") : path.join(app.getPath("userData"), "data", "web", "index.html");
|
||||
void win.loadFile(htmlPath);
|
||||
}
|
||||
}
|
||||
|
||||
let closeServeFn: (() => Promise<void>) | undefined;
|
||||
@ -111,8 +110,8 @@ app.whenReady().then(async () => {
|
||||
// 使用自定义路径加载模块
|
||||
const mod = requireWithCustomPaths(servePath);
|
||||
closeServeFn = mod.closeServe;
|
||||
const port = await mod.default(false);
|
||||
console.log("%c Line:37 🍺 port", "background:#e41a6a", port);
|
||||
const port = await mod.default(true);
|
||||
console.log("%c Line:112 🍇 port", "background:#2eafb0", port);
|
||||
createMainWindow(port);
|
||||
} catch (err) {
|
||||
console.error("[服务启动失败]:", err);
|
||||
|
||||
@ -17,6 +17,7 @@ const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
export default async function startServe(randomPort: Boolean = false) {
|
||||
console.log("%c Line:20 🍰 randomPort", "background:#b03734", randomPort);
|
||||
const io = new Server(server, { cors: { origin: "*" } });
|
||||
socketInit(io);
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import { exec } from "child_process";
|
||||
import { success, error } from "@/lib/responseFormat";
|
||||
import { validateFields } from "@/middleware/middleware";
|
||||
import { isEletron } from "@/utils/getPath";
|
||||
import u from "@/utils";
|
||||
import path from "path";
|
||||
const router = express.Router();
|
||||
|
||||
export default router.post(
|
||||
@ -17,7 +19,9 @@ export default router.post(
|
||||
}
|
||||
const { path: folderPath } = req.body;
|
||||
const platform = process.platform;
|
||||
const cmd = platform === "win32" ? `explorer "${folderPath}"` : platform === "darwin" ? `open "${folderPath}"` : `xdg-open "${folderPath}"`;
|
||||
const target = u.getPath(folderPath);
|
||||
console.log("%c Line:23 🎂 target", "background:#fca650", target);
|
||||
const cmd = platform === "win32" ? `explorer "${target}"` : platform === "darwin" ? `open "${target}"` : `xdg-open "${target}"`;
|
||||
exec(cmd, (err) => {
|
||||
if (err) {
|
||||
return res.status(200).send(error(err.message));
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
import { pipeline, env as transformersEnv, FeatureExtractionPipeline } from "@huggingface/transformers";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import getPath from "@/utils/getPath";
|
||||
import db from "@/utils/db";
|
||||
|
||||
// ── 模型配置 ──
|
||||
// const modelOnnxFile = ["all-MiniLM-L6-v2", "onnx", "model_fp16.onnx"]; // 模型文件路径
|
||||
// const modelDtype = "fp16" as const; // 量化类型:fp32
|
||||
let extractor: FeatureExtractionPipeline | null = null;
|
||||
|
||||
export async function initEmbedding(): Promise<void> {
|
||||
if (extractor) return;
|
||||
|
||||
const modelConfigData = await db("o_setting").whereIn("key", ["modelOnnxFile", "modelDtype"]);
|
||||
const modelObj: Record<string, string> = {};
|
||||
Object.entries(modelConfigData).forEach(([key, value]) => {
|
||||
modelObj[key] = value as string;
|
||||
});
|
||||
let modelOnnxFile = modelObj?.modelOnnxFile ? JSON.parse(modelObj.modelOnnxFile) : ["all-MiniLM-L6-v2", "onnx", "model_fp16.onnx"]; // 模型文件路径
|
||||
let modelDtype = modelObj?.modelDtype ?? ("fp16" as const); // 量化类型:fp32
|
||||
const onnxPath = path.join(getPath("models"), ...modelOnnxFile);
|
||||
if (!fs.existsSync(onnxPath)) {
|
||||
throw new Error(`Embedding 模型文件不存在: ${onnxPath}`);
|
||||
}
|
||||
|
||||
transformersEnv.allowRemoteModels = false;
|
||||
transformersEnv.allowLocalModels = true;
|
||||
transformersEnv.localModelPath = getPath("models").replace(/\\/g, "/") + "/";
|
||||
|
||||
const modelFolder = modelOnnxFile[0];
|
||||
// @ts-ignore - pipeline 重载联合类型过于复杂
|
||||
extractor = await pipeline("feature-extraction", modelFolder, { dtype: modelDtype });
|
||||
}
|
||||
|
||||
export async function getEmbedding(text: string): Promise<number[]> {
|
||||
if (!extractor) await initEmbedding();
|
||||
const output = await extractor!(text, { pooling: "mean", normalize: true });
|
||||
return Array.from(output.data as Float32Array);
|
||||
}
|
||||
|
||||
export function cosineSimilarity(a: number[], b: number[]): number {
|
||||
return a.reduce((dot, v, i) => dot + v * b[i], 0);
|
||||
}
|
||||
|
||||
export async function disposeEmbedding(): Promise<void> {
|
||||
await extractor?.dispose?.();
|
||||
extractor = null;
|
||||
}
|
||||
@ -1,7 +1,4 @@
|
||||
import * as ONNX_WEB from "onnxruntime-web";
|
||||
// 强制 @huggingface/transformers 使用 onnxruntime-web 而非 onnxruntime-node
|
||||
(globalThis as any)[Symbol.for("onnxruntime")] = ONNX_WEB;
|
||||
|
||||
import { pipeline, env as transformersEnv, FeatureExtractionPipeline } from "@huggingface/transformers";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
@ -1,22 +1,28 @@
|
||||
import path from "path";
|
||||
import isPathInside from "is-path-inside";
|
||||
|
||||
export default (fileName?: string[] | string) => {
|
||||
let dbPath: string;
|
||||
let basePath: string;
|
||||
if (typeof process.versions?.electron !== "undefined") {
|
||||
const { app } = require("electron");
|
||||
const userDataDir: string = app.getPath("userData");
|
||||
dbPath = path.join(userDataDir, "data");
|
||||
basePath = path.join(userDataDir, "data");
|
||||
} else {
|
||||
dbPath = path.join(process.cwd(), "data");
|
||||
basePath = path.join(process.cwd(), "data");
|
||||
}
|
||||
if (fileName) {
|
||||
let dbPath: string;
|
||||
if (Array.isArray(fileName)) {
|
||||
dbPath = path.join(dbPath, ...fileName);
|
||||
dbPath = path.resolve(basePath, ...fileName);
|
||||
} else {
|
||||
dbPath = path.join(dbPath, fileName);
|
||||
dbPath = path.resolve(basePath, fileName);
|
||||
}
|
||||
if (!isPathInside(dbPath, basePath) && dbPath !== basePath) {
|
||||
throw new Error("路径逃逸错误,路径必须在数据目录内");
|
||||
}
|
||||
return dbPath;
|
||||
}
|
||||
return dbPath;
|
||||
return basePath;
|
||||
};
|
||||
|
||||
export function isEletron() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user