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