支持单位授权与客户端限制
This commit is contained in:
@@ -20,7 +20,7 @@ import {
|
||||
getUpdateState,
|
||||
openReleasesPage,
|
||||
} from "../services/autoUpdater";
|
||||
import { getDeviceId } from "../services/system/deviceId";
|
||||
import { getDeviceId, getPrimaryLocalIp } from "../services/system/deviceId";
|
||||
import { getTrayManager } from "../window/trayManager";
|
||||
import { getAutoLaunchManager } from "../window/autoLaunchManager";
|
||||
import { APP_DISPLAY_NAME } from "@shared/constants";
|
||||
@@ -284,6 +284,10 @@ export function registerAppHandlers(ctx: HandlerContext): void {
|
||||
return getDeviceId();
|
||||
});
|
||||
|
||||
ipcMain.handle("app:getPrimaryLocalIp", () => {
|
||||
return getPrimaryLocalIp();
|
||||
});
|
||||
|
||||
ipcMain.handle("app:checkUpdate", async () => {
|
||||
try {
|
||||
return await checkForUpdates();
|
||||
|
||||
@@ -314,7 +314,7 @@ describe("AcpEngine.prompt", () => {
|
||||
{ messageID: "rid-timeout-001" },
|
||||
);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(60_001);
|
||||
await vi.advanceTimersByTimeAsync(600_001);
|
||||
await expect(promptPromise).resolves.toBeDefined();
|
||||
|
||||
expect(onPromptEnd).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -28,6 +28,18 @@ export function getDeviceId(): string {
|
||||
return cachedDeviceId;
|
||||
}
|
||||
|
||||
export function getPrimaryLocalIp(): string | null {
|
||||
const interfaces = os.networkInterfaces();
|
||||
for (const entries of Object.values(interfaces)) {
|
||||
for (const entry of entries ?? []) {
|
||||
if (entry.family === "IPv4" && !entry.internal) {
|
||||
return entry.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function logSystemInfo(): void {
|
||||
// 固定字段尽量保持稳定输出,便于跨平台日志分析与检索。
|
||||
const baseInfo = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Main process system services
|
||||
export { getDeviceId } from './deviceId';
|
||||
export { getDeviceId, getPrimaryLocalIp } from './deviceId';
|
||||
export { getAppEnv, setMirrorConfig } from './dependencies';
|
||||
export {
|
||||
getAppDataDir,
|
||||
|
||||
@@ -503,6 +503,7 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
openReleasesPage: () => ipcRenderer.invoke("app:openReleasesPage"),
|
||||
getUpdateDebugInfo: () => ipcRenderer.invoke("app:getUpdateDebugInfo"),
|
||||
getDeviceId: () => ipcRenderer.invoke("app:getDeviceId"),
|
||||
getPrimaryLocalIp: () => ipcRenderer.invoke("app:getPrimaryLocalIp"),
|
||||
},
|
||||
|
||||
// Permissions (macOS)
|
||||
|
||||
@@ -192,6 +192,7 @@ export interface ClientRegisterParams {
|
||||
password: string;
|
||||
savedKey?: string;
|
||||
deviceId?: string;
|
||||
name?: string;
|
||||
sandboxConfigValue: SandboxValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ vi.stubGlobal("window", {
|
||||
electronAPI: {
|
||||
app: {
|
||||
getDeviceId: vi.fn(async () => "mock-device-id"),
|
||||
getPrimaryLocalIp: vi.fn(async () => "192.168.1.23"),
|
||||
},
|
||||
settings: {
|
||||
get: mockSettingsGet,
|
||||
@@ -166,6 +167,7 @@ describe("auth - savedKey 认证 (快捷登录)", () => {
|
||||
expect(params.username).toBe("");
|
||||
expect(params.password).toBe("");
|
||||
expect(params.savedKey).toBe(SAVED_KEY);
|
||||
expect(params.name).toBe("ip-192.168.1.23");
|
||||
});
|
||||
|
||||
it("username/password 为 null + 有 savedKey → 应正常同步", async () => {
|
||||
@@ -313,6 +315,9 @@ describe("auth - savedKey 认证 (快捷登录)", () => {
|
||||
expect(store["auth.password"]).toBeUndefined();
|
||||
// savedKey 应保存
|
||||
expect(store["auth.saved_key"]).toBe(CONFIG_KEY_FROM_SERVER);
|
||||
|
||||
const [params] = mockRegisterClient.mock.calls[0];
|
||||
expect(params.name).toBe("ip-192.168.1.23");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -203,6 +203,17 @@ async function getLocalSandboxValue(): Promise<SandboxValue> {
|
||||
};
|
||||
}
|
||||
|
||||
async function getDefaultSandboxName(): Promise<string | undefined> {
|
||||
try {
|
||||
const ip = await window.electronAPI?.app.getPrimaryLocalIp?.();
|
||||
const normalized = typeof ip === "string" ? ip.trim() : "";
|
||||
return normalized ? `ip-${normalized}` : undefined;
|
||||
} catch (error) {
|
||||
logger.warn("Failed to resolve default sandbox name", "Auth", error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 错误处理 ===
|
||||
/**
|
||||
* 获取友好的错误信息
|
||||
@@ -288,11 +299,13 @@ export async function loginAndRegister(
|
||||
|
||||
// 构建注册参数
|
||||
const deviceId = await window.electronAPI?.app.getDeviceId();
|
||||
const defaultSandboxName = await getDefaultSandboxName();
|
||||
const params: ClientRegisterParams = {
|
||||
username,
|
||||
password,
|
||||
savedKey: savedKey || undefined,
|
||||
deviceId: deviceId || undefined,
|
||||
name: defaultSandboxName,
|
||||
sandboxConfigValue: await getLocalSandboxValue(),
|
||||
};
|
||||
|
||||
@@ -471,11 +484,13 @@ export async function reRegisterClient(): Promise<ClientRegisterResponse | null>
|
||||
logger.info("Re-registering client (using savedKey)...", "Auth");
|
||||
|
||||
const deviceId = await window.electronAPI?.app.getDeviceId();
|
||||
const defaultSandboxName = await getDefaultSandboxName();
|
||||
const params: ClientRegisterParams = {
|
||||
username: username || "",
|
||||
password: "", // 密码不持久化,使用 savedKey 认证
|
||||
savedKey,
|
||||
deviceId: deviceId || undefined,
|
||||
name: defaultSandboxName,
|
||||
sandboxConfigValue: await getLocalSandboxValue(),
|
||||
};
|
||||
|
||||
@@ -575,11 +590,13 @@ export async function syncConfigToServer(options?: {
|
||||
}
|
||||
|
||||
const deviceId = await window.electronAPI?.app.getDeviceId();
|
||||
const defaultSandboxName = await getDefaultSandboxName();
|
||||
const params: ClientRegisterParams = {
|
||||
username: username || "",
|
||||
password: "", // 密码不持久化,使用 savedKey 认证
|
||||
savedKey,
|
||||
deviceId: deviceId || undefined,
|
||||
name: defaultSandboxName,
|
||||
sandboxConfigValue: await getLocalSandboxValue(),
|
||||
};
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ export const PROCESS_KILL_ESCALATION_TIMEOUT = 5000;
|
||||
export const ACP_ABORT_TIMEOUT = 15_000;
|
||||
|
||||
/** ACP prompt 等待响应超时 (ms) */
|
||||
export const ACP_PROMPT_TIMEOUT = 60_000;
|
||||
export const ACP_PROMPT_TIMEOUT = 600_000;
|
||||
|
||||
/**
|
||||
* 用户主动取消会话时,挂在 `Error` 上的 `code`(与 `message` 语言无关)。
|
||||
|
||||
@@ -515,6 +515,7 @@ export interface AppAPI {
|
||||
error?: string;
|
||||
}>;
|
||||
getDeviceId: () => Promise<string>;
|
||||
getPrimaryLocalIp: () => Promise<string | null>;
|
||||
}
|
||||
|
||||
export type PermissionStatus = "granted" | "denied" | "unknown";
|
||||
|
||||
Reference in New Issue
Block a user