Skip to content

Commit 5309ad0

Browse files
committed
rewrite
1 parent 5013e81 commit 5309ad0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+854
-324
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ lerna-debug.log*
1414
# OS
1515
.DS_Store
1616

17+
.clinic
18+
1719
# Tests
1820
/coverage
1921
/.nyc_output

README.md

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,6 @@ Well, in terms of pure document generation chops (not talking about templating b
5353

5454
However, unlike those services, JSReports does not gatekeep **ANY** functionality behind payment tiers. Rather, their business model is oriented around usage of their 'Studio' template builder GUI, which has SaaS and self-hosted versions, while the JSReport server code itself is completely open source and accessible.
5555

56-
So, by making use of the [jsreport core package](https://github.com/jsreport/jsreport/tree/master/packages/jsreport-core), and with shockingly little effort, you can have self-hosted document generation that's on-par with any SaaS out there. **For $0.00.**
56+
So, by making use of the [jsreport core package](https://github.com/jsreport/jsreport/tree/master/packages/jsreport-core) you can have self-hosted document generation that's on-par with any SaaS out there.
5757

58-
The [JSReport Studio GUI](https://playground.jsreport.net/w/admin/S3xqZ0Zc) is actually quite good, if that's something you're looking for. Very 'developer oriented' and flexible, unlike other templating backends I've tried (which are usually intended for document creators, not devs).
59-
60-
## Notes
61-
62-
- Passing a string of javascript to create custom template handlers seems...kinda weird? Am I missing something? I suppose it makes more sense in the context of the Studio GUI.
63-
64-
- Note how the jsreport instance config affects the asset placeholder paths in [template.html](src/jsreport/examples/flight-ticket/template.html). Specifically the `rootDirectory` option:
65-
66-
```typescript
67-
this.jsreport = require('@jsreport/jsreport-core')({
68-
sandbox: { allowedModules: '*' },
69-
rootDirectory: __dirname,
70-
extensions: {
71-
assets: {
72-
allowedFiles: '**/*.*',
73-
searchOnDiskIfNotFoundInStore: true,
74-
},
75-
},
76-
});
77-
```
58+
The [JSReport Studio GUI](https://playground.jsreport.net/w/admin/S3xqZ0Zc) is actually quite good, if that's something you're looking for.

nest-cli.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"$schema": "https://json.schemastore.org/nest-cli",
33
"collection": "@nestjs/schematics",
44
"sourceRoot": "src",
5-
"compilerOptions": {"assets": ["**/template.*", "**/*.png", "**/*.css"]}
5+
"compilerOptions": {"assets": ["**/template.*", "**/*.png", "**/*.css", "**/*.config.json"]}
66
}

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,23 @@
2424
"@jsreport/jsreport-chrome-pdf": "^3.3.0",
2525
"@jsreport/jsreport-core": "^3.11.4",
2626
"@jsreport/jsreport-docx": "^3.7.1",
27+
"@jsreport/jsreport-fs-store": "^3.2.4",
2728
"@jsreport/jsreport-handlebars": "^3.2.1",
2829
"@jsreport/jsreport-html-to-xlsx": "^3.3.1",
2930
"@jsreport/jsreport-unoconv": "^3.0.1",
3031
"@jsreport/jsreport-xlsx": "^3.4.0",
3132
"@nestjs/common": "^9.4.2",
3233
"@nestjs/core": "^9.4.2",
3334
"@nestjs/platform-express": "^9.4.2",
35+
"@nestjs/terminus": "^9.2.2",
3436
"bwip-js": "^3.4.0",
3537
"handlebars": "^4.7.7",
3638
"lodash": "^4.17.21",
3739
"moment": "^2.29.4",
3840
"puppeteer": "^20.4.0",
3941
"reflect-metadata": "^0.1.13",
4042
"rxjs": "^7.8.1",
43+
"v8": "^0.1.0",
4144
"xlsx": "^0.18.5"
4245
},
4346
"devDependencies": {

src/app.module.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Module } from '@nestjs/common';
22
import { AppController } from './app.controller';
3-
import { JsReportModule } from './jsreport/jsreport.module';
4-
3+
import { JsReportModule } from './js-report/js-report.module';
54
@Module({
65
imports: [JsReportModule],
76
controllers: [AppController],
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { JsReporter } from '../js-report.provider';
2+
import fs from 'fs';
3+
4+
export class AssetHelper {
5+
private readonly jsReport: JsReporter;
6+
7+
constructor(jsReport: JsReporter) {
8+
this.jsReport = jsReport;
9+
}
10+
11+
async stored(name: string) {
12+
const result = await this.jsReport.documentStore
13+
.collection('assets')
14+
.find({ name });
15+
16+
return result.length > 0;
17+
}
18+
19+
async insert(asset: {
20+
name: string;
21+
path?: string;
22+
content?: any;
23+
encoding?: string;
24+
}) {
25+
const isStored = await this.stored(asset.name);
26+
27+
if (!isStored) {
28+
if (asset.path && !asset.content) {
29+
asset.content = fs.readFileSync(asset.path, asset.encoding as any);
30+
}
31+
}
32+
33+
await this.jsReport.documentStore.collection('assets').insert(asset);
34+
}
35+
36+
async insertAll(
37+
assets: Array<{
38+
name: string;
39+
path?: string;
40+
content?: any;
41+
encoding?: string;
42+
}>,
43+
) {
44+
for (const asset of assets) {
45+
await this.insert(asset);
46+
}
47+
}
48+
49+
async upsert(asset: {
50+
name: string;
51+
path?: string;
52+
content?: any;
53+
encoding?: string;
54+
}) {
55+
const isStored = await this.stored(asset.name);
56+
57+
if (!isStored) {
58+
await this.insert(asset);
59+
} else {
60+
if (asset.path && !asset.content) {
61+
asset.content = fs.readFileSync(asset.path, asset.encoding as any);
62+
}
63+
64+
await this.jsReport.documentStore
65+
.collection('assets')
66+
.update(
67+
{ name: asset.name },
68+
{ $set: { content: asset.content, encoding: asset.encoding } },
69+
);
70+
}
71+
}
72+
73+
async remove(name: string) {
74+
const isStored = await this.stored(name);
75+
76+
if (isStored) {
77+
await this.jsReport.documentStore.collection('assets').remove({ name });
78+
}
79+
}
80+
81+
async find(name: string) {
82+
const isStored = await this.stored(name);
83+
84+
if (isStored) {
85+
const found = await this.jsReport.documentStore
86+
.collection('assets')
87+
.find({ name });
88+
89+
return found[found.length - 1] as {
90+
name: string;
91+
path?: string;
92+
content?: any;
93+
encoding?: string;
94+
};
95+
}
96+
}
97+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { JsReporter } from '../js-report.provider';
2+
3+
import { Named } from '../types';
4+
5+
export class TemplateHelper {
6+
private readonly jsReport: JsReporter;
7+
8+
constructor(jsReport: JsReporter) {
9+
this.jsReport = jsReport;
10+
}
11+
12+
async stored(name: string) {
13+
const result = await this.jsReport.documentStore
14+
.collection('templates')
15+
.find({ name });
16+
17+
return result.length > 0;
18+
}
19+
20+
async insertFn<T = unknown>(name: string, templateFn: () => Named & T) {
21+
const isStored = await this.stored(name);
22+
if (!isStored) {
23+
await this.insert(templateFn());
24+
}
25+
}
26+
27+
async insert<T = unknown>(template: Named & T) {
28+
const isStored = await this.stored(template.name);
29+
if (!isStored) {
30+
await this.jsReport.documentStore
31+
.collection('templates')
32+
.insert(template);
33+
}
34+
}
35+
36+
async upsert<T = unknown>(template: Named & T) {
37+
const isStored = await this.stored(template.name);
38+
39+
if (!isStored) {
40+
await this.insert(template);
41+
} else {
42+
await this.jsReport.documentStore
43+
.collection('templates')
44+
.update({ name: template.name }, { $set: template });
45+
}
46+
}
47+
48+
async remove(name: string) {
49+
const isStored = await this.stored(name);
50+
51+
if (isStored) {
52+
await this.jsReport.documentStore
53+
.collection('templates')
54+
.remove({ name });
55+
}
56+
}
57+
58+
async find<T = unknown>(name: string) {
59+
const isStored = await this.stored(name);
60+
61+
if (isStored) {
62+
const found = await this.jsReport.documentStore
63+
.collection('templates')
64+
.find({ name });
65+
66+
return found[found.length - 1] as Named & T;
67+
}
68+
}
69+
}
Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
import { Controller, Res, Get, Query, StreamableFile } from '@nestjs/common';
2-
import { JsReportService } from './jsreport.service';
32
import { Response } from 'express';
3+
import * as Reports from './reports';
4+
import { JsReportResult } from './types';
45

56
@Controller('reports')
67
export class JsReportController {
7-
constructor(private readonly jsreport: JsReportService) {}
8+
constructor(
9+
private student: Reports.StudentReport,
10+
private population: Reports.PopulationReport,
11+
private ticket: Reports.TicketReport,
12+
private html: Reports.HtmlToXlsxReport,
13+
private invoice: Reports.InvoiceReport,
14+
private invoiceXlsx: Reports.InvoiceXlsxReport,
15+
) {}
16+
17+
getContentHeaders(result: JsReportResult, pdf = false, defaultType = 'docx') {
18+
return {
19+
'Content-Type': pdf
20+
? 'application/pdf'
21+
: `${result.meta.contentType}; charset=utf-8`,
22+
'Content-Disposition': `attachment; filename="${result.meta.reportName}.${
23+
pdf ? 'pdf' : defaultType
24+
}"`,
25+
'Content-Length': result.content.byteLength,
26+
};
27+
}
828

929
// student docx ///////////////////////////
1030
@Get('/student')
@@ -14,9 +34,9 @@ export class JsReportController {
1434
) {
1535
const pdf = query?.pdf === '1' ? true : false;
1636

17-
const result = await this.jsreport.renderStudent(pdf);
37+
const result = await this.student.render(pdf);
1838

19-
res.set(this.jsreport.getContentHeaders(result, pdf, 'docx'));
39+
res.set(this.getContentHeaders(result, pdf, 'docx'));
2040

2141
return new StreamableFile(result.stream as any);
2242
}
@@ -29,9 +49,9 @@ export class JsReportController {
2949
) {
3050
const pdf = query?.pdf === '1' ? true : false;
3151

32-
const result = await this.jsreport.renderInvoice(pdf);
52+
const result = await this.invoice.render(pdf);
3353

34-
res.set(this.jsreport.getContentHeaders(result, pdf, 'docx'));
54+
res.set(this.getContentHeaders(result, pdf, 'docx'));
3555

3656
return new StreamableFile(result.stream as any);
3757
}
@@ -44,9 +64,9 @@ export class JsReportController {
4464
) {
4565
const pdf = query?.pdf === '1' ? true : false;
4666

47-
const result = await this.jsreport.renderInvoiceSheet(pdf);
67+
const result = await this.invoiceXlsx.render(pdf);
4868

49-
res.set(this.jsreport.getContentHeaders(result, pdf, 'xlsx'));
69+
res.set(this.getContentHeaders(result, pdf, 'xlsx'));
5070

5171
return new StreamableFile(result.stream as any);
5272
}
@@ -59,9 +79,9 @@ export class JsReportController {
5979
) {
6080
const pdf = query?.pdf === '1' ? true : false;
6181

62-
const result = await this.jsreport.renderPopulation(pdf);
82+
const result = await this.population.render(pdf);
6383

64-
res.set(this.jsreport.getContentHeaders(result, pdf, 'xlsx'));
84+
res.set(this.getContentHeaders(result, pdf, 'xlsx'));
6585

6686
return new StreamableFile(result.stream as any);
6787
}
@@ -74,17 +94,17 @@ export class JsReportController {
7494
) {
7595
const pdf = query?.pdf === '1' ? true : false;
7696

77-
const result = await this.jsreport.renderHtmlToXlsx(pdf);
97+
const result = await this.html.render(pdf);
7898

79-
res.set(this.jsreport.getContentHeaders(result, pdf, 'xlsx'));
99+
res.set(this.getContentHeaders(result, pdf, 'xlsx'));
80100

81101
return new StreamableFile(result.stream as any);
82102
}
83103

84104
// ticket chrome-pdf (html to pdf) ///////////////////////////
85105
@Get('/ticket')
86106
async ticketReport(@Res({ passthrough: true }) res: Response) {
87-
const result = await this.jsreport.renderTicket();
107+
const result = await this.ticket.render();
88108

89109
res.set({
90110
'Content-Type': `application/pdf`,

src/js-report/js-report.module.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Module } from '@nestjs/common';
2+
import { JsReportProvider } from './js-report.provider';
3+
import { JsReportService } from './js-report.service';
4+
import * as Reports from './reports';
5+
import { JsReportController } from './js-report.controller';
6+
7+
@Module({
8+
providers: [
9+
JsReportProvider,
10+
JsReportService,
11+
Reports.HtmlToXlsxReport,
12+
Reports.InvoiceReport,
13+
Reports.InvoiceXlsxReport,
14+
Reports.PopulationReport,
15+
Reports.PopulationReport,
16+
Reports.StudentReport,
17+
Reports.TicketReport,
18+
],
19+
controllers: [JsReportController],
20+
})
21+
export class JsReportModule {}

0 commit comments

Comments
 (0)