Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ET-33] Feature/certificate generator #18

Merged
merged 9 commits into from
Nov 15, 2023
1,057 changes: 991 additions & 66 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"init:db": "docker compose up -d && npx prisma generate && npx prisma migrate dev && npx prisma db seed",
"start": "node dist/main",
"start": "node dist/src/main",
"dev": "nest build --webpack --webpackPath webpack-hmr.config.js --watch",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"start:prod": "node dist/src/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
Expand All @@ -36,10 +36,13 @@
"ajv": "^6.12.6",
"bcrypt": "^5.1.1",
"class-validator": "^0.14.0",
"handlebars": "^4.7.8",
"jspdf": "^2.5.1",
"passport": "^0.6.0",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"puppeteer": "^21.5.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
Expand Down
9 changes: 8 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ConfigModule } from '@nestjs/config';
import { JobModule } from './job/job.module';
import { ContractModule } from './contract/contract.module';

@Module({
imports: [ConfigModule.forRoot(), AuthModule, UsersModule, JobModule],
imports: [
ConfigModule.forRoot(),
AuthModule,
UsersModule,
JobModule,
ContractModule,
],
controllers: [AppController],
providers: [AppService],
})
Expand Down
101 changes: 101 additions & 0 deletions src/contract/assets/template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{title}}</title>
<style>
html {
background-color: rgb(156, 156, 156);
}
body {
size : A4;
padding: 2cm;
background-color: white;
}
h1 {
text-align: center;
}
h3 {
text-align: center;
}
table {
margin-top: 0.25cm;
}
table tr td {
padding: 5px;
}
.signature {
display: flex;
justify-content: space-between;
margin-top: 2.5cm;
}
</style>
</head>
<body>
<div>
<h1>Surat Perjanjian Kerja Lepas</h1>
<h3>Nomor: {{id}}</h3>
<p>Yang bertanda tangan di bawah ini:</p>
<table>
<tr>
<td>UserId</td>
<td>:</td>
<td>{{userId}}</td>
</tr>
</table>
<p>Selanjutnya disebut sebagai <b>PEKERJA</b></p>
<p>Dengan ini menyatakan bahwa PEKERJA telah sepakat untuk bekerja pada <b>PERUSAHAAN</b> dengan ketentuan sebagai berikut:</p>
<table>
<tr>
<td>Judul lowongan pekerjaan</td>
<td>:</td>
<td>{{title}}</td>
</tr>
</table>
<p>Selanjutnya disebut sebagai <b>PERUSAHAAN</b></p>
<br>
<p>PEKERJA akan melakukan pekerjaan <b>{{title}}</b> dengan ketentuan sebagai berikut:</p>
<table>
<tr>
<td>Waktu Kerja</td>
<td>:</td>
<td>{{contract.work_time}}</td>
</tr>
<tr>
<td>Upah</td>
<td>:</td>
<td>{{contract.salary}}</td>
</tr>
<tr>
<td>Periode Pembayaran</td>
<td>:</td>
<td>{{contract.payment_method}}</td>
</tr>
<tr>
<td>Periode Kontrak</td>
<td>:</td>
<td>{{contract.periode}}</td>
</tr>
</table>
<p>Demikian surat perjanjian ini dibuat dan ditandatangani oleh kedua belah pihak pada tanggal {{contract.createdAt}}.</p>
<div class="signature">
<p>{{contract.worker.name}}</p>
<p>{{contract.company.name}}</p>
</div>
<br>
<br>
<table>
<tr>
<td>Created date</td>
<td>:</td>
<td>{{createdAt}}</td>
</tr>
<tr>
<td>Description</td>
<td>:</td>
<td>{{description}}</td>
</tr>
</table>
</div>
</body>
</html>
95 changes: 95 additions & 0 deletions src/contract/assets/template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
export default {
name: 'template',
template: `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
html {
background-color: rgb(156, 156, 156);
}
body {
size : A4;
padding: 2cm;
background-color: white;
}
h1 {
text-align: center;
}
h3 {
text-align: center;
}
table {
margin-top: 0.25cm;
}
table tr td {
padding: 5px;
}
.signature {
display: flex;
justify-content: space-between;
margin-top: 2.5cm;
}
</style>
</head>
<body>
<div>
<h1>Surat Perjanjian Kerja Lepas</h1>
<p>Yang bertanda tangan di bawah ini:</p>
<table>
<tr>
<td>Nama</td>
<td>:</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
</tr>
<tr>
<td>No. Telepon</td>
<td>:</td>
</tr>
</table>
<p>Selanjutnya disebut sebagai <b>PEKERJA</b></p>
<p>Dengan ini menyatakan bahwa PEKERJA telah sepakat untuk bekerja pada <b>PERUSAHAAN</b> dengan ketentuan sebagai berikut:</p>
<table>
<tr>
<td>Nama</td>
<td>:</td>
</tr>
<tr>
<td>Alamat</td>
<td>:</td>
</tr>
<tr>
<td>No. Telepon</td>
<td>:</td>
</tr>
</table>
<p>Selanjutnya disebut sebagai <b>PERUSAHAAN</b></p>
<br>
<table>
<tr>
<td>Waktu Kerja</td>
<td>:</td>
</tr>
<tr>
<td>Upah</td>
<td>:</td>
</tr>
<tr>
<td>Periode Pembayaran</td>
<td>:</td>
</tr>
<tr>
<td>Periode Kontrak</td>
<td>:</td>
</tr>
</table>
<div class="signature">
</div>
</div>
</body>
</html>`,
};
33 changes: 33 additions & 0 deletions src/contract/contract.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import {
Controller,
HttpStatus,
Post,
Get,
Res,
UseGuards,
StreamableFile,
Header,
Param,
} from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { Roles } from '../auth/roles/role.decorator';
Expand All @@ -13,6 +17,8 @@ import { RoleGuard } from '../auth/roles/role.guard';
import { Role } from '../auth/roles/role.enum';
import { ContractService } from './contract.service';
import { ContractCreateDto } from './dto/contract-create.dto';
import { createReadStream } from 'fs';
import { join } from 'path';

