From 231628453f6ad0673bd6fce84e3ac5cb12338a05 Mon Sep 17 00:00:00 2001 From: Misaka Date: Sat, 28 Feb 2026 22:32:22 +0800 Subject: [PATCH] feat: implement Playwright service for browser automation - Add playwright types for Credentials, Session, and LoginResult - Implement PlaywrightService with launchBrowser, login, closeBrowser, and newPage methods - Support browser configuration (headless mode, slowMo, timeout) - Integrate with LoggerService for comprehensive logging - Handle HTTPS errors via ERP configuration Co-Authored-By: Claude Sonnet 4.5 --- src/main/models/playwright.types.ts | 17 +++++ src/main/services/playwright.service.ts | 99 +++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/main/models/playwright.types.ts create mode 100644 src/main/services/playwright.service.ts diff --git a/src/main/models/playwright.types.ts b/src/main/models/playwright.types.ts new file mode 100644 index 0000000..09fc757 --- /dev/null +++ b/src/main/models/playwright.types.ts @@ -0,0 +1,17 @@ +import { Browser, Page, Frame } from 'playwright'; + +export interface Credentials { + username: string; + password: string; +} + +export interface Session { + browser: Browser; + context: any; + page: Page; + mainFrame: Frame; +} + +export interface LoginResult extends Session { + success: boolean; +} diff --git a/src/main/services/playwright.service.ts b/src/main/services/playwright.service.ts new file mode 100644 index 0000000..a7c2ded --- /dev/null +++ b/src/main/services/playwright.service.ts @@ -0,0 +1,99 @@ +import { chromium, Browser, BrowserContext, Page, Frame } from 'playwright'; +import { Credentials, Session, LoginResult } from '../models/playwright.types'; +import { BrowserConfig, ERPConfig } from '../models/config.types'; +import { LoggerService } from './logger.service'; + +export class PlaywrightService { + private browser?: Browser; + private context?: BrowserContext; + + constructor( + private browserConfig: BrowserConfig, + private erpConfig: ERPConfig + ) {} + + async launchBrowser(): Promise { + try { + LoggerService.info('Launching Chromium browser...'); + + this.browser = await chromium.launch({ + headless: this.browserConfig.headless, + slowMo: this.browserConfig.slowMo, + }); + + this.context = await this.browser.newContext({ + ignoreHTTPSErrors: this.erpConfig.ignoreHttpsErrors, + }); + + LoggerService.info('Browser launched successfully'); + return this.browser; + } catch (error) { + LoggerService.error('Failed to launch browser', error); + throw error; + } + } + + async login(credentials: Credentials): Promise { + if (!this.browser || !this.context) { + throw new Error('Browser not launched'); + } + + try { + LoggerService.info('Attempting ERP login...'); + + const page = await this.context.newPage(); + await page.goto(this.erpConfig.baseUrl, { + timeout: this.browserConfig.timeout, + }); + + // Wait for login form + await page.waitForSelector('input[name="username"]', { + timeout: this.browserConfig.timeout, + }); + + // Fill credentials + await page.fill('input[name="username"]', credentials.username); + await page.fill('input[name="password"]', credentials.password); + + // Click login button + await page.click('button[type="submit"]'); + + // Wait for navigation + await page.waitForLoadState('domcontentloaded'); + + // Get main frame + const mainFrame = page.mainFrame(); + + LoggerService.info('Login successful'); + + return { + success: true, + browser: this.browser, + context: this.context, + page, + mainFrame, + }; + } catch (error) { + LoggerService.error('Login failed', error); + throw error; + } + } + + async closeBrowser(): Promise { + if (this.context) { + await this.context.close(); + LoggerService.info('Browser context closed'); + } + if (this.browser) { + await this.browser.close(); + LoggerService.info('Browser closed'); + } + } + + async newPage(): Promise { + if (!this.context) { + throw new Error('Browser context not initialized'); + } + return await this.context.newPage(); + } +}