diff --git a/API_CONTRACTS.md b/API_CONTRACTS.md index 8211e10..874abc0 100644 --- a/API_CONTRACTS.md +++ b/API_CONTRACTS.md @@ -18,21 +18,20 @@ Retorna a lista de impressoras instaladas no servidor onde a API está rodando. ```json [ { - "name": "POS-80", - "portName": "USB001", - "driverName": "Generic / Text Only", - "printerStatus": 0, - "deviceId": "POS-80" + "Name": "POS-80", + "DriverName": "Generic / Text Only", + "PrinterStatus": 0 }, { - "name": "Microsoft Print to PDF", - "portName": "PORTPROMPT:", - "driverName": "Microsoft Print To PDF", - "printerStatus": 0 + "Name": "Microsoft Print to PDF", + "DriverName": "Microsoft Print To PDF", + "PrinterStatus": 0 } ] ``` +> Nota: atualmente esse endpoint repassa o resultado do PowerShell `Get-Printer` (Windows) com chaves em PascalCase. + --- ### 2. Imprimir HTML (Etiquetas/Layouts) @@ -53,6 +52,16 @@ Converte um código HTML (com suporte a Tailwind CSS) para PDF e imprime na impr } ``` +- **Exemplo de Resposta:** + +```json +{ + "success": true, + "message": "Tarefa enviada para a fila de impressão", + "jobId": "123" +} +``` + - **Detalhes:** - O backend injeta automaticamente o script do Tailwind CSS. - O HTML é renderizado via Puppeteer (Chrome headless). @@ -82,22 +91,29 @@ Envia comandos de texto diretamente para a impressora. Ideal para cupons simples } ``` +- **Exemplo de Resposta:** + +```json +{ + "success": true, + "message": "Print successful!" +} +``` + ## Tipos (TypeScript Interfaces) Se estiver usando TypeScript no Frontend, utilize estas interfaces: ```typescript export interface Printer { - name: string; - portName?: string; - driverName?: string; - printerStatus?: number; - deviceId?: string; + Name: string; + DriverName?: string; + PrinterStatus?: number; } export interface PrintHtmlPayload { html: string; - printerName: string; + printerName?: string; width?: string; height?: string; jobId?: string; @@ -109,6 +125,17 @@ export interface PrintTextPayload { upsideDown?: boolean; printerInterface?: string; } + +export interface PrintHtmlResponse { + success: boolean; + message: string; + jobId?: string; +} + +export interface PrintTextResponse { + success: boolean; + message: string; +} ``` ## Swagger diff --git a/README.md b/README.md index 95222bf..92f8035 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,19 @@ A solução utiliza uma arquitetura baseada em filas para assegurar a resiliênc O serviço utiliza as seguintes definições no arquivo `.env`: -| Variável | Descrição | Exemplo | -| :------------------- | :------------------------------- | :------------------------ | -| `REDIS_HOST` | Endereço do servidor Redis | `localhost` | -| `REDIS_PORT` | Porta do servidor Redis | `6379` | -| `DB_USERNAME` | Usuário do Banco de Dados Oracle | `admin` | -| `DB_PASSWORD` | Senha do Banco de Dados Oracle | `1234` | -| `DB_CONNECT_STRING` | String de conexão Oracle | `(DESCRIPTION=...)` | -| `ORACLE_CLIENT_PATH` | Caminho do Oracle Instant Client | `C:\oracle\instantclient` | +| Variável | Descrição | Exemplo | +| :------------------- | :-------------------------------------------- | :------------------------ | +| `BODY_LIMIT` | Limite do body (JSON/urlencoded) | `2mb` | +| `REDIS_HOST` | Endereço do servidor Redis | `localhost` | +| `REDIS_PORT` | Porta do servidor Redis | `6379` | +| `REDIS_PASSWORD` | Senha do Redis (opcional) | `minha-senha` | +| `DB_ENABLED` | Habilita inicialização do Oracle/TypeORM | `true` | +| `DB_USERNAME` | Usuário do Banco de Dados Oracle | `admin` | +| `DB_PASSWORD` | Senha do Banco de Dados Oracle | `1234` | +| `DB_CONNECT_STRING` | String de conexão Oracle | `(DESCRIPTION=...)` | +| `DB_RETRY_ATTEMPTS` | Tentativas de conexão ao iniciar | `5` | +| `DB_RETRY_DELAY_MS` | Delay base (ms) entre tentativas | `2000` | +| `ORACLE_CLIENT_PATH` | Caminho do Oracle Instant Client (thick mode) | `C:\oracle\instantclient` | ### Pré-requisitos @@ -102,9 +107,10 @@ A interface de monitoramento em tempo real das filas está disponível no endpoi Permite a visualização de tarefas ativas, concluídas e falhas, além da reinjeção manual de jobs. -### Telemetria +### Swagger -O serviço está instrumentado com **OpenTelemetry**, exportando traces via protocolo OTLP para análise de performance e rastreabilidade distribuída. +A documentação interativa (OpenAPI) está disponível em: +`http://localhost:3000/api` --- diff --git a/src/controller/printer.controller.ts b/src/controller/printer.controller.ts index 3ba5e95..845ed64 100644 --- a/src/controller/printer.controller.ts +++ b/src/controller/printer.controller.ts @@ -7,12 +7,20 @@ import { HttpStatus, Param, } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { + ApiBody, + ApiOkResponse, + ApiOperation, + ApiParam, + ApiTags, +} from '@nestjs/swagger'; import { PrinterService } from '../services/printer.service'; import { PrintDataDto } from '../dto/print-data.dto'; import { PrintHtmlDto } from '../dto/print-html.dto'; import { PrinterDto } from '../dto/printer.dto'; import { getPrinters } from '../services/get-printer'; +import { PrintResultDto } from '../dto/print-result.dto'; +import { PrintHtmlResultDto } from '../dto/print-html-result.dto'; @ApiTags('Printer') @Controller('printer') @@ -24,7 +32,15 @@ export class PrinterController { @ApiOperation({ summary: 'Envia um comando de impressão de texto simples (ESC/POS)', }) - @ApiResponse({ status: 200, description: 'Impressão enviada com sucesso' }) + @ApiParam({ + name: 'printerName', + example: 'POS-80', + description: 'Nome da impressora (será usado como printerInterface)', + }) + @ApiOkResponse({ + description: 'Impressão enviada com sucesso', + type: PrintResultDto, + }) async print( @Param('printerName') printerName: string, @Body() printData: PrintDataDto, @@ -36,6 +52,15 @@ export class PrinterController { @Post('print') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Envia um comando de impressão (Compatibilidade)' }) + @ApiBody({ + type: PrintDataDto, + description: + 'Compatibilidade: informe printerInterface no body ou use /printer/:printerName/print.', + }) + @ApiOkResponse({ + description: 'Impressão enviada com sucesso', + type: PrintResultDto, + }) async printLegacy(@Body() printData: PrintDataDto) { return this.printerService.printReceipt(printData); } @@ -46,7 +71,15 @@ export class PrinterController { summary: 'Converte HTML para PDF e imprime na impressora especificada na URL', }) - @ApiResponse({ status: 200, description: 'HTML enviado para impressão' }) + @ApiParam({ + name: 'printerName', + example: 'POS-80', + description: 'Nome da impressora para o spooler (pdf-to-printer)', + }) + @ApiOkResponse({ + description: 'HTML enviado para impressão (job enfileirado)', + type: PrintHtmlResultDto, + }) async printHtml( @Param('printerName') printerName: string, @Body() printHtmlDto: PrintHtmlDto, @@ -58,14 +91,22 @@ export class PrinterController { @Post('print-html') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Converte HTML para PDF (Compatibilidade)' }) + @ApiBody({ + type: PrintHtmlDto, + description: + 'Compatibilidade: informe printerName no body ou use /printer/:printerName/print-html.', + }) + @ApiOkResponse({ + description: 'HTML enviado para impressão (job enfileirado)', + type: PrintHtmlResultDto, + }) async printHtmlLegacy(@Body() printHtmlDto: PrintHtmlDto) { return this.printerService.printHtml(printHtmlDto); } @Get('list') @ApiOperation({ summary: 'Lista impressoras disponíveis no sistema' }) - @ApiResponse({ - status: 200, + @ApiOkResponse({ description: 'Lista de impressoras encontradas', type: [PrinterDto], }) diff --git a/src/dto/print-data.dto.ts b/src/dto/print-data.dto.ts index 1dc267b..659d3ab 100644 --- a/src/dto/print-data.dto.ts +++ b/src/dto/print-data.dto.ts @@ -1,31 +1,36 @@ import { ApiProperty } from '@nestjs/swagger'; export class PrintDataDto { - @ApiProperty({ - example: ['--------------------------------', ' TESTE DE IMPRESSAO ', '--------------------------------'], - description: 'Lista de linhas de texto para imprimir' + @ApiProperty({ + example: [ + '--------------------------------', + ' TESTE DE IMPRESSAO ', + '--------------------------------', + ], + description: 'Lista de linhas de texto para imprimir', }) lines: string[]; - @ApiProperty({ - required: false, + @ApiProperty({ + required: false, enum: ['left', 'center', 'right'], example: 'center', - description: 'Alinhamento do texto' + description: 'Alinhamento do texto', }) alignment?: 'left' | 'center' | 'right'; - @ApiProperty({ - required: false, + @ApiProperty({ + required: false, example: false, - description: 'Se verdadeiro, imprime de cabeça para baixo (se suportado)' + description: 'Se verdadeiro, imprime de cabeça para baixo (se suportado)', }) upsideDown?: boolean; @ApiProperty({ required: false, example: 'tcp://10.1.119.13', - description: 'Interface da impressora. Ex: tcp://ip:porta ou printer:NomeDaImpressora' + description: + 'Interface da impressora. Obrigatorio ao usar /printer/print; ignorado ao usar /printer/:printerName/print. Ex: tcp://ip:porta ou printer:NomeDaImpressora', }) printerInterface?: string; } diff --git a/src/dto/print-html-result.dto.ts b/src/dto/print-html-result.dto.ts new file mode 100644 index 0000000..fa898f5 --- /dev/null +++ b/src/dto/print-html-result.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { PrintResultDto } from './print-result.dto'; + +export class PrintHtmlResultDto extends PrintResultDto { + @ApiProperty({ + required: false, + example: '123', + description: 'ID do job enfileirado no BullMQ', + }) + jobId?: string; +} diff --git a/src/dto/print-html.dto.ts b/src/dto/print-html.dto.ts index eba317d..019b9b0 100644 --- a/src/dto/print-html.dto.ts +++ b/src/dto/print-html.dto.ts @@ -1,28 +1,38 @@ import { ApiProperty } from '@nestjs/swagger'; export class PrintHtmlDto { - @ApiProperty({ - example: '

