Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
bbe01cd231
|
|||
|
10c68cfb37
|
|||
|
c21076a049
|
|||
|
fc774d5af3
|
|||
|
dd0b60b2ba
|
|||
|
6add09c7df
|
|||
|
d064b51e1b
|
|||
|
744f567b7c
|
|||
|
7dbce9e870
|
|||
|
72ed2495f6
|
|||
|
05e0c63931
|
|||
| fcbcec85c5 | |||
|
b47b2f804f
|
|||
|
5f4fc74cd9
|
|||
| 300b8fd01a | |||
| 8b951dfb3f | |||
|
517e8a0819
|
|||
|
3af4b521d3
|
|||
| dca2829323 | |||
| 57a84c256a | |||
| b78534e1b0 | |||
| 9b85b54da5 | |||
| 4b5a86282d | |||
|
c9cb03ea95
|
|||
| c7f57548f3 | |||
| 8d00307170 | |||
| 5e92b9a48f | |||
| 01e1323555 | |||
| f8465721cd | |||
| 4cea7cb32f | |||
|
72303b1105
|
|||
| 451b7fbe05 | |||
|
2a3322612d
|
|||
| 4b4d66ae78 | |||
| c935950eb0 | |||
| 573b921197 | |||
|
274c13e358
|
|||
| ff0421da2f | |||
| 915baa6efa | |||
| bac004d74e | |||
| b7b7f6a0ae | |||
| 11efdebacf | |||
| 0f2d6f58d6 | |||
| df8bd1133b | |||
| 22fb3edd78 | |||
| ded610f114 | |||
| a4c8dade23 | |||
| b6fc069042 | |||
| 60cc343adf | |||
| 010f2046ad | |||
| c18cb7f135 | |||
| 2e7c3e8a5b | |||
| ac9be793bd | |||
| c18fc4ec93 | |||
| 981bae4786 | |||
| 754d0ca58c | |||
| fa26ed6012 | |||
| cc4a2b4ab4 | |||
| e97e209746 | |||
| 8f30d8933f | |||
| f78037c0f1 | |||
| 3c02e13997 | |||
| d8f3a6ed06 | |||
| 2ee4c06055 | |||
| 76418f65e1 | |||
| a57e0909b9 |
89
.drone.yml
89
.drone.yml
@@ -33,53 +33,23 @@ steps:
|
||||
- git clone $DRONE_REMOTE_URL .
|
||||
- git checkout dev
|
||||
- name: build dev
|
||||
image: plugins/docker
|
||||
depends_on: [clone]
|
||||
depends_on: ["clone"]
|
||||
image: registry.odit.services/library/drone-kaniko
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: registry.odit.services/lfk/document-server
|
||||
build_args:
|
||||
- NPM_REGISTRY_DOMAIN:
|
||||
from_secret: npmjs_domain
|
||||
- NPM_REGISTRY_TOKEN:
|
||||
from_secret: npmjs_token
|
||||
repo: lfk/document-server
|
||||
tags:
|
||||
- dev
|
||||
cache: true
|
||||
registry: registry.odit.services
|
||||
mtu: 1000
|
||||
- name: run changelog export
|
||||
depends_on: ["clone"]
|
||||
image: node:latest
|
||||
commands:
|
||||
- npx auto-changelog --commit-limit false -p -u --hide-credit
|
||||
- name: push new changelog to repo
|
||||
depends_on: ["run changelog export"]
|
||||
image: appleboy/drone-git-push
|
||||
settings:
|
||||
branch: dev
|
||||
commit: true
|
||||
commit_message: 🧾New changelog file version [CI SKIP] [skip ci]
|
||||
author_email: bot@odit.services
|
||||
remote: git@git.odit.services:lfk/document-server.git
|
||||
ssh_key:
|
||||
from_secret: git_ssh
|
||||
- name: run full license export
|
||||
depends_on: ["clone"]
|
||||
image: node:14.15.1-alpine3.12
|
||||
commands:
|
||||
- yarn
|
||||
- yarn licenses:export
|
||||
- name: push new licenses file to repo
|
||||
depends_on: ["run full license export"]
|
||||
image: appleboy/drone-git-push
|
||||
settings:
|
||||
branch: dev
|
||||
commit: true
|
||||
commit_message: 📖New license file version [CI SKIP] [skip ci]
|
||||
author_email: bot@odit.services
|
||||
remote: git@git.odit.services:lfk/document-server.git
|
||||
skip_verify: true
|
||||
ssh_key:
|
||||
from_secret: git_ssh
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- dev
|
||||
@@ -99,30 +69,24 @@ steps:
|
||||
commands:
|
||||
- git clone $DRONE_REMOTE_URL .
|
||||
- git checkout dev
|
||||
- git merge main
|
||||
- git checkout main
|
||||
- name: build latest
|
||||
- name: build dev
|
||||
depends_on: ["clone"]
|
||||
image: plugins/docker
|
||||
image: registry.odit.services/library/drone-kaniko
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: registry.odit.services/lfk/document-server
|
||||
build_args:
|
||||
- NPM_REGISTRY_DOMAIN:
|
||||
from_secret: npmjs_domain
|
||||
- NPM_REGISTRY_TOKEN:
|
||||
from_secret: npmjs_token
|
||||
repo: lfk/document-server
|
||||
tags:
|
||||
- latest
|
||||
cache: true
|
||||
registry: registry.odit.services
|
||||
mtu: 1000
|
||||
- name: push merge to repo
|
||||
depends_on: ["clone"]
|
||||
image: appleboy/drone-git-push
|
||||
settings:
|
||||
branch: dev
|
||||
commit: false
|
||||
remote: git@git.odit.services:lfk/document-server.git
|
||||
ssh_key:
|
||||
from_secret: git_ssh
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
@@ -137,18 +101,23 @@ name: build:tags
|
||||
|
||||
steps:
|
||||
- name: build $DRONE_TAG
|
||||
image: plugins/docker
|
||||
depends_on: [clone]
|
||||
depends_on: ["clone"]
|
||||
image: registry.odit.services/library/drone-kaniko
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: registry.odit.services/lfk/document-server
|
||||
build_args:
|
||||
- NPM_REGISTRY_DOMAIN:
|
||||
from_secret: npmjs_domain
|
||||
- NPM_REGISTRY_TOKEN:
|
||||
from_secret: npmjs_token
|
||||
repo: lfk/document-server
|
||||
tags:
|
||||
- '${DRONE_TAG}'
|
||||
- "${DRONE_TAG}"
|
||||
cache: true
|
||||
registry: registry.odit.services
|
||||
mtu: 1000
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
- tag
|
||||
|
||||
78
CHANGELOG.md
78
CHANGELOG.md
@@ -2,8 +2,83 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||
|
||||
#### [v0.6.1](https://git.odit.services/lfk/document-server/compare/v0.6.0...v0.6.1)
|
||||
|
||||
- Dependency cleanup [`c21076a`](https://git.odit.services/lfk/document-server/commit/c21076a049484e353735db8542a932d93d0520c1)
|
||||
- Dependency Bumps [`fc774d5`](https://git.odit.services/lfk/document-server/commit/fc774d5af39d7b6db999e85f2be8e109400cf862)
|
||||
- Slimmed down docker build thx to copy [`10c68cf`](https://git.odit.services/lfk/document-server/commit/10c68cfb37025642688cde40cac69a8ff7f6c513)
|
||||
|
||||
#### [v0.6.0](https://git.odit.services/lfk/document-server/compare/v0.5.1...v0.6.0)
|
||||
|
||||
> 23 February 2023
|
||||
|
||||
- Merge branch 'bugfix/44-runner-certificates-result-in-a-status-500' into dev [`#44`](https://git.odit.services/lfk/document-server/issues/44)
|
||||
- Lockfile [`b47b2f8`](https://git.odit.services/lfk/document-server/commit/b47b2f804f5419bff6f0cddbc1dbc12d29afd935)
|
||||
- Fixed Locale comma format [`2a33226`](https://git.odit.services/lfk/document-server/commit/2a3322612d473bd9002cf8d6f9807f9dc7d687da)
|
||||
- Added autochangelog [`d064b51`](https://git.odit.services/lfk/document-server/commit/d064b51e1b3e00d9c49b1e983bd75def6d8512be)
|
||||
- Moved drone to kaniko [`744f567`](https://git.odit.services/lfk/document-server/commit/744f567b7cc7fcbe916143bb98f1dd784fe794da)
|
||||
- Updated translation due to customer request [`517e8a0`](https://git.odit.services/lfk/document-server/commit/517e8a081970b9ce4f6f225a7f9b5b54cca1258e)
|
||||
- 🚀Bumped version to v0.6.0 [`dd0b60b`](https://git.odit.services/lfk/document-server/commit/dd0b60b2ba98efaeaeeea3b056f6937c5579598a)
|
||||
- Bumped docker images [`05e0c63`](https://git.odit.services/lfk/document-server/commit/05e0c639319a2a635bbcf0f5f6bd08d6a46dce74)
|
||||
- wrap distanceDonations.reduce in array length check [`bac004d`](https://git.odit.services/lfk/document-server/commit/bac004d74eb954d1753d4efcdb927822b89fa757)
|
||||
- Nerf [`7dbce9e`](https://git.odit.services/lfk/document-server/commit/7dbce9e87046567de1ea46deaea427a5353057b3)
|
||||
- Version pin [`6add09c`](https://git.odit.services/lfk/document-server/commit/6add09c7df6872b6922f6624ee89b4a04ad6fc0a)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`300b8fd`](https://git.odit.services/lfk/document-server/commit/300b8fd01a0b601935c3658b195759dd19041c5f)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`ff0421d`](https://git.odit.services/lfk/document-server/commit/ff0421da2f16a8f79f9987dabea7bdcb4ef88c05)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`fcbcec8`](https://git.odit.services/lfk/document-server/commit/fcbcec85c531d774be1337f179dae2aa1d5f3839)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`b78534e`](https://git.odit.services/lfk/document-server/commit/b78534e1b0165522ff0ee1dfd110d8ba78f183d8)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`5e92b9a`](https://git.odit.services/lfk/document-server/commit/5e92b9a48fcbb8ab6a179c53f180a7a6bd743ae7)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`dca2829`](https://git.odit.services/lfk/document-server/commit/dca2829323f3a19247455df99498633605d09603)
|
||||
- Fixed decimal separator in docker [`c9cb03e`](https://git.odit.services/lfk/document-server/commit/c9cb03ea95acccd6cdc8b86c747c787938840d07)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`451b7fb`](https://git.odit.services/lfk/document-server/commit/451b7fbe0543991e8a203e38daa350a954ae0e11)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`573b921`](https://git.odit.services/lfk/document-server/commit/573b9211972a55df0a38742cb6eb789d6fd3717b)
|
||||
- Added image [`5f4fc74`](https://git.odit.services/lfk/document-server/commit/5f4fc74cd9ff6764d209d49822020cda70b3514e)
|
||||
- 🚀Bumped version to v0.6.0 [`72ed249`](https://git.odit.services/lfk/document-server/commit/72ed2495f6feb90643d3c9d7fa943a2703533331)
|
||||
- Changed default kilometer formatting to min 2 decimals, max 3 [`3af4b52`](https://git.odit.services/lfk/document-server/commit/3af4b521d302e3aab558d20f1fccca57ea61fff3)
|
||||
- Merge pull request 'Fixed decimal separator' (#47) from dev into main [`57a84c2`](https://git.odit.services/lfk/document-server/commit/57a84c256a978f583508bdf772499466a73b4a22)
|
||||
- 🚀Bumped version to v0.5.4 [`4b5a862`](https://git.odit.services/lfk/document-server/commit/4b5a86282d0618a0c5b00fc796efe77db2103356)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`c7f5754`](https://git.odit.services/lfk/document-server/commit/c7f57548f316e1bb6635bd56bd269d80ac1e220f)
|
||||
- Merge pull request 'Hotfixes' (#46) from dev into main [`8d00307`](https://git.odit.services/lfk/document-server/commit/8d003071704b5a6d7b0d70aff2f9cb05c3660b78)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`f846572`](https://git.odit.services/lfk/document-server/commit/f8465721cddfb55d51eb30d29d74ef63d825b5ac)
|
||||
- Fix for runner donation array [`72303b1`](https://git.odit.services/lfk/document-server/commit/72303b11052276ad15373887f9e04183841f56f4)
|
||||
- 🚀Bumped version to v0.5.3 [`01e1323`](https://git.odit.services/lfk/document-server/commit/01e1323555fe67f6f0ce3c18163e475035bd1cdd)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`4b4d66a`](https://git.odit.services/lfk/document-server/commit/4b4d66ae784150f7e1cc491a3fc5d84c93273aee)
|
||||
- Merge pull request 'v0.5.2: hotfix TypeError in Runner Certificate generation' (#45) from dev into main [`c935950`](https://git.odit.services/lfk/document-server/commit/c935950eb052bce71185fc74c750ec77f081e7df)
|
||||
- 🚀Bumped version to v0.5.2 [`274c13e`](https://git.odit.services/lfk/document-server/commit/274c13e358f16207fe8bb5cdc1b9ede0582ecb46)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`b7b7f6a`](https://git.odit.services/lfk/document-server/commit/b7b7f6a0ae304d24f90a3de3931f53cf08770060)
|
||||
|
||||
#### [v0.5.1](https://git.odit.services/lfk/document-server/compare/v0.5.0...v0.5.1)
|
||||
|
||||
> 22 April 2021
|
||||
|
||||
- Merge pull request 'Release 0.5.1' (#43) from dev into main [`11efdeb`](https://git.odit.services/lfk/document-server/commit/11efdebacf076ecfe0e10cdcda37ac07464901ce)
|
||||
- Quick callstack fix🛠 [`76418f6`](https://git.odit.services/lfk/document-server/commit/76418f65e1e111e83838f0d42c541ae6a8063a09)
|
||||
- Fixed barcode generation for runenrcard pdfs🐞 [`f78037c`](https://git.odit.services/lfk/document-server/commit/f78037c0f15162d5b98986edf20d263961f43e69)
|
||||
- Updated docker-compose example🐳 [`a4c8dad`](https://git.odit.services/lfk/document-server/commit/a4c8dade23e448d4d4caefe304a6cd9195c873a4)
|
||||
- Now laoding card subtitle from env [`e97e209`](https://git.odit.services/lfk/document-server/commit/e97e2097463f4c88947e865a38ea1e5aa2be1f5d)
|
||||
- typo fixes [`fa26ed6`](https://git.odit.services/lfk/document-server/commit/fa26ed6012ded759d3702587dba67c2090324801)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`2ee4c06`](https://git.odit.services/lfk/document-server/commit/2ee4c060557a44db1974a015412288f7942ebe72)
|
||||
- more typo fixes [`981bae4`](https://git.odit.services/lfk/document-server/commit/981bae4786a2fa12a1355122e8c5a1e95e29cf32)
|
||||
- You can now configure the card's code format distinct from the others [`ac9be79`](https://git.odit.services/lfk/document-server/commit/ac9be793bd598771174f5313ef8288240306ba5c)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`0f2d6f5`](https://git.odit.services/lfk/document-server/commit/0f2d6f58d6a8a8888263778cf1a14c73b28e774e)
|
||||
- 🚀Bumped version to v0.5.1 [`22fb3ed`](https://git.odit.services/lfk/document-server/commit/22fb3edd7836ba4ca35e6b208ab6f6620da60f4a)
|
||||
- Emoji+Chinese fixes🌍 [`b6fc069`](https://git.odit.services/lfk/document-server/commit/b6fc069042dc9c5d7ec97f2660568e8e105780b9)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`cc4a2b4`](https://git.odit.services/lfk/document-server/commit/cc4a2b4ab4c2cb9976797f93e8348607fb88ea7d)
|
||||
- Dependenc bump 🔝 [`d8f3a6e`](https://git.odit.services/lfk/document-server/commit/d8f3a6ed063a9cdf6189e85ae01a5516b4295892)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`a57e090`](https://git.odit.services/lfk/document-server/commit/a57e0909b919a1c720c9994b6baa8910b78dc569)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`ded610f`](https://git.odit.services/lfk/document-server/commit/ded610f11464a27429b8184a32554e99aed63f72)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`60cc343`](https://git.odit.services/lfk/document-server/commit/60cc343adf71ed3b849d1d93af3d60cbc2820fed)
|
||||
- Added new config options to reamde [`010f204`](https://git.odit.services/lfk/document-server/commit/010f2046ad326898c75b6546e4d70a6f78346d8b)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`2e7c3e8`](https://git.odit.services/lfk/document-server/commit/2e7c3e8a5b7f6a0461254b33c6f412929719c966)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`754d0ca`](https://git.odit.services/lfk/document-server/commit/754d0ca58ccf8f77570ff6218f2dec61cfb4f808)
|
||||
- Fixed typo in translation [`8f30d89`](https://git.odit.services/lfk/document-server/commit/8f30d8933f105b4bf112c81222a72ca1931145d7)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`3c02e13`](https://git.odit.services/lfk/document-server/commit/3c02e13997b1626fb0e6496da4c58eac2cc6fcf8)
|
||||
|
||||
#### [v0.5.0](https://git.odit.services/lfk/document-server/compare/v0.4.3...v0.5.0)
|
||||
|
||||
> 31 March 2021
|
||||
|
||||
- Merge pull request 'Release 0.5.0' (#42) from dev into main [`a81db03`](https://git.odit.services/lfk/document-server/commit/a81db03ba3b274c44be4b4c0c318083bdeb07987)
|
||||
- Added translations [`ac572f1`](https://git.odit.services/lfk/document-server/commit/ac572f1ea31cb66985e04cb5d56cc67f521e990d)
|
||||
- Added translations [`7fea1ca`](https://git.odit.services/lfk/document-server/commit/7fea1ca78ff6fdbb38dee0edd9918eaeb1264d18)
|
||||
- Sorted translations 🌍 [`2278e4a`](https://git.odit.services/lfk/document-server/commit/2278e4ad06947b540323856ea1e71022562ea719)
|
||||
@@ -23,11 +98,12 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
- Added template strings [`2b21957`](https://git.odit.services/lfk/document-server/commit/2b2195727b15b8666edf0d925f2e68a98030153d)
|
||||
- Fixed background opacity [`2a4cfdb`](https://git.odit.services/lfk/document-server/commit/2a4cfdb2f88ad3ac1ebc925199a440756e9e9d3a)
|
||||
- Now with embedded background [`64fce5b`](https://git.odit.services/lfk/document-server/commit/64fce5bd019a00bf34c1ebd133c1904bb577b67b)
|
||||
- Made footer text configureable [`63c7beb`](https://git.odit.services/lfk/document-server/commit/63c7beb8b9cdc564186c5b86a4f305c8575f5b9f)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`7ae4750`](https://git.odit.services/lfk/document-server/commit/7ae47503076f6721d1cfd82fbf8218b9febfa580)
|
||||
- 🚀Bumped version to v0.5.0 [`f623c0a`](https://git.odit.services/lfk/document-server/commit/f623c0a7cd06f707ac488456c9e8a051d3ceae46)
|
||||
- Merge pull request 'Generate runner certificates feature/36-runner_certificates' (#41) from feature/36-runner_certificates into dev [`d3f7d1a`](https://git.odit.services/lfk/document-server/commit/d3f7d1a6c9858d7fdf09c696622962e6f8471e78)
|
||||
- disabled testing for now [`cec8930`](https://git.odit.services/lfk/document-server/commit/cec893032dea9f312e37841232a9434e19b79003)
|
||||
- Added missing interpolations [`b43aeec`](https://git.odit.services/lfk/document-server/commit/b43aeec0cf40a9c37a10072062ab5d93102f6c81)
|
||||
- Made footer text configureable [`63c7beb`](https://git.odit.services/lfk/document-server/commit/63c7beb8b9cdc564186c5b86a4f305c8575f5b9f)
|
||||
- 🧾New changelog file version [CI SKIP] [skip ci] [`f1084b5`](https://git.odit.services/lfk/document-server/commit/f1084b59a74dcc5981fd314721c36726706f386c)
|
||||
- disabled testing for now [`e75f151`](https://git.odit.services/lfk/document-server/commit/e75f15142e293349a071a7cdcc53cc10780304f6)
|
||||
- Removed temporary background-image fix [`5ba26c4`](https://git.odit.services/lfk/document-server/commit/5ba26c4cbfae7d3f31d3709aaeb372c14de78fa9)
|
||||
|
||||
34
Dockerfile
34
Dockerfile
@@ -1,25 +1,29 @@
|
||||
# Typescript Build
|
||||
FROM registry.odit.services/hub/library/node:14.15.1-alpine3.12
|
||||
FROM registry.odit.services/hub/library/node:19.0.1-alpine3.16 AS build
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
RUN npm i -g pnpm
|
||||
RUN pnpm i
|
||||
COPY tsconfig.json ./
|
||||
COPY src ./src
|
||||
RUN pnpm run build
|
||||
RUN pnpm run build \
|
||||
&& rm -rf /app/node_modules \
|
||||
&& pnpm i --production --prefer-offline
|
||||
|
||||
# final image
|
||||
FROM registry.odit.services/hub/library/alpine:3.13.1
|
||||
FROM registry.odit.services/hub/library/alpine:3.16
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache \
|
||||
chromium \
|
||||
nss \
|
||||
freetype \
|
||||
freetype-dev \
|
||||
harfbuzz \
|
||||
ca-certificates \
|
||||
ttf-freefont \
|
||||
nodejs \
|
||||
yarn
|
||||
chromium \
|
||||
nss \
|
||||
freetype \
|
||||
freetype-dev \
|
||||
harfbuzz \
|
||||
ca-certificates \
|
||||
ttf-freefont \
|
||||
nodejs \
|
||||
font-noto-emoji \
|
||||
&& apk add wqy-zenhei --update-cache --repository https://nl.alpinelinux.org/alpine/edge/testing
|
||||
|
||||
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
|
||||
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
|
||||
@@ -33,7 +37,7 @@ RUN addgroup -S pptruser && adduser -S -g pptruser pptruser \
|
||||
# Run everything after as non-privileged user.
|
||||
USER pptruser
|
||||
|
||||
COPY package.json ./
|
||||
RUN yarn
|
||||
COPY --from=0 /app/dist app
|
||||
COPY --from=build /app/package.json /app/
|
||||
COPY --from=build /app/node_modules /app/node_modules
|
||||
COPY --from=build /app/dist app
|
||||
ENTRYPOINT ["node", "app/app.js"]
|
||||
@@ -39,6 +39,9 @@ The basic generation mechanism makes the templates and routes interchangeable (i
|
||||
| DISCLAIMER_TEXT | String | N/A | A disclaimer that will get displayed on the bottom of each sponsoring contract. R/N You can only provide the disclaimer for one language.
|
||||
| DONATIONS_FOOTER_TEXT | String | N/A | A text that will get displayed on the bottom of each runner certificate's second page. R/N You can only provide the text for one language.
|
||||
| CONTRACTS_PER_RUNNER | Number | 1 | The amount of contracts that get created per runner (per request).
|
||||
| CODEFORMAT | String | code39 | The barcode format for everything except.
|
||||
| CODEFORMAT_CARDS | String | code39 | The barcode format for runnercards (overwrites CODEFORMAT).
|
||||
| CARD_SUBTITLE | String | Empty | A subtitle that get's displayed on the cards under the eventname.
|
||||
|
||||
## Templates
|
||||
> The document server uses html templates to generate various pdf documents.
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
version: "3"
|
||||
services:
|
||||
document_server:
|
||||
# image: registry.odit.services/lfk/beamershow:0.1.3
|
||||
build: .
|
||||
ports:
|
||||
- 4010:4010
|
||||
environment:
|
||||
APP_PORT: 4010
|
||||
NODE_ENV: production
|
||||
EVENT_NAME: "Lauf für Kaya! 2021"
|
||||
EVENT_NAME: "Testen für Kaya!"
|
||||
CURRENCY_SYMBOL: "€"
|
||||
API_KEY: RYRccAJ4SKZnZaEci6Nyk9Z6mw3sD94fyKJ74WNzi6hLkxGNyJDrKPkxBmPwvR4f
|
||||
API_KEY: NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe
|
||||
CONTRACTS_PER_RUNNER: 2
|
||||
SPONSORING_RECEIPT_MINIMUM_AMOUNT: 50
|
||||
DISCLAIMER_TEXT: "Rechtsgrundlage unserer Datenverarbeitung aufgrund freiwilliger Einwilligung ist Art. 6 Abs. 1 e), Abs. 3 DSGVO i.V.m. Art. 85 BayEUG. Mit Ihrer Unterschrift willigen Sie in unsere Datennutzung zum Zwecke des Lauf für Kaya! ein. Die Daten für Spendenquittungen"
|
||||
SPONSORING_RECEIPT_MINIMUM_AMOUNT: 42
|
||||
DISCLAIMER_TEXT: "Hier könnte ihre Werbung stehen"
|
||||
CODEFORMAT: "code39"
|
||||
CODEFORMAT_CARDS: "ean13"
|
||||
CARD_SUBTITLE: "Hier könnte mehr Werbung stehen"
|
||||
|
||||
57
licenses.md
57
licenses.md
@@ -62,25 +62,13 @@ THE SOFTWARE.
|
||||
**License**: MIT
|
||||
**Description**: Promise based HTTP client for the browser and node.js
|
||||
## License Text
|
||||
Copyright (c) 2014-present Matt Zabriskie
|
||||
# Copyright (c) 2014-present Matt Zabriskie & Collaborators
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
# bwip-js
|
||||
@@ -848,6 +836,35 @@ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SOFTWARE
|
||||
|
||||
|
||||
# auto-changelog
|
||||
**Author**: Pete Cook <pete@cookpete.com> (https://github.com/cookpete)
|
||||
**Repo**: https://github.com/CookPete/auto-changelog
|
||||
**License**: MIT
|
||||
**Description**: Command line tool for generating a changelog from git tags and commit history
|
||||
## License Text
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017 Pete Cook https://cookpete.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
# cp-cli
|
||||
**Author**: ?
|
||||
**Repo**: https://github.com/screendriver/cp-cli
|
||||
@@ -966,14 +983,6 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
# start-server-and-test
|
||||
**Author**: Gleb Bahmutov <gleb.bahmutov@gmail.com>
|
||||
**Repo**: https://github.com/bahmutov/start-server-and-test
|
||||
**License**: MIT
|
||||
**Description**: Starts server, waits for URL, then runs test command; when the tests end, shuts down server
|
||||
## License Text
|
||||
|
||||
|
||||
# ts-node
|
||||
**Author**: Blake Embrey
|
||||
**Repo**: https://github.comTypeStrong/ts-node
|
||||
|
||||
186
package.json
186
package.json
@@ -1,90 +1,96 @@
|
||||
{
|
||||
"name": "@odit/lfk-document-server",
|
||||
"version": "0.5.0",
|
||||
"description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more",
|
||||
"main": "src/app.ts",
|
||||
"scripts": {
|
||||
"dev": "nodemon src/app.ts",
|
||||
"build": "rimraf ./dist && tsc && cp-cli ./src/templates ./dist/templates && cp-cli ./src/locales ./dist/locales",
|
||||
"licenses:export": "license-exporter --markdown",
|
||||
"release": "release-it --only-version",
|
||||
"translations:sort": "node sort_translations.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@git.odit.services:lfk/document-server.git"
|
||||
},
|
||||
"keywords": [
|
||||
"odit",
|
||||
"lfk",
|
||||
"pdf",
|
||||
"generate"
|
||||
],
|
||||
"author": {
|
||||
"name": "ODIT.Services",
|
||||
"email": "info@odit.services",
|
||||
"url": "https://odit.services"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Philipp Dormann",
|
||||
"email": "philipp@philippdormann.de",
|
||||
"url": "https://philippdormann.de"
|
||||
},
|
||||
{
|
||||
"name": "Nicolai Ort",
|
||||
"email": "info@nicolai-ort.com",
|
||||
"url": "https://nicolai-ort.com"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-NC-SA-4.0",
|
||||
"dependencies": {
|
||||
"@odit/class-validator-jsonschema": "2.1.1",
|
||||
"async-helpers": "0.3.17",
|
||||
"axios": "0.21.1",
|
||||
"bwip-js": "2.1.1",
|
||||
"cheerio": "1.0.0-rc.5",
|
||||
"class-transformer": "0.3.1",
|
||||
"class-validator": "0.13.1",
|
||||
"consola": "2.15.3",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "8.2.0",
|
||||
"express": "4.17.1",
|
||||
"handlebars": "4.7.7",
|
||||
"i18next": "20.1.0",
|
||||
"i18next-fs-backend": "1.1.1",
|
||||
"mime-types": "2.1.29",
|
||||
"pdf-lib": "1.16.0",
|
||||
"puppeteer": "8.0.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"routing-controllers": "0.9.0-alpha.6",
|
||||
"routing-controllers-openapi": "2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@odit/license-exporter": "0.0.11",
|
||||
"@types/express": "4.17.11",
|
||||
"@types/node": "14.14.22",
|
||||
"@types/puppeteer": "5.4.3",
|
||||
"cp-cli": "2.0.0",
|
||||
"faker": "5.3.1",
|
||||
"nodemon": "2.0.7",
|
||||
"release-it": "^14.2.2",
|
||||
"rimraf": "3.0.2",
|
||||
"start-server-and-test": "1.12.0",
|
||||
"ts-node": "9.1.1",
|
||||
"typescript": "4.1.3"
|
||||
},
|
||||
"release-it": {
|
||||
"git": {
|
||||
"commit": true,
|
||||
"requireCleanWorkingDir": false,
|
||||
"commitMessage": "🚀Bumped version to v${version}",
|
||||
"requireBranch": "dev",
|
||||
"push": false,
|
||||
"tag": false
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "@odit/lfk-document-server",
|
||||
"version": "0.6.1",
|
||||
"description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more",
|
||||
"main": "src/app.ts",
|
||||
"scripts": {
|
||||
"dev": "nodemon src/app.ts",
|
||||
"build": "rimraf ./dist && tsc && cp-cli ./src/templates ./dist/templates && cp-cli ./src/locales ./dist/locales",
|
||||
"licenses:export": "license-exporter --markdown",
|
||||
"changelog:export": "auto-changelog --commit-limit false -p -u --hide-credit",
|
||||
"release": "release-it --only-version",
|
||||
"translations:sort": "node sort_translations.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@git.odit.services:lfk/document-server.git"
|
||||
},
|
||||
"keywords": [
|
||||
"odit",
|
||||
"lfk",
|
||||
"pdf",
|
||||
"generate"
|
||||
],
|
||||
"author": {
|
||||
"name": "ODIT.Services",
|
||||
"email": "info@odit.services",
|
||||
"url": "https://odit.services"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Philipp Dormann",
|
||||
"email": "philipp@philippdormann.de",
|
||||
"url": "https://philippdormann.de"
|
||||
},
|
||||
{
|
||||
"name": "Nicolai Ort",
|
||||
"email": "info@nicolai-ort.com",
|
||||
"url": "https://nicolai-ort.com"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-NC-SA-4.0",
|
||||
"dependencies": {
|
||||
"@odit/class-validator-jsonschema": "2.1.1",
|
||||
"async-helpers": "0.3.17",
|
||||
"axios": "1.3.4",
|
||||
"bwip-js": "3.3.0",
|
||||
"cheerio": "1.0.0-rc.5",
|
||||
"class-transformer": "0.3.1",
|
||||
"class-validator": "0.13.1",
|
||||
"consola": "2.15.3",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "16.0.3",
|
||||
"express": "4.18.2",
|
||||
"handlebars": "4.7.7",
|
||||
"i18next": "20.1.0",
|
||||
"i18next-fs-backend": "1.1.1",
|
||||
"mime-types": "2.1.35",
|
||||
"pdf-lib": "1.17.1",
|
||||
"puppeteer": "8.0.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"routing-controllers": "0.9.0-alpha.6",
|
||||
"routing-controllers-openapi": "2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@odit/license-exporter": "0.0.11",
|
||||
"@types/express": "4.17.17",
|
||||
"@types/node": "18.14.0",
|
||||
"@types/puppeteer": "5.4.3",
|
||||
"auto-changelog": "2.4.0",
|
||||
"cp-cli": "2.0.0",
|
||||
"faker": "5.3.1",
|
||||
"nodemon": "2.0.7",
|
||||
"release-it": "15.6.0",
|
||||
"rimraf": "3.0.2",
|
||||
"ts-node": "9.1.1",
|
||||
"typescript": "4.1.3"
|
||||
},
|
||||
"release-it": {
|
||||
"git": {
|
||||
"commit": true,
|
||||
"requireCleanWorkingDir": false,
|
||||
"commitMessage": "🚀Bumped version to v${version}",
|
||||
"requireBranch": "dev",
|
||||
"push": true,
|
||||
"tag": true,
|
||||
"tagName": "v${version}",
|
||||
"tagAnnotation": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
},
|
||||
"hooks": {
|
||||
"after:bump": "npm run changelog:export && npm run licenses:export && git add CHANGELOG.md && git add licenses.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4815
pnpm-lock.yaml
generated
Normal file
4815
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,309 +1,308 @@
|
||||
import axios from 'axios';
|
||||
import cheerio from "cheerio";
|
||||
import fs from "fs";
|
||||
import Handlebars from 'handlebars';
|
||||
import i18next from "i18next";
|
||||
import Backend from 'i18next-fs-backend';
|
||||
import mime from "mime-types";
|
||||
import path from 'path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import puppeteer from "puppeteer";
|
||||
import { awaitAsyncHandlebarHelpers, helpers } from './asyncHelpers';
|
||||
import { config } from './config';
|
||||
import { CertificateRunner } from './models/CertificateRunner';
|
||||
import { Runner } from './models/Runner';
|
||||
import { RunnerCard } from './models/RunnerCard';
|
||||
import { RunnerGroup } from './models/RunnerGroup';
|
||||
|
||||
/**
|
||||
* This class is responsible for all things pdf creation.
|
||||
* This uses the html templates from src/templates.
|
||||
*/
|
||||
export class PdfCreator {
|
||||
private templateDir = path.join(__dirname, '/templates');
|
||||
private browser;
|
||||
private static interpolations = { eventname: config.eventname, sponsoring_receipt_minimum_amount: config.sponsoring_receipt_minimum_amount, currency_symbol: config.currency_symbol }
|
||||
private static contractsPerRunner = config.contracts_per_runner;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* Initializes i18n(ext), Handlebars and puppeteer.
|
||||
*/
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* Initializes i18n(ext), Handlebars and puppeteer.
|
||||
*/
|
||||
public async init() {
|
||||
const minimal_args = [
|
||||
'--autoplay-policy=user-gesture-required',
|
||||
'--disable-background-networking',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-backgrounding-occluded-windows',
|
||||
'--disable-breakpad',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-component-update',
|
||||
'--disable-default-apps',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-domain-reliability',
|
||||
'--disable-extensions',
|
||||
'--disable-features=AudioServiceOutOfProcess',
|
||||
'--disable-hang-monitor',
|
||||
'--disable-ipc-flooding-protection',
|
||||
'--disable-notifications',
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-print-preview',
|
||||
'--disable-prompt-on-repost',
|
||||
'--disable-renderer-backgrounding',
|
||||
'--disable-speech-api',
|
||||
'--disable-sync',
|
||||
'--hide-scrollbars',
|
||||
'--ignore-gpu-blacklist',
|
||||
'--metrics-recording-only',
|
||||
'--mute-audio',
|
||||
'--no-default-browser-check',
|
||||
'--no-first-run',
|
||||
'--no-pings',
|
||||
'--no-zygote',
|
||||
'--password-store=basic',
|
||||
'--use-gl=swiftshader',
|
||||
'--no-sandbox'
|
||||
];
|
||||
await i18next
|
||||
.use(Backend)
|
||||
.init({
|
||||
fallbackLng: 'en',
|
||||
lng: 'en',
|
||||
backend: {
|
||||
loadPath: path.join(__dirname, '/locales/{{lng}}.json')
|
||||
}
|
||||
});
|
||||
|
||||
await Handlebars.registerHelper(helpers);
|
||||
await Handlebars.registerHelper('__',
|
||||
function (str) {
|
||||
return i18next.t(str, PdfCreator.interpolations).toString();
|
||||
}
|
||||
);
|
||||
await Handlebars.registerHelper('--sponsor',
|
||||
function (str) {
|
||||
const index = (parseInt(str) % config.sponor_logos.length);
|
||||
if (isNaN(index)) {
|
||||
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="
|
||||
}
|
||||
return config.sponor_logos[index];
|
||||
}
|
||||
);
|
||||
await Handlebars.registerHelper('--format_kilometers',
|
||||
function (str) {
|
||||
let meters = parseInt(str);
|
||||
return ((meters / 1000).toFixed(3).toString())
|
||||
}
|
||||
);
|
||||
await Handlebars.registerHelper('--format_currency',
|
||||
function (str) {
|
||||
let meters = parseInt(str);
|
||||
return ((meters / 100).toFixed(2).toString())
|
||||
}
|
||||
);
|
||||
this.browser = await puppeteer.launch({ headless: true, args: minimal_args });
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sponsoring contract pdfs.
|
||||
* @param runner The runner you want to generate the contracts for.
|
||||
* @param locale The locale used for the contracts (default:en)
|
||||
*/
|
||||
public async generateSponsoringContract(runners: Runner[], locale: string = "en", codeformat: string = config.codeformat): Promise<Buffer> {
|
||||
if (runners.length == 1 && Object.keys(runners[0]).length == 0) {
|
||||
runners[0] = this.generateEmptyRunner();
|
||||
}
|
||||
for (var i = 1; i < PdfCreator.contractsPerRunner; i++) {
|
||||
runners = runners.reduce(function (res, current, index, array) {
|
||||
return res.concat([current, current]);
|
||||
}, []);
|
||||
}
|
||||
if (runners.length > 50) {
|
||||
let pdf_promises = new Array<Promise<Buffer>>();
|
||||
let i, j;
|
||||
for (i = 0, j = runners.length; i < j; i += 50) {
|
||||
let chunk = runners.slice(i, i + 50);
|
||||
pdf_promises.push(this.generateSponsoringContract(chunk, locale));
|
||||
}
|
||||
const pdfs = await Promise.all(pdf_promises);
|
||||
return await this.mergePdfs(pdfs);
|
||||
}
|
||||
await i18next.changeLanguage(locale);
|
||||
const template_source = fs.readFileSync(`${this.templateDir}/sponsoring_contract.html`, 'utf8');
|
||||
const template = Handlebars.compile(template_source);
|
||||
let result = template({ runners, codeformat, disclaimer: config.disclaimer_text });
|
||||
result = await awaitAsyncHandlebarHelpers(result);
|
||||
const pdf = await this.renderPdf(result, { format: "A5", landscape: true });
|
||||
return pdf
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate runner card pdfs.
|
||||
* @param cards The runner cars you want to generate the cards for.
|
||||
* @param locale The locale used for the cards (default:en)
|
||||
*/
|
||||
public async generateRunnerCards(cards: RunnerCard[], locale: string = "en", codeformat: string = config.codeformat): Promise<Buffer> {
|
||||
if (cards.length > 10) {
|
||||
let pdf_promises = new Array<Promise<Buffer>>();
|
||||
let i, j;
|
||||
for (i = 0, j = cards.length; i < j; i += 10) {
|
||||
let chunk = cards.slice(i, i + 10);
|
||||
pdf_promises.push(this.generateRunnerCards(chunk, locale));
|
||||
}
|
||||
const pdfs = await Promise.all(pdf_promises);
|
||||
return await this.mergePdfs(pdfs);
|
||||
}
|
||||
const cards_swapped = this.swapArrayPairs(cards);
|
||||
await i18next.changeLanguage(locale);
|
||||
const template_source = fs.readFileSync(`${this.templateDir}/runner_card.html`, 'utf8');
|
||||
const template = Handlebars.compile(template_source);
|
||||
let result = template({ cards, cards_swapped, eventname: config.eventname, codeformat: codeformat })
|
||||
result = await awaitAsyncHandlebarHelpers(result);
|
||||
const pdf = await this.renderPdf(result, { format: "A4", landscape: false });
|
||||
return pdf
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sponsoring contract pdfs.
|
||||
* @param runner The runner you want to generate the contracts for.
|
||||
* @param locale The locale used for the contracts (default:en)
|
||||
*/
|
||||
public async generateRunnerCertficates(runners: CertificateRunner[], locale: string = "en"): Promise<Buffer> {
|
||||
if (runners.length > 50) {
|
||||
let pdf_promises = new Array<Promise<Buffer>>();
|
||||
let i, j;
|
||||
for (i = 0, j = runners.length; i < j; i += 50) {
|
||||
let chunk = runners.slice(i, i + 50);
|
||||
pdf_promises.push(this.generateRunnerCertficates(chunk, locale));
|
||||
}
|
||||
const pdfs = await Promise.all(pdf_promises);
|
||||
return await this.mergePdfs(pdfs);
|
||||
}
|
||||
await i18next.changeLanguage(locale);
|
||||
const template_source = fs.readFileSync(`${this.templateDir}/runner_certificate.html`, 'utf8');
|
||||
const template = Handlebars.compile(template_source);
|
||||
let result = template({ runners, eventname: config.eventname, currency_symbol: config.currency_symbol, donations_footer_text: config.donations_footer_text });
|
||||
result = await awaitAsyncHandlebarHelpers(result);
|
||||
const pdf = await this.renderPdf(result, { format: "A4", landscape: false, printBackground: true });
|
||||
return pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all images in html to base64.
|
||||
* Works with image files in the template directory or images from urls.
|
||||
* @param html The html string whoms images shall get replaced.
|
||||
*/
|
||||
public async imgToBase64(html): Promise<string> {
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
$('img').each(async (index, element) => {
|
||||
let imgsrc = $(element).attr("src");
|
||||
if (imgsrc.startsWith("data:image")) {
|
||||
return;
|
||||
}
|
||||
const img_type = mime.lookup(imgsrc);
|
||||
|
||||
if (!(img_type.includes("image"))) {
|
||||
throw new Error("File is not image mime type");
|
||||
}
|
||||
|
||||
let image;
|
||||
if (imgsrc.startsWith("http")) {
|
||||
image = (await axios.get(imgsrc)).data;
|
||||
image = Buffer.from(image).toString('base64');
|
||||
}
|
||||
else {
|
||||
if (imgsrc.startsWith("./")) {
|
||||
imgsrc = imgsrc.replace("./", "");
|
||||
}
|
||||
image = fs.readFileSync(`${this.templateDir}/${imgsrc}`, { encoding: "base64" });
|
||||
}
|
||||
|
||||
image = `data:${img_type};base64,${image}`
|
||||
$(element).attr("src", image)
|
||||
});
|
||||
|
||||
return $.html();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method manages the creation of pdfs via puppeteer.
|
||||
* @param html The HTML that should get rendered.
|
||||
* @param options Puppeteer PDF option (eg: {format: "A4"})
|
||||
*/
|
||||
public async renderPdf(html: string, options): Promise<any> {
|
||||
html = await this.imgToBase64(html);
|
||||
let page = await this.browser.newPage();
|
||||
await page.setContent(html);
|
||||
const pdf = await page.pdf(options);
|
||||
await page.close();
|
||||
return pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges multiple pdfs into one.
|
||||
* @param pdfs The pdfs you want to merge as an buffer array.
|
||||
* @returns The merged pdf as a buffer.
|
||||
*/
|
||||
private async mergePdfs(pdfs: Buffer[]): Promise<Buffer> {
|
||||
const mergedPdf = await PDFDocument.create();
|
||||
|
||||
for (const pdfBuffer of pdfs) {
|
||||
const pdf = await PDFDocument.load(pdfBuffer);
|
||||
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
||||
copiedPages.forEach((page) => {
|
||||
mergedPdf.addPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
return <Buffer>(await mergedPdf.save());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new dummy runner with halfspaces for all strings.
|
||||
* Can be used to generate empty sponsoring contracts.
|
||||
* @returns A new runner object that apears to be empty.
|
||||
*/
|
||||
private generateEmptyRunner(): Runner {
|
||||
let group = new RunnerGroup();
|
||||
group.id = 0;
|
||||
group.name = " ";
|
||||
let runner = new Runner();
|
||||
runner.id = 0;
|
||||
runner.firstname = " ";
|
||||
runner.lastname = " ";
|
||||
runner.group = group;
|
||||
return runner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps pairs (0/1, 2/3, ...) of elements in an array recursively.
|
||||
* If the last element has no partner it inserts an empty element at the end and swaps the two
|
||||
* This is needed to generate pdfs with front- and backside that get printet on one paper.
|
||||
* @param array The array which's pairs shall get switched.
|
||||
* @returns Array with swapped pairs,
|
||||
*/
|
||||
private swapArrayPairs(array): Array<any> {
|
||||
if (array.length == 1) {
|
||||
return [null, array[0]];
|
||||
}
|
||||
if (array.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rest = this.swapArrayPairs(array.slice(2))
|
||||
if (!rest) {
|
||||
return [array[1], array[0]]
|
||||
}
|
||||
return [array[1], array[0]].concat(rest);
|
||||
}
|
||||
import axios from 'axios';
|
||||
import cheerio from "cheerio";
|
||||
import fs from "fs";
|
||||
import Handlebars from 'handlebars';
|
||||
import i18next from "i18next";
|
||||
import Backend from 'i18next-fs-backend';
|
||||
import mime from "mime-types";
|
||||
import path from 'path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import puppeteer from "puppeteer";
|
||||
import { awaitAsyncHandlebarHelpers, helpers } from './asyncHelpers';
|
||||
import { config } from './config';
|
||||
import { CertificateRunner } from './models/CertificateRunner';
|
||||
import { Runner } from './models/Runner';
|
||||
import { RunnerCard } from './models/RunnerCard';
|
||||
import { RunnerGroup } from './models/RunnerGroup';
|
||||
|
||||
/**
|
||||
* This class is responsible for all things pdf creation.
|
||||
* This uses the html templates from src/templates.
|
||||
*/
|
||||
export class PdfCreator {
|
||||
private templateDir = path.join(__dirname, '/templates');
|
||||
private browser;
|
||||
private static interpolations = { eventname: config.eventname, sponsoring_receipt_minimum_amount: config.sponsoring_receipt_minimum_amount, currency_symbol: config.currency_symbol }
|
||||
private static contractsPerRunner = config.contracts_per_runner;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* Initializes i18n(ext), Handlebars and puppeteer.
|
||||
*/
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* Initializes i18n(ext), Handlebars and puppeteer.
|
||||
*/
|
||||
public async init() {
|
||||
const minimal_args = [
|
||||
'--autoplay-policy=user-gesture-required',
|
||||
'--disable-background-networking',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-backgrounding-occluded-windows',
|
||||
'--disable-breakpad',
|
||||
'--disable-client-side-phishing-detection',
|
||||
'--disable-component-update',
|
||||
'--disable-default-apps',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-domain-reliability',
|
||||
'--disable-extensions',
|
||||
'--disable-features=AudioServiceOutOfProcess',
|
||||
'--disable-hang-monitor',
|
||||
'--disable-ipc-flooding-protection',
|
||||
'--disable-notifications',
|
||||
'--disable-offer-store-unmasked-wallet-cards',
|
||||
'--disable-popup-blocking',
|
||||
'--disable-print-preview',
|
||||
'--disable-prompt-on-repost',
|
||||
'--disable-renderer-backgrounding',
|
||||
'--disable-speech-api',
|
||||
'--disable-sync',
|
||||
'--hide-scrollbars',
|
||||
'--ignore-gpu-blacklist',
|
||||
'--metrics-recording-only',
|
||||
'--mute-audio',
|
||||
'--no-default-browser-check',
|
||||
'--no-first-run',
|
||||
'--no-pings',
|
||||
'--no-zygote',
|
||||
'--password-store=basic',
|
||||
'--use-gl=swiftshader',
|
||||
'--no-sandbox'
|
||||
];
|
||||
await i18next
|
||||
.use(Backend)
|
||||
.init({
|
||||
fallbackLng: 'en',
|
||||
lng: 'en',
|
||||
backend: {
|
||||
loadPath: path.join(__dirname, '/locales/{{lng}}.json')
|
||||
}
|
||||
});
|
||||
|
||||
await Handlebars.registerHelper(helpers);
|
||||
await Handlebars.registerHelper('__',
|
||||
function (str) {
|
||||
return i18next.t(str, PdfCreator.interpolations).toString();
|
||||
}
|
||||
);
|
||||
await Handlebars.registerHelper('--sponsor',
|
||||
function (str) {
|
||||
const index = (parseInt(str) % config.sponor_logos.length);
|
||||
if (isNaN(index)) {
|
||||
return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="
|
||||
}
|
||||
return config.sponor_logos[index];
|
||||
}
|
||||
);
|
||||
await Handlebars.registerHelper('--format_kilometers',
|
||||
function (str) {
|
||||
let meters = parseInt(str);
|
||||
return ((meters / 1000).toLocaleString("en-EN", { minimumFractionDigits: 2, maximumFractionDigits: 3 }).replace(".", ","));
|
||||
}
|
||||
);
|
||||
await Handlebars.registerHelper('--format_currency',
|
||||
function (str) {
|
||||
let meters = parseInt(str);
|
||||
return ((meters / 100).toLocaleString("en-EN", { minimumFractionDigits: 2, maximumFractionDigits: 2 }).replace(".", ","));
|
||||
}
|
||||
);
|
||||
this.browser = await puppeteer.launch({ headless: true, args: minimal_args });
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sponsoring contract pdfs.
|
||||
* @param runner The runner you want to generate the contracts for.
|
||||
* @param locale The locale used for the contracts (default:en)
|
||||
*/
|
||||
public async generateSponsoringContract(runners: Runner[], locale: string = "en", codeformat: string = config.codeformat): Promise<Buffer> {
|
||||
if (runners.length == 1 && Object.keys(runners[0]).length == 0) {
|
||||
runners[0] = this.generateEmptyRunner();
|
||||
}
|
||||
if (runners.length > 50) {
|
||||
let pdf_promises = new Array<Promise<Buffer>>();
|
||||
let i, j;
|
||||
for (i = 0, j = runners.length; i < j; i += 50) {
|
||||
let chunk = runners.slice(i, i + 50);
|
||||
pdf_promises.push(this.generateSponsoringContract(chunk, locale));
|
||||
}
|
||||
const pdfs = await Promise.all(pdf_promises);
|
||||
return await this.mergePdfs(pdfs);
|
||||
}
|
||||
for (var i = 1; i < PdfCreator.contractsPerRunner; i++) {
|
||||
runners = runners.reduce(function (res, current, index, array) {
|
||||
return res.concat([current, current]);
|
||||
}, []);
|
||||
}
|
||||
await i18next.changeLanguage(locale);
|
||||
const template_source = fs.readFileSync(`${this.templateDir}/sponsoring_contract.html`, 'utf8');
|
||||
const template = Handlebars.compile(template_source);
|
||||
let result = template({ runners, codeformat, disclaimer: config.disclaimer_text });
|
||||
result = await awaitAsyncHandlebarHelpers(result);
|
||||
const pdf = await this.renderPdf(result, { format: "A5", landscape: true });
|
||||
return pdf
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate runner card pdfs.
|
||||
* @param cards The runner cars you want to generate the cards for.
|
||||
* @param locale The locale used for the cards (default:en)
|
||||
*/
|
||||
public async generateRunnerCards(cards: RunnerCard[], locale: string = "en", codeformat: string = config.codeformat_cards): Promise<Buffer> {
|
||||
if (cards.length > 10) {
|
||||
let pdf_promises = new Array<Promise<Buffer>>();
|
||||
let i, j;
|
||||
for (i = 0, j = cards.length; i < j; i += 10) {
|
||||
let chunk = cards.slice(i, i + 10);
|
||||
pdf_promises.push(this.generateRunnerCards(chunk, locale, codeformat));
|
||||
}
|
||||
const pdfs = await Promise.all(pdf_promises);
|
||||
return await this.mergePdfs(pdfs);
|
||||
}
|
||||
const cards_swapped = this.swapArrayPairs(cards);
|
||||
await i18next.changeLanguage(locale);
|
||||
const template_source = fs.readFileSync(`${this.templateDir}/runner_card.html`, 'utf8');
|
||||
const template = Handlebars.compile(template_source);
|
||||
let result = template({ cards, cards_swapped, eventname: config.eventname, codeformat: codeformat, card_subtitle: config.card_subtitle })
|
||||
result = await awaitAsyncHandlebarHelpers(result);
|
||||
const pdf = await this.renderPdf(result, { format: "A4", landscape: false });
|
||||
return pdf
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sponsoring contract pdfs.
|
||||
* @param runner The runner you want to generate the contracts for.
|
||||
* @param locale The locale used for the contracts (default:en)
|
||||
*/
|
||||
public async generateRunnerCertficates(runners: CertificateRunner[], locale: string = "en"): Promise<Buffer> {
|
||||
if (runners.length > 50) {
|
||||
let pdf_promises = new Array<Buffer>();
|
||||
let i, j;
|
||||
for (i = 0, j = runners.length; i < j; i += 50) {
|
||||
let chunk = runners.slice(i, i + 50);
|
||||
pdf_promises.push(await this.generateRunnerCertficates(chunk, locale));
|
||||
}
|
||||
return await this.mergePdfs(pdf_promises);
|
||||
}
|
||||
await i18next.changeLanguage(locale);
|
||||
const template_source = fs.readFileSync(`${this.templateDir}/runner_certificate.html`, 'utf8');
|
||||
const template = Handlebars.compile(template_source);
|
||||
let result = template({ runners, eventname: config.eventname, currency_symbol: config.currency_symbol, donations_footer_text: config.donations_footer_text });
|
||||
result = await awaitAsyncHandlebarHelpers(result);
|
||||
const pdf = await this.renderPdf(result, { format: "A4", landscape: false, printBackground: true });
|
||||
return pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all images in html to base64.
|
||||
* Works with image files in the template directory or images from urls.
|
||||
* @param html The html string whoms images shall get replaced.
|
||||
*/
|
||||
public async imgToBase64(html): Promise<string> {
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
$('img').each(async (index, element) => {
|
||||
let imgsrc = $(element).attr("src");
|
||||
if (imgsrc.startsWith("data:image")) {
|
||||
return;
|
||||
}
|
||||
const img_type = mime.lookup(imgsrc);
|
||||
|
||||
if (!(img_type.includes("image"))) {
|
||||
throw new Error("File is not image mime type");
|
||||
}
|
||||
|
||||
let image;
|
||||
if (imgsrc.startsWith("http")) {
|
||||
image = (await axios.get(imgsrc)).data;
|
||||
image = Buffer.from(image).toString('base64');
|
||||
}
|
||||
else {
|
||||
if (imgsrc.startsWith("./")) {
|
||||
imgsrc = imgsrc.replace("./", "");
|
||||
}
|
||||
image = fs.readFileSync(`${this.templateDir}/${imgsrc}`, { encoding: "base64" });
|
||||
}
|
||||
|
||||
image = `data:${img_type};base64,${image}`
|
||||
$(element).attr("src", image)
|
||||
});
|
||||
|
||||
return $.html();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method manages the creation of pdfs via puppeteer.
|
||||
* @param html The HTML that should get rendered.
|
||||
* @param options Puppeteer PDF option (eg: {format: "A4"})
|
||||
*/
|
||||
public async renderPdf(html: string, options): Promise<any> {
|
||||
html = await this.imgToBase64(html);
|
||||
let page = await this.browser.newPage();
|
||||
await page.setContent(html);
|
||||
const pdf = await page.pdf(options);
|
||||
await page.close();
|
||||
return pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges multiple pdfs into one.
|
||||
* @param pdfs The pdfs you want to merge as an buffer array.
|
||||
* @returns The merged pdf as a buffer.
|
||||
*/
|
||||
private async mergePdfs(pdfs: Buffer[]): Promise<Buffer> {
|
||||
const mergedPdf = await PDFDocument.create();
|
||||
|
||||
for (const pdfBuffer of pdfs) {
|
||||
const pdf = await PDFDocument.load(pdfBuffer);
|
||||
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
||||
copiedPages.forEach((page) => {
|
||||
mergedPdf.addPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
return <Buffer>(await mergedPdf.save());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new dummy runner with halfspaces for all strings.
|
||||
* Can be used to generate empty sponsoring contracts.
|
||||
* @returns A new runner object that apears to be empty.
|
||||
*/
|
||||
private generateEmptyRunner(): Runner {
|
||||
let group = new RunnerGroup();
|
||||
group.id = 0;
|
||||
group.name = " ";
|
||||
let runner = new Runner();
|
||||
runner.id = 0;
|
||||
runner.firstname = " ";
|
||||
runner.lastname = " ";
|
||||
runner.group = group;
|
||||
return runner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps pairs (0/1, 2/3, ...) of elements in an array recursively.
|
||||
* If the last element has no partner it inserts an empty element at the end and swaps the two
|
||||
* This is needed to generate pdfs with front- and backside that get printet on one paper.
|
||||
* @param array The array which's pairs shall get switched.
|
||||
* @returns Array with swapped pairs,
|
||||
*/
|
||||
private swapArrayPairs(array): Array<any> {
|
||||
if (array.length == 1) {
|
||||
return [null, array[0]];
|
||||
}
|
||||
if (array.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rest = this.swapArrayPairs(array.slice(2))
|
||||
if (!rest) {
|
||||
return [array[1], array[0]]
|
||||
}
|
||||
return [array[1], array[0]].concat(rest);
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,14 @@ export const config = {
|
||||
eventname: process.env.EVENT_NAME || "Please set the event name",
|
||||
currency_symbol: process.env.CURRENCY_SYMBOL || "€",
|
||||
sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10",
|
||||
codeformat: process.env.CODEFORMAT || "qrcode",
|
||||
codeformat: process.env.CODEFORMAT || "code39",
|
||||
codeformat_cards: process.env.CODEFORMAT_CARDS || process.env.CODEFORMAT || "code39",
|
||||
sponor_logos: getSponsorLogos(),
|
||||
api_key: getApiKey(),
|
||||
disclaimer_text: process.env.DISCLAIMER_TEXT || "",
|
||||
donations_footer_text: process.env.DONATIONS_FOOTER_TEXT || "",
|
||||
contracts_per_runner: parseInt(process.env.CONTRACTS_PER_RUNNER) || 1,
|
||||
card_subtitle: process.env.CARD_SUBTITLE || ""
|
||||
}
|
||||
let errors = 0
|
||||
if (typeof config.internal_port !== "number") {
|
||||
|
||||
@@ -38,7 +38,7 @@ export class PdfController {
|
||||
|
||||
@Post('/cards')
|
||||
@OpenAPI({ description: "Generate runner card pdfs from runner card objects.<br>You can choose your prefered locale by passing the 'locale' query-param." })
|
||||
async generateCards(@Body({ validate: true, options: { limit: "500mb" } }) cards: RunnerCard | RunnerCard[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("download") download: boolean) {
|
||||
async generateCards(@Body({ validate: true, options: { limit: "500mb" } }) cards: RunnerCard | RunnerCard[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("codeformat") codeformat: string, @QueryParam("download") download: boolean) {
|
||||
if (!this.initialized) {
|
||||
await this.pdf.init();
|
||||
this.initialized = true;
|
||||
@@ -47,7 +47,7 @@ export class PdfController {
|
||||
cards = [cards];
|
||||
}
|
||||
cards = this.mapCardGroupNames(cards);
|
||||
const contracts = await this.pdf.generateRunnerCards(cards, locale);
|
||||
const contracts = await this.pdf.generateRunnerCards(cards, locale, codeformat);
|
||||
res.setHeader('content-type', 'application/pdf');
|
||||
if (download) {
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="cards.pdf"')
|
||||
@@ -97,12 +97,21 @@ export class PdfController {
|
||||
else {
|
||||
runner.group.fullName = `${runner.group.parentGroup.name}/${runner.group.name}`;
|
||||
}
|
||||
runner.donationPerDistanceTotal = runner.distanceDonations.reduce(function (sum, current) {
|
||||
return sum + current.amountPerDistance;
|
||||
}, 0);
|
||||
runner.donationTotal = runner.distanceDonations.reduce(function (sum, current) {
|
||||
return sum + current.amount;
|
||||
}, 0);
|
||||
runner.donationPerDistanceTotal = 0;
|
||||
if (!Array.isArray(runner.distanceDonations)){
|
||||
runner.distanceDonations = [].concat(runner.distanceDonations)
|
||||
}
|
||||
if (runner.distanceDonations.length > 0) {
|
||||
runner.donationPerDistanceTotal += runner.distanceDonations.reduce(function (sum, current) {
|
||||
return sum + current.amountPerDistance;
|
||||
}, 0);
|
||||
}
|
||||
runner.donationTotal = 0;
|
||||
if (runner.distanceDonations.length > 0) {
|
||||
runner.donationTotal += runner.distanceDonations.reduce(function (sum, current) {
|
||||
return sum + current.amount;
|
||||
}, 0);
|
||||
}
|
||||
response.push(runner)
|
||||
}
|
||||
return response;
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
{
|
||||
"address": "Adresse",
|
||||
"betrag-km": "Betrag/KM",
|
||||
"city": "Stadt",
|
||||
"date": "Datum",
|
||||
"firstname": "Vorname",
|
||||
"fuer-den-guten-zweck-zurueckgelegt": "für den guten Zweck zurückgelegt",
|
||||
"gesamt": "Gesamt",
|
||||
"gesamtbetrag": "Gesamtbetrag",
|
||||
"group": "Team/Klasse",
|
||||
"hat-beim-eventname": "Hat beim {{eventname}}",
|
||||
"house_number": "Hausnummer",
|
||||
"id": "ID",
|
||||
"lastname": "Nachname",
|
||||
"location": "Ort",
|
||||
"please_use_blockletters": "Bitte in DRUCKBUCHSTABEN schreiben",
|
||||
"postalcode": "Postleitzahl",
|
||||
"signature": "Unterschrift",
|
||||
"sponsor": "Sponsor",
|
||||
"sponsor-in": "Sponsor:in",
|
||||
"sponsoring_address_condition": "Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen - Spendenquittungen können erst ab einem Gesamtbetrag von {{sponsoring_receipt_minimum_amount}}{{currency_symbol}} ausgestellt werden",
|
||||
"sponsoring_amount_per_distance": "mit einem Betrag von _____{{currency_symbol}} pro gelaufenem Kilometer zu unterstützen.",
|
||||
"sponsoring_subtitle": "Ich/Wir sind bereit anlässlich des {{eventname}}",
|
||||
"sponsoring_title": "Sponsoringerklärung",
|
||||
"sponsorings": "Sponsorings",
|
||||
"street": "Straße",
|
||||
"urkunde": "Urkunde"
|
||||
{
|
||||
"address": "Adresse",
|
||||
"betrag-km": "Betrag/ km",
|
||||
"city": "Stadt",
|
||||
"date": "Datum",
|
||||
"firstname": "Vorname",
|
||||
"fuer-den-guten-zweck-zurueckgelegt": "für den guten Zweck zurückgelegt.",
|
||||
"gesamt": "Gesamt",
|
||||
"gesamtbetrag": "Gesamtbetrag",
|
||||
"group": "Team/ Klasse",
|
||||
"hat-beim-eventname": "hat beim {{eventname}}",
|
||||
"house_number": "Hausnummer",
|
||||
"id": "ID",
|
||||
"lastname": "Nachname",
|
||||
"location": "Ort",
|
||||
"mit_unterstuetzung_von": "Mit Unterstützung von:",
|
||||
"please_use_blockletters": "Bitte in DRUCKBUCHSTABEN schreiben",
|
||||
"postalcode": "Postleitzahl",
|
||||
"signature": "Unterschrift",
|
||||
"sponsor": "Sponsor",
|
||||
"sponsor-in": "Sponsor:in",
|
||||
"sponsoring_address_condition": "Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen - Spendenquittungen können erst ab einem Gesamtbetrag von {{sponsoring_receipt_minimum_amount}}{{currency_symbol}} ausgestellt werden",
|
||||
"sponsoring_amount_per_distance": "mit einem Betrag von _____{{currency_symbol}} pro gelaufenem Kilometer zu unterstützen.",
|
||||
"sponsoring_subtitle": "Ich bin/ Wir sind bereit anlässlich des {{eventname}}",
|
||||
"sponsoring_title": "Sponsoringerklärung",
|
||||
"sponsorings": "Sponsorings",
|
||||
"street": "Straße",
|
||||
"urkunde": "Urkunde"
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"address": "Address",
|
||||
"betrag-km": "Amount/KM",
|
||||
"betrag-km": "Amount/ km",
|
||||
"city": "City",
|
||||
"date": "date",
|
||||
"firstname": "First name",
|
||||
"fuer-den-guten-zweck-zurueckgelegt": "for our good cuse at the {{eventname}}",
|
||||
"fuer-den-guten-zweck-zurueckgelegt": "for our good cause at the {{eventname}}",
|
||||
"gesamt": "Combined",
|
||||
"gesamtbetrag": "Total",
|
||||
"group": "Team/class",
|
||||
"group": "Team/ class",
|
||||
"hat-beim-eventname": "Ran",
|
||||
"house_number": "House number",
|
||||
"id": "ID",
|
||||
"lastname": "Last name",
|
||||
"location": "Location",
|
||||
"mit_unterstuetzung_von": "Supported by:",
|
||||
"please_use_blockletters": "Please write in BLOCK LETTERS.",
|
||||
"postalcode": "Postal code",
|
||||
"signature": "Signature",
|
||||
@@ -20,9 +21,9 @@
|
||||
"sponsor-in": "Donor",
|
||||
"sponsoring_address_condition": "You have to provide an address if you want a donation receipt - Donation receipts can't be issued for total donation amounts under {{sponsoring_receipt_minimum_amount}}{{currency_symbol}}",
|
||||
"sponsoring_amount_per_distance": "with the amount of _____{{currency_symbol}} per kilometer run.",
|
||||
"sponsoring_subtitle": "On the ocation of the {{eventname}} I/We want to support",
|
||||
"sponsoring_subtitle": "On the occasion of the {{eventname}} I/We want to support",
|
||||
"sponsoring_title": "Sponsoring contract",
|
||||
"sponsorings": "Donations",
|
||||
"street": "Street",
|
||||
"urkunde": "Certifcate"
|
||||
"urkunde": "Certificate"
|
||||
}
|
||||
@@ -33,18 +33,18 @@
|
||||
{{#each cards}}
|
||||
<div class="column is-half runnercard">
|
||||
<p class="title is-5" style="text-align: center; padding-bottom: 0; margin-top: -0.75rem;">{{../eventname}}</p>
|
||||
<p style="text-align: center; margin-top: -1.5rem; font-size: small;">lauf-fuer-kaya.de - am 01.01.2021</p>
|
||||
<p style="font-size: small;">Mit unterstützung von:</p>
|
||||
<p style="text-align: center; margin-top: -1.5rem; font-size: small;">{{../card_subtitle}}</p>
|
||||
<p style="font-size: small;">{{__ "mit_unterstuetzung_von"}}</p>
|
||||
<div class="columns" style="height: 6rem; overflow: hidden;">
|
||||
<div class="column is-two-thirds">
|
||||
<div class="column is-half">
|
||||
<!--SPONSOR LOGO HERE-->
|
||||
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
|
||||
src="{{--sponsor this.id}}" />
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="column is-half">
|
||||
<!--BARCODE HERE-->
|
||||
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
|
||||
src="{{--bc this.id ../codeformat}}" />
|
||||
src="{{--bc this.code ../codeformat}}" />
|
||||
</div>
|
||||
</div>
|
||||
<p>{{this.runner.lastname}}, {{this.runner.firstname}} {{this.runner.middlename}}</p>
|
||||
@@ -61,7 +61,7 @@
|
||||
<div style="height: 2cm; padding: 0 0 2.25cm 0">
|
||||
<img style="object-fit: cover; max-height: 2cm;" src="{{--sponsor this.id}}" />
|
||||
</div>
|
||||
<img style="object-fit: cover; max-height: 2.5cm; position: relative;" src="{{--bc this.id ../codeformat}}" />
|
||||
<img style="object-fit: cover; max-height: 2.5cm; position: relative;" src="{{--bc this.code ../codeformat}}" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user