refactor(session): auto-create named browser sessions
- create and register named browser sessions on open/exec when missing - align scenario step DTO/entity/service by removing sessionName usage - update MCP scenario-step schemas to use optional title fields
This commit is contained in:
@@ -9,8 +9,10 @@ import type { BrowserContext } from "playwright";
|
||||
import { Readability } from "@mozilla/readability";
|
||||
import { JSDOM } from "jsdom";
|
||||
import { SessionContextService } from "../session/session-context.service";
|
||||
import { SessionService } from "../session/session.service";
|
||||
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
||||
import type { ExecResult } from "../code-executor/code-executor.service";
|
||||
import type { Cookie } from "playwright";
|
||||
|
||||
export type { ExecResult } from "../code-executor/code-executor.service";
|
||||
|
||||
@@ -26,9 +28,54 @@ export class BrowserService {
|
||||
|
||||
constructor(
|
||||
private readonly sessionContextService: SessionContextService,
|
||||
private readonly sessionService: SessionService,
|
||||
private readonly codeExecutor: CodeExecutorService,
|
||||
) {}
|
||||
|
||||
private parseCookies(raw: string | null | undefined): Cookie[] {
|
||||
if (!raw) return [];
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
return Array.isArray(parsed) ? (parsed as Cookie[]) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private async getOrCreateNamedHandle(sessionName: string) {
|
||||
try {
|
||||
return await this.sessionContextService.getHandle(sessionName);
|
||||
} catch {
|
||||
this.logger.log(
|
||||
`[${sessionName}] creating session context automatically`,
|
||||
);
|
||||
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH,
|
||||
});
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
||||
const existing = await this.sessionService.findBySessionName(sessionName);
|
||||
const cookies = this.parseCookies(existing?.cookies);
|
||||
if (cookies.length > 0) {
|
||||
await context.addCookies(cookies);
|
||||
}
|
||||
|
||||
this.sessionContextService.register(sessionName, browser, context, page);
|
||||
|
||||
await this.sessionService.upsert(
|
||||
sessionName,
|
||||
existing?.token ?? `auto:${sessionName}`,
|
||||
await context.cookies(),
|
||||
{},
|
||||
);
|
||||
|
||||
return { browser, context, page };
|
||||
}
|
||||
}
|
||||
|
||||
private rethrow(err: unknown, label: string, operation: string): never {
|
||||
if (err instanceof HttpException) {
|
||||
throw err;
|
||||
@@ -90,8 +137,7 @@ export class BrowserService {
|
||||
this.logger.log(`[${label}] open: ${url}`);
|
||||
|
||||
if (sessionName) {
|
||||
const { context } =
|
||||
await this.sessionContextService.getHandle(sessionName);
|
||||
const { context } = await this.getOrCreateNamedHandle(sessionName);
|
||||
try {
|
||||
return await this.extractContent(context, url, readerMode, selector);
|
||||
} catch (err) {
|
||||
@@ -123,7 +169,7 @@ export class BrowserService {
|
||||
|
||||
if (sessionName) {
|
||||
const { page, context } =
|
||||
await this.sessionContextService.getHandle(sessionName);
|
||||
await this.getOrCreateNamedHandle(sessionName);
|
||||
this.logger.log(`[${label}] exec: using persistent context`);
|
||||
try {
|
||||
if (url) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IsOptional, IsString, IsUrl } from "class-validator";
|
||||
export class ExecDto {
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
"Session name previously created by POST /login. If omitted, executes without a stored session.",
|
||||
"Optional named browser session. If it does not exist yet, it is created automatically.",
|
||||
example: "test-session-1",
|
||||
})
|
||||
@IsOptional()
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IsBoolean, IsOptional, IsString, IsUrl } from "class-validator";
|
||||
export class OpenDto {
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
"Session name previously created by POST /login. If omitted, opens the URL without a stored session.",
|
||||
"Optional named browser session. If it does not exist yet, it is created automatically.",
|
||||
example: "test-session-1",
|
||||
})
|
||||
@IsOptional()
|
||||
|
||||
Reference in New Issue
Block a user