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 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-02-28 22:32:22 +08:00
parent 550309c2e7
commit 231628453f
2 changed files with 116 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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<Browser> {
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<LoginResult> {
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<void> {
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<Page> {
if (!this.context) {
throw new Error('Browser context not initialized');
}
return await this.context.newPage();
}
}