feat(browser,mcp): resolve environment and credentials from DB in exec
- POST /exec now accepts environmentId (UUID) and credentials (alias → UUID) instead of raw payloads; resolves entities via EnvironmentService and CredentialService before passing data to browserService.exec - exec_code MCP tool updated with same schema: environmentId + credentials map - CredentialModule imported into BrowserModule and McpModule - docs(scenario): rewrite script runtime section for single context argument - docs(mcp): update exec_code description to reflect new parameter shapes - style: reorder imports across server source (formatter)
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ The server currently registers the following tools.
|
|||||||
| Tool | Description |
|
| Tool | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `open_url` | Open URL with optional `sessionName`, return page/title/content |
|
| `open_url` | Open URL with optional `sessionName`, return page/title/content |
|
||||||
| `exec_code` | Execute Playwright JS with `page` and `context` in scope |
|
| `exec_code` | Execute Playwright JS — script receives a single `context` argument (see `scenario.md`). Optional `environmentId` (UUID) resolves environment from DB for `context.getEnv()`. Optional `credentials` (`{ alias: credentialUUID }`) resolves credentials from DB for `context.getCredential(alias)`. |
|
||||||
|
|
||||||
## Scenarios
|
## Scenarios
|
||||||
|
|
||||||
|
|||||||
+19
-15
@@ -17,24 +17,28 @@ Current scheduler behavior is exec-centric:
|
|||||||
|
|
||||||
## Script runtime
|
## Script runtime
|
||||||
|
|
||||||
`execCode` and `validateCode` execute as async JavaScript with:
|
`execCode` and `validateCode` execute as async JavaScript with a single `context` argument:
|
||||||
|
|
||||||
- `page`: Playwright `Page`
|
```js
|
||||||
- `context`: Playwright `BrowserContext`
|
async (context) => {
|
||||||
- `helpers`: utility object
|
// your code here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Available in scope:
|
||||||
|
|
||||||
|
- `context.page`: Playwright `Page`
|
||||||
|
- `context.browser`: Playwright `BrowserContext`
|
||||||
|
- `context.env`: shallow copy of environment key/value map
|
||||||
|
- `context.getEnv(key)`: required environment value lookup (throws if missing)
|
||||||
|
- `context.getCredential(alias)`: credential payload assigned to the scenario alias
|
||||||
|
- `context.getStepOutput(order)`: prior step output by absolute order (`1`, `2`, ...) or relative (`-1` = previous step)
|
||||||
|
- `context.dumpDom(selector?)`: simplified DOM snapshot
|
||||||
|
- `context.log(...args)`, `context.warn(...args)`, `context.error(...args)`: structured step logs
|
||||||
|
- `context.runSnippet(name, ...args)`: execute stored snippet code with the same context
|
||||||
- `console`: proxied to run logs (`log`, `warn`, `error`, etc.)
|
- `console`: proxied to run logs (`log`, `warn`, `error`, etc.)
|
||||||
|
|
||||||
`validateCode` additionally receives `result`, which is the value returned by `execCode`.
|
`validateCode` additionally receives `result` in scope, which is the value returned by `execCode`.
|
||||||
|
|
||||||
## Available helpers
|
|
||||||
|
|
||||||
- `helpers.dumpDom(selector?)`: simplified DOM snapshot
|
|
||||||
- `helpers.log(...args)`, `helpers.warn(...args)`, `helpers.error(...args)`: structured step logs
|
|
||||||
- `helpers.getStepOutput(order)`: prior step output by absolute order (`0`, `1`, ...) or relative (`-1` previous step)
|
|
||||||
- `helpers.getCredential(alias)`: credential payload assigned to the scenario alias
|
|
||||||
- `helpers.env`: shallow copy of environment URL map
|
|
||||||
- `helpers.getEnv(key)`: required environment value lookup (throws if missing)
|
|
||||||
- `helpers.runSnippet(name, ...args)`: execute stored snippet code in the same page/context/helpers scope
|
|
||||||
|
|
||||||
## Validation contract
|
## Validation contract
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -1,23 +1,23 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
import { AppConfig, validateAppConfig } from "./config/app.config";
|
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
||||||
import { ScheduleModule } from "@nestjs/schedule";
|
import { ScheduleModule } from "@nestjs/schedule";
|
||||||
import { HealthModule } from "./health/health.module";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { BrowserModule } from "./browser/browser.module";
|
import { BrowserModule } from "./browser/browser.module";
|
||||||
import { SessionEntity } from "./session/session.entity";
|
import { AppConfig, validateAppConfig } from "./config/app.config";
|
||||||
import { EnvironmentEntity } from "./environment/environment.entity";
|
|
||||||
import { EnvironmentModule } from "./environment/environment.module";
|
|
||||||
import { CredentialEntity } from "./credential/credential.entity";
|
import { CredentialEntity } from "./credential/credential.entity";
|
||||||
import { CredentialModule } from "./credential/credential.module";
|
import { CredentialModule } from "./credential/credential.module";
|
||||||
|
import { EnvironmentEntity } from "./environment/environment.entity";
|
||||||
|
import { EnvironmentModule } from "./environment/environment.module";
|
||||||
|
import { HealthModule } from "./health/health.module";
|
||||||
import { McpModule } from "./mcp/mcp.module";
|
import { McpModule } from "./mcp/mcp.module";
|
||||||
import { ScenarioEntity } from "./scenario/scenario.entity";
|
|
||||||
import { ScenarioStepEntity } from "./scenario/scenario-step.entity";
|
|
||||||
import { ScenarioRunEntity } from "./scenario/scenario-run.entity";
|
|
||||||
import { ScenarioRunStepEntity } from "./scenario/scenario-run-step.entity";
|
|
||||||
import { ScenarioRunLogEntity } from "./scenario/scenario-run-log.entity";
|
|
||||||
import { ScenarioCredentialEntity } from "./scenario/scenario-credential.entity";
|
import { ScenarioCredentialEntity } from "./scenario/scenario-credential.entity";
|
||||||
|
import { ScenarioRunLogEntity } from "./scenario/scenario-run-log.entity";
|
||||||
|
import { ScenarioRunStepEntity } from "./scenario/scenario-run-step.entity";
|
||||||
|
import { ScenarioRunEntity } from "./scenario/scenario-run.entity";
|
||||||
|
import { ScenarioStepEntity } from "./scenario/scenario-step.entity";
|
||||||
|
import { ScenarioEntity } from "./scenario/scenario.entity";
|
||||||
import { ScenarioModule } from "./scenario/scenario.module";
|
import { ScenarioModule } from "./scenario/scenario.module";
|
||||||
|
import { SessionEntity } from "./session/session.entity";
|
||||||
import { SnippetEntity } from "./snippet/snippet.entity";
|
import { SnippetEntity } from "./snippet/snippet.entity";
|
||||||
import { SnippetModule } from "./snippet/snippet.module";
|
import { SnippetModule } from "./snippet/snippet.module";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Body, Controller, Post } from "@nestjs/common";
|
import { Body, Controller, Post } from "@nestjs/common";
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
import { BrowserService, ExecResult, OpenResult } from "./browser.service";
|
|
||||||
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
||||||
import { OpenDto } from "./dto/open.dto";
|
import { CredentialService } from "../credential/credential.service";
|
||||||
|
import { EnvironmentService } from "../environment/environment.service";
|
||||||
|
import { BrowserService, ExecResult, OpenResult } from "./browser.service";
|
||||||
import { ExecDto } from "./dto/exec.dto";
|
import { ExecDto } from "./dto/exec.dto";
|
||||||
|
import { OpenDto } from "./dto/open.dto";
|
||||||
|
|
||||||
@ApiTags("browser")
|
@ApiTags("browser")
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -11,6 +13,8 @@ export class BrowserController {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly browserService: BrowserService,
|
private readonly browserService: BrowserService,
|
||||||
private readonly codeExecutor: CodeExecutorService,
|
private readonly codeExecutor: CodeExecutorService,
|
||||||
|
private readonly environmentService: EnvironmentService,
|
||||||
|
private readonly credentialService: CredentialService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Post("open")
|
@Post("open")
|
||||||
@@ -54,8 +58,26 @@ export class BrowserController {
|
|||||||
@ApiResponse({ status: 400, description: "Invalid input" })
|
@ApiResponse({ status: 400, description: "Invalid input" })
|
||||||
@ApiResponse({ status: 404, description: "Session not found" })
|
@ApiResponse({ status: 404, description: "Session not found" })
|
||||||
@ApiResponse({ status: 500, description: "Execution failed" })
|
@ApiResponse({ status: 500, description: "Execution failed" })
|
||||||
exec(@Body() dto: ExecDto): Promise<ExecResult> {
|
async exec(@Body() dto: ExecDto): Promise<ExecResult> {
|
||||||
this.codeExecutor.validate(dto.code);
|
this.codeExecutor.validate(dto.code);
|
||||||
return this.browserService.exec(dto.sessionName, dto.code, dto.url, dto.environment, dto.credentials);
|
|
||||||
|
// Resolve environment from DB
|
||||||
|
let environment: Record<string, string | undefined> | undefined;
|
||||||
|
if (dto.environmentId) {
|
||||||
|
const env = await this.environmentService.findOne(dto.environmentId);
|
||||||
|
environment = env.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve credentials from DB (alias → payload)
|
||||||
|
let credentials: Record<string, unknown> | undefined;
|
||||||
|
if (dto.credentials && Object.keys(dto.credentials).length > 0) {
|
||||||
|
credentials = {};
|
||||||
|
for (const [alias, credId] of Object.entries(dto.credentials)) {
|
||||||
|
const cred = await this.credentialService.findOne(credId);
|
||||||
|
credentials[alias] = cred.data ? JSON.parse(cred.data) : {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.browserService.exec(dto.sessionName, dto.code, dto.url, environment, credentials);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
import { CodeExecutorModule } from "../code-executor/code-executor.module";
|
||||||
|
import { CredentialModule } from "../credential/credential.module";
|
||||||
|
import { EnvironmentModule } from "../environment/environment.module";
|
||||||
|
import { SessionModule } from "../session/session.module";
|
||||||
|
import { SnippetModule } from "../snippet/snippet.module";
|
||||||
import { BrowserController } from "./browser.controller";
|
import { BrowserController } from "./browser.controller";
|
||||||
import { BrowserService } from "./browser.service";
|
import { BrowserService } from "./browser.service";
|
||||||
import { SessionModule } from "../session/session.module";
|
|
||||||
import { CodeExecutorModule } from "../code-executor/code-executor.module";
|
|
||||||
import { SnippetModule } from "../snippet/snippet.module";
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [SessionModule, CodeExecutorModule, SnippetModule],
|
imports: [SessionModule, CodeExecutorModule, SnippetModule, EnvironmentModule, CredentialModule],
|
||||||
controllers: [BrowserController],
|
controllers: [BrowserController],
|
||||||
providers: [BrowserService],
|
providers: [BrowserService],
|
||||||
exports: [BrowserService],
|
exports: [BrowserService],
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
|
import { Readability } from "@mozilla/readability";
|
||||||
import {
|
import {
|
||||||
Injectable,
|
|
||||||
HttpException,
|
HttpException,
|
||||||
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
|
||||||
import { chromium } from "playwright";
|
|
||||||
import type { BrowserContext } from "playwright";
|
|
||||||
import { Readability } from "@mozilla/readability";
|
|
||||||
import { JSDOM } from "jsdom";
|
import { JSDOM } from "jsdom";
|
||||||
|
import type { BrowserContext, Cookie } from "playwright";
|
||||||
|
import { chromium } from "playwright";
|
||||||
|
import type { ExecResult, ScriptLogger } from "../code-executor/code-executor.service";
|
||||||
|
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
||||||
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
|
import type { EnvironmentData } from "../environment/environment.entity";
|
||||||
import { SessionContextService } from "../session/session-context.service";
|
import { SessionContextService } from "../session/session-context.service";
|
||||||
import { SessionService } from "../session/session.service";
|
import { SessionService } from "../session/session.service";
|
||||||
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
|
||||||
import type { ExecResult, ScriptLogger } from "../code-executor/code-executor.service";
|
|
||||||
import { SnippetService } from "../snippet/snippet.service";
|
import { SnippetService } from "../snippet/snippet.service";
|
||||||
import type { Cookie } from "playwright";
|
|
||||||
import type { EnvironmentData } from "../environment/environment.entity";
|
|
||||||
|
|
||||||
export type { ExecResult } from "../code-executor/code-executor.service";
|
export type { ExecResult } from "../code-executor/code-executor.service";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsObject, IsOptional, IsString, IsUrl } from "class-validator";
|
import { IsObject, IsOptional, IsString, IsUUID, IsUrl } from "class-validator";
|
||||||
import type { EnvironmentData } from "../../environment/environment.entity";
|
|
||||||
|
|
||||||
export class ExecDto {
|
export class ExecDto {
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
@@ -23,27 +22,27 @@ export class ExecDto {
|
|||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description:
|
description:
|
||||||
"JavaScript code to execute. Receives `page` (Playwright Page) and `context` (BrowserContext) as arguments. May be async. Return value is serialised and returned.",
|
"Async JavaScript body. Use `context.page` for Playwright, `context.getEnv(key)`, `context.getCredential(alias)`, `context.runSnippet(name, ...args)`.",
|
||||||
example: "return await page.title();",
|
example: "return await context.page.title();",
|
||||||
})
|
})
|
||||||
@IsString()
|
@IsString()
|
||||||
code: string;
|
code: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description:
|
description:
|
||||||
"Key/value map of environment variables available via `helpers.env` and `helpers.getEnv()` in the script.",
|
"UUID of an existing environment entity. Its key/value data is available via `context.env` and `context.getEnv()` in the script.",
|
||||||
example: { BASE_URL: "https://example.com" },
|
example: "a6a1fca5-0f61-48ed-ae97-011dc7236387",
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsObject()
|
@IsUUID()
|
||||||
environment?: EnvironmentData;
|
environmentId?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description:
|
description:
|
||||||
"Key/value map of credentials available via `helpers.getCredential()` in the script.",
|
"Map of alias → credential UUID. Each credential is loaded from the DB and available via `context.getCredential(alias)` in the script.",
|
||||||
example: { admin: { username: "user", password: "pass" } },
|
example: { pkcs_key: "e3b0c442-98fc-11d8-9669-0800200c9a66" },
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsObject()
|
@IsObject()
|
||||||
credentials?: Record<string, unknown>;
|
credentials?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
|
||||||
import { parse } from "acorn";
|
|
||||||
import type { Page, BrowserContext } from "playwright";
|
|
||||||
import { expect as playwrightExpect } from "@playwright/test";
|
import { expect as playwrightExpect } from "@playwright/test";
|
||||||
import { dumpDom } from "./dom-helpers";
|
import { parse } from "acorn";
|
||||||
import type { DomNode } from "./dom-helpers";
|
import type { BrowserContext, Page } from "playwright";
|
||||||
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
import type { EnvironmentData } from "../environment/environment.entity";
|
import type { EnvironmentData } from "../environment/environment.entity";
|
||||||
|
import type { DomNode } from "./dom-helpers";
|
||||||
|
import { dumpDom } from "./dom-helpers";
|
||||||
|
|
||||||
export interface ExecResult {
|
export interface ExecResult {
|
||||||
result: unknown;
|
result: unknown;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Page, BrowserContext } from "playwright";
|
import type { BrowserContext, Page } from "playwright";
|
||||||
import type { ExecContext, ScriptLogger } from "./code-executor.service";
|
|
||||||
import type { EnvironmentData } from "../environment/environment.entity";
|
import type { EnvironmentData } from "../environment/environment.entity";
|
||||||
|
import type { ExecContext, ScriptLogger } from "./code-executor.service";
|
||||||
|
|
||||||
export class ExecContextBuilder {
|
export class ExecContextBuilder {
|
||||||
private readonly ctx: Partial<ExecContext> = {};
|
private readonly ctx: Partial<ExecContext> = {};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { IsInt, IsString, Min, Max } from "class-validator";
|
|
||||||
import { plainToInstance } from "class-transformer";
|
import { plainToInstance } from "class-transformer";
|
||||||
import { validateSync } from "class-validator";
|
import { IsInt, IsString, Max, Min, validateSync } from "class-validator";
|
||||||
|
|
||||||
import pkg from "../../package.json";
|
import pkg from "../../package.json";
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
import { CredentialService } from "./credential.service";
|
|
||||||
import { CreateCredentialDto } from "./dto/create-credential.dto";
|
|
||||||
import { UpdateCredentialDto } from "./dto/update-credential.dto";
|
|
||||||
import { CredentialExportDto } from "./dto/credential-export.dto";
|
|
||||||
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
||||||
import { CredentialOrderBy } from "./credential.service";
|
import { CredentialOrderBy, CredentialService } from "./credential.service";
|
||||||
|
import { CreateCredentialDto } from "./dto/create-credential.dto";
|
||||||
|
import { CredentialExportDto } from "./dto/credential-export.dto";
|
||||||
|
import { UpdateCredentialDto } from "./dto/update-credential.dto";
|
||||||
|
|
||||||
@ApiTags("credentials")
|
@ApiTags("credentials")
|
||||||
@Controller("credentials")
|
@Controller("credentials")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
|
import { CredentialController } from "./credential.controller";
|
||||||
import { CredentialEntity } from "./credential.entity";
|
import { CredentialEntity } from "./credential.entity";
|
||||||
import { CredentialService } from "./credential.service";
|
import { CredentialService } from "./credential.service";
|
||||||
import { CredentialController } from "./credential.controller";
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([CredentialEntity])],
|
imports: [TypeOrmModule.forFeature([CredentialEntity])],
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { Repository } from "typeorm";
|
import { Repository } from "typeorm";
|
||||||
|
import {
|
||||||
|
PaginatedResult,
|
||||||
|
PaginationQueryDto,
|
||||||
|
} from "../common/dto/pagination.dto";
|
||||||
import { CredentialEntity } from "./credential.entity";
|
import { CredentialEntity } from "./credential.entity";
|
||||||
import { CreateCredentialDto } from "./dto/create-credential.dto";
|
import { CreateCredentialDto } from "./dto/create-credential.dto";
|
||||||
import { UpdateCredentialDto } from "./dto/update-credential.dto";
|
|
||||||
import { CredentialExportDto } from "./dto/credential-export.dto";
|
import { CredentialExportDto } from "./dto/credential-export.dto";
|
||||||
import {
|
import { UpdateCredentialDto } from "./dto/update-credential.dto";
|
||||||
PaginationQueryDto,
|
|
||||||
PaginatedResult,
|
|
||||||
} from "../common/dto/pagination.dto";
|
|
||||||
|
|
||||||
export type CredentialOrderBy =
|
export type CredentialOrderBy =
|
||||||
| "id"
|
| "id"
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
import { EnvironmentService } from "./environment.service";
|
|
||||||
import { CreateEnvironmentDto } from "./dto/create-environment.dto";
|
|
||||||
import { UpdateEnvironmentDto } from "./dto/update-environment.dto";
|
|
||||||
import { EnvironmentExportDto } from "./dto/environment-export.dto";
|
|
||||||
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
||||||
import { EnvironmentOrderBy } from "./environment.service";
|
import { CreateEnvironmentDto } from "./dto/create-environment.dto";
|
||||||
|
import { EnvironmentExportDto } from "./dto/environment-export.dto";
|
||||||
|
import { UpdateEnvironmentDto } from "./dto/update-environment.dto";
|
||||||
|
import { EnvironmentOrderBy, EnvironmentService } from "./environment.service";
|
||||||
|
|
||||||
@ApiTags("environments")
|
@ApiTags("environments")
|
||||||
@Controller("environments")
|
@Controller("environments")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
|
import { EnvironmentController } from "./environment.controller";
|
||||||
import { EnvironmentEntity } from "./environment.entity";
|
import { EnvironmentEntity } from "./environment.entity";
|
||||||
import { EnvironmentService } from "./environment.service";
|
import { EnvironmentService } from "./environment.service";
|
||||||
import { EnvironmentController } from "./environment.controller";
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([EnvironmentEntity])],
|
imports: [TypeOrmModule.forFeature([EnvironmentEntity])],
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { Repository } from "typeorm";
|
import { Repository } from "typeorm";
|
||||||
import { EnvironmentEntity } from "./environment.entity";
|
|
||||||
import { CreateEnvironmentDto } from "./dto/create-environment.dto";
|
|
||||||
import { UpdateEnvironmentDto } from "./dto/update-environment.dto";
|
|
||||||
import { EnvironmentExportDto } from "./dto/environment-export.dto";
|
|
||||||
import {
|
import {
|
||||||
PaginationQueryDto,
|
|
||||||
PaginatedResult,
|
PaginatedResult,
|
||||||
|
PaginationQueryDto,
|
||||||
} from "../common/dto/pagination.dto";
|
} from "../common/dto/pagination.dto";
|
||||||
|
import { CreateEnvironmentDto } from "./dto/create-environment.dto";
|
||||||
|
import { EnvironmentExportDto } from "./dto/environment-export.dto";
|
||||||
|
import { UpdateEnvironmentDto } from "./dto/update-environment.dto";
|
||||||
|
import { EnvironmentEntity } from "./environment.entity";
|
||||||
|
|
||||||
export type EnvironmentOrderBy = "id" | "name" | "createdAt" | "updatedAt";
|
export type EnvironmentOrderBy = "id" | "name" | "createdAt" | "updatedAt";
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
HttpException,
|
HttpException,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
|
|
||||||
@Catch(BadRequestException, InternalServerErrorException)
|
@Catch(BadRequestException, InternalServerErrorException)
|
||||||
export class HttpExceptionFilter implements ExceptionFilter {
|
export class HttpExceptionFilter implements ExceptionFilter {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
NestInterceptor,
|
NestInterceptor,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
|
||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import { Observable, tap } from "rxjs";
|
import { Observable, tap } from "rxjs";
|
||||||
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LoggingInterceptor implements NestInterceptor {
|
export class LoggingInterceptor implements NestInterceptor {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
NestInterceptor,
|
NestInterceptor,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { Request } from "express";
|
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
|
import type { Request } from "express";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { traceStorage } from "../common/trace-context";
|
import { traceStorage } from "../common/trace-context";
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -1,14 +1,14 @@
|
|||||||
import { NestFactory } from "@nestjs/core";
|
|
||||||
import { ConfigService } from "@nestjs/config";
|
|
||||||
import { AppConfig } from "./config/app.config";
|
|
||||||
import { RequestMethod, ValidationPipe } from "@nestjs/common";
|
import { RequestMethod, ValidationPipe } from "@nestjs/common";
|
||||||
import { TraceLogger } from "./common/trace-logger";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { NestFactory } from "@nestjs/core";
|
||||||
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
||||||
import { AppModule } from "./app.module";
|
|
||||||
import { HttpExceptionFilter } from "./filters/http-exception.filter";
|
|
||||||
import { TraceInterceptor } from "./interceptors/trace.interceptor";
|
|
||||||
import { LoggingInterceptor } from "./interceptors/logging.interceptor";
|
|
||||||
import { name as pkgName, version as pkgVersion } from "../package.json";
|
import { name as pkgName, version as pkgVersion } from "../package.json";
|
||||||
|
import { AppModule } from "./app.module";
|
||||||
|
import { TraceLogger } from "./common/trace-logger";
|
||||||
|
import { AppConfig } from "./config/app.config";
|
||||||
|
import { HttpExceptionFilter } from "./filters/http-exception.filter";
|
||||||
|
import { LoggingInterceptor } from "./interceptors/logging.interceptor";
|
||||||
|
import { TraceInterceptor } from "./interceptors/trace.interceptor";
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const API_PREFIX = "api/v1";
|
const API_PREFIX = "api/v1";
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { McpController } from "./mcp.controller";
|
|
||||||
import { McpService } from "./mcp.service";
|
|
||||||
import { SessionModule } from "../session/session.module";
|
|
||||||
import { EnvironmentModule } from "../environment/environment.module";
|
|
||||||
import { BrowserModule } from "../browser/browser.module";
|
import { BrowserModule } from "../browser/browser.module";
|
||||||
import { CodeExecutorModule } from "../code-executor/code-executor.module";
|
import { CodeExecutorModule } from "../code-executor/code-executor.module";
|
||||||
|
import { CredentialModule } from "../credential/credential.module";
|
||||||
|
import { EnvironmentModule } from "../environment/environment.module";
|
||||||
import { ScenarioModule } from "../scenario/scenario.module";
|
import { ScenarioModule } from "../scenario/scenario.module";
|
||||||
|
import { SessionModule } from "../session/session.module";
|
||||||
|
import { McpController } from "./mcp.controller";
|
||||||
|
import { McpService } from "./mcp.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
SessionModule,
|
SessionModule,
|
||||||
EnvironmentModule,
|
EnvironmentModule,
|
||||||
|
CredentialModule,
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
CodeExecutorModule,
|
CodeExecutorModule,
|
||||||
ScenarioModule,
|
ScenarioModule,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { SessionService } from "../session/session.service";
|
|||||||
import { SessionContextService } from "../session/session-context.service";
|
import { SessionContextService } from "../session/session-context.service";
|
||||||
import { EnvironmentService } from "../environment/environment.service";
|
import { EnvironmentService } from "../environment/environment.service";
|
||||||
import type { EnvironmentData } from "../environment/environment.entity";
|
import type { EnvironmentData } from "../environment/environment.entity";
|
||||||
|
import { CredentialService } from "../credential/credential.service";
|
||||||
import { BrowserService } from "../browser/browser.service";
|
import { BrowserService } from "../browser/browser.service";
|
||||||
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
||||||
import { ScenarioService } from "../scenario/scenario.service";
|
import { ScenarioService } from "../scenario/scenario.service";
|
||||||
@@ -19,6 +20,7 @@ export class McpService {
|
|||||||
private readonly sessionService: SessionService,
|
private readonly sessionService: SessionService,
|
||||||
private readonly sessionContextService: SessionContextService,
|
private readonly sessionContextService: SessionContextService,
|
||||||
private readonly environmentService: EnvironmentService,
|
private readonly environmentService: EnvironmentService,
|
||||||
|
private readonly credentialService: CredentialService,
|
||||||
private readonly browserService: BrowserService,
|
private readonly browserService: BrowserService,
|
||||||
private readonly codeExecutor: CodeExecutorService,
|
private readonly codeExecutor: CodeExecutorService,
|
||||||
private readonly scenarioService: ScenarioService,
|
private readonly scenarioService: ScenarioService,
|
||||||
@@ -309,7 +311,7 @@ export class McpService {
|
|||||||
"exec_code",
|
"exec_code",
|
||||||
{
|
{
|
||||||
description:
|
description:
|
||||||
"Execute arbitrary Playwright JavaScript with `page` and `context` in scope",
|
"Execute Playwright JS — script receives a single `context` argument (see scenario.md). Use `environmentId` (UUID) and `credentials` (alias → credential UUID) to resolve data from the DB and enable context.getEnv() / context.getCredential() / context.runSnippet().",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
sessionName: z
|
sessionName: z
|
||||||
.string()
|
.string()
|
||||||
@@ -320,19 +322,54 @@ export class McpService {
|
|||||||
code: z
|
code: z
|
||||||
.string()
|
.string()
|
||||||
.describe(
|
.describe(
|
||||||
"JavaScript code body to execute (async-safe, may use `page` and `context`)",
|
"Async JavaScript body. Use `context.page` for Playwright, `context.getEnv(key)`, `context.getCredential(alias)`, `context.runSnippet(name, ...args)`.",
|
||||||
),
|
),
|
||||||
url: z
|
url: z
|
||||||
.string()
|
.string()
|
||||||
.url()
|
.url()
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Optional URL to navigate to before running code"),
|
.describe("Optional URL to navigate to before running code"),
|
||||||
|
environmentId: z
|
||||||
|
.string()
|
||||||
|
.uuid()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"UUID of an existing environment entity. Its key/value data is available via context.env and context.getEnv().",
|
||||||
|
),
|
||||||
|
credentials: z
|
||||||
|
.record(z.string(), z.string())
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"Map of alias → credential UUID. Each credential is loaded from the DB and available via context.getCredential(alias).",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ sessionName, code, url }) => {
|
async ({ sessionName, code, url, environmentId, credentials }) => {
|
||||||
try {
|
try {
|
||||||
this.codeExecutor.validate(code);
|
this.codeExecutor.validate(code);
|
||||||
const result = await this.browserService.exec(sessionName, code, url);
|
|
||||||
|
let environment: EnvironmentData | undefined;
|
||||||
|
if (environmentId) {
|
||||||
|
const env = await this.environmentService.findOne(environmentId);
|
||||||
|
environment = env.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolvedCredentials: Record<string, unknown> | undefined;
|
||||||
|
if (credentials && Object.keys(credentials).length > 0) {
|
||||||
|
resolvedCredentials = {};
|
||||||
|
for (const [alias, credId] of Object.entries(credentials)) {
|
||||||
|
const cred = await this.credentialService.findOne(credId);
|
||||||
|
resolvedCredentials[alias] = cred.data ? JSON.parse(cred.data) : {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.browserService.exec(
|
||||||
|
sessionName,
|
||||||
|
code,
|
||||||
|
url,
|
||||||
|
environment,
|
||||||
|
resolvedCredentials,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text" as const, text: JSON.stringify(result) }],
|
content: [{ type: "text" as const, text: JSON.stringify(result) }],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,5 +12,4 @@ export class CreateScenarioStepDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
execCode?: string;
|
execCode?: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export class ScenarioStepExportDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
execCode: string | null;
|
execCode: string | null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ScenarioExportDto {
|
export class ScenarioExportDto {
|
||||||
|
|||||||
@@ -18,5 +18,4 @@ export class UpdateScenarioStepDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
execCode?: string;
|
execCode?: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsOptional, IsString, IsNotEmpty } from "class-validator";
|
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||||
|
|
||||||
export class UpdateScenarioDto {
|
export class UpdateScenarioDto {
|
||||||
@ApiPropertyOptional({ example: "Updated scenario name" })
|
@ApiPropertyOptional({ example: "Updated scenario name" })
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
ManyToOne,
|
|
||||||
JoinColumn,
|
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
Unique,
|
Unique,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { ScenarioEntity } from "./scenario.entity";
|
|
||||||
import { CredentialEntity } from "../credential/credential.entity";
|
import { CredentialEntity } from "../credential/credential.entity";
|
||||||
|
import { ScenarioEntity } from "./scenario.entity";
|
||||||
|
|
||||||
@Entity("scenario_credentials")
|
@Entity("scenario_credentials")
|
||||||
@Unique(["scenarioId", "alias"])
|
@Unique(["scenarioId", "alias"])
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
ManyToOne,
|
Entity,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { ScenarioRunEntity } from "./scenario-run.entity";
|
|
||||||
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
||||||
|
import { ScenarioRunEntity } from "./scenario-run.entity";
|
||||||
|
|
||||||
export type LogLevel = "log" | "warn" | "error";
|
export type LogLevel = "log" | "warn" | "error";
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
Entity,
|
||||||
ManyToOne,
|
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { ScenarioRunEntity } from "./scenario-run.entity";
|
import { ScenarioRunEntity } from "./scenario-run.entity";
|
||||||
import { ScenarioStepEntity } from "./scenario-step.entity";
|
import { ScenarioStepEntity } from "./scenario-step.entity";
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
JoinColumn,
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { ScenarioEntity } from "./scenario.entity";
|
|
||||||
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
||||||
|
import { ScenarioEntity } from "./scenario.entity";
|
||||||
|
|
||||||
export type RunStatus = "pending" | "in_progress" | "pass" | "fail";
|
export type RunStatus = "pending" | "in_progress" | "pass" | "fail";
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
|
||||||
import { traceStorage } from "../common/trace-context";
|
|
||||||
import { Interval } from "@nestjs/schedule";
|
import { Interval } from "@nestjs/schedule";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { Repository } from "typeorm";
|
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import { chromium } from "playwright";
|
|
||||||
import type { Browser, BrowserContext, Page } from "playwright";
|
import type { Browser, BrowserContext, Page } from "playwright";
|
||||||
import { ScenarioRunEntity } from "./scenario-run.entity";
|
import { chromium } from "playwright";
|
||||||
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
import { Repository } from "typeorm";
|
||||||
import { ScenarioRunLogEntity } from "./scenario-run-log.entity";
|
|
||||||
import { ScenarioStepEntity } from "./scenario-step.entity";
|
|
||||||
import type { ScriptLogger } from "../code-executor/code-executor.service";
|
import type { ScriptLogger } from "../code-executor/code-executor.service";
|
||||||
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
import { CodeExecutorService } from "../code-executor/code-executor.service";
|
||||||
import { ScenarioService } from "./scenario.service";
|
import { traceStorage } from "../common/trace-context";
|
||||||
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
|
import { EnvironmentData, EnvironmentEntity } from "../environment/environment.entity";
|
||||||
import { SnippetService } from "../snippet/snippet.service";
|
import { SnippetService } from "../snippet/snippet.service";
|
||||||
import { EnvironmentEntity, EnvironmentData } from "../environment/environment.entity";
|
import { ScenarioRunLogEntity } from "./scenario-run-log.entity";
|
||||||
|
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
||||||
|
import { ScenarioRunEntity } from "./scenario-run.entity";
|
||||||
|
import { ScenarioStepEntity } from "./scenario-step.entity";
|
||||||
|
import { ScenarioService } from "./scenario.service";
|
||||||
|
|
||||||
interface BrowserHandle {
|
interface BrowserHandle {
|
||||||
browser: Browser;
|
browser: Browser;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
Entity,
|
||||||
ManyToOne,
|
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { ScenarioEntity } from "./scenario.entity";
|
import { ScenarioEntity } from "./scenario.entity";
|
||||||
|
|
||||||
|
|||||||
@@ -14,17 +14,16 @@ import {
|
|||||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
import { stringify as yamlStringify } from "yaml";
|
import { stringify as yamlStringify } from "yaml";
|
||||||
import { ScenarioService } from "./scenario.service";
|
|
||||||
import { CreateScenarioDto } from "./dto/create-scenario.dto";
|
|
||||||
import { UpdateScenarioDto } from "./dto/update-scenario.dto";
|
|
||||||
import { CreateScenarioStepDto } from "./dto/create-scenario-step.dto";
|
|
||||||
import { UpdateScenarioStepDto } from "./dto/update-scenario-step.dto";
|
|
||||||
import { AddScenarioCredentialDto } from "./dto/add-scenario-credential.dto";
|
import { AddScenarioCredentialDto } from "./dto/add-scenario-credential.dto";
|
||||||
import { PaginationQueryDto } from "./dto/pagination-query.dto";
|
|
||||||
import { ScenarioOrderBy } from "./scenario.service";
|
|
||||||
import { RunsQueryDto } from "./dto/runs-query.dto";
|
|
||||||
import { CreateScenarioRunDto } from "./dto/create-scenario-run.dto";
|
import { CreateScenarioRunDto } from "./dto/create-scenario-run.dto";
|
||||||
|
import { CreateScenarioStepDto } from "./dto/create-scenario-step.dto";
|
||||||
|
import { CreateScenarioDto } from "./dto/create-scenario.dto";
|
||||||
|
import { PaginationQueryDto } from "./dto/pagination-query.dto";
|
||||||
|
import { RunsQueryDto } from "./dto/runs-query.dto";
|
||||||
import { ScenarioExportDto } from "./dto/scenario-export.dto";
|
import { ScenarioExportDto } from "./dto/scenario-export.dto";
|
||||||
|
import { UpdateScenarioStepDto } from "./dto/update-scenario-step.dto";
|
||||||
|
import { UpdateScenarioDto } from "./dto/update-scenario.dto";
|
||||||
|
import { ScenarioOrderBy, ScenarioService } from "./scenario.service";
|
||||||
|
|
||||||
@ApiTags("scenarios")
|
@ApiTags("scenarios")
|
||||||
@Controller("scenarios")
|
@Controller("scenarios")
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
Entity,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { ScenarioStepEntity } from "./scenario-step.entity";
|
|
||||||
import { ScenarioCredentialEntity } from "./scenario-credential.entity";
|
import { ScenarioCredentialEntity } from "./scenario-credential.entity";
|
||||||
|
import { ScenarioStepEntity } from "./scenario-step.entity";
|
||||||
|
|
||||||
@Entity("scenarios")
|
@Entity("scenarios")
|
||||||
export class ScenarioEntity {
|
export class ScenarioEntity {
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { ScenarioEntity } from "./scenario.entity";
|
import { CodeExecutorModule } from "../code-executor/code-executor.module";
|
||||||
import { ScenarioStepEntity } from "./scenario-step.entity";
|
|
||||||
import { ScenarioRunEntity } from "./scenario-run.entity";
|
|
||||||
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
|
||||||
import { ScenarioRunLogEntity } from "./scenario-run-log.entity";
|
|
||||||
import { ScenarioCredentialEntity } from "./scenario-credential.entity";
|
|
||||||
import { CredentialEntity } from "../credential/credential.entity";
|
import { CredentialEntity } from "../credential/credential.entity";
|
||||||
import { EnvironmentEntity } from "../environment/environment.entity";
|
import { EnvironmentEntity } from "../environment/environment.entity";
|
||||||
import { ScenarioService } from "./scenario.service";
|
|
||||||
import { ScenarioController } from "./scenario.controller";
|
|
||||||
import { ScenarioSchedulerService } from "./scenario-scheduler.service";
|
|
||||||
import { CodeExecutorModule } from "../code-executor/code-executor.module";
|
|
||||||
import { SessionModule } from "../session/session.module";
|
|
||||||
import { EnvironmentModule } from "../environment/environment.module";
|
import { EnvironmentModule } from "../environment/environment.module";
|
||||||
|
import { SessionModule } from "../session/session.module";
|
||||||
import { SnippetModule } from "../snippet/snippet.module";
|
import { SnippetModule } from "../snippet/snippet.module";
|
||||||
|
import { ScenarioCredentialEntity } from "./scenario-credential.entity";
|
||||||
|
import { ScenarioRunLogEntity } from "./scenario-run-log.entity";
|
||||||
|
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
||||||
|
import { ScenarioRunEntity } from "./scenario-run.entity";
|
||||||
|
import { ScenarioSchedulerService } from "./scenario-scheduler.service";
|
||||||
|
import { ScenarioStepEntity } from "./scenario-step.entity";
|
||||||
|
import { ScenarioController } from "./scenario.controller";
|
||||||
|
import { ScenarioEntity } from "./scenario.entity";
|
||||||
|
import { ScenarioService } from "./scenario.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
@@ -5,25 +5,25 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { Like, Repository } from "typeorm";
|
import { Like, Repository } from "typeorm";
|
||||||
import { ScenarioEntity } from "./scenario.entity";
|
import {
|
||||||
import { ScenarioStepEntity } from "./scenario-step.entity";
|
PaginatedResult,
|
||||||
import { ScenarioRunEntity } from "./scenario-run.entity";
|
PaginationQueryDto,
|
||||||
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
} from "../common/dto/pagination.dto";
|
||||||
import { ScenarioRunLogEntity } from "./scenario-run-log.entity";
|
|
||||||
import { ScenarioCredentialEntity } from "./scenario-credential.entity";
|
|
||||||
import { CredentialEntity } from "../credential/credential.entity";
|
import { CredentialEntity } from "../credential/credential.entity";
|
||||||
import { EnvironmentEntity } from "../environment/environment.entity";
|
import { EnvironmentEntity } from "../environment/environment.entity";
|
||||||
import { CreateScenarioDto } from "./dto/create-scenario.dto";
|
|
||||||
import { UpdateScenarioDto } from "./dto/update-scenario.dto";
|
|
||||||
import { CreateScenarioStepDto } from "./dto/create-scenario-step.dto";
|
|
||||||
import { UpdateScenarioStepDto } from "./dto/update-scenario-step.dto";
|
|
||||||
import { AddScenarioCredentialDto } from "./dto/add-scenario-credential.dto";
|
import { AddScenarioCredentialDto } from "./dto/add-scenario-credential.dto";
|
||||||
import {
|
import { CreateScenarioStepDto } from "./dto/create-scenario-step.dto";
|
||||||
PaginationQueryDto,
|
import { CreateScenarioDto } from "./dto/create-scenario.dto";
|
||||||
PaginatedResult,
|
|
||||||
} from "../common/dto/pagination.dto";
|
|
||||||
import { RunsQueryDto } from "./dto/runs-query.dto";
|
import { RunsQueryDto } from "./dto/runs-query.dto";
|
||||||
import { ScenarioExportDto } from "./dto/scenario-export.dto";
|
import { ScenarioExportDto } from "./dto/scenario-export.dto";
|
||||||
|
import { UpdateScenarioStepDto } from "./dto/update-scenario-step.dto";
|
||||||
|
import { UpdateScenarioDto } from "./dto/update-scenario.dto";
|
||||||
|
import { ScenarioCredentialEntity } from "./scenario-credential.entity";
|
||||||
|
import { ScenarioRunLogEntity } from "./scenario-run-log.entity";
|
||||||
|
import { ScenarioRunStepEntity } from "./scenario-run-step.entity";
|
||||||
|
import { ScenarioRunEntity } from "./scenario-run.entity";
|
||||||
|
import { ScenarioStepEntity } from "./scenario-step.entity";
|
||||||
|
import { ScenarioEntity } from "./scenario.entity";
|
||||||
|
|
||||||
export { PaginatedResult } from "../common/dto/pagination.dto";
|
export { PaginatedResult } from "../common/dto/pagination.dto";
|
||||||
export type ScenarioOrderBy = "id" | "name" | "createdAt" | "updatedAt";
|
export type ScenarioOrderBy = "id" | "name" | "createdAt" | "updatedAt";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Injectable,
|
|
||||||
OnModuleDestroy,
|
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
|
OnModuleDestroy,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { Browser, BrowserContext, Page } from "playwright";
|
import type { Browser, BrowserContext, Page } from "playwright";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Interval } from "@nestjs/schedule";
|
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { AppConfig } from "../config/app.config";
|
import { Interval } from "@nestjs/schedule";
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
import { SessionService } from "./session.service";
|
import { AppConfig } from "../config/app.config";
|
||||||
import { SessionContextService } from "./session-context.service";
|
import { SessionContextService } from "./session-context.service";
|
||||||
|
import { SessionService } from "./session.service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Periodically:
|
* Periodically:
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
import { SessionService } from "./session.service";
|
|
||||||
import { SessionContextService } from "./session-context.service";
|
|
||||||
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
||||||
import { SessionOrderBy } from "./session.service";
|
import { SessionContextService } from "./session-context.service";
|
||||||
|
import { SessionOrderBy, SessionService } from "./session.service";
|
||||||
|
|
||||||
function maskToken(token: string, head = 6, tail = 4): string {
|
function maskToken(token: string, head = 6, tail = 4): string {
|
||||||
if (token.length <= head + tail + 1) return token;
|
if (token.length <= head + tail + 1) return token;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { SessionEntity } from "./session.entity";
|
|
||||||
import { SessionService } from "./session.service";
|
|
||||||
import { SessionController } from "./session.controller";
|
|
||||||
import { SessionContextService } from "./session-context.service";
|
import { SessionContextService } from "./session-context.service";
|
||||||
import { SessionSchedulerService } from "./session-scheduler.service";
|
import { SessionSchedulerService } from "./session-scheduler.service";
|
||||||
|
import { SessionController } from "./session.controller";
|
||||||
|
import { SessionEntity } from "./session.entity";
|
||||||
|
import { SessionService } from "./session.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([SessionEntity])],
|
imports: [TypeOrmModule.forFeature([SessionEntity])],
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Injectable, OnApplicationBootstrap } from "@nestjs/common";
|
import { Injectable, OnApplicationBootstrap } from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { LessThan, Repository } from "typeorm";
|
|
||||||
import { SessionEntity } from "./session.entity";
|
|
||||||
import { TraceLogger } from "../common/trace-logger";
|
|
||||||
import type { Cookie } from "playwright";
|
import type { Cookie } from "playwright";
|
||||||
|
import { LessThan, Repository } from "typeorm";
|
||||||
import {
|
import {
|
||||||
PaginationQueryDto,
|
|
||||||
PaginatedResult,
|
PaginatedResult,
|
||||||
|
PaginationQueryDto,
|
||||||
} from "../common/dto/pagination.dto";
|
} from "../common/dto/pagination.dto";
|
||||||
|
import { TraceLogger } from "../common/trace-logger";
|
||||||
|
import { SessionEntity } from "./session.entity";
|
||||||
|
|
||||||
export type SessionOrderBy =
|
export type SessionOrderBy =
|
||||||
| "id"
|
| "id"
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import {
|
|||||||
Query,
|
Query,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
import { SnippetService, SnippetOrderBy } from "./snippet.service";
|
|
||||||
import { CreateSnippetDto } from "./dto/create-snippet.dto";
|
|
||||||
import { UpdateSnippetDto } from "./dto/update-snippet.dto";
|
|
||||||
import { SnippetExportDto } from "./dto/snippet-export.dto";
|
|
||||||
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
import { PaginationQueryDto } from "../common/dto/pagination.dto";
|
||||||
|
import { CreateSnippetDto } from "./dto/create-snippet.dto";
|
||||||
|
import { SnippetExportDto } from "./dto/snippet-export.dto";
|
||||||
|
import { UpdateSnippetDto } from "./dto/update-snippet.dto";
|
||||||
|
import { SnippetOrderBy, SnippetService } from "./snippet.service";
|
||||||
|
|
||||||
@ApiTags("snippets")
|
@ApiTags("snippets")
|
||||||
@Controller("snippets")
|
@Controller("snippets")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
|
import { SnippetController } from "./snippet.controller";
|
||||||
import { SnippetEntity } from "./snippet.entity";
|
import { SnippetEntity } from "./snippet.entity";
|
||||||
import { SnippetService } from "./snippet.service";
|
import { SnippetService } from "./snippet.service";
|
||||||
import { SnippetController } from "./snippet.controller";
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([SnippetEntity])],
|
imports: [TypeOrmModule.forFeature([SnippetEntity])],
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { Repository } from "typeorm";
|
import { Repository } from "typeorm";
|
||||||
import { SnippetEntity } from "./snippet.entity";
|
|
||||||
import { CreateSnippetDto } from "./dto/create-snippet.dto";
|
|
||||||
import { UpdateSnippetDto } from "./dto/update-snippet.dto";
|
|
||||||
import { SnippetExportDto } from "./dto/snippet-export.dto";
|
|
||||||
import {
|
import {
|
||||||
PaginationQueryDto,
|
|
||||||
PaginatedResult,
|
PaginatedResult,
|
||||||
|
PaginationQueryDto,
|
||||||
} from "../common/dto/pagination.dto";
|
} from "../common/dto/pagination.dto";
|
||||||
|
import { CreateSnippetDto } from "./dto/create-snippet.dto";
|
||||||
|
import { SnippetExportDto } from "./dto/snippet-export.dto";
|
||||||
|
import { UpdateSnippetDto } from "./dto/update-snippet.dto";
|
||||||
|
import { SnippetEntity } from "./snippet.entity";
|
||||||
|
|
||||||
export type SnippetOrderBy = "id" | "alias" | "title" | "createdAt" | "updatedAt";
|
export type SnippetOrderBy = "id" | "alias" | "title" | "createdAt" | "updatedAt";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user