Skip to content

Commit e7b7881

Browse files
committed
Forbedrer feilhåndtering ved pdf-generering
1 parent e1a7d3f commit e7b7881

File tree

5 files changed

+66
-59
lines changed

5 files changed

+66
-59
lines changed

common/pruneString.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const pruneString = (str: string, maxLength: number) => {
2+
if (str.length <= maxLength) {
3+
return str;
4+
}
5+
6+
return `${str.slice(0, maxLength)} (...)`;
7+
};

server/src/cms/CmsArchiveSite.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,9 @@ export class CmsArchiveSite {
165165

166166
const { filename, data } = result;
167167

168-
const contentType = mime.lookup(filename) || 'application/octet-stream';
169-
170168
return res
171169
.setHeader('Content-Disposition', `attachment; filename="${filename}"`)
172-
.setHeader('Content-Type', contentType)
170+
.setHeader('Content-Type', 'application/pdf')
173171
.cookie(DOWNLOAD_COOKIE_NAME, true)
174172
.send(data);
175173
});
@@ -255,11 +253,9 @@ export class CmsArchiveSite {
255253
contentDisposition: string,
256254
res: Response
257255
) {
258-
const contentType = mime.lookup(filename) || 'application/octet-stream';
259-
260256
return res
261257
.setHeader('Content-Disposition', contentDisposition)
262-
.setHeader('Content-Type', contentType)
258+
.setHeader('Content-Type', mime.lookup(filename, 'application/octet-stream'))
263259
.send(Buffer.from(base64Data, 'base64'));
264260
}
265261
}

server/src/pdf/PdfGenerator.ts

+39-31
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { Browser } from 'puppeteer';
22
import { CmsArchiveContentService } from '../cms/CmsArchiveContentService';
3-
import { generatePdfFilename, generatePdfFooter, pixelWidthToA4Scale } from './pdf-utils';
3+
import {
4+
generateErrorFilename,
5+
generatePdfFilename,
6+
generatePdfFooter,
7+
pixelWidthToA4Scale,
8+
} from './pdf-utils';
49
import { CmsContent } from '../../../common/cms-documents/content';
510
import archiver from 'archiver';
611
import { Response } from 'express';
7-
import mime from 'mime';
812
import { DOWNLOAD_COOKIE_NAME } from '../../../common/downloadCookie';
913

