From bc0865ede71e45536ed74c7468f08c23c4699da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 28 May 2026 20:28:07 -0400 Subject: [PATCH] Extract the social login window into its own module social-service.ts exceeded 500 lines and mixed the Electron browser-login window concern with the social domain logic. Move openSocialLoginWindow and its navigation policy into social-login-window.ts and re-export the public types. Behaviour unchanged; unit and e2e suites remain green. --- .../src/main/services/social-login-window.ts | 47 +++++++++++++++++ .../src/main/services/social-service.ts | 50 ++----------------- 2 files changed, 50 insertions(+), 47 deletions(-) create mode 100644 AgentCompany-New/src/main/services/social-login-window.ts diff --git a/AgentCompany-New/src/main/services/social-login-window.ts b/AgentCompany-New/src/main/services/social-login-window.ts new file mode 100644 index 00000000..23e912fc --- /dev/null +++ b/AgentCompany-New/src/main/services/social-login-window.ts @@ -0,0 +1,47 @@ +import { BrowserWindow, session } from "electron"; + +export interface SocialLoginWindowInput { + title: string; + loginUrl: string; + partitionKey: string; + allowedHosts: string[]; + sandboxEnabled: boolean; +} + +export type SocialLoginWindowLauncher = (input: SocialLoginWindowInput) => void; + +export function openSocialLoginWindow(input: SocialLoginWindowInput): void { + const socialSession = session.fromPartition(input.partitionKey); + socialSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false)); + socialSession.setPermissionCheckHandler(() => false); + + const win = new BrowserWindow({ + width: 1280, + height: 860, + title: input.title, + webPreferences: { + session: socialSession, + nodeIntegration: false, + contextIsolation: true, + sandbox: input.sandboxEnabled, + }, + }); + attachNavigationPolicy(win, input.allowedHosts); + win.loadURL(input.loginUrl); +} + +function attachNavigationPolicy(win: BrowserWindow, allowedHosts: string[]): void { + win.webContents.setWindowOpenHandler(() => ({ action: "deny" })); + const guard = (event: Electron.Event, url: string) => { + if (!isAllowedNavigation(allowedHosts, url)) event.preventDefault(); + }; + win.webContents.on("will-navigate", guard); + win.webContents.on("will-redirect", guard); +} + +function isAllowedNavigation(allowedHosts: string[], url: string): boolean { + const parsed = new URL(url); + if (parsed.protocol !== "https:") return false; + const host = parsed.hostname.toLowerCase(); + return allowedHosts.some((entry) => host === entry || host.endsWith(`.${entry}`)); +} diff --git a/AgentCompany-New/src/main/services/social-service.ts b/AgentCompany-New/src/main/services/social-service.ts index 3474cbb2..38e572f4 100644 --- a/AgentCompany-New/src/main/services/social-service.ts +++ b/AgentCompany-New/src/main/services/social-service.ts @@ -1,5 +1,4 @@ import type { DatabaseSync } from "node:sqlite"; -import { BrowserWindow, session } from "electron"; import type { CreateScheduledPostInput, CreateSocialAccountInput, @@ -11,6 +10,7 @@ import { createId } from "../../shared/ids"; import { ApprovalService } from "./approval-service"; import { AuditService } from "./audit-service"; import { SocialAuditService } from "./social-audit-service"; +import { openSocialLoginWindow, type SocialLoginWindowLauncher } from "./social-login-window"; import { SOCIAL_BROWSER_ACTION, SocialBrowserPolicyService, @@ -73,15 +73,7 @@ interface GrantScope { riskCeiling: RiskLevel; } -export interface SocialLoginWindowInput { - title: string; - loginUrl: string; - partitionKey: string; - allowedHosts: string[]; - sandboxEnabled: boolean; -} - -export type SocialLoginWindowLauncher = (input: SocialLoginWindowInput) => void; +export type { SocialLoginWindowInput, SocialLoginWindowLauncher } from "./social-login-window"; export class SocialService { private readonly audit: AuditService; @@ -488,40 +480,4 @@ function loginEvidenceMetadata(input: { actorType: "employee", actorId: input.actorId, }; -} - -function openSocialLoginWindow(input: SocialLoginWindowInput): void { - const socialSession = session.fromPartition(input.partitionKey); - socialSession.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false)); - socialSession.setPermissionCheckHandler(() => false); - - const win = new BrowserWindow({ - width: 1280, - height: 860, - title: input.title, - webPreferences: { - session: socialSession, - nodeIntegration: false, - contextIsolation: true, - sandbox: input.sandboxEnabled, - }, - }); - attachNavigationPolicy(win, input.allowedHosts); - win.loadURL(input.loginUrl); -} - -function attachNavigationPolicy(win: BrowserWindow, allowedHosts: string[]): void { - win.webContents.setWindowOpenHandler(() => ({ action: "deny" })); - const guard = (event: Electron.Event, url: string) => { - if (!isAllowedNavigation(allowedHosts, url)) event.preventDefault(); - }; - win.webContents.on("will-navigate", guard); - win.webContents.on("will-redirect", guard); -} - -function isAllowedNavigation(allowedHosts: string[], url: string): boolean { - const parsed = new URL(url); - if (parsed.protocol !== "https:") return false; - const host = parsed.hostname.toLowerCase(); - return allowedHosts.some((entry) => host === entry || host.endsWith(`.${entry}`)); -} +} \ No newline at end of file