refactor: implement precise login result detection in ERP auth service
Add waitForLoginResult() method with Promise.race to detect three login outcomes: - Success: detects .nc-workbench-icon element - Failure: detects '名称或密码错误' error text - Force login: clicks confirm button and re-detects Improves login reliability by properly handling all authentication scenarios. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { chromium, type BrowserContext, type Page } from 'playwright'
|
import { chromium } from 'playwright'
|
||||||
import type { ErpConfig, ErpSession } from '../../types/erp.types'
|
import type { ErpConfig, ErpSession } from '../../types/erp.types'
|
||||||
import { createLogger } from '../logger'
|
import { createLogger } from '../logger'
|
||||||
|
|
||||||
@@ -89,29 +89,11 @@ export class ErpAuthService {
|
|||||||
throw new Error(`Failed to click login button: ${e}`)
|
throw new Error(`Failed to click login button: ${e}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for navigation after login
|
await page.waitForLoadState('domcontentloaded', { timeout: 10000 }).catch(() => {
|
||||||
// The login will redirect to the main page which has a different structure
|
|
||||||
try {
|
|
||||||
await page.waitForLoadState('domcontentloaded', { timeout: 10000 })
|
|
||||||
} catch (e) {
|
|
||||||
log.warn('Page load state check timed out, continuing')
|
log.warn('Page load state check timed out, continuing')
|
||||||
}
|
})
|
||||||
|
|
||||||
// Handle force login confirmation dialog if present (Python: get_by_role("button", name="确定"))
|
await this.waitForLoginResult(mainFrame as unknown as import('playwright').Frame)
|
||||||
try {
|
|
||||||
const confirmBtn = mainFrame.getByRole('button', { name: '确定' })
|
|
||||||
const count = await confirmBtn.count()
|
|
||||||
if (count > 0) {
|
|
||||||
log.info('Force login detected, clicking confirm button')
|
|
||||||
await confirmBtn.first().click()
|
|
||||||
await page.waitForTimeout(2000)
|
|
||||||
} else {
|
|
||||||
log.debug('Normal login, no confirmation dialog')
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// No force login dialog, continue
|
|
||||||
log.debug('Normal login, no confirmation dialog')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create session with mainFrame (Python returns main_frame as part of login result)
|
// Create session with mainFrame (Python returns main_frame as part of login result)
|
||||||
this.session = {
|
this.session = {
|
||||||
@@ -126,6 +108,45 @@ export class ErpAuthService {
|
|||||||
return this.session
|
return this.session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for login result: success, failure, or force login confirmation
|
||||||
|
*/
|
||||||
|
private async waitForLoginResult(mainFrame: import('playwright').Frame): Promise<void> {
|
||||||
|
const successLocator = mainFrame.locator('.nc-workbench-icon')
|
||||||
|
const errorLocator = mainFrame.getByText('名称或密码错误')
|
||||||
|
const forceLoginButton = mainFrame.getByRole('button', { name: '确定' })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.race([
|
||||||
|
successLocator.waitFor({ state: 'visible', timeout: 15000 }),
|
||||||
|
errorLocator.waitFor({ state: 'visible', timeout: 15000 }),
|
||||||
|
forceLoginButton.waitFor({ state: 'visible', timeout: 5000 }).then(async () => {
|
||||||
|
log.info('Force login dialog detected, clicking confirm')
|
||||||
|
await forceLoginButton.click()
|
||||||
|
await this.waitForLoginResult(mainFrame)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
const hasError = await errorLocator.isVisible()
|
||||||
|
if (hasError) {
|
||||||
|
throw new Error('ERP 登录失败:名称或密码错误')
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('Login successful')
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && error.message.includes('名称或密码错误')) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasError = await errorLocator.isVisible().catch(() => false)
|
||||||
|
if (hasError) {
|
||||||
|
throw new Error('ERP 登录失败:名称或密码错误')
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info('Login successful')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close browser and cleanup session
|
* Close browser and cleanup session
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user