parent
581d21d316
commit
7c918115c1
@ -1,68 +1,88 @@ |
||||
import { InjectQueue } from '@nestjs/bull'; |
||||
import { Body, Controller, Get, NotFoundException, Param, Post, Res, StreamableFile } from '@nestjs/common'; |
||||
import { |
||||
Body, |
||||
Controller, |
||||
Get, |
||||
NotFoundException, |
||||
Param, |
||||
Post, |
||||
Res, |
||||
StreamableFile, |
||||
} from '@nestjs/common'; |
||||
import { ApiResponse } from '@nestjs/swagger'; |
||||
import { Response } from 'express' |
||||
import { Readable } from 'stream'; |
||||
import { CreateJobRequestDto, GetJobResponseDto, JobStatusDto } from './screenshots.dto' |
||||
import { Response } from 'express'; |
||||
import { |
||||
CreateJobRequestDto, |
||||
GetJobResponseDto, |
||||
JobStatusDto, |
||||
} from './screenshots.dto'; |
||||
import { QUEUE_NAME, ScreenshotsQueue } from './shared'; |
||||
|
||||
@Controller('screenshots') |
||||
export class ScreenshotsController { |
||||
constructor(@InjectQueue(QUEUE_NAME) private readonly screenshotsQueue: ScreenshotsQueue) {} |
||||
constructor( |
||||
@InjectQueue(QUEUE_NAME) |
||||
private readonly screenshotsQueue: ScreenshotsQueue, |
||||
) {} |
||||
|
||||
@Post() |
||||
async createScreenshotJob(@Body() createScreenshotJobDto: CreateJobRequestDto): Promise<{ jobId: string }> { |
||||
async createScreenshotJob( |
||||
@Body() createScreenshotJobDto: CreateJobRequestDto, |
||||
): Promise<{ jobId: string }> { |
||||
const result = await this.screenshotsQueue.add({ |
||||
pageUrl: new URL(createScreenshotJobDto.pageUrl), |
||||
imageType: createScreenshotJobDto.imageType, |
||||
}) |
||||
}); |
||||
return { |
||||
jobId: result.id.toString() |
||||
} |
||||
jobId: result.id.toString(), |
||||
}; |
||||
} |
||||
|
||||
@Get(':id') |
||||
@ApiResponse({ status: 404 }) |
||||
async getJob(@Param('id') id: string): Promise<GetJobResponseDto> { |
||||
const jobInfo = await this.screenshotsQueue.getJob(id) |
||||
const jobInfo = await this.screenshotsQueue.getJob(id); |
||||
|
||||
if (!jobInfo) { |
||||
throw new NotFoundException() |
||||
throw new NotFoundException(); |
||||
} |
||||
|
||||
switch (await jobInfo.getState()) { |
||||
case 'completed': |
||||
return { status: JobStatusDto.Completed } |
||||
return { status: JobStatusDto.Completed }; |
||||
case 'failed': |
||||
return { status: JobStatusDto.Failed } |
||||
return { status: JobStatusDto.Failed }; |
||||
default: |
||||
return { status: JobStatusDto.Queued } |
||||
return { status: JobStatusDto.Queued }; |
||||
} |
||||
} |
||||
|
||||
@Get(':id/result') |
||||
@ApiResponse({ status: 404 }) |
||||
async getScreenshot(@Param('id') id: string, @Res({ passthrough: true }) res: Response): Promise<StreamableFile> { |
||||
const jobInfo = await this.screenshotsQueue.getJob(id) |
||||
async getScreenshot( |
||||
@Param('id') id: string, |
||||
@Res({ passthrough: true }) res: Response, |
||||
): Promise<StreamableFile> { |
||||
const jobInfo = await this.screenshotsQueue.getJob(id); |
||||
|
||||
if (!jobInfo) { |
||||
throw new NotFoundException() |
||||
throw new NotFoundException(); |
||||
} |
||||
|
||||
if (!await jobInfo.isCompleted()) { |
||||
throw new NotFoundException() |
||||
if (!(await jobInfo.isCompleted())) { |
||||
throw new NotFoundException(); |
||||
} |
||||
|
||||
switch (jobInfo.data.imageType) { |
||||
case 'jpeg': |
||||
res.setHeader('Content-Type', 'image/jpeg') |
||||
break |
||||
res.setHeader('Content-Type', 'image/jpeg'); |
||||
break; |
||||
case 'png': |
||||
res.setHeader('Content-Type', 'image/png') |
||||
break |
||||
res.setHeader('Content-Type', 'image/png'); |
||||
break; |
||||
} |
||||
|
||||
const buffer = Buffer.from(await jobInfo.returnvalue as string, 'base64') |
||||
return new StreamableFile(buffer) |
||||
const buffer = Buffer.from((await jobInfo.returnvalue) as string, 'base64'); |
||||
return new StreamableFile(buffer); |
||||
} |
||||
} |
||||
|
@ -1,26 +1,26 @@ |
||||
import { Injectable } from '@nestjs/common'; |
||||
import puppeteer from 'puppeteer' |
||||
import puppeteer from 'puppeteer'; |
||||
|
||||
type TakeScreenshotOptions = { |
||||
imageType: 'jpeg' | 'png' |
||||
url: URL |
||||
} |
||||
imageType: 'jpeg' | 'png'; |
||||
url: URL; |
||||
}; |
||||
|
||||
@Injectable() |
||||
export class ScreenshotterService { |
||||
async takeScreenshot(options: TakeScreenshotOptions): Promise<Buffer> { |
||||
const browser = await puppeteer.launch() |
||||
const browser = await puppeteer.launch(); |
||||
try { |
||||
const page = await browser.newPage() |
||||
await page.goto(options.url.toString()) |
||||
const image = await page.screenshot({ |
||||
const page = await browser.newPage(); |
||||
await page.goto(options.url.toString()); |
||||
const image = (await page.screenshot({ |
||||
type: options.imageType, |
||||
encoding: 'binary' |
||||
}) as Buffer |
||||
encoding: 'binary', |
||||
})) as Buffer; |
||||
|
||||
return image |
||||
return image; |
||||
} finally { |
||||
browser.close() |
||||
browser.close(); |
||||
} |
||||
} |
||||
} |
||||
|
@ -1,12 +1,12 @@ |
||||
import { Job, Queue } from "bull" |
||||
import { Job, Queue } from 'bull'; |
||||
|
||||
export const QUEUE_NAME = 'screenshots' |
||||
export const QUEUE_NAME = 'screenshots'; |
||||
|
||||
export type ScreenshotJobData = { |
||||
pageUrl: URL |
||||
imageType: 'jpeg' | 'png' |
||||
} |
||||
pageUrl: URL; |
||||
imageType: 'jpeg' | 'png'; |
||||
}; |
||||
|
||||
export type ScreenshotJob = Job<ScreenshotJobData> |
||||
export type ScreenshotJob = Job<ScreenshotJobData>; |
||||
|
||||
export type ScreenshotsQueue = Queue<ScreenshotJobData> |
||||
export type ScreenshotsQueue = Queue<ScreenshotJobData>; |
||||
|
@ -1,3 +1,3 @@ |
||||
const { toMatchImageSnapshot } = require('jest-image-snapshot'); |
||||
import { toMatchImageSnapshot } from 'jest-image-snapshot'; |
||||
|
||||
expect.extend({ toMatchImageSnapshot }); |
||||
|
Loading…
Reference in new issue