完成动态端口+打包
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
|
asar: true
|
||||||
|
|
||||||
|
extraResources:
|
||||||
|
- from: data
|
||||||
|
to: data
|
||||||
|
filter:
|
||||||
|
- "**/*"
|
||||||
|
- "!db2.sqlite"
|
||||||
|
- "!logs/**"
|
||||||
|
- "!oss/**"
|
||||||
|
|
||||||
win:
|
win:
|
||||||
target:
|
target:
|
||||||
- target: nsis
|
- target: nsis
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon --inspect --exec tsx src/app.ts",
|
"dev": "nodemon --inspect --exec tsx src/app.ts",
|
||||||
"dev:gui": "electronmon -r tsx scripts/main.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",
|
"lint": "tsc --noEmit",
|
||||||
"build": "cross-env NODE_ENV=prod tsx scripts/build.ts",
|
"build": "cross-env NODE_ENV=prod tsx scripts/build.ts",
|
||||||
"pack": "electron-builder --dir",
|
"pack": "electron-builder --dir",
|
||||||
@ -88,4 +89,4 @@
|
|||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,6 +12,7 @@ const defaultPort = 10588;
|
|||||||
function initializeData(): void {
|
function initializeData(): void {
|
||||||
const srcDir = path.join(process.resourcesPath, "data");
|
const srcDir = path.join(process.resourcesPath, "data");
|
||||||
const destDir = path.join(app.getPath("userData"), "data");
|
const destDir = path.join(app.getPath("userData"), "data");
|
||||||
|
if (fs.existsSync(destDir)) return;
|
||||||
copyDirRecursive(srcDir, destDir);
|
copyDirRecursive(srcDir, destDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,20 +35,12 @@ function getNodeModulesPaths(): string[] {
|
|||||||
const paths: string[] = [];
|
const paths: string[] = [];
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
// external 依赖(原生模块)在 unpacked 目录
|
// external 依赖(原生模块)在 unpacked 目录
|
||||||
const unpackedNodeModules = path.join(
|
const unpackedNodeModules = path.join(process.resourcesPath, "app.asar.unpacked", "node_modules");
|
||||||
process.resourcesPath,
|
|
||||||
"app.asar.unpacked",
|
|
||||||
"node_modules"
|
|
||||||
);
|
|
||||||
if (fs.existsSync(unpackedNodeModules)) {
|
if (fs.existsSync(unpackedNodeModules)) {
|
||||||
paths.push(unpackedNodeModules);
|
paths.push(unpackedNodeModules);
|
||||||
}
|
}
|
||||||
// 普通依赖在 asar 内
|
// 普通依赖在 asar 内
|
||||||
const asarNodeModules = path.join(
|
const asarNodeModules = path.join(process.resourcesPath, "app.asar", "node_modules");
|
||||||
process.resourcesPath,
|
|
||||||
"app.asar",
|
|
||||||
"node_modules"
|
|
||||||
);
|
|
||||||
paths.push(asarNodeModules);
|
paths.push(asarNodeModules);
|
||||||
} else {
|
} else {
|
||||||
paths.push(path.join(process.cwd(), "node_modules"));
|
paths.push(path.join(process.cwd(), "node_modules"));
|
||||||
@ -89,10 +82,16 @@ function createMainWindow(port: any): void {
|
|||||||
show: true,
|
show: true,
|
||||||
autoHideMenuBar: 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 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");
|
if (process.env.VITE_DEV) {
|
||||||
void win.loadFile(htmlPath);
|
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;
|
let closeServeFn: (() => Promise<void>) | undefined;
|
||||||
@ -111,8 +110,8 @@ app.whenReady().then(async () => {
|
|||||||
// 使用自定义路径加载模块
|
// 使用自定义路径加载模块
|
||||||
const mod = requireWithCustomPaths(servePath);
|
const mod = requireWithCustomPaths(servePath);
|
||||||
closeServeFn = mod.closeServe;
|
closeServeFn = mod.closeServe;
|
||||||
const port = await mod.default(false);
|
const port = await mod.default(true);
|
||||||
console.log("%c Line:37 🍺 port", "background:#e41a6a", port);
|
console.log("%c Line:112 🍇 port", "background:#2eafb0", port);
|
||||||
createMainWindow(port);
|
createMainWindow(port);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("[服务启动失败]:", err);
|
console.error("[服务启动失败]:", err);
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const app = express();
|
|||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
export default async function startServe(randomPort: Boolean = false) {
|
export default async function startServe(randomPort: Boolean = false) {
|
||||||
|
console.log("%c Line:20 🍰 randomPort", "background:#b03734", randomPort);
|
||||||
const io = new Server(server, { cors: { origin: "*" } });
|
const io = new Server(server, { cors: { origin: "*" } });
|
||||||
socketInit(io);
|
socketInit(io);
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import { exec } from "child_process";
|
|||||||
import { success, error } from "@/lib/responseFormat";
|
import { success, error } from "@/lib/responseFormat";
|
||||||
import { validateFields } from "@/middleware/middleware";
|
import { validateFields } from "@/middleware/middleware";
|
||||||
import { isEletron } from "@/utils/getPath";
|
import { isEletron } from "@/utils/getPath";
|
||||||
|
import u from "@/utils";
|
||||||
|
import path from "path";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
export default router.post(
|
export default router.post(
|
||||||
@ -17,7 +19,9 @@ export default router.post(
|
|||||||
}
|
}
|
||||||
const { path: folderPath } = req.body;
|
const { path: folderPath } = req.body;
|
||||||
const platform = process.platform;
|
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) => {
|
exec(cmd, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(200).send(error(err.message));
|
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";
|
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 { pipeline, env as transformersEnv, FeatureExtractionPipeline } from "@huggingface/transformers";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|||||||
@ -1,22 +1,28 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
import isPathInside from "is-path-inside";
|
||||||
|
|
||||||
export default (fileName?: string[] | string) => {
|
export default (fileName?: string[] | string) => {
|
||||||
let dbPath: string;
|
let basePath: string;
|
||||||
if (typeof process.versions?.electron !== "undefined") {
|
if (typeof process.versions?.electron !== "undefined") {
|
||||||
const { app } = require("electron");
|
const { app } = require("electron");
|
||||||
const userDataDir: string = app.getPath("userData");
|
const userDataDir: string = app.getPath("userData");
|
||||||
dbPath = path.join(userDataDir, "data");
|
basePath = path.join(userDataDir, "data");
|
||||||
} else {
|
} else {
|
||||||
dbPath = path.join(process.cwd(), "data");
|
basePath = path.join(process.cwd(), "data");
|
||||||
}
|
}
|
||||||
if (fileName) {
|
if (fileName) {
|
||||||
|
let dbPath: string;
|
||||||
if (Array.isArray(fileName)) {
|
if (Array.isArray(fileName)) {
|
||||||
dbPath = path.join(dbPath, ...fileName);
|
dbPath = path.resolve(basePath, ...fileName);
|
||||||
} else {
|
} 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() {
|
export function isEletron() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user