From 0e47623a6bc044e7e765bd9a8e0ed4531fc3e675 Mon Sep 17 00:00:00 2001 From: Andrii Arsenin Date: Tue, 14 Apr 2026 18:09:28 +0300 Subject: [PATCH] feat(browser,mcp): resolve environment and credentials from DB in exec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- docs/mcp.md | 2 +- docs/scenario.md | 34 +++++++------- server/src/app.module.ts | 22 ++++----- server/src/browser/browser.controller.ts | 30 +++++++++++-- server/src/browser/browser.module.ts | 10 +++-- server/src/browser/browser.service.ts | 17 ++++--- server/src/browser/dto/exec.dto.ts | 21 +++++---- .../code-executor/code-executor.service.ts | 10 ++--- .../src/code-executor/exec-context.builder.ts | 4 +- server/src/config/app.config.ts | 3 +- .../src/credential/credential.controller.ts | 9 ++-- server/src/credential/credential.entity.ts | 4 +- server/src/credential/credential.module.ts | 2 +- server/src/credential/credential.service.ts | 10 ++--- .../src/environment/environment.controller.ts | 9 ++-- server/src/environment/environment.entity.ts | 4 +- server/src/environment/environment.module.ts | 2 +- server/src/environment/environment.service.ts | 10 ++--- server/src/filters/http-exception.filter.ts | 2 +- .../src/interceptors/logging.interceptor.ts | 2 +- server/src/interceptors/trace.interceptor.ts | 2 +- server/src/main.ts | 16 +++---- server/src/mcp/mcp.module.ts | 10 +++-- server/src/mcp/mcp.service.ts | 45 +++++++++++++++++-- .../scenario/dto/create-scenario-step.dto.ts | 1 - .../src/scenario/dto/scenario-export.dto.ts | 1 - .../scenario/dto/update-scenario-step.dto.ts | 1 - .../src/scenario/dto/update-scenario.dto.ts | 2 +- .../scenario/scenario-credential.entity.ts | 10 ++--- .../src/scenario/scenario-run-log.entity.ts | 8 ++-- .../src/scenario/scenario-run-step.entity.ts | 8 ++-- server/src/scenario/scenario-run.entity.ts | 10 ++--- .../scenario/scenario-scheduler.service.ts | 20 ++++----- server/src/scenario/scenario-step.entity.ts | 8 ++-- server/src/scenario/scenario.controller.ts | 15 +++---- server/src/scenario/scenario.entity.ts | 8 ++-- server/src/scenario/scenario.module.ts | 22 ++++----- server/src/scenario/scenario.service.ts | 28 ++++++------ server/src/session/session-context.service.ts | 4 +- .../src/session/session-scheduler.service.ts | 6 +-- server/src/session/session.controller.ts | 5 +-- server/src/session/session.entity.ts | 4 +- server/src/session/session.module.ts | 6 +-- server/src/session/session.service.ts | 8 ++-- server/src/snippet/snippet.controller.ts | 8 ++-- server/src/snippet/snippet.entity.ts | 4 +- server/src/snippet/snippet.module.ts | 2 +- server/src/snippet/snippet.service.ts | 10 ++--- 48 files changed, 268 insertions(+), 211 deletions(-) diff --git a/docs/mcp.md b/docs/mcp.md index 3ad46e4..a4e6971 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -28,7 +28,7 @@ The server currently registers the following tools. | Tool | Description | |---|---| | `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 diff --git a/docs/scenario.md b/docs/scenario.md index 902eed1..e08f7a9 100644 --- a/docs/scenario.md +++ b/docs/scenario.md @@ -17,24 +17,28 @@ Current scheduler behavior is exec-centric: ## 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` -- `context`: Playwright `BrowserContext` -- `helpers`: utility object +```js +async (context) => { + // 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.) -`validateCode` additionally receives `result`, 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 +`validateCode` additionally receives `result` in scope, which is the value returned by `execCode`. ## Validation contract diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 7e7c17b..99917e5 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -1,23 +1,23 @@ import { Module } from "@nestjs/common"; import { ConfigModule, ConfigService } from "@nestjs/config"; -import { AppConfig, validateAppConfig } from "./config/app.config"; -import { TypeOrmModule } from "@nestjs/typeorm"; import { ScheduleModule } from "@nestjs/schedule"; -import { HealthModule } from "./health/health.module"; +import { TypeOrmModule } from "@nestjs/typeorm"; import { BrowserModule } from "./browser/browser.module"; -import { SessionEntity } from "./session/session.entity"; -import { EnvironmentEntity } from "./environment/environment.entity"; -import { EnvironmentModule } from "./environment/environment.module"; +import { AppConfig, validateAppConfig } from "./config/app.config"; import { CredentialEntity } from "./credential/credential.entity"; 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 { 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 { 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 { SessionEntity } from "./session/session.entity"; import { SnippetEntity } from "./snippet/snippet.entity"; import { SnippetModule } from "./snippet/snippet.module"; diff --git a/server/src/browser/browser.controller.ts b/server/src/browser/browser.controller.ts index dff50d2..b328499 100644 --- a/server/src/browser/browser.controller.ts +++ b/server/src/browser/browser.controller.ts @@ -1,9 +1,11 @@ import { Body, Controller, Post } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { BrowserService, ExecResult, OpenResult } from "./browser.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 { OpenDto } from "./dto/open.dto"; @ApiTags("browser") @Controller() @@ -11,6 +13,8 @@ export class BrowserController { constructor( private readonly browserService: BrowserService, private readonly codeExecutor: CodeExecutorService, + private readonly environmentService: EnvironmentService, + private readonly credentialService: CredentialService, ) {} @Post("open") @@ -54,8 +58,26 @@ export class BrowserController { @ApiResponse({ status: 400, description: "Invalid input" }) @ApiResponse({ status: 404, description: "Session not found" }) @ApiResponse({ status: 500, description: "Execution failed" }) - exec(@Body() dto: ExecDto): Promise { + async exec(@Body() dto: ExecDto): Promise { 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 | undefined; + if (dto.environmentId) { + const env = await this.environmentService.findOne(dto.environmentId); + environment = env.data; + } + + // Resolve credentials from DB (alias → payload) + let credentials: Record | 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); } } diff --git a/server/src/browser/browser.module.ts b/server/src/browser/browser.module.ts index 07152a3..9390f15 100644 --- a/server/src/browser/browser.module.ts +++ b/server/src/browser/browser.module.ts @@ -1,12 +1,14 @@ 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 { 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({ - imports: [SessionModule, CodeExecutorModule, SnippetModule], + imports: [SessionModule, CodeExecutorModule, SnippetModule, EnvironmentModule, CredentialModule], controllers: [BrowserController], providers: [BrowserService], exports: [BrowserService], diff --git a/server/src/browser/browser.service.ts b/server/src/browser/browser.service.ts index ac4015e..c60c59f 100644 --- a/server/src/browser/browser.service.ts +++ b/server/src/browser/browser.service.ts @@ -1,20 +1,19 @@ +import { Readability } from "@mozilla/readability"; import { - Injectable, HttpException, + Injectable, InternalServerErrorException, } 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 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 { 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 type { Cookie } from "playwright"; -import type { EnvironmentData } from "../environment/environment.entity"; export type { ExecResult } from "../code-executor/code-executor.service"; diff --git a/server/src/browser/dto/exec.dto.ts b/server/src/browser/dto/exec.dto.ts index 022a0b7..271f39b 100644 --- a/server/src/browser/dto/exec.dto.ts +++ b/server/src/browser/dto/exec.dto.ts @@ -1,6 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsObject, IsOptional, IsString, IsUrl } from "class-validator"; -import type { EnvironmentData } from "../../environment/environment.entity"; +import { IsObject, IsOptional, IsString, IsUUID, IsUrl } from "class-validator"; export class ExecDto { @ApiPropertyOptional({ @@ -23,27 +22,27 @@ export class ExecDto { @ApiProperty({ description: - "JavaScript code to execute. Receives `page` (Playwright Page) and `context` (BrowserContext) as arguments. May be async. Return value is serialised and returned.", - example: "return await page.title();", + "Async JavaScript body. Use `context.page` for Playwright, `context.getEnv(key)`, `context.getCredential(alias)`, `context.runSnippet(name, ...args)`.", + example: "return await context.page.title();", }) @IsString() code: string; @ApiPropertyOptional({ description: - "Key/value map of environment variables available via `helpers.env` and `helpers.getEnv()` in the script.", - example: { BASE_URL: "https://example.com" }, + "UUID of an existing environment entity. Its key/value data is available via `context.env` and `context.getEnv()` in the script.", + example: "a6a1fca5-0f61-48ed-ae97-011dc7236387", }) @IsOptional() - @IsObject() - environment?: EnvironmentData; + @IsUUID() + environmentId?: string; @ApiPropertyOptional({ description: - "Key/value map of credentials available via `helpers.getCredential()` in the script.", - example: { admin: { username: "user", password: "pass" } }, + "Map of alias → credential UUID. Each credential is loaded from the DB and available via `context.getCredential(alias)` in the script.", + example: { pkcs_key: "e3b0c442-98fc-11d8-9669-0800200c9a66" }, }) @IsOptional() @IsObject() - credentials?: Record; + credentials?: Record; } diff --git a/server/src/code-executor/code-executor.service.ts b/server/src/code-executor/code-executor.service.ts index d7f3ebd..9581337 100644 --- a/server/src/code-executor/code-executor.service.ts +++ b/server/src/code-executor/code-executor.service.ts @@ -3,13 +3,13 @@ import { Injectable, InternalServerErrorException, } 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 { dumpDom } from "./dom-helpers"; -import type { DomNode } from "./dom-helpers"; +import { parse } from "acorn"; +import type { BrowserContext, Page } from "playwright"; +import { TraceLogger } from "../common/trace-logger"; import type { EnvironmentData } from "../environment/environment.entity"; +import type { DomNode } from "./dom-helpers"; +import { dumpDom } from "./dom-helpers"; export interface ExecResult { result: unknown; diff --git a/server/src/code-executor/exec-context.builder.ts b/server/src/code-executor/exec-context.builder.ts index 6080109..0ed756b 100644 --- a/server/src/code-executor/exec-context.builder.ts +++ b/server/src/code-executor/exec-context.builder.ts @@ -1,6 +1,6 @@ -import type { Page, BrowserContext } from "playwright"; -import type { ExecContext, ScriptLogger } from "./code-executor.service"; +import type { BrowserContext, Page } from "playwright"; import type { EnvironmentData } from "../environment/environment.entity"; +import type { ExecContext, ScriptLogger } from "./code-executor.service"; export class ExecContextBuilder { private readonly ctx: Partial = {}; diff --git a/server/src/config/app.config.ts b/server/src/config/app.config.ts index 37ed288..0214525 100644 --- a/server/src/config/app.config.ts +++ b/server/src/config/app.config.ts @@ -1,6 +1,5 @@ -import { IsInt, IsString, Min, Max } from "class-validator"; import { plainToInstance } from "class-transformer"; -import { validateSync } from "class-validator"; +import { IsInt, IsString, Max, Min, validateSync } from "class-validator"; import pkg from "../../package.json"; diff --git a/server/src/credential/credential.controller.ts b/server/src/credential/credential.controller.ts index 136c2c4..529489c 100644 --- a/server/src/credential/credential.controller.ts +++ b/server/src/credential/credential.controller.ts @@ -11,12 +11,11 @@ import { Query, } from "@nestjs/common"; 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 { 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") @Controller("credentials") diff --git a/server/src/credential/credential.entity.ts b/server/src/credential/credential.entity.ts index e97a2ac..53f2a09 100644 --- a/server/src/credential/credential.entity.ts +++ b/server/src/credential/credential.entity.ts @@ -1,8 +1,8 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, + Entity, + PrimaryGeneratedColumn, UpdateDateColumn, } from "typeorm"; diff --git a/server/src/credential/credential.module.ts b/server/src/credential/credential.module.ts index 9c8f1cb..05a0bb4 100644 --- a/server/src/credential/credential.module.ts +++ b/server/src/credential/credential.module.ts @@ -1,8 +1,8 @@ import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; +import { CredentialController } from "./credential.controller"; import { CredentialEntity } from "./credential.entity"; import { CredentialService } from "./credential.service"; -import { CredentialController } from "./credential.controller"; @Module({ imports: [TypeOrmModule.forFeature([CredentialEntity])], diff --git a/server/src/credential/credential.service.ts b/server/src/credential/credential.service.ts index eed9798..1288934 100644 --- a/server/src/credential/credential.service.ts +++ b/server/src/credential/credential.service.ts @@ -1,14 +1,14 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; import { Repository } from "typeorm"; +import { + PaginatedResult, + PaginationQueryDto, +} from "../common/dto/pagination.dto"; import { CredentialEntity } from "./credential.entity"; import { CreateCredentialDto } from "./dto/create-credential.dto"; -import { UpdateCredentialDto } from "./dto/update-credential.dto"; import { CredentialExportDto } from "./dto/credential-export.dto"; -import { - PaginationQueryDto, - PaginatedResult, -} from "../common/dto/pagination.dto"; +import { UpdateCredentialDto } from "./dto/update-credential.dto"; export type CredentialOrderBy = | "id" diff --git a/server/src/environment/environment.controller.ts b/server/src/environment/environment.controller.ts index 8b70f40..43d02b0 100644 --- a/server/src/environment/environment.controller.ts +++ b/server/src/environment/environment.controller.ts @@ -11,12 +11,11 @@ import { Query, } from "@nestjs/common"; 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 { 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") @Controller("environments") diff --git a/server/src/environment/environment.entity.ts b/server/src/environment/environment.entity.ts index 3fbf1f1..22a222d 100644 --- a/server/src/environment/environment.entity.ts +++ b/server/src/environment/environment.entity.ts @@ -1,8 +1,8 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, + Entity, + PrimaryGeneratedColumn, UpdateDateColumn, } from "typeorm"; diff --git a/server/src/environment/environment.module.ts b/server/src/environment/environment.module.ts index bb07ed7..4762ead 100644 --- a/server/src/environment/environment.module.ts +++ b/server/src/environment/environment.module.ts @@ -1,8 +1,8 @@ import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; +import { EnvironmentController } from "./environment.controller"; import { EnvironmentEntity } from "./environment.entity"; import { EnvironmentService } from "./environment.service"; -import { EnvironmentController } from "./environment.controller"; @Module({ imports: [TypeOrmModule.forFeature([EnvironmentEntity])], diff --git a/server/src/environment/environment.service.ts b/server/src/environment/environment.service.ts index ad0e676..14f839d 100644 --- a/server/src/environment/environment.service.ts +++ b/server/src/environment/environment.service.ts @@ -5,14 +5,14 @@ import { } from "@nestjs/common"; import { InjectRepository } from "@nestjs/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 { - PaginationQueryDto, PaginatedResult, + PaginationQueryDto, } 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"; diff --git a/server/src/filters/http-exception.filter.ts b/server/src/filters/http-exception.filter.ts index 81fe3e4..9a8fc46 100644 --- a/server/src/filters/http-exception.filter.ts +++ b/server/src/filters/http-exception.filter.ts @@ -6,8 +6,8 @@ import { HttpException, InternalServerErrorException, } from "@nestjs/common"; -import { TraceLogger } from "../common/trace-logger"; import type { Request, Response } from "express"; +import { TraceLogger } from "../common/trace-logger"; @Catch(BadRequestException, InternalServerErrorException) export class HttpExceptionFilter implements ExceptionFilter { diff --git a/server/src/interceptors/logging.interceptor.ts b/server/src/interceptors/logging.interceptor.ts index 51c3bc6..ba03c0e 100644 --- a/server/src/interceptors/logging.interceptor.ts +++ b/server/src/interceptors/logging.interceptor.ts @@ -4,9 +4,9 @@ import { Injectable, NestInterceptor, } from "@nestjs/common"; -import { TraceLogger } from "../common/trace-logger"; import type { Request, Response } from "express"; import { Observable, tap } from "rxjs"; +import { TraceLogger } from "../common/trace-logger"; @Injectable() export class LoggingInterceptor implements NestInterceptor { diff --git a/server/src/interceptors/trace.interceptor.ts b/server/src/interceptors/trace.interceptor.ts index 36008b5..225d66b 100644 --- a/server/src/interceptors/trace.interceptor.ts +++ b/server/src/interceptors/trace.interceptor.ts @@ -4,8 +4,8 @@ import { Injectable, NestInterceptor, } from "@nestjs/common"; -import type { Request } from "express"; import * as crypto from "crypto"; +import type { Request } from "express"; import { Observable } from "rxjs"; import { traceStorage } from "../common/trace-context"; diff --git a/server/src/main.ts b/server/src/main.ts index 9e7559b..5481ecd 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -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 { TraceLogger } from "./common/trace-logger"; +import { ConfigService } from "@nestjs/config"; +import { NestFactory } from "@nestjs/core"; 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 { 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() { const API_PREFIX = "api/v1"; diff --git a/server/src/mcp/mcp.module.ts b/server/src/mcp/mcp.module.ts index 608c35d..680dd5f 100644 --- a/server/src/mcp/mcp.module.ts +++ b/server/src/mcp/mcp.module.ts @@ -1,16 +1,18 @@ 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 { 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 { SessionModule } from "../session/session.module"; +import { McpController } from "./mcp.controller"; +import { McpService } from "./mcp.service"; @Module({ imports: [ SessionModule, EnvironmentModule, + CredentialModule, BrowserModule, CodeExecutorModule, ScenarioModule, diff --git a/server/src/mcp/mcp.service.ts b/server/src/mcp/mcp.service.ts index 0934b86..f9084fa 100644 --- a/server/src/mcp/mcp.service.ts +++ b/server/src/mcp/mcp.service.ts @@ -7,6 +7,7 @@ import { SessionService } from "../session/session.service"; import { SessionContextService } from "../session/session-context.service"; import { EnvironmentService } from "../environment/environment.service"; import type { EnvironmentData } from "../environment/environment.entity"; +import { CredentialService } from "../credential/credential.service"; import { BrowserService } from "../browser/browser.service"; import { CodeExecutorService } from "../code-executor/code-executor.service"; import { ScenarioService } from "../scenario/scenario.service"; @@ -19,6 +20,7 @@ export class McpService { private readonly sessionService: SessionService, private readonly sessionContextService: SessionContextService, private readonly environmentService: EnvironmentService, + private readonly credentialService: CredentialService, private readonly browserService: BrowserService, private readonly codeExecutor: CodeExecutorService, private readonly scenarioService: ScenarioService, @@ -309,7 +311,7 @@ export class McpService { "exec_code", { 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: { sessionName: z .string() @@ -320,19 +322,54 @@ export class McpService { code: z .string() .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 .string() .url() .optional() .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 { 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 | 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 { content: [{ type: "text" as const, text: JSON.stringify(result) }], }; diff --git a/server/src/scenario/dto/create-scenario-step.dto.ts b/server/src/scenario/dto/create-scenario-step.dto.ts index ac670ed..0548597 100644 --- a/server/src/scenario/dto/create-scenario-step.dto.ts +++ b/server/src/scenario/dto/create-scenario-step.dto.ts @@ -12,5 +12,4 @@ export class CreateScenarioStepDto { @IsString() @IsNotEmpty() execCode?: string; - } diff --git a/server/src/scenario/dto/scenario-export.dto.ts b/server/src/scenario/dto/scenario-export.dto.ts index ef50c4a..12ed015 100644 --- a/server/src/scenario/dto/scenario-export.dto.ts +++ b/server/src/scenario/dto/scenario-export.dto.ts @@ -22,7 +22,6 @@ export class ScenarioStepExportDto { @IsString() @IsNotEmpty() execCode: string | null; - } export class ScenarioExportDto { diff --git a/server/src/scenario/dto/update-scenario-step.dto.ts b/server/src/scenario/dto/update-scenario-step.dto.ts index a8a24f5..e0c6b7e 100644 --- a/server/src/scenario/dto/update-scenario-step.dto.ts +++ b/server/src/scenario/dto/update-scenario-step.dto.ts @@ -18,5 +18,4 @@ export class UpdateScenarioStepDto { @IsString() @IsNotEmpty() execCode?: string; - } diff --git a/server/src/scenario/dto/update-scenario.dto.ts b/server/src/scenario/dto/update-scenario.dto.ts index 6c7f215..5ea98ed 100644 --- a/server/src/scenario/dto/update-scenario.dto.ts +++ b/server/src/scenario/dto/update-scenario.dto.ts @@ -1,5 +1,5 @@ import { ApiPropertyOptional } from "@nestjs/swagger"; -import { IsOptional, IsString, IsNotEmpty } from "class-validator"; +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; export class UpdateScenarioDto { @ApiPropertyOptional({ example: "Updated scenario name" }) diff --git a/server/src/scenario/scenario-credential.entity.ts b/server/src/scenario/scenario-credential.entity.ts index 7a57564..b72a2fc 100644 --- a/server/src/scenario/scenario-credential.entity.ts +++ b/server/src/scenario/scenario-credential.entity.ts @@ -1,14 +1,14 @@ import { - Entity, - PrimaryGeneratedColumn, Column, - ManyToOne, - JoinColumn, CreateDateColumn, + Entity, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, Unique, } from "typeorm"; -import { ScenarioEntity } from "./scenario.entity"; import { CredentialEntity } from "../credential/credential.entity"; +import { ScenarioEntity } from "./scenario.entity"; @Entity("scenario_credentials") @Unique(["scenarioId", "alias"]) diff --git a/server/src/scenario/scenario-run-log.entity.ts b/server/src/scenario/scenario-run-log.entity.ts index b694469..ea4ecab 100644 --- a/server/src/scenario/scenario-run-log.entity.ts +++ b/server/src/scenario/scenario-run-log.entity.ts @@ -1,13 +1,13 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - ManyToOne, + Entity, JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, } from "typeorm"; -import { ScenarioRunEntity } from "./scenario-run.entity"; import { ScenarioRunStepEntity } from "./scenario-run-step.entity"; +import { ScenarioRunEntity } from "./scenario-run.entity"; export type LogLevel = "log" | "warn" | "error"; diff --git a/server/src/scenario/scenario-run-step.entity.ts b/server/src/scenario/scenario-run-step.entity.ts index e6d94cf..fdd9d5c 100644 --- a/server/src/scenario/scenario-run-step.entity.ts +++ b/server/src/scenario/scenario-run-step.entity.ts @@ -1,11 +1,11 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - UpdateDateColumn, - ManyToOne, + Entity, JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, } from "typeorm"; import { ScenarioRunEntity } from "./scenario-run.entity"; import { ScenarioStepEntity } from "./scenario-step.entity"; diff --git a/server/src/scenario/scenario-run.entity.ts b/server/src/scenario/scenario-run.entity.ts index 22ebdcf..8e69629 100644 --- a/server/src/scenario/scenario-run.entity.ts +++ b/server/src/scenario/scenario-run.entity.ts @@ -1,15 +1,15 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - UpdateDateColumn, + Entity, + JoinColumn, ManyToOne, OneToMany, - JoinColumn, + PrimaryGeneratedColumn, + UpdateDateColumn, } from "typeorm"; -import { ScenarioEntity } from "./scenario.entity"; import { ScenarioRunStepEntity } from "./scenario-run-step.entity"; +import { ScenarioEntity } from "./scenario.entity"; export type RunStatus = "pending" | "in_progress" | "pass" | "fail"; diff --git a/server/src/scenario/scenario-scheduler.service.ts b/server/src/scenario/scenario-scheduler.service.ts index a0f254b..43fd51e 100644 --- a/server/src/scenario/scenario-scheduler.service.ts +++ b/server/src/scenario/scenario-scheduler.service.ts @@ -1,21 +1,21 @@ import { Injectable } from "@nestjs/common"; -import { TraceLogger } from "../common/trace-logger"; -import { traceStorage } from "../common/trace-context"; import { Interval } from "@nestjs/schedule"; import { InjectRepository } from "@nestjs/typeorm"; -import { Repository } from "typeorm"; import * as crypto from "crypto"; -import { chromium } from "playwright"; import type { Browser, BrowserContext, Page } from "playwright"; -import { ScenarioRunEntity } from "./scenario-run.entity"; -import { ScenarioRunStepEntity } from "./scenario-run-step.entity"; -import { ScenarioRunLogEntity } from "./scenario-run-log.entity"; -import { ScenarioStepEntity } from "./scenario-step.entity"; +import { chromium } from "playwright"; +import { Repository } from "typeorm"; import type { ScriptLogger } 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 { 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 { browser: Browser; diff --git a/server/src/scenario/scenario-step.entity.ts b/server/src/scenario/scenario-step.entity.ts index f8d9086..9b7c0a9 100644 --- a/server/src/scenario/scenario-step.entity.ts +++ b/server/src/scenario/scenario-step.entity.ts @@ -1,11 +1,11 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - UpdateDateColumn, - ManyToOne, + Entity, JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, } from "typeorm"; import { ScenarioEntity } from "./scenario.entity"; diff --git a/server/src/scenario/scenario.controller.ts b/server/src/scenario/scenario.controller.ts index b3023bf..ca1d485 100644 --- a/server/src/scenario/scenario.controller.ts +++ b/server/src/scenario/scenario.controller.ts @@ -14,17 +14,16 @@ import { import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { Response } from "express"; 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 { 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 { 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 { UpdateScenarioStepDto } from "./dto/update-scenario-step.dto"; +import { UpdateScenarioDto } from "./dto/update-scenario.dto"; +import { ScenarioOrderBy, ScenarioService } from "./scenario.service"; @ApiTags("scenarios") @Controller("scenarios") diff --git a/server/src/scenario/scenario.entity.ts b/server/src/scenario/scenario.entity.ts index de0c5d1..e22b66c 100644 --- a/server/src/scenario/scenario.entity.ts +++ b/server/src/scenario/scenario.entity.ts @@ -1,13 +1,13 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - UpdateDateColumn, + Entity, OneToMany, + PrimaryGeneratedColumn, + UpdateDateColumn, } from "typeorm"; -import { ScenarioStepEntity } from "./scenario-step.entity"; import { ScenarioCredentialEntity } from "./scenario-credential.entity"; +import { ScenarioStepEntity } from "./scenario-step.entity"; @Entity("scenarios") export class ScenarioEntity { diff --git a/server/src/scenario/scenario.module.ts b/server/src/scenario/scenario.module.ts index 35aca68..357ffee 100644 --- a/server/src/scenario/scenario.module.ts +++ b/server/src/scenario/scenario.module.ts @@ -1,20 +1,20 @@ import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; -import { ScenarioEntity } from "./scenario.entity"; -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 { CodeExecutorModule } from "../code-executor/code-executor.module"; import { CredentialEntity } from "../credential/credential.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 { SessionModule } from "../session/session.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({ imports: [ diff --git a/server/src/scenario/scenario.service.ts b/server/src/scenario/scenario.service.ts index 6bc4cde..2ed19c3 100644 --- a/server/src/scenario/scenario.service.ts +++ b/server/src/scenario/scenario.service.ts @@ -5,25 +5,25 @@ import { } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; import { Like, Repository } from "typeorm"; -import { ScenarioEntity } from "./scenario.entity"; -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 { + PaginatedResult, + PaginationQueryDto, +} from "../common/dto/pagination.dto"; import { CredentialEntity } from "../credential/credential.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 { - PaginationQueryDto, - PaginatedResult, -} from "../common/dto/pagination.dto"; +import { CreateScenarioStepDto } from "./dto/create-scenario-step.dto"; +import { CreateScenarioDto } from "./dto/create-scenario.dto"; import { RunsQueryDto } from "./dto/runs-query.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 type ScenarioOrderBy = "id" | "name" | "createdAt" | "updatedAt"; diff --git a/server/src/session/session-context.service.ts b/server/src/session/session-context.service.ts index 4377aff..64758a1 100644 --- a/server/src/session/session-context.service.ts +++ b/server/src/session/session-context.service.ts @@ -1,8 +1,8 @@ import { - Injectable, - OnModuleDestroy, BadRequestException, + Injectable, NotFoundException, + OnModuleDestroy, } from "@nestjs/common"; import type { Browser, BrowserContext, Page } from "playwright"; import { TraceLogger } from "../common/trace-logger"; diff --git a/server/src/session/session-scheduler.service.ts b/server/src/session/session-scheduler.service.ts index ceca126..d2f53a3 100644 --- a/server/src/session/session-scheduler.service.ts +++ b/server/src/session/session-scheduler.service.ts @@ -1,10 +1,10 @@ import { Injectable } from "@nestjs/common"; -import { Interval } from "@nestjs/schedule"; import { ConfigService } from "@nestjs/config"; -import { AppConfig } from "../config/app.config"; +import { Interval } from "@nestjs/schedule"; import { TraceLogger } from "../common/trace-logger"; -import { SessionService } from "./session.service"; +import { AppConfig } from "../config/app.config"; import { SessionContextService } from "./session-context.service"; +import { SessionService } from "./session.service"; /** * Periodically: diff --git a/server/src/session/session.controller.ts b/server/src/session/session.controller.ts index 57d1afc..2ef6e25 100644 --- a/server/src/session/session.controller.ts +++ b/server/src/session/session.controller.ts @@ -9,10 +9,9 @@ import { Query, } from "@nestjs/common"; 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 { 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 { if (token.length <= head + tail + 1) return token; diff --git a/server/src/session/session.entity.ts b/server/src/session/session.entity.ts index 99f1f73..3df7b41 100644 --- a/server/src/session/session.entity.ts +++ b/server/src/session/session.entity.ts @@ -1,8 +1,8 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, + Entity, + PrimaryGeneratedColumn, UpdateDateColumn, } from "typeorm"; diff --git a/server/src/session/session.module.ts b/server/src/session/session.module.ts index d063fc0..d264c1d 100644 --- a/server/src/session/session.module.ts +++ b/server/src/session/session.module.ts @@ -1,10 +1,10 @@ import { Module } from "@nestjs/common"; 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 { SessionSchedulerService } from "./session-scheduler.service"; +import { SessionController } from "./session.controller"; +import { SessionEntity } from "./session.entity"; +import { SessionService } from "./session.service"; @Module({ imports: [TypeOrmModule.forFeature([SessionEntity])], diff --git a/server/src/session/session.service.ts b/server/src/session/session.service.ts index fe9c3a5..6ed8463 100644 --- a/server/src/session/session.service.ts +++ b/server/src/session/session.service.ts @@ -1,13 +1,13 @@ import { Injectable, OnApplicationBootstrap } from "@nestjs/common"; 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 { LessThan, Repository } from "typeorm"; import { - PaginationQueryDto, PaginatedResult, + PaginationQueryDto, } from "../common/dto/pagination.dto"; +import { TraceLogger } from "../common/trace-logger"; +import { SessionEntity } from "./session.entity"; export type SessionOrderBy = | "id" diff --git a/server/src/snippet/snippet.controller.ts b/server/src/snippet/snippet.controller.ts index d98dbd5..dd47e5b 100644 --- a/server/src/snippet/snippet.controller.ts +++ b/server/src/snippet/snippet.controller.ts @@ -11,11 +11,11 @@ import { Query, } from "@nestjs/common"; 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 { 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") @Controller("snippets") diff --git a/server/src/snippet/snippet.entity.ts b/server/src/snippet/snippet.entity.ts index 658636b..d010144 100644 --- a/server/src/snippet/snippet.entity.ts +++ b/server/src/snippet/snippet.entity.ts @@ -1,8 +1,8 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, + Entity, + PrimaryGeneratedColumn, UpdateDateColumn, } from "typeorm"; diff --git a/server/src/snippet/snippet.module.ts b/server/src/snippet/snippet.module.ts index a498e53..146d976 100644 --- a/server/src/snippet/snippet.module.ts +++ b/server/src/snippet/snippet.module.ts @@ -1,8 +1,8 @@ import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; +import { SnippetController } from "./snippet.controller"; import { SnippetEntity } from "./snippet.entity"; import { SnippetService } from "./snippet.service"; -import { SnippetController } from "./snippet.controller"; @Module({ imports: [TypeOrmModule.forFeature([SnippetEntity])], diff --git a/server/src/snippet/snippet.service.ts b/server/src/snippet/snippet.service.ts index 58f39ca..d1e88ac 100644 --- a/server/src/snippet/snippet.service.ts +++ b/server/src/snippet/snippet.service.ts @@ -6,14 +6,14 @@ import { } from "@nestjs/common"; import { InjectRepository } from "@nestjs/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 { - PaginationQueryDto, PaginatedResult, + 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 { SnippetEntity } from "./snippet.entity"; export type SnippetOrderBy = "id" | "alias" | "title" | "createdAt" | "updatedAt";