fix: 恢复本地 Convex Auth 链路

- 修复 desktop 脚本在 Linux 下优先使用后端虚拟环境可执行文件
- 移除 Convex Auth 的本地密钥兜底,回到真实 deployment env
- 首页未登录时先跳转 /auth,避免根路由直接触发 workspace 初始化失败
This commit is contained in:
lix-2026
2026-04-17 23:33:24 +08:00
parent ddf285b82d
commit cfc3af8984
5 changed files with 135 additions and 14 deletions
+74 -7
View File
@@ -19,12 +19,38 @@ const net = require("net");
const { URL } = require("url");
const fs = require("fs");
const rootDir = path.resolve(__dirname, "..");
const frontendDir = path.join(rootDir, "wolai-frontend");
const backendDir = path.join(rootDir, "wolai-backend");
const pythonBin = process.env.PYTHON_BIN || "python";
const celeryBin = process.env.CELERY_BIN || "celery";
const rootDir = path.resolve(__dirname, "..");
const frontendDir = path.join(rootDir, "wolai-frontend");
const backendDir = path.join(rootDir, "wolai-backend");
function resolveBackendExecutable(envName, fallbackName) {
const fromEnv = (process.env[envName] || "").trim();
if (fromEnv) return fromEnv;
const isWin = process.platform === "win32";
const candidates = isWin
? [
path.join(backendDir, ".venv", "Scripts", `${fallbackName}.exe`),
path.join(backendDir, ".venv312", "Scripts", `${fallbackName}.exe`),
path.join(backendDir, "venv", "Scripts", `${fallbackName}.exe`),
]
: [
path.join(backendDir, ".venv-linux", "bin", fallbackName),
path.join(backendDir, ".venv", "bin", fallbackName),
path.join(backendDir, "venv", "bin", fallbackName),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
return fallbackName;
}
const pythonBin = resolveBackendExecutable("PYTHON_BIN", "python");
const celeryBin = resolveBackendExecutable("CELERY_BIN", "celery");
const celeryPoolFromEnv = (process.env.CELERY_POOL || "").trim();
const skipCelery =
(process.env.SKIP_CELERY || "").toLowerCase() === "1" ||
@@ -89,11 +115,44 @@ async function findFreePort(startPort) {
}
function getListeningPidsByPort(port) {
const pids = new Set();
if (process.platform !== "win32") {
try {
const out = execSync(`ss -ltnp 'sport = :${port}'`, { encoding: "utf8" });
for (const match of out.matchAll(/pid=(\d+)/g)) {
const pid = Number(match[1]);
if (Number.isFinite(pid) && pid > 0) {
pids.add(pid);
}
}
} catch {
// ignore
}
if (pids.size > 0) {
return [...pids];
}
try {
const out = execSync(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`, { encoding: "utf8" });
for (const line of out.split(/\r?\n/)) {
const pid = Number(line.trim());
if (Number.isFinite(pid) && pid > 0) {
pids.add(pid);
}
}
} catch {
// ignore
}
return [...pids];
}
try {
// 说明:netstat 输出示例:
// TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 12345
const out = execSync("netstat -ano -p tcp", { encoding: "utf8" });
const pids = new Set();
for (const line of out.split(/\r?\n/)) {
if (!line.includes(`:${port}`)) continue;
if (!/LISTENING/i.test(line)) continue;
@@ -110,6 +169,14 @@ function getListeningPidsByPort(port) {
}
function getProcessNameByPid(pid) {
if (process.platform !== "win32") {
try {
return execSync(`ps -p ${pid} -o comm=`, { encoding: "utf8" }).trim();
} catch {
return "";
}
}
try {
// 输出为 CSV,示例:
// "Image Name","PID","Session Name","Session#","Mem Usage"
+27 -1
View File
@@ -22,7 +22,33 @@ const path = require("path");
const rootDir = path.resolve(__dirname, "..");
const backendDir = path.join(rootDir, "wolai-backend");
const pythonBin = process.env.PYTHON_BIN || "python";
function resolveBackendPython() {
const fromEnv = (process.env.PYTHON_BIN || "").trim();
if (fromEnv) return fromEnv;
const isWin = process.platform === "win32";
const candidates = isWin
? [
path.join(backendDir, ".venv", "Scripts", "python.exe"),
path.join(backendDir, ".venv312", "Scripts", "python.exe"),
path.join(backendDir, "venv", "Scripts", "python.exe"),
]
: [
path.join(backendDir, ".venv-linux", "bin", "python"),
path.join(backendDir, ".venv", "bin", "python"),
path.join(backendDir, "venv", "bin", "python"),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
return "python";
}
const pythonBin = resolveBackendPython();
const dataDir = process.env.MNOTE_DATA_DIR || path.join(rootDir, "data-desktop-test");
const backendCmd =
+28 -2
View File
@@ -29,8 +29,34 @@ const backendDir = path.join(rootDir, "wolai-backend");
const ingestDir = path.join(rootDir, "services", "ingest_service");
const ragGatewayDir = path.join(rootDir, "services", "rag_gateway");
const pythonBin = process.env.PYTHON_BIN || "python";
const celeryBin = process.env.CELERY_BIN || "celery";
function resolveBackendExecutable(envName, fallbackName) {
const fromEnv = (process.env[envName] || "").trim();
if (fromEnv) return fromEnv;
const isWin = process.platform === "win32";
const candidates = isWin
? [
path.join(backendDir, ".venv", "Scripts", `${fallbackName}.exe`),
path.join(backendDir, ".venv312", "Scripts", `${fallbackName}.exe`),
path.join(backendDir, "venv", "Scripts", `${fallbackName}.exe`),
]
: [
path.join(backendDir, ".venv-linux", "bin", fallbackName),
path.join(backendDir, ".venv", "bin", fallbackName),
path.join(backendDir, "venv", "bin", fallbackName),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
return fallbackName;
}
const pythonBin = resolveBackendExecutable("PYTHON_BIN", "python");
const celeryBin = resolveBackendExecutable("CELERY_BIN", "celery");
const skipCelery =
(process.env.SKIP_CELERY || "").toLowerCase() === "1" ||
(process.env.SKIP_CELERY || "").toLowerCase() === "true";
-4
View File
@@ -1,10 +1,6 @@
import { Password } from "@convex-dev/auth/providers/Password";
import { convexAuth } from "@convex-dev/auth/server";
if (!process.env.JWT_PRIVATE_KEY) {
process.env.JWT_PRIVATE_KEY = "-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCPcrWDwjh74nu1 gfYo9ywVIRMedfPwbNiDjHnovzHAIeToc1+Lw3ju/io9OME3KLWzIibUHX39LilK sSQKigJ3+i7J13IquJD++ozY9C8Dp/P34txVY+ECSQRdIBbETLJxYBWXOx6Ysw/d /CUuDOdEw2IhNvN97sTh5jUZe6f/H6mfCpJ3X5SZrqrIUTTL6r4Lj9ZS3PUX5ivU 6sd2rXzpDsUQgrNgmvGN3w9nAHudxAUv/zOGa4802Z+z14LCBIX6v6PIK79YQ4s9 /5hH73MesA6vgzWShJcyVy3guyde+duhpECo1q3d6dIZeqrazQwqAW3vcpmEFJdX jkpociDfAgMBAAECggEAAYtRE+mH1SGThlkvTrKWeWXBQG8xoJFzZTsiZtSEExbq UWxIh4cjqqL2znDpd5ALILIJ6/ejTxHrpN+yTSC+NQ9u6IJWusoA2ZXV5VH/nZD1 yeHZ0FuCZRVnJB9/zz4qH5lSsi2TPz6SOagIuG2wIafeyw+94EmtOedSBAO2Q8NN 3jHoINBCyRu6hU3ml0h7daoIhUw9ONI7MZUlYvuV7Ti3yf+czzpqwQx3gZA39sa8 qUbJPFk6ts+CVjqSAdYVSfU3TC1Us1usOM3+04mACp3vc6ZUKxnB4Dsk/sSHj71n EcZ3fOR7EgjmXf6wBiq24T2+0UzoHW2yDaiAowr6iQKBgQDDs2eJTopFejt47EMm zY//e8fyUDKx07CWwUZKP78IMTZcp8BwHmKTSY+VbQLvVYnNxADkckYapokjU3a7 GMOXxvKVLqfgU/oUBIkRFDRo+nasFCd5cPpnYjQ6lGalBkDghVlyvq7kKF/MPtIK dLqsV3tZDXoHX2mjTga94ygTZwKBgQC7pa+eZdWiz275mpYql9Gs6N3G3HhcpRxb oWYWekCtZQ9gtecpvA9e0tW3ShoF19ksWYGpM4vxAOXul5Ei9IPVVNHc7RvGF5CO 0Zryx3qaVk7E6XQc3BQVVAenT2fNiv3+fkCMeAvcZBZOGiPPziCCT3wU3bd3tsn8 EsMiAPvTyQKBgEs0iWhBr29NrsckfBXQTzMN/WOIIEMoJ6d3dKyZ3K6oQszOhmxP sPALB8uTjdotk/xoAzPHGlupfe/+ZhU2Sgvsn1JnEIprmyHQMGBI1G83OR2dzSGl IgVSvuF4IA3w3kOp2xr2Xj09qrrRtWPhQc9y+urY+/kTWIQyOvMD9WWnAoGADdUN 2BBLqj++P3oMvcEJPMTBrGoOGU42g+6m1ttWLzH26zsdei8ZtvS1ulglCO87XBCR BUb+dtqJGIhls3zwxuYEvlNgK78K8ewzjtfzirL4BX3sCECU3mmeUtAAp98qD/uA iJpEzY83MbStlSDtto1jaSpa3uFDjGhZqAUIizkCgYABf/HSMwWsnvRgo0JMmDru 3L/xScJiFVdFV9vNiSckjUYhx7lgFqEjsDEN1mZiRIa2UhLOqpo5S/cIXfMkeEPh 9Rusf9STwNwOOyhFRrHCxqhTH6Ivnj6oZSM/UrS8GvCVMPWs1z8k45FWKI/IRXQq AT5PRKihrWbg63/rXaSCxw== -----END PRIVATE KEY-----";
}
/**
* Convex Auth 配置
*
+6
View File
@@ -2,6 +2,7 @@ import { redirect } from "next/navigation";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
import { convexAuthNextjsToken } from "@convex-dev/auth/nextjs/server";
function makeId(): string {
return typeof crypto.randomUUID === "function"
@@ -14,6 +15,11 @@ export default async function Home() {
redirect("/auth");
}
const token = await convexAuthNextjsToken();
if (!token) {
redirect("/auth");
}
const { auth, client } = await getAuthedConvexClient();
const { activeWorkspaceId } = await client.mutation(api.workspaces.ensureDefaultWorkspace, {