@ApiTags('Contract')
@Controller('contract')
Expand All @@ -31,4 +37,31 @@ export class ContractController {
return res.status(error.status).json({ error: error.message });
}
}

//9432dae7-ba04-410a-a4ff-27d6da87ae63
@Get('generate/:contractId')
@Roles(Role.USER)
@Header('Content-Type', 'application/pdf')
async generateContract(
@Res({ passthrough: true }) res,
@Param() params: any,
) {
const contractId = params.contractId;
try {
await this.contractService.generate(contractId);
const file = createReadStream(
join(process.cwd(), '/src/contract/temp/', `${params.contractId}.pdf`),
);
return await new StreamableFile(file);
} catch (error) {
console.error('#generateContract error caused by: ', error);
return res.status(error.status).json({ error: error.message });
} finally {
this.contractService.removeFile(contractId);
console.log(
'#generateContract removed residual file for contractId: ',
contractId,
);
}
}
}
47 changes: 47 additions & 0 deletions src/contract/contract.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Injectable } from '@nestjs/common';
import { compile } from 'handlebars';
import { launch } from 'puppeteer';
import { rm } from 'fs';
import { join } from 'path';
import { IContract } from './interface/contract.interface';

@Injectable()
export class ContractHelper {
// eslint-disable-next-line @typescript-eslint/no-empty-function
constructor() {}

async createPdf(contractData: IContract) {
const { template, ...data } = contractData;

const templates = compile(template);
const html = templates(data);
const pdfPath = join(
process.cwd(),
'/src/contract/temp/',
`${data.id}.pdf`,
);

const browser = await launch({
args: ['--no-sandbox'],
headless: true,
});
const page = await browser.newPage();

await page.goto(`data:text/html;charset=UTF-8,${html}`, {
waitUntil: 'networkidle0',
});
// await page.pdf({ path: pdfPath, format: 'A4' });
await page.pdf({ path: pdfPath });
await browser.close();
}

removeFile(path) {
rm(path, (err) => {
if (err) {
console.log(err);
} else {
console.log('#removeFile success');
}
});
}
}
8 changes: 7 additions & 1 deletion src/contract/contract.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { PrismaService } from '../prisma/prisma.service';
import { ContractService } from './contract.service';
import { ContractRepository } from './contract.repository';
import { ContractController } from './contract.controller';
import { ContractHelper } from './contract.helper';

@Module({
providers: [ContractService, PrismaService, ContractRepository],
providers: [
ContractService,
ContractHelper,
PrismaService,
ContractRepository,
],
exports: [ContractService],
controllers: [ContractController],
})
Expand Down
8 changes: 8 additions & 0 deletions src/contract/contract.repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { IContract } from './interface/contract.interface';

@Injectable()
export class ContractRepository {
Expand All @@ -14,4 +15,11 @@ export class ContractRepository {
data: contract,
});
}
async get(id: any): Promise<IContract> {
return this.prisma.contract.findUnique({
where: {
id,
},
});
}
}
Loading