Titulo

Conteudo do pedido...

', - description: 'Conteúdo HTML para impressão' + @ApiProperty({ + example: '

Titulo

Conteudo do pedido...

', + description: 'Conteúdo HTML para impressão', }) html: string; - @ApiProperty({ - example: 'POS-80', - description: 'Nome da impressora onde será impresso o PDF gerado' + @ApiProperty({ + example: 'POS-80', + required: false, + description: + 'Nome da impressora. Obrigatorio ao usar /printer/print-html; ignorado ao usar /printer/:printerName/print-html.', }) - printerName: string; + printerName?: string; - @ApiProperty({ example: '60mm', required: false, description: 'Largura da etiqueta (default: 80mm)' }) + @ApiProperty({ + example: '60mm', + required: false, + description: 'Largura da etiqueta (default: 80mm)', + }) width?: string; - @ApiProperty({ example: '40mm', required: false, description: 'Altura da etiqueta (default: auto)' }) + @ApiProperty({ + example: '40mm', + required: false, + description: 'Altura da etiqueta (default: auto)', + }) height?: string; - @ApiProperty({ - example: 'impresso-12345', - required: false, - description: 'ID único do job para idempotência (evita duplicados)' + @ApiProperty({ + example: 'impresso-12345', + required: false, + description: 'ID único do job para idempotência (evita duplicados)', }) jobId?: string; } diff --git a/src/dto/print-result.dto.ts b/src/dto/print-result.dto.ts new file mode 100644 index 0000000..c86aa91 --- /dev/null +++ b/src/dto/print-result.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class PrintResultDto { + @ApiProperty({ example: true }) + success: boolean; + + @ApiProperty({ example: 'Print successful!' }) + message: string; +} diff --git a/src/dto/printer.dto.ts b/src/dto/printer.dto.ts index fee49c4..7be3bc3 100644 --- a/src/dto/printer.dto.ts +++ b/src/dto/printer.dto.ts @@ -1,18 +1,24 @@ import { ApiProperty } from '@nestjs/swagger'; export class PrinterDto { - @ApiProperty({ example: 'POS-80', description: 'Nome da impressora' }) - name: string; + @ApiProperty({ + example: 'POS-80', + description: 'Nome da impressora (retornado pelo PowerShell: Get-Printer)', + }) + Name: string; - @ApiProperty({ example: 'USB001', description: 'Porta da impressora (Win32)' }) - portName?: string; + @ApiProperty({ + example: 'Generic / Text Only', + required: false, + description: 'Nome do driver (retornado pelo PowerShell: Get-Printer)', + }) + DriverName?: string; - @ApiProperty({ example: 'Generic / Text Only', description: 'Nome do driver' }) - driverName?: string; - - @ApiProperty({ example: 0, description: 'Status da impressora (0 = Idle)' }) - printerStatus?: number; - - @ApiProperty({ example: 'POS-80', description: 'ID do dispositivo (usado para impressão)' }) - deviceId?: string; + @ApiProperty({ + example: 0, + required: false, + description: + 'Status da impressora (retornado pelo PowerShell: Get-Printer)', + }) + PrinterStatus?: number; } diff --git a/src/main.ts b/src/main.ts index cdbf065..9d8846f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,9 +18,11 @@ async function bootstrap() { app.use(urlencoded({ extended: true, limit: bodyLimit })); const config = new DocumentBuilder() - .setTitle('API Documentation') - .setDescription('The API description') - .setVersion('1.0') + .setTitle('Servico de Impressao') + .setDescription( + 'Microservico NestJS para impressao local (ESC/POS e HTML->PDF), com processamento assincrono via BullMQ/Redis e opcional integracao Oracle via TypeORM.', + ) + .setVersion('1.0.0') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document);