1014
const DEFAULT_WIDTH_PX = 1024;
@@ -35,21 +39,27 @@ export class PdfGenerator {
3539
width: number = DEFAULT_WIDTH_PX
3640
) {
3741
if (versionKeys.length === 0) {
38-
return res.status(400).send();
42+
return res
43+
.status(400)
44+
.cookie(DOWNLOAD_COOKIE_NAME, false)
45+
.send('Version keys array must be non-empty');
3946
}
4047

4148
const contentVersions = await this.contentService.getContentVersions(versionKeys);
4249
if (!contentVersions || contentVersions.length === 0) {
43-
return res.status(404).cookie(DOWNLOAD_COOKIE_NAME, false).send();
50+
return res
51+
.status(404)
52+
.cookie(DOWNLOAD_COOKIE_NAME, false)
53+
.send('No content versions found');
4454
}
4555

4656
const newestVersion = contentVersions[0];
47-
const oldestVersion = contentVersions.slice(-1)[0];
57+
const oldestVersion = contentVersions[contentVersions.length - 1];
4858

49-
const zipFilename = `${newestVersion.name}_${newestVersion.meta.timestamp}-${oldestVersion.meta.timestamp}.zip`;
59+
const zipFilename = `${newestVersion.name}_${oldestVersion.meta.timestamp}-${newestVersion.meta.timestamp}.zip`;
5060

5161
res.setHeader('Content-Disposition', `attachment; filename="${zipFilename}"`)
52-
.setHeader('Content-Type', mime.lookup(zipFilename) || 'application/octet-stream')
62+
.setHeader('Content-Type', 'application/zip')
5363
.setHeader('Transfer-Encoding', 'chunked')
5464
.cookie(DOWNLOAD_COOKIE_NAME, true);
5565

@@ -68,20 +78,14 @@ export class PdfGenerator {
6878
continue;
6979
}
7080

71-
await this.generateContentPdf(content, width).then((pdf) => {
72-
if (!pdf) {
73-
return;
74-
}
75-
81+
await this.generateContentPdf(content, width).then((result) => {
7682
if (!res.headersSent) {
7783
// Set an estimate for content-length, which allows clients to track the download progress
7884
// This header is not according to spec for chunked responses, but browsers seem to respect it
79-
res.setHeader('Content-Length', pdf.length * contentVersions.length);
85+
res.setHeader('Content-Length', result.data.length * contentVersions.length);
8086
}
8187

82-
const fileName = generatePdfFilename(content);
83-
84-
archive.append(pdf, { name: fileName });
88+
archive.append(result.data, { name: result.filename });
8589
});
8690
}
8791

@@ -97,21 +101,18 @@ export class PdfGenerator {
97101
return null;
98102
}
99103

100-
const data = await this.generateContentPdf(content, width);
101-
if (!data) {
102-
return null;
103-
}
104-
105-
return {
106-
data,
107-
filename: generatePdfFilename(content),
108-
};
104+
return this.generateContentPdf(content, width);
109105
}
110106

111-
private async generateContentPdf(content: CmsContent, width: number): Promise<Buffer | null> {
112-
const { html } = content;
107+
private async generateContentPdf(content: CmsContent, width: number): Promise<PdfResult> {
108+
const { html, versionKey } = content;
113109
if (!html) {
114-
return null;
110+
return {
111+
data: Buffer.from(
112+
`Could not generate PDF from content version ${versionKey} - HTML field was empty`
113+
),
114+
filename: generateErrorFilename(content),
115+
};
115116
}
116117

117118
const widthActual = width >= MIN_WIDTH_PX ? width : DEFAULT_WIDTH_PX;
@@ -145,10 +146,17 @@ export class PdfGenerator {
145146

146147
await page.close();
147148

148-
return pdf;
149+
return {
150+
data: pdf,
151+
filename: generatePdfFilename(content),
152+
};
149153
} catch (e) {
150-
console.error(`Error while generating PDF - ${e}`);
151-
return null;
154+
const msg = `Error while generating PDF for content version ${versionKey} - ${e}`;
155+
console.error(msg);
156+
return {
157+
data: Buffer.from(msg),
158+
filename: generateErrorFilename(content),
159+
};
152160
}
153161
}
154162
}

server/src/pdf/pdf-utils.ts

+16-13
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
1-
import { CmsContent, CmsContentDocument } from '../../../common/cms-documents/content';
1+
import { CmsContent } from '../../../common/cms-documents/content';
22
import { formatTimestamp } from '../../../common/timestamp';
3+
import { pruneString } from '../../../common/pruneString';
34

45
const PUPPETEER_PDF_DPI = 96;
56
const A4_INCH_WIDTH = 8.27;
67
const SCALE_FACTOR = PUPPETEER_PDF_DPI * A4_INCH_WIDTH;
78

8-
const MIN = 0.1;
9-
const MAX = 2;
9+
const MIN_SCALE = 0.1;
10+
const MAX_SCALE = 2;
1011

11-
export const pixelWidthToA4Scale = (pxWidth: number): number =>
12-
Math.max(MIN, Math.min(MAX, (1 / pxWidth) * SCALE_FACTOR));
12+
export const pixelWidthToA4Scale = (pxWidth: number) =>
13+
Math.max(MIN_SCALE, Math.min(MAX_SCALE, (1 / pxWidth) * SCALE_FACTOR));
1314

1415
export const generatePdfFooter = (content: CmsContent) => `
15-
<div style="font-size: 10px; margin-bottom:-16px; padding: 4px; width: 100%; display: flex; justify-content: space-between; flex-wrap: nowrap">
16-
<div style="white-space: nowrap; overflow-x: hidden; text-overflow: ellipsis; flex-shrink: 1">
17-
${content.displayName}
18-
</div>
19-
<div style="white-space: nowrap; flex-shrink: 0">
20-
[${formatTimestamp(content.meta.timestamp)} // version: ${content.versionKey}] - (<span class="pageNumber"></span>/<span class="totalPages"></span>)
16+
<div style="font-size: 10px; margin-bottom:-16px; padding: 4px; width: 100%; display: flex; justify-content: space-between; white-space: nowrap">
17+
<div style="overflow-x: hidden; text-overflow: ellipsis">${pruneString(content.displayName, 110)}</div>
18+
<div>
19+
[Endret: ${formatTimestamp(content.meta.timestamp)} // Versjon: ${content.versionKey}] - (<span class="pageNumber"></span>/<span class="totalPages"></span>)
2120
</div>
2221
</div>`;
2322

24-
export const generatePdfFilename = (content: CmsContentDocument) =>
25-
`${content.meta.timestamp}_${content.name}_${content.contentKey}-${content.versionKey}.pdf`;
23+
const generateFilename = (content: CmsContent) =>
24+
`${content.meta.timestamp}_${content.name}_${content.contentKey}-${content.versionKey}`;
25+
26+
export const generatePdfFilename = (content: CmsContent) => `${generateFilename(content)}.pdf`;
27+
28+
export const generateErrorFilename = (content: CmsContent) => `${generateFilename(content)}.txt`;

src/components/main-section/content-view/version-selector/VersionSelector.tsx

+2-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CmsContent } from '../../../../../common/cms-documents/content';
44
import { useApiFetch } from '../../../../fetch/useApiFetch';
55
import { useAppState } from '../../../../context/app-state/useAppState';
66
import { formatTimestamp } from '../../../../../common/timestamp';
7+
import { pruneString } from '../../../../../common/pruneString';
78

89
const TITLE_MAX_LENGTH = 100;
910

@@ -39,18 +40,10 @@ export const VersionSelector = ({ content }: Props) => {
3940
const dateTime = formatTimestamp(version.timestamp);
4041
return (
4142
<option value={version.key} key={version.key}>
42-
{`${pruneTitle(version.title)} - [${dateTime}]`}
43+
{`${pruneString(version.title, TITLE_MAX_LENGTH)} - [${dateTime}]`}
4344
</option>
4445
);
4546
})}
4647
</Select>
4748
);
4849
};
49-
50-
const pruneTitle = (title: string) => {
51-
if (title.length < TITLE_MAX_LENGTH) {
52-
return title;
53-
}
54-
55-
return `${title.slice(0, TITLE_MAX_LENGTH)} (...)`;
56-
};

0 commit comments

Comments
 (0)