Compare commits

...

525 Commits
6 ... main

Author SHA1 Message Date
ac471b28a6
fix(services/templater): Update kilometer formatting to include dynamic separator based on locale
All checks were successful
Build release images / build-container (push) Successful in 4m8s
Build latest image / build-container (push) Successful in 4m14s
2025-05-27 16:51:09 +02:00
76d982fa04
fix(templates/certificates): Remove 'km' suffix from formatted distance output and calculate the suffix dynamicly
All checks were successful
Build release images / build-container (push) Successful in 3m49s
Build latest image / build-container (push) Successful in 4m9s
2025-05-26 16:59:42 +02:00
14795e1831
fix(templates/cards): Update placeholder text for unassigned runners
All checks were successful
Build latest image / build-container (push) Successful in 4m11s
Build release images / build-container (push) Successful in 4m15s
2025-05-26 16:52:44 +02:00
c48a1f855f
fix(docker): Switch to alpine baseimage for temp file support
All checks were successful
Build release images / build-container (push) Successful in 4m15s
Build latest image / build-container (push) Successful in 4m22s
2025-05-01 18:24:12 +02:00
92380802e9
perf(cards): Implement generation splitting support for large datasets
All checks were successful
Build release images / build-container (push) Successful in 4m17s
Build latest image / build-container (push) Successful in 4m19s
2025-05-01 18:11:46 +02:00
a38a0149b7
fix(templates/certificates): Add point at end of sentence
All checks were successful
Build latest image / build-container (push) Successful in 2m4s
Build release images / build-container (push) Successful in 2m27s
2025-04-25 17:33:43 +02:00
af587b0ac1
feat(certificate): update footer image
All checks were successful
Build latest image / build-container (push) Successful in 1m31s
Build release images / build-container (push) Successful in 1m34s
2025-04-25 16:46:28 +02:00
50e3eff294
feat(certificates): Update footer to generation
All checks were successful
Build latest image / build-container (push) Successful in 1m35s
Build release images / build-container (push) Successful in 1m43s
2025-04-25 16:41:32 +02:00
bc17f7256b
Merge branch 'main' of git.odit.services:lfk/document-server
All checks were successful
Build latest image / build-container (push) Successful in 1m34s
2025-04-25 15:50:45 +02:00
d2f3eea8a5
fix(templater): Fix epc generation 2025-04-25 15:50:28 +02:00
f902c61490
feat(certificate): update footer image
All checks were successful
Build release images / build-container (push) Successful in 2m13s
Build latest image / build-container (push) Successful in 2m19s
2025-04-23 11:57:44 +02:00
11e8cc5b1d
feat(certificate): Add SepaConfig to certificate generation and CombinedGroupName to runners
All checks were successful
Build release images / build-container (push) Successful in 1m57s
Build latest image / build-container (push) Successful in 2m22s
2025-04-17 22:22:50 +02:00
84155b7404
feat(templater): Add GenerateEPC method for generating EPC QR codes 2025-04-17 22:22:46 +02:00
45b37197ec
feat(models): Add CombinedGroupName to RunnerWithDonations and introduce SepaConfig struct 2025-04-17 22:22:43 +02:00
f65848924c
feat(templates): Update donation transfer text and add SEPA support in certificate templates 2025-04-17 22:22:37 +02:00
98d584867e
feat(config): Add SEPA fields to environment configuration 2025-04-17 22:22:29 +02:00
376e8de1a4
feat(config): Add CurrencyIdentifier to configuration 2025-04-17 22:01:02 +02:00
2911391fb9
feat(config): Add SEPA fields to configuration 2025-04-17 21:51:27 +02:00
6d2e0241c9
feat(templates): Added selfservice qr
All checks were successful
Build release images / build-container (push) Successful in 2m12s
Build latest image / build-container (push) Successful in 2m26s
2025-04-17 21:44:58 +02:00
afc5b1f0c6
fix(models): Add required SelfServiceLink field to RunnerWithDonations struct 2025-04-17 21:43:54 +02:00
4a76ee469b
fix(barcode): Use auto encoding for QR code generation to support all characters 2025-04-17 21:43:44 +02:00
b58bf700df
chore(static) Add base64 encoded image for new sponsors
All checks were successful
Build latest image / build-container (push) Successful in 2m2s
Build release images / build-container (push) Successful in 2m6s
2025-04-14 18:08:03 +02:00
efd3a35802
fix(templates): Update titles for runner and certificate templates 2025-04-14 17:56:49 +02:00
0f7e44a42a
fix(models): Correct typo in SponsoringReceiptMinimum mapstructure tag
All checks were successful
Build latest image / build-container (push) Successful in 2m6s
Build release images / build-container (push) Successful in 2m17s
2025-04-10 15:29:29 +02:00
f90e5d75fa
fix(contracts): Minimum was not read correctly
All checks were successful
Build release images / build-container (push) Successful in 2m17s
Build latest image / build-container (push) Successful in 2m27s
2025-04-10 15:16:22 +02:00
31d4ec5f27
fix(templates): Correct spacing in group name display
All checks were successful
Build release images / build-container (push) Successful in 1m51s
Build latest image / build-container (push) Successful in 2m13s
2025-03-26 19:29:32 +01:00
d61d4d6e7e
refactor(ci): Switch to actions
All checks were successful
Build latest image / build-container (push) Successful in 1m39s
2025-03-22 22:48:35 +01:00
606ce6b940
docs(swagger): Build new docs
Some checks failed
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-12-17 17:51:51 +01:00
750fa70332
feat(models): Support nested groups 2024-12-17 17:51:11 +01:00
7d503edbc9
feat(templates): Support nested groups 2024-12-17 17:50:54 +01:00
5c9235df8d
fix(templates): Enable blank cards 2024-12-17 17:38:42 +01:00
11ea0858bb
feat(services): Logging
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
2024-12-17 16:23:52 +01:00
4d57cf827d
refactor(handler): Move array manipulation
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2024-12-17 16:07:40 +01:00
df9f7fdc13
feat(handlers): Added info logging 2024-12-17 16:07:02 +01:00
cdd2b5e250
feat(logging): Debug logging 2024-12-17 15:48:12 +01:00
94b766f106
feat(logger): Log levels 2024-12-17 15:45:40 +01:00
a2e94f715b
refactor(logs): Replaced main logger with zap
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2024-12-16 17:31:23 +01:00
f64daaf817
feat: Request-IDs for better debugging 2024-12-16 17:23:58 +01:00
b4bb732303
fix(config): typoed defaults
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
2024-12-16 17:02:49 +01:00
3dee3e72af
fix: Bad dependency
All checks were successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
2024-12-16 16:41:52 +01:00
f5914e5c38
style: Formatting
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-12-16 16:39:56 +01:00
5a5a7179e9
feat: Use CORS
Some checks failed
ci/woodpecker/push/build Pipeline failed
ci/woodpecker/tag/release Pipeline failed
2024-12-16 16:39:02 +01:00
6c57d63891
fix(config): Added SPONSORING_DISCLAIMER default
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2024-12-16 16:31:08 +01:00
b502e2fbd5
fix(config): Defaults for everyone
Some checks failed
ci/woodpecker/push/build Pipeline failed
ci/woodpecker/tag/release Pipeline was successful
2024-12-16 16:30:02 +01:00
c9475d0093
feat(config): log config on boot
All checks were successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
2024-12-16 16:17:26 +01:00
1cc19e0085
fix(config): Default apikey
Some checks failed
ci/woodpecker/push/build Pipeline failed
ci/woodpecker/tag/release Pipeline was successful
2024-12-16 16:15:11 +01:00
b792806481
docs(swagger): New barcode padding docs
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
2024-12-12 19:17:29 +01:00
ea6a4a7080
refactor(barcode): Switch to inclusive padding 2024-12-12 19:16:55 +01:00
de6fe4991c
feat(barcode): Padding 2024-12-12 19:11:01 +01:00
1d068b2655
docs(swagger): New swaggerdoc
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2024-12-12 18:50:21 +01:00
ef25adf5ed
refactor(models): Group now only has a name
Some checks failed
ci/woodpecker/push/build Pipeline failed
ci/woodpecker/tag/release Pipeline was successful
2024-12-12 18:48:39 +01:00
c09c00ec68
feat(pdfs): Set download names for pdf generation responses
All checks were successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
2024-12-12 18:24:07 +01:00
1f4981b0a9
fix(config): Don't fail on missing env
Some checks failed
ci/woodpecker/push/build Pipeline failed
ci/woodpecker/tag/release Pipeline was successful
2024-12-12 18:18:41 +01:00
c9f28612be
docs(README): Updated benchmark section
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
2024-12-12 18:05:52 +01:00
c2d192d8a3
docs(README): Added performance results 2024-12-12 17:59:33 +01:00
31734596f6
feat(barcode): Implement cache 2024-12-12 17:59:14 +01:00
850fa0a760
feat(barcode): Baseline for barcode caching 2024-12-12 17:44:41 +01:00
1c0a9860fa
feat(container): Provide default redis 2024-12-12 17:44:26 +01:00
7c32f1aad2
perf(templates): Cache templates in map 2024-12-12 17:34:55 +01:00
b9e550d6f5
refactor(services): Move staticservice to struct and interface 2024-12-12 17:11:59 +01:00
2a4f126377
feat(barcodes): Make width and height configureable 2024-12-12 17:03:10 +01:00
5c932158e9
refactor(handlers): Use shared gotenberg 2024-12-12 16:53:51 +01:00
1296c9e399
refactor(pdf): Share templater 2024-12-12 16:52:06 +01:00
0f2452eca0
feat(handlers): Implement barcode generation 2024-12-12 16:49:33 +01:00
b6cc98a165
refactor(services): Extract barcode generation 2024-12-12 16:43:14 +01:00
c5da33f10f
chore: Formatting 2024-12-12 16:33:23 +01:00
69f4de6739
docs(swagger): Move security annotations 2024-12-12 16:33:10 +01:00
649ac2a3c2
docs(swagger): Added barcode generation docs 2024-12-12 16:31:07 +01:00
5587fdaaa8
feat(barcodes): Baseline for implementation 2024-12-12 16:30:46 +01:00
8f676f08a9
refactor: Switch to pdf subpath 2024-12-12 16:28:06 +01:00
28de60d375 Merge pull request 'go' (#49) from go into main
Reviewed-on: #49
Reviewed-by: Philipp Dormann <philipp@noreply.git.odit.services>
2024-12-12 15:11:30 +00:00
d19029b5ad
docs(swagger): Updated swagger metadata 2024-12-11 19:45:05 +01:00
1dfd96869d
docs(swagger): Swagger auth 2024-12-11 19:39:18 +01:00
a1ba28cacb
docs(dev): Updated air commands 2024-12-11 19:33:06 +01:00
d2bace87af
fix(dev): Add air config for linux/macos 2024-12-11 19:31:48 +01:00
9d507b9572
feat(ci): Added ci config 2024-12-11 19:30:02 +01:00
e89c17806f
docs: Updated readme 2024-12-11 19:29:04 +01:00
924f76a100
refactor(images): Move sponsor images to folder and load them dynamicly 2024-12-11 19:22:50 +01:00
99ec0933ea
feat(container): New dev compose with just external services 2024-12-11 19:21:32 +01:00
cceca7f5e1
feat(container): Deliver default static stuff 2024-12-11 18:51:37 +01:00
54d294a8b4
feat(container): Added document-server 2024-12-11 18:51:06 +01:00
41291b9200
refactor(config): Load gotenberg url from env 2024-12-11 18:45:53 +01:00
4faf76a073
feat: Config from env 2024-12-11 18:30:46 +01:00
715eb8e1cb
chore(models): Removed unused param 2024-12-11 18:07:52 +01:00
2686bee1d1
refactor(auth): Switch to query auth for full link support (to be fixed in future) 2024-12-11 17:59:11 +01:00
57a3777891
fix(templates): Fix string style 2024-12-11 17:56:24 +01:00
f6dc33edb4
feat(v1): header auth for all endpoints 2024-12-11 17:55:29 +01:00
e2cd445aeb
feat(docs): Annotated required params 2024-12-11 17:45:51 +01:00
f5debf58fc
feat(docs): New swagger doc 2024-12-09 17:29:31 +01:00
11a9b51197
refactor(templates): Dynamic image loading 2024-12-09 17:28:53 +01:00
c5dc4f7e79
refactor(templates): Dynamic template loading 2024-12-09 17:22:37 +01:00
f9f30e96c7
refactor(templater): Format decimals according to locale 2024-12-09 17:15:06 +01:00
eff3354867
feat(templates): English certificate template 2024-12-09 17:13:01 +01:00
2bfff006ed
fix(templates): Added missing base64 image 2024-12-09 17:10:13 +01:00
53eab5db94
feat(certificate): Implement adding up 2024-12-09 17:08:01 +01:00
af73b35b18
fix(services): Updated templater comma logic 2024-12-09 17:03:13 +01:00
692f378eab
feat(templater): Implement unit formats 2024-12-09 16:59:39 +01:00
12f61e373f
fix(certificate): Expose german template 2024-12-09 16:48:59 +01:00
79a0062f60
feat(certificates): Endpoint with handler 2024-12-09 16:48:06 +01:00
561e7151c6
feat(certificate): German template 2024-12-09 16:44:46 +01:00
2108c88001
feat(certificates): Base types 2024-12-09 16:36:14 +01:00
2537235ce6
feat(static): Added more sponsor logos 2024-12-03 20:00:42 +01:00
4deda8adf1
feat(templates): Get static mounted images 2024-12-03 19:56:57 +01:00
d910a722df
feat(templates): Fallback image 2024-12-03 19:48:08 +01:00
e10448f1e3
feat(docs): Fresh openapi 2024-12-03 19:25:47 +01:00
f880e9f10c
feat(cards): Generate placeholders for cards without a pair 2024-12-03 19:20:28 +01:00
b179541532
fix(handlers): Return cards as pdf 2024-12-03 19:04:09 +01:00
5098d27ae9
refactor(templates): Add Card ids to card 2024-12-03 19:02:18 +01:00
d8fb9ea2fd
refactor(services): Barcodes always accept strings 2024-12-03 18:55:27 +01:00
5dbe7816cd
fix(card): Use correct template endpoints 2024-12-03 18:51:34 +01:00
1657a10dec
fix(templates): Use correct names 2024-12-03 18:45:27 +01:00
7d22a32cb4
feat(cards): Implement endpoint for card generation 2024-12-03 18:44:17 +01:00
ed4941b403
refactor(templates): Use go syntax for swapped cards 2024-12-03 18:43:46 +01:00
c227c291c9
refactor(handlers): Extract function 2024-12-03 18:36:55 +01:00
389922f22d
feat(model): Card Models 2024-12-03 18:27:24 +01:00
670290b60b
refactor(models): Rename Request model 2024-12-03 18:27:11 +01:00
3345571bd8
refactor(templates): Switched to golang templates 2024-12-03 18:23:20 +01:00
d51e78a442
feat(container): Dockerfile 2024-12-03 18:05:45 +01:00
bd70ac4542
refactor(models): Move options model to models 2024-12-03 18:03:25 +01:00
62f04f7d1c
feat(services): Support for multiple barcode formats 🥳 2024-12-03 18:02:15 +01:00
8812bf2410
feat(services): EAN 13 generation 2024-12-03 17:34:11 +01:00
323c0b0ff9
feat(templates): English Contract template 2024-12-03 16:29:58 +01:00
e9c28efd47
feat(handlers): Implemented contracts generation 2024-12-03 16:25:59 +01:00
e5f9eff54f
feat(templates): Working german sponsoring template 2024-12-03 16:25:43 +01:00
382a799038
feat(services): Implement pdf conversion through gotenberg 2024-12-03 16:25:17 +01:00
f4f5c8b63a
feat(services): Baseline templater 2024-12-03 16:24:57 +01:00
c0dd30f08c
feat(dev): Docker compose for gotenberg 2024-12-03 16:24:34 +01:00
3eb914d640
feat(template): First templates 2024-12-03 15:04:10 +01:00
ba7e02fa30
feat(contracts): Provide locale via post request 2024-12-03 14:47:53 +01:00
56e09dafb9
feat(dev): Automagic building of new api docs 2024-12-03 14:47:37 +01:00
145ebd8346
feat(contracts): Baseline handler 2024-12-02 17:30:59 +01:00
13e9c88a8e
feat(docs): First model for swagger 2024-12-02 17:26:03 +01:00
7d571ad46b
docs: Basic readme 2024-12-02 17:16:17 +01:00
491d31e2e9
feat(docs): Swagger generation 2024-12-02 17:13:05 +01:00
3f35d35016
refactor: Ignore temp stuff for air 2024-12-02 17:12:54 +01:00
aeeb7b3448
refactor: Go Baseline 2024-12-02 16:53:32 +01:00
f9b471b59e
refactor: Switch to new language 2024-12-02 16:43:54 +01:00
8f3faee573
Revert "updated templates"
This reverts commit 6ff2ba702b4df86a67a6adce0b5bbff6a1622569.
2024-12-02 15:57:04 +01:00
6ff2ba702b
updated templates 2024-11-21 17:51:23 +01:00
4ecefe8534
fix(ci): Switched to non-drone env vars 2024-02-13 17:25:07 +01:00
ab9295ee23
Frozen lockfile instead of offline
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-29 20:15:10 +02:00
29d4e21656
lockfile 2023-03-29 20:12:06 +02:00
490f24422b
Switched docker over to pnpm w/cache 2023-03-29 20:11:55 +02:00
67eb761a99
switched ci to pnpm cache 2023-03-29 20:10:34 +02:00
22978e5bbc
🚀Bumped version to v0.6.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-15 14:02:13 +01:00
0dbd7311ab
Added static img folder for directly serving images 2023-03-15 14:01:49 +01:00
cbda10e2a1
Added dockerignore for smaller build env 2023-03-15 13:56:53 +01:00
ee3c443216
Fixed docs not getting bundeled 2023-03-15 13:56:33 +01:00
bbe01cd231
🚀Bumped version to v0.6.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-23 09:36:41 +01:00
10c68cfb37
Slimmed down docker build thx to copy
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-23 09:27:00 +01:00
c21076a049
Dependency cleanup 2023-02-23 09:23:12 +01:00
fc774d5af3
Dependency Bumps 2023-02-23 09:18:08 +01:00
dd0b60b2ba
🚀Bumped version to v0.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-23 09:03:09 +01:00
6add09c7df
Version pin 2023-02-23 09:02:45 +01:00
d064b51e1b
Added autochangelog 2023-02-23 09:02:34 +01:00
744f567b7c
Moved drone to kaniko
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-23 09:00:15 +01:00
7dbce9e870
Nerf 2023-02-23 08:57:44 +01:00
72ed2495f6
🚀Bumped version to v0.6.0 2023-02-23 08:56:28 +01:00
05e0c63931
Bumped docker images 2023-02-23 08:56:08 +01:00
fcbcec85c5 🧾New changelog file version [CI SKIP] [skip ci] 2023-02-23 07:48:32 +00:00
b47b2f804f
Lockfile
Some checks failed
continuous-integration/drone/push Build is failing
2023-02-23 08:40:45 +01:00
5f4fc74cd9
Added image 2023-02-23 08:40:38 +01:00
300b8fd01a 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-26 15:18:00 +00:00
8b951dfb3f Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-26 17:09:58 +02:00
517e8a0819
Updated translation due to customer request 2021-07-26 17:09:46 +02:00
3af4b521d3
Changed default kilometer formatting to min 2 decimals, max 3 2021-07-26 17:06:17 +02:00
dca2829323 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 16:17:13 +00:00
57a84c256a Merge pull request 'Fixed decimal separator' (#47) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #47
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-07-19 16:15:35 +00:00
b78534e1b0 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 16:14:59 +00:00
9b85b54da5 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-19 18:14:03 +02:00
4b5a86282d 🚀Bumped version to v0.5.4 2021-07-19 18:13:53 +02:00
c9cb03ea95
Fixed decimal separator in docker 2021-07-19 18:13:37 +02:00
c7f57548f3 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:53:52 +00:00
8d00307170 Merge pull request 'Hotfixes' (#46) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #46
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-07-19 15:53:40 +00:00
5e92b9a48f 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:50:28 +00:00
01e1323555 🚀Bumped version to v0.5.3
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-19 17:49:35 +02:00
f8465721cd 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:41:59 +00:00
4cea7cb32f Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-19 17:41:13 +02:00
72303b1105
Fix for runner donation array 2021-07-19 17:39:52 +02:00
451b7fbe05 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-19 15:23:19 +00:00
2a3322612d
Fixed Locale comma format
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-19 17:22:03 +02:00
4b4d66ae78 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-04 11:42:27 +00:00
c935950eb0 Merge pull request 'v0.5.2: hotfix TypeError in Runner Certificate generation' (#45) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #45
2021-07-04 11:41:55 +00:00
573b921197 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-04 11:40:08 +00:00
274c13e358
🚀Bumped version to v0.5.2
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-04 13:39:06 +02:00
ff0421da2f 🧾New changelog file version [CI SKIP] [skip ci] 2021-07-04 11:34:41 +00:00
915baa6efa Merge branch 'bugfix/44-runner-certificates-result-in-a-status-500' into dev
All checks were successful
continuous-integration/drone/push Build is passing
close #44
2021-07-04 13:33:50 +02:00
bac004d74e wrap distanceDonations.reduce in array length check
ref #44
2021-07-04 13:32:53 +02:00
b7b7f6a0ae 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 16:16:50 +00:00
11efdebacf Merge pull request 'Release 0.5.1' (#43) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #43
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-22 16:16:12 +00:00
0f2d6f58d6 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 16:14:54 +00:00
df8bd1133b Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-22 18:14:02 +02:00
22fb3edd78 🚀Bumped version to v0.5.1 2021-04-22 18:13:50 +02:00
ded610f114 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 16:13:22 +00:00
a4c8dade23 Updated docker-compose example🐳
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-22 18:12:38 +02:00
b6fc069042 Emoji+Chinese fixes🌍 2021-04-22 18:10:32 +02:00
60cc343adf 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:55:26 +00:00
010f2046ad Added new config options to reamde
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-22 17:54:43 +02:00
c18cb7f135 Merge branch 'dev' of git.odit.services:lfk/document-server into dev 2021-04-22 17:50:46 +02:00
2e7c3e8a5b 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:50:43 +00:00
ac9be793bd You can now configure the card's code format distinct from the others 2021-04-22 17:50:42 +02:00
c18fc4ec93 Merge branch 'dev' of https://git.odit.services/lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-22 17:49:40 +02:00
981bae4786 more typo fixes 2021-04-22 17:49:31 +02:00
754d0ca58c 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:47:59 +00:00
fa26ed6012 typo fixes
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-22 17:47:19 +02:00
cc4a2b4ab4 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 15:44:08 +00:00
e97e209746 Now laoding card subtitle from env
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-22 17:41:36 +02:00
8f30d8933f Fixed typo in translation 2021-04-22 17:35:02 +02:00
f78037c0f1 Fixed barcode generation for runenrcard pdfs🐞 2021-04-22 17:32:36 +02:00
3c02e13997 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-16 19:10:33 +00:00
d8f3a6ed06 Dependenc bump 🔝
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-16 21:09:03 +02:00
2ee4c06055 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-11 17:49:45 +00:00
76418f65e1 Quick callstack fix🛠
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-11 19:49:05 +02:00
a57e0909b9 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-31 18:16:18 +00:00
a81db03ba3 Merge pull request 'Release 0.5.0' (#42) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #42
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-31 18:15:32 +00:00
7ae4750307 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-31 17:20:16 +00:00
f623c0a7cd 🚀Bumped version to v0.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-31 19:15:06 +02:00
3fc612488d 📖New license file version [CI SKIP] [skip ci] 2021-03-31 17:11:04 +00:00
f220e70743 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-31 17:10:05 +00:00
d3f7d1a6c9 Merge pull request 'Generate runner certificates feature/36-runner_certificates' (#41) from feature/36-runner_certificates into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #41
2021-03-31 17:09:24 +00:00
82159bed53 Pinned dev dependencies
ref #36
2021-03-31 17:19:48 +02:00
479e28c46c Pinned depencencies (and bumped some)
ref #36
2021-03-31 17:18:43 +02:00
e75f15142e disabled testing for now
ref #36
2021-03-31 17:15:57 +02:00
cec893032d disabled testing for now
ref #36
2021-03-31 17:15:29 +02:00
2278e4ad06 Sorted translations 🌍
ref #36
2021-03-31 17:14:20 +02:00
5a98688d60 Documented new env var
ref #36
2021-03-31 17:13:54 +02:00
63c7beb8b9 Made footer text configureable
ref #36
2021-03-31 17:12:41 +02:00
2a4cfdb2f8 Fixed background opacity
ref #36
2021-03-31 17:08:57 +02:00
a580841973 Addest first coupple of test improvements
ref #36
2021-03-31 16:43:59 +02:00
6b23dea477 Now calculateing total donations (perdistance)
ref #36
2021-03-31 16:31:44 +02:00
e0add846bb Now formatting currency ans distance
ref #36
2021-03-31 16:24:05 +02:00
1d12de7045 Fixed typo
ref #36
2021-03-31 16:17:48 +02:00
b43aeec0cf Added missing interpolations
ref #36
2021-03-31 16:16:37 +02:00
405bb20601 Merge branch 'feature/36-runner_certificates' of git.odit.services:lfk/document-server into feature/36-runner_certificates 2021-03-31 16:15:51 +02:00
ac572f1ea3 Added translations
ref #36
2021-03-31 16:15:49 +02:00
7fea1ca78f Added translations
ref #46
2021-03-31 16:15:42 +02:00
64fce5bd01 Now with embedded background
ref #36
2021-03-31 16:09:15 +02:00
5ba26c4cbf Removed temporary background-image fix
ref #36
2021-03-31 16:05:13 +02:00
b82a32ae3e Fixed page size+background image
ref #36
2021-03-31 15:42:52 +02:00
0af9b81b38 Added new basic certificate endpoint
ref #36
2021-03-31 15:22:16 +02:00
955e11846b Added function for generateing runner certificates
ref #36
2021-03-31 15:18:36 +02:00
d1577cd08d Merge branch 'feature/36-runner_certificates' of git.odit.services:lfk/document-server into feature/36-runner_certificates 2021-03-31 15:13:14 +02:00
6767c3b2d1 Added template strings
ref #36
2021-03-31 15:13:12 +02:00
2b2195727b Added template strings
ref #46
2021-03-31 15:13:08 +02:00
3ca2237953 Added backside table
ref #36
2021-03-31 15:09:14 +02:00
8d6ea4dbf9 Fixed bg image opacity overlay
ref #36
2021-03-31 15:00:12 +02:00
8b71608792 Added front certificate design
ref #36
2021-03-31 14:51:41 +02:00
f1084b59a7 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-30 16:08:57 +00:00
c8dc998ecd Merge pull request 'Release 0.4.3' (#40) from dev into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #40
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-30 16:07:37 +00:00
3df3d26708 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-30 16:07:01 +00:00
922e762aa2 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-30 18:06:02 +02:00
c3beb3e103 🚀Bumped version to v0.4.3 2021-03-30 18:05:48 +02:00
457ea26cf8 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-30 16:05:37 +00:00
c2d2b66f2f Pipeline mtu fix
Some checks reported errors
continuous-integration/drone/push Build was killed
2021-03-30 18:04:54 +02:00
289a0d8671 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 16:48:57 +00:00
ce3053c0ba Merge pull request 'Release 0.4.2' (#39) from dev into main
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build was killed
Reviewed-on: #39
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-29 16:46:31 +00:00
6608456c68 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 16:44:43 +00:00
1cbe5a1614 🚀Bumped version to v0.4.2
Some checks failed
continuous-integration/drone/push Build is failing
2021-03-29 18:43:29 +02:00
9584bfed8b Fixed faulty object mapping 2021-03-29 18:42:50 +02:00
0839ff6359 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-20 17:13:45 +00:00
4739193709 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-20 18:12:54 +01:00
0ade57536e 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-20 17:13:17 +00:00
d17108f4b9 🚀Bumped version to v0.4.1 2021-03-20 18:12:50 +01:00
3c42ca3042 Emergency bugfixes 🐞
Some checks failed
continuous-integration/drone/push Build is failing
2021-03-20 18:12:34 +01:00
073433f308 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-15 14:31:35 +00:00
8bac1fadd6 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-14 20:20:59 +00:00
a478081727 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-14 20:14:03 +00:00
b1978e796f Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-14 21:13:19 +01:00
c51ec74d30 Let's try the mtu fix
´
2021-03-14 21:13:14 +01:00
b8f0d1fa60 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-14 20:05:37 +00:00
96886c74bc Now using our own images to build stuff 🚀
Some checks reported errors
continuous-integration/drone/push Build was killed
2021-03-14 21:04:51 +01:00
8d3cc34395 Merge pull request 'Alpha Release 0.4.0' (#38) from dev into main
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
Reviewed-on: #38
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-14 15:51:53 +00:00
85519bc2e4 🚀Bumped version to v0.4.0
Some checks failed
continuous-integration/drone/push Build is failing
2021-03-14 16:33:17 +01:00
ed02306738 Merge pull request 'Configureable contracts amount feature/35-contracts_per_runner' (#37) from feature/35-contracts_per_runner into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #37
2021-03-14 15:31:26 +00:00
31a59500fa Documented the new config var
ref #35
2021-03-13 14:03:14 +01:00
d01b4a0b99 Added config for amount of contracts per runner
ref #35
2021-03-13 14:01:30 +01:00
d9919404b5 Added logic for generateing multiple contracts per runner
ref #35
2021-03-13 13:59:34 +01:00
b180e04045 Updated ci with new kubernetes secrets 🚀🚀🚀
Some checks failed
continuous-integration/drone/push Build is failing
ref odit/org#12
2021-03-12 18:58:09 +01:00
7f28525ec2 Merge branch 'feature/33-disclaimer' into dev
Some checks failed
continuous-integration/drone/push Build is failing
2021-03-10 20:15:39 +01:00
677bd86133 Changed ci pipeline type to kubernetes
ref odit/org#12 (comment)
2021-03-10 20:11:16 +01:00
b612562d34 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-20 15:19:11 +00:00
e9d3574599 Merge pull request 'Configurable sponsoring disclaimer feature/33-disclaimer' (#34) from feature/33-disclaimer into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #34
2021-02-20 15:18:55 +00:00
9dd62cefa9 Added reamde entry for the new env var
ref #33
2021-02-20 16:05:58 +01:00
0797c678c8 Merge branch 'feature/33-disclaimer' of git.odit.services:lfk/document-server into feature/33-disclaimer 2021-02-20 16:04:02 +01:00
5e4d6f44da MAde disclaimer configurable via env
ref #33
2021-02-20 16:04:00 +01:00
885765ac71 MAde disclaimer configurable via env 2021-02-20 16:03:54 +01:00
03ed6d5bc1 Updated styleing
ref #33
2021-02-20 16:03:15 +01:00
4e1e124d0d Added disclaimer to template
ref #33
2021-02-20 15:58:11 +01:00
19fbf50f6f 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-19 19:10:02 +00:00
21b5e048ed Merge pull request 'Alpha Release 0.3.3 - Download options' (#32) from dev into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #32
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-19 19:09:25 +00:00
c012b4943d 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-19 19:08:18 +00:00
7c23dba493 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-19 20:07:59 +01:00
ba566bcc33 🚀Bumped version to v0.3.3 2021-02-19 20:04:44 +01:00
a386c5bef8 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-19 19:04:35 +00:00
1ca5d3ea07 Added download option to cards
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-19 20:04:15 +01:00
fd8b7e56da 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-19 18:35:54 +00:00
55877de2aa Added download header
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-19 19:35:06 +01:00
d1a29c1cbb 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-18 15:22:54 +00:00
e6f7dd2be8 Merge pull request 'Alpha Release 0.3.2' (#31) from dev into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #31
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-18 15:22:26 +00:00
49590b897e 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-18 15:18:27 +00:00
a9019e4c67 🚀Bumped version to v0.3.2
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-18 16:18:01 +01:00
cc6a53b258 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-18 15:17:10 +00:00
e0db6f6a78 Merge pull request 'Now using full group names feature/18-group_names' (#30) from feature/18-group_names into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #30
2021-02-18 15:16:53 +00:00
0fcfb30d5c Updated templates with full group name
ref #18
2021-02-17 18:59:14 +01:00
c2909082a2 Now manually parsing runnergroup full names
ref #18
2021-02-17 18:57:21 +01:00
92c52401b3 📖New license file version [CI SKIP] [skip ci] 2021-02-16 17:49:26 +00:00
7ca7266ea4 Pinned routing controllers version as temp workaround for routing controllers openapi being broken again
All checks were successful
continuous-integration/drone/push Build is passing
Initial failure: https://ci.odit.services/lfk/document-server/47/1/2
2021-02-16 18:47:52 +01:00
adf11ab1c3 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-13 20:13:40 +00:00
db91661556 Merge pull request 'Alpha Release 0.3.1 - API Keys' (#29) from dev into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #29
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-13 20:13:14 +00:00
9d7d044384 📖New license file version [CI SKIP] [skip ci] 2021-02-13 20:11:44 +00:00
95099c5fbd 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-13 20:10:54 +00:00
782861bd93 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-13 21:10:34 +01:00
dcde424b77 🚀Bumped version to v0.3.1 2021-02-13 21:10:26 +01:00
b7c6c6e157 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-13 20:09:52 +00:00
2d031dae03 Merge pull request 'API Key based auth feature/26-api_auth' (#27) from feature/26-api_auth into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #27
2021-02-13 20:09:36 +00:00
729f2d7240 Added auth to openapi spec
ref #26
2021-02-13 16:24:47 +01:00
454309278e Added api key to env doc
ref #26
2021-02-13 16:16:36 +01:00
7be211f8b7 Fixed bug
ref #26
2021-02-13 16:14:08 +01:00
bdeadd274b Implemented basic auth
ref #26
2021-02-13 16:09:58 +01:00
e306cdb2c8 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-12 17:04:08 +00:00
406add3d51 Merge pull request 'Alpha Release 0.3.0 - Runnercard generation' (#25) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #25
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-12 17:03:26 +00:00
e74b9a4c9d 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-12 17:01:30 +00:00
449a96b302 🚀Bumped version to v0.3.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-12 18:00:42 +01:00
703eaa0e9d Merge pull request 'Card generation feature/14-card_generation' (#24) from feature/14-card_generation into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #24
2021-02-12 16:59:53 +00:00
b4dc1d2be7 Merge branch 'dev' into feature/14-card_generation 2021-02-12 16:56:20 +00:00
cf0f5839ee Added new env vars to readme
ref #14
2021-02-12 17:55:48 +01:00
29376a7782 Now loading sponsor logos from env
ref #14
2021-02-12 17:55:37 +01:00
08e858726c Fixed runnercard backside padding
ref #14
2021-02-12 17:45:52 +01:00
68a1b8f3e0 Implmented sponsoring image selection from array
ref #14
2021-02-12 17:19:57 +01:00
9697d53a15 Fixed bug in array swapping function
ref #14
2021-02-12 17:06:20 +01:00
d38923c4ad Added card generation speed tests (part 2)
ref #14
2021-02-09 19:40:02 +01:00
03c7d590d0 Merge branch 'dev' into feature/14-card_generation
# Conflicts:
#	src/PdfCreator.ts
#	src/asyncHelpers.ts
#	src/controllers/PdfController.ts
#	src/tests/speedtest.ts
2021-02-09 19:38:27 +01:00
8a90f63b09 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 18:38:04 +00:00
491cdb8d71 🚀Bumped version to v0.2.0
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-09 19:37:46 +01:00
68572b194e Added card generation speed tests (part 1)
ref #14
2021-02-09 19:37:32 +01:00
1eb634fe11 Merge branch 'dev' into feature/14-card_generation 2021-02-09 19:28:21 +01:00
dbccbf68f4 📖New license file version [CI SKIP] [skip ci] 2021-02-09 18:26:21 +00:00
96204d809e 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 18:24:56 +00:00
7ac8edb5cf Merge pull request 'Added speedtest feature/19-speed_test' (#23) from feature/19-speed_test into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #23
2021-02-09 18:24:37 +00:00
05561db5b8 Merge branch 'dev' into feature/19-speed_test 2021-02-09 19:22:30 +01:00
149bf1849d 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 18:21:49 +00:00
b952ac4d72 Merge pull request 'Alpha Release 0.2.0 - The barcode release' (#22) from dev into main
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
Reviewed-on: #22
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-09 18:21:14 +00:00
5c075bce8b Added barcode generatin
ref #19
2021-02-09 19:15:08 +01:00
b77945a9e4 Merge branch 'dev' into feature/14-card_generation 2021-02-09 18:43:32 +01:00
aefe5493b0 Beautified output a bit
ref #19
2021-02-09 18:43:15 +01:00
75b8b281b8 Added speedtest script to package
ref #19
2021-02-09 18:02:43 +01:00
44f139aa01 Merge branch 'dev' into feature/19-speed_test 2021-02-09 17:54:44 +01:00
a1b0a1918d Implemented basic contracts tests in various sizes
ref #19
2021-02-09 17:54:13 +01:00
467d8df6db Merge branch 'dev' into feature/19-speed_test 2021-02-09 17:50:05 +01:00
fea0b1dc05 📖New license file version [CI SKIP] [skip ci] 2021-02-09 16:45:18 +00:00
7122fe7dbe 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 16:44:09 +00:00
ff36b4871f Merge pull request 'Barcode generation feature/13-barcode_generation' (#21) from feature/13-barcode_generation into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #21
2021-02-09 16:43:45 +00:00
03f63e3777 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 16:43:32 +00:00
ecd02a1af7 Merge pull request 'Alpha Release 0.1.3 - More env vars' (#20) from dev into main
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
Reviewed-on: #20
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-09 16:43:06 +00:00
b0d42939a6 Merge branch 'dev' into feature/13-barcode_generation
# Conflicts:
#	src/PdfCreator.ts
#	src/config.ts
2021-02-09 17:35:20 +01:00
502345782f Added fallback error image
ref #13
2021-02-09 17:28:21 +01:00
9a7c1d64fd Now loading barcode format from env with overwrite via query param
ref #13
2021-02-09 17:28:04 +01:00
4b79b29ee6 Now with working code scaleing
ref #13
2021-02-09 17:02:02 +01:00
edc846ab05 Implemented async barcode generation using async helpers
ref #13
2021-02-09 16:38:18 +01:00
0d27916188 tmp 2021-02-08 18:12:35 +01:00
0894446085 Implemented runner generation using fakerjs
ref #19
2021-02-08 18:08:01 +01:00
4187a8e820 Fixed broken mime-type
ref #13
2021-02-08 17:52:04 +01:00
e1ec193a4f Removed promise
ref #13
2021-02-08 17:48:09 +01:00
ad9a8a4fe0 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-08 16:39:08 +00:00
6a14232889 🚀Bumped version to v0.1.3
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-08 17:38:42 +01:00
b6296b8d97 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-08 16:37:35 +00:00
bc4d16e6f8 Merge pull request 'Load more stuff from env feature/16-env_vars' (#17) from feature/16-env_vars into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #17
2021-02-08 16:37:07 +00:00
e3a45a61ac Implemented first experimental speedtest
ref #19
2021-02-08 17:32:02 +01:00
7f58dd694b Fixed double-sided printing
ref #14
2021-02-08 16:14:10 +01:00
68f46a45b5 Added **very** basic backside
ref #14
2021-02-08 15:57:09 +01:00
016f746c7c Styled front
ref #14
2021-02-08 15:53:44 +01:00
b77bb3ad9d Added new env vars to readme
ref #16
2021-02-08 10:23:02 +01:00
b4ebae283b Now loading interpolation vars from config/env
ref #16
2021-02-08 10:22:49 +01:00
3bb322ede5 Added new env vars to config
ref #16
2021-02-08 10:22:29 +01:00
b92a6f7b2b Added sizing for the real cards
ref #14
2021-02-08 10:14:27 +01:00
d3a213ce33 Added basic logic to generate two-sided runnercards
ref #14
2021-02-08 10:07:58 +01:00
8fc6c7176e Added basic card generation function
ref #14
2021-02-08 09:55:29 +01:00
929ac81515 Added cards api endpoint
ref #14
2021-02-08 09:51:52 +01:00
75d2ac3c5f Added todo
ref #13
2021-02-08 09:47:28 +01:00
3e2b011d28 Switched to using the current runner's id as the barcode text
ref #13
2021-02-08 09:24:10 +01:00
1c06689800 Reworked template layout for barcode
ref #13
2021-02-08 09:20:45 +01:00
a35f8cfd3a First part of the handlebars barcode generation
ref #13
2021-02-08 09:11:59 +01:00
27d1d69360 Added barcode field to template
ref #13
2021-02-08 09:11:27 +01:00
8072d0b194 Added basic barcode generation
ref #13
2021-02-08 08:32:39 +01:00
a30600943d 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 18:06:06 +00:00
123cf8ad48 Merge pull request 'Alpha Release 0.1.2 - Hotfix release' (#15) from dev into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #15
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-07 18:05:41 +00:00
22b1e0097e 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 17:59:40 +00:00
7e507d4cc4 🚀Bumped version to v0.1.2
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-07 18:59:20 +01:00
f7dfd6d0c3 PAtch: Copy locales
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-07 18:59:10 +01:00
cbff3074d1 Merge pull request 'Alpha Release 0.1.1 - Hotfix release' (#12) from dev into main
Some checks reported errors
continuous-integration/drone/tag Build encountered an error
continuous-integration/drone/push Build is passing
Reviewed-on: #12
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-07 17:41:14 +00:00
9755e437c2 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 17:36:21 +00:00
fe0f45ea92 Revert "Now only using our mirror to build"
Some checks reported errors
continuous-integration/drone/push Build was killed
This reverts commit b07c5a96f2a670f2279a6575b03f9c553b59e0a9.
2021-02-07 18:36:05 +01:00
b07c5a96f2 Now only using our mirror to build
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-07 18:35:11 +01:00
dba765cb01 Switched to using our mirrored images for buildi
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-02-07 18:16:38 +01:00
8ca015ecf8 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2021-02-07 18:04:19 +01:00
6ab3946c31 Pinned alpine version 2021-02-07 17:55:07 +01:00
603a814ad4 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 16:53:09 +00:00
7736ccdc0c Merge branch 'dev' of git.odit.services:lfk/document-server into dev
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-07 17:52:55 +01:00
d1b07f39fd 🚀Bumped version to v0.1.1 2021-02-07 17:52:50 +01:00
8d4e7a16d1 📖New license file version [CI SKIP] [skip ci] 2021-02-07 16:52:46 +00:00
4fca5afdfa 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 16:51:51 +00:00
ea8028dfd5 Updated dockerfile & build for dockerized puppeteer
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-07 17:51:36 +01:00
bc2b6fadd9 Merge pull request 'Alpha Release 0.1.0 - Contract generation' (#11) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #11
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-07 15:53:07 +00:00
ee19efa0e6 📖New license file version [CI SKIP] [skip ci] 2021-02-07 15:39:51 +00:00
f3de80af78 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 15:38:51 +00:00
6a42fe37d0 🚀Bumped version to v0.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-07 16:38:31 +01:00
84259d37d4 Merge pull request 'Sponsoring contract generation feature/5-sponsoring_contracts' (#10) from feature/5-sponsoring_contracts into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #10
2021-02-07 15:37:51 +00:00
140fda11cc Added optimization args
ref #5
2021-02-07 16:37:24 +01:00
e92820e12f Parellized the chunks
ref #5
2021-02-07 16:28:26 +01:00
4773a5f18c Added sentence about large requests to the /contracts openapi description
ref #5
2021-02-07 16:11:35 +01:00
06dedc0797 Removed puppeteer args
ref #5
2021-02-07 16:07:38 +01:00
785544ca16 Added pdf merging for big requests (over 100 runners)
ref #5
2021-02-07 16:04:56 +01:00
4f191dcb52 Expanded max request body size to 500mb
ref #5
2021-02-07 15:41:53 +01:00
1ced0e3175 Added option to generate empty sponsoring contracts
ref #5
2021-02-07 13:11:54 +01:00
5e1252545b Removed the example data from the fill-in fields
ref #5
2021-02-07 13:04:45 +01:00
2d0b7ce79e Formatting
ref #5
2021-02-07 12:59:11 +01:00
1962499d18 Sorted locales
ref #5
2021-02-07 12:55:40 +01:00
1e67672ef0 Added translation sorting secript
ref #
2021-02-07 12:55:24 +01:00
e401d0ec72 Removed base64 image
ref #5
2021-02-07 12:32:54 +01:00
42443af734 Added automatic img to base64 conversion
ref #5
2021-02-07 12:32:23 +01:00
c07319b250 Now awaiting language change
ref #5
2021-02-06 22:02:16 +01:00
388d8a2dc6 Removed fullname from groups (to be renabled later)
ref #5
2021-02-06 21:48:47 +01:00
ee8ba99cc7 Fixed the controller not waiting for initialization
ref #5
2021-02-06 21:37:56 +01:00
47a05facb3 Moved pdf creatior initialization to new function
ref #5
2021-02-06 21:30:18 +01:00
f755f4f9fb Added full interpolation support to the i18n
ref #5
2021-02-06 21:15:48 +01:00
784d7c656f Added a bunch of english and german translations 🌎
ref #5
2021-02-06 20:33:47 +01:00
e345c36dd0 Added i18n strings
ref #5
2021-02-06 20:33:21 +01:00
c617c40e9d Working Styleing
ref #5
2021-02-06 20:15:33 +01:00
a9e3360ee2 First working (TM) template code
ref #5
2021-02-06 20:04:08 +01:00
13776d1ecb Added first parts of template
ref #5
2021-02-06 19:16:45 +01:00
f69e7779e4 Updated readme
ref #5
2021-02-05 22:23:48 +01:00
8ec5de4877 Merge branch 'feature/5-sponsoring_contracts' of git.odit.services:lfk/document-server into feature/5-sponsoring_contracts 2021-02-05 22:19:50 +01:00
7c3813b94d Now with workin i18n-ally config🥳
ref #5
2021-02-05 22:19:47 +01:00
b4232e51e0 Now with workin i18n-ally config🥳
ref #5
2021-02-05 22:19:38 +01:00
cd51e78282 Added comments
ref #5
2021-02-05 22:02:26 +01:00
f833ae222e Now accepting arrays for sponsoring contract generation
ref #5
2021-02-05 21:56:33 +01:00
4cd437b6af Removed useless await
ref #5
2021-02-05 21:48:47 +01:00
9be3e789a5 Merge branch 'feature/5-sponsoring_contracts' of git.odit.services:lfk/document-server into feature/5-sponsoring_contracts 2021-02-05 21:48:18 +01:00
119102aef8 Updated comments
ref #5
2021-02-05 21:48:15 +01:00
cf9cd298b6 Updated comments
ref #5
2021-02-05 21:47:23 +01:00
05e471878f Updated readme for handlebars
ref #5
2021-02-05 21:29:21 +01:00
6f81566cb8 Implemented language selection by query param
ref #5
2021-02-05 21:27:14 +01:00
a596188c00 Updated template for i18n
ref #5
2021-02-05 21:20:20 +01:00
75eb925267 Added translations using i18next
ref #5
2021-02-05 21:20:05 +01:00
19697692bc Added example locales
ref #5
2021-02-05 21:17:08 +01:00
4acf3e39ce Switched to handlebars for templateing
ref #5
2021-02-05 20:08:47 +01:00
3af76a53e3 first working pdf generation from class 🎉
ref #5
2021-02-05 17:59:26 +01:00
d58453faf9 Edited html template
ref #5
2021-02-05 17:49:42 +01:00
bd6ec6215d Fixed data validation problem
ref #5
2021-02-05 17:48:14 +01:00
96d863bfc8 Merge branch 'feature/5-sponsoring_contracts' of git.odit.services:lfk/document-server into feature/5-sponsoring_contracts
# Conflicts:
#	src/PdfCreator.ts
2021-02-05 14:09:57 +01:00
ba7cedd187 PDF Creator now accepts single instances of class
ref #5
2021-02-05 14:09:44 +01:00
78205ee8c7 PDF Creator now accepts single instances of class
ref #5
2021-02-05 14:09:20 +01:00
041c0ed6bb Added a coupple of real replacement strings to the sponsoring template
ref #5
2021-02-05 13:59:13 +01:00
6121b1e3bf Updated the contract template to use external css
ref #5
2021-02-05 13:57:04 +01:00
5afd26ea22 Switched to puppeteer for pdf generation
ref #5
2021-02-05 13:53:46 +01:00
4585a83838 Merge pull request 'Patch 0.0.2' (#9) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #9
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-03 17:24:33 +00:00
c07c6aeeab Fixed file broken in merge
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-03 18:23:08 +01:00
9a115e75b1 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-03 18:21:04 +01:00
f4a34dd36b 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-03 18:21:01 +01:00
42d6c40d0c 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-03 17:11:34 +00:00
de432c4481 🚀Bumped version to v0.0.2
Some checks failed
continuous-integration/drone/push Build is failing
2021-02-03 18:11:15 +01:00
73915da203 Merge pull request 'Basic documentation feature/6-documentation' (#8) from feature/6-documentation into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #8
2021-02-03 17:10:01 +00:00
15cd5f6579 Merge branch 'dev' into feature/6-documentation 2021-02-03 17:09:50 +00:00
95b882aced Typos
ref #6
2021-02-03 18:07:35 +01:00
376087fe66 Merge branch 'feature/6-documentation' of git.odit.services:lfk/document-server into feature/6-documentation 2021-02-03 18:06:17 +01:00
68e34a9054 Typos
ref #6
2021-02-03 18:06:13 +01:00
465efe0300 📖New license file version [CI SKIP] [skip ci] 2021-02-03 17:02:45 +00:00
782b41756d Merge branch 'dev' into feature/6-documentation 2021-02-03 17:02:30 +00:00
15f16415c0 Merge pull request 'Implemented the basics for templateing feature/3-pdf_templateing' (#4) from feature/3-pdf_templateing into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #4
2021-02-03 17:01:59 +00:00
82b5f9e86e Merge branch 'dev' into feature/3-pdf_templateing 2021-02-03 17:01:41 +00:00
b5e79e51ef Merge pull request 'Implemented the basic request classes feature/1-input_classes' (#2) from feature/1-input_classes into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #2
2021-02-03 16:49:20 +00:00
8141269dd9 Added a template section to the readme
ref #6
2021-02-02 14:48:27 +01:00
56b72275ac Added editor and stageing infos to the README
ref #6
2021-02-02 14:36:00 +01:00
df94b1b750 Added a basic readme
ref #6
2021-02-02 14:35:10 +01:00
15ff412d75 Merge branch 'feature/6-documentation' of git.odit.services:lfk/document-server into feature/6-documentation 2021-02-02 14:31:37 +01:00
36396af50a Updated apidoc viewer settings
ref #6
2021-02-02 14:31:36 +01:00
4d45e0f80d Updated apidoc viewer settings 2021-02-02 14:31:26 +01:00
6435c8a88e Added openapi viewers
ref #6
2021-02-02 14:31:09 +01:00
3fb8be22b7 Added typeing to the buffer and stream conversion
ref #3
2021-02-02 11:34:01 +01:00
7d5b5750ad Created a pdf class that takes care of the pdf wrapping
ref #3
2021-02-02 11:30:51 +01:00
64bd1ffc3a Created a barebones pdf controller
ref #3
2021-02-02 11:28:24 +01:00
3ca38abe93 Switched to the splitted functions
ref #3
2021-02-02 11:28:07 +01:00
2c7b0254ec Adjusted template font styleing
ref #3
2021-02-02 11:27:14 +01:00
f726a6e699 Added a bs example template
ref #3
2021-02-02 11:20:47 +01:00
a7d4001ad9 Added html-pdf package
ref #3
2021-02-02 11:03:06 +01:00
4617f2c5bd Resolved fun issues with promises
ref #3
2021-02-02 11:02:23 +01:00
557cc26f28 Added Barebones pdf creator class
ref #3
2021-02-02 10:01:31 +01:00
8a30265fc6 Added templates folder
ref #3
2021-02-02 09:59:48 +01:00
9f3758de39 Added PDFs to gitignore
ref #3
2021-02-02 09:59:34 +01:00
cfa65e83ca Added a basic donor class
ref #1
2021-02-02 09:09:10 +01:00
1d73e4ed9c Added the donation classes
ref #1
2021-02-02 09:05:21 +01:00
1d1fa50327 Moved distance to the main runner object
ref #1
2021-02-02 09:05:02 +01:00
d08bdfd961 Added a certificaterunner class
ref #1
2021-02-02 08:59:30 +01:00
aae4f507ea Runners now use the runnergroup class instead of strings
ref #1
2021-02-02 08:50:31 +01:00
cb7325bbf9 Added a runnercard class
ref #1
2021-02-02 08:49:41 +01:00
e29c17a29a Added a simplified runnergroup class
ref #1
2021-02-02 08:49:18 +01:00
63f9523766 Added input class for contract generation
ref #1
2021-01-30 18:32:28 +01:00
d291cf0d1b Added address class and errors
ref #1
2021-01-30 18:32:01 +01:00
d493e74bf3 📖New license file version [CI SKIP] [skip ci] 2021-01-30 17:11:56 +00:00
83c4bd62cb fixed license-exporter call
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-30 18:11:27 +01:00
8f250f747a 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 17:07:00 +00:00
0c4e5a182c Added license export and release scripts
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-30 18:06:46 +01:00
48d7cad9f3 Added drone file 2021-01-30 18:05:37 +01:00
185e66fb5b Added release-it config 2021-01-30 17:55:07 +01:00
cbc421cd83 Added build script 2021-01-30 17:53:08 +01:00
b9d4cc3619 Added dockerfile and docker-compose 2021-01-30 17:50:09 +01:00
1d62e7f14c Added a simple status controller 2021-01-30 17:47:34 +01:00
4efb62921b Cleaned up openapi stuff 2021-01-30 17:47:22 +01:00
c2d714116e Added missing dependencies 2021-01-30 17:47:10 +01:00
cff70110fd Added version to config 2021-01-30 17:45:13 +01:00
814b564752 Added dev script 2021-01-30 17:44:04 +01:00
915ff92418 Added base app 2021-01-30 17:27:43 +01:00
94e5e51f8d Added openapi dependency 2021-01-30 17:27:36 +01:00
57afbd4b6c Added loaders 2021-01-30 17:27:25 +01:00
6539fd7855 Added errorhandler middleware 2021-01-30 17:27:01 +01:00
9d62225478 Added openapi related classes 2021-01-30 17:26:51 +01:00
b1747f6374 Added version to config 2021-01-30 17:25:36 +01:00
ae466b4d7e Added vscode workspace config 2021-01-30 17:22:42 +01:00
e1f03788db Added tsconfig 2021-01-30 17:22:17 +01:00
278c56386b Added config class 2021-01-30 17:21:52 +01:00
66134c0e1e Added files to gitignore 2021-01-30 17:21:46 +01:00
61c7dbff0b Merge branch 'dev' of git.odit.services:lfk/document-server into dev
# Conflicts:
#	package.json
2021-01-30 17:20:56 +01:00
43bb728d0d Added basic dependencies 2021-01-30 17:20:42 +01:00
d450ceac74 Added basic dependencies 2021-01-30 17:19:06 +01:00
b0427a6d81 Initialized package 2021-01-30 17:16:12 +01:00
48 changed files with 3652 additions and 493 deletions

46
.air.linux.toml Normal file
View File

@ -0,0 +1,46 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "tmp\\main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

46
.air.windows.toml Normal file
View File

@ -0,0 +1,46 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "tmp\\main.exe"
cmd = "go build -o ./tmp/main.exe ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
tmp
docker-compose.yaml
.air*.toml

23
.env Normal file
View File

@ -0,0 +1,23 @@
LOGLEVEL=debug
PORT=3000
PRODUCION=false
APIKEY=lfk
EVENTNAME=Lauf für Kaya! 2025
CURRENCYSYMBOL=
GOTENBERG_BASEURL=http://localhost:3001
REDIS_ADDR=localhost:6379
CARD_SUBTITLE=Kaya ist cool
CARD_BARCODEFORMAT=ean13
# CARD_BARCODEPREFIX=
SPONSORING_RECEIPTMINIMUM=40
SPONSORING_DISCLAIMER=Kaya ist cool, aber pass auf, dass du nicht zu viel Geld sammelst!
SPONSORING_BARCODEFORMAT=code128
# SPONSORING_BARCODEPREFIX=
CERTIFICATE_FOOTER=Kaya ist cool, danke für deine Unterstützung!
SEPA_BIC=FNOMDEB2
SEPA_NAME=ODIT.Services
SEPA_IBAN=DE25100180000690238989

27
.gitea/workflows/dev.yaml Normal file
View File

@ -0,0 +1,27 @@
name: Build latest image
on:
push:
branches:
- main
jobs:
build-container:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to registry
uses: docker/login-action@v3
with:
registry: registry.odit.services
username: ${{ vars.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: |
${{ vars.REGISTRY }}/lfk/document-server:latest
platforms: linux/amd64,linux/arm64

View File

@ -0,0 +1,27 @@
name: Build release images
on:
push:
tags:
- "*.*.*"
jobs:
build-container:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to registry
uses: docker/login-action@v3
with:
registry: registry.odit.services
username: ${{ vars.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: |
${{ vars.REGISTRY }}/lfk/document-server:${{ github.ref_name }}
platforms: linux/amd64,linux/arm64

130
.gitignore vendored
View File

@ -1,129 +1 @@
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
tmp

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.18
RUN mkdir -p /tmp && chmod 1777 /tmp
COPY --from=builder /app/server /server
COPY static /static
ENTRYPOINT [ "/server" ]

362
LICENSE
View File

@ -1,362 +0,0 @@
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Creative
Commons Corporation ("Creative Commons") is not a law firm and does not provide
legal services or legal advice. Distribution of Creative Commons public licenses
does not create a lawyer-client or other relationship. Creative Commons makes
its licenses and related information available on an "as-is" basis. Creative
Commons gives no warranties regarding its licenses, any material licensed
under their terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the fullest
extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and conditions
that creators and other rights holders may use to share original works of
authorship and other material subject to copyright and certain other rights
specified in the public license below. The following considerations are for
informational purposes only, are not exhaustive, and do not form part of our
licenses.
Considerations for licensors: Our public licenses are intended for use by
those authorized to give the public permission to use material in ways otherwise
restricted by copyright and certain other rights. Our licenses are irrevocable.
Licensors should read and understand the terms and conditions of the license
they choose before applying it. Licensors should also secure all rights necessary
before applying our licenses so that the public can reuse the material as
expected. Licensors should clearly mark any material not subject to the license.
This includes other CC-licensed material, or material used under an exception
or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public licenses, a licensor
grants the public permission to use the licensed material under specified
terms and conditions. If the licensor's permission is not necessary for any
reasonfor example, because of any applicable exception or limitation to copyrightthen
that use is not regulated by the license. Our licenses grant only permissions
under copyright and certain other rights that a licensor has authority to
grant. Use of the licensed material may still be restricted for other reasons,
including because others have copyright or other rights in the material. A
licensor may make special requests, such as asking that all changes be marked
or described. Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More considerations for the public
: wiki.creativecommons.org/Considerations_for_licensees
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree to
be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike
4.0 International Public License ("Public License"). To the extent this Public
License may be interpreted as a contract, You are granted the Licensed Rights
in consideration of Your acceptance of these terms and conditions, and the
Licensor grants You such rights in consideration of benefits the Licensor
receives from making the Licensed Material available under these terms and
conditions.
Section 1 Definitions.
a. Adapted Material means material subject to Copyright and Similar Rights
that is derived from or based upon the Licensed Material and in which the
Licensed Material is translated, altered, arranged, transformed, or otherwise
modified in a manner requiring permission under the Copyright and Similar
Rights held by the Licensor. For purposes of this Public License, where the
Licensed Material is a musical work, performance, or sound recording, Adapted
Material is always produced where the Licensed Material is synched in timed
relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright and Similar
Rights in Your contributions to Adapted Material in accordance with the terms
and conditions of this Public License.
c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses,
approved by Creative Commons as essentially the equivalent of this Public
License.
d. Copyright and Similar Rights means copyright and/or similar rights closely
related to copyright including, without limitation, performance, broadcast,
sound recording, and Sui Generis Database Rights, without regard to how the
rights are labeled or categorized. For purposes of this Public License, the
rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
e. Effective Technological Measures means those measures that, in the absence
of proper authority, may not be circumvented under laws fulfilling obligations
under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996,
and/or similar international agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or any other
exception or limitation to Copyright and Similar Rights that applies to Your
use of the Licensed Material.
g. License Elements means the license attributes listed in the name of a Creative
Commons Public License. The License Elements of this Public License are Attribution,
NonCommercial, and ShareAlike.
h. Licensed Material means the artistic or literary work, database, or other
material to which the Licensor applied this Public License.
i. Licensed Rights means the rights granted to You subject to the terms and
conditions of this Public License, which are limited to all Copyright and
Similar Rights that apply to Your use of the Licensed Material and that the
Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights under this
Public License.
k. NonCommercial means not primarily intended for or directed towards commercial
advantage or monetary compensation. For purposes of this Public License, the
exchange of the Licensed Material for other material subject to Copyright
and Similar Rights by digital file-sharing or similar means is NonCommercial
provided there is no payment of monetary compensation in connection with the
exchange.
l. Share means to provide material to the public by any means or process that
requires permission under the Licensed Rights, such as reproduction, public
display, public performance, distribution, dissemination, communication, or
importation, and to make material available to the public including in ways
that members of the public may access the material from a place and at a time
individually chosen by them.
m. Sui Generis Database Rights means rights other than copyright resulting
from Directive 96/9/EC of the European Parliament and of the Council of 11
March 1996 on the legal protection of databases, as amended and/or succeeded,
as well as other essentially equivalent rights anywhere in the world.
n. You means the individual or entity exercising the Licensed Rights under
this Public License. Your has a corresponding meaning.
Section 2 Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License, the Licensor
hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive,
irrevocable license to exercise the Licensed Rights in the Licensed Material
to:
A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial
purposes only; and
B. produce, reproduce, and Share Adapted Material for NonCommercial purposes
only.
2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions
and Limitations apply to Your use, this Public License does not apply, and
You do not need to comply with its terms and conditions.
3. Term. The term of this Public License is specified in Section 6(a).
4. Media and formats; technical modifications allowed. The Licensor authorizes
You to exercise the Licensed Rights in all media and formats whether now known
or hereafter created, and to make technical modifications necessary to do
so. The Licensor waives and/or agrees not to assert any right or authority
to forbid You from making technical modifications necessary to exercise the
Licensed Rights, including technical modifications necessary to circumvent
Effective Technological Measures. For purposes of this Public License, simply
making modifications authorized by this Section 2(a)(4) never produces Adapted
Material.
5. Downstream recipients.
A. Offer from the Licensor Licensed Material. Every recipient of the Licensed
Material automatically receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this Public License.
B. Additional offer from the Licensor Adapted Material. Every recipient
of Adapted Material from You automatically receives an offer from the Licensor
to exercise the Licensed Rights in the Adapted Material under the conditions
of the Adapter's License You apply.
C. No downstream restrictions. You may not offer or impose any additional
or different terms or conditions on, or apply any Effective Technological
Measures to, the Licensed Material if doing so restricts exercise of the Licensed
Rights by any recipient of the Licensed Material.
6. No endorsement. Nothing in this Public License constitutes or may be construed
as permission to assert or imply that You are, or that Your use of the Licensed
Material is, connected with, or sponsored, endorsed, or granted official status
by, the Licensor or others designated to receive attribution as provided in
Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not licensed under this
Public License, nor are publicity, privacy, and/or other similar personality
rights; however, to the extent possible, the Licensor waives and/or agrees
not to assert any such rights held by the Licensor to the limited extent necessary
to allow You to exercise the Licensed Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this Public License.
3. To the extent possible, the Licensor waives any right to collect royalties
from You for the exercise of the Licensed Rights, whether directly or through
a collecting society under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly reserves any right
to collect such royalties, including when the Licensed Material is used other
than for NonCommercial purposes.
Section 3 License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the following
conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified form), You must:
A. retain the following if it is supplied by the Licensor with the Licensed
Material:
i. identification of the creator(s) of the Licensed Material and any others
designated to receive attribution, in any reasonable manner requested by the
Licensor (including by pseudonym if designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of warranties;
v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
B. indicate if You modified the Licensed Material and retain an indication
of any previous modifications; and
C. indicate the Licensed Material is licensed under this Public License, and
include the text of, or the URI or hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner
based on the medium, means, and context in which You Share the Licensed Material.
For example, it may be reasonable to satisfy the conditions by providing a
URI or hyperlink to a resource that includes the required information.
3. If requested by the Licensor, You must remove any of the information required
by Section 3(a)(1)(A) to the extent reasonably practicable.
b. ShareAlike.In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons license with
the same License Elements, this version or later, or a BY-NC-SA Compatible
License.
2. You must include the text of, or the URI or hyperlink to, the Adapter's
License You apply. You may satisfy this condition in any reasonable manner
based on the medium, means, and context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms or conditions
on, or apply any Effective Technological Measures to, Adapted Material that
restrict exercise of the rights granted under the Adapter's License You apply.
Section 4 Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that apply to
Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract,
reuse, reproduce, and Share all or a substantial portion of the contents of
the database for NonCommercial purposes only;
b. if You include all or a substantial portion of the database contents in
a database in which You have Sui Generis Database Rights, then the database
in which You have Sui Generis Database Rights (but not its individual contents)
is Adapted Material, including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share all or
a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not replace
Your obligations under this Public License where the Licensed Rights include
other Copyright and Similar Rights.
Section 5 Disclaimer of Warranties and Limitation of Liability.
a. Unless otherwise separately undertaken by the Licensor, to the extent possible,
the Licensor offers the Licensed Material as-is and as-available, and makes
no representations or warranties of any kind concerning the Licensed Material,
whether express, implied, statutory, or other. This includes, without limitation,
warranties of title, merchantability, fitness for a particular purpose, non-infringement,
absence of latent or other defects, accuracy, or the presence or absence of
errors, whether or not known or discoverable. Where disclaimers of warranties
are not allowed in full or in part, this disclaimer may not apply to You.
b. To the extent possible, in no event will the Licensor be liable to You
on any legal theory (including, without limitation, negligence) or otherwise
for any direct, special, indirect, incidental, consequential, punitive, exemplary,
or other losses, costs, expenses, or damages arising out of this Public License
or use of the Licensed Material, even if the Licensor has been advised of
the possibility of such losses, costs, expenses, or damages. Where a limitation
of liability is not allowed in full or in part, this limitation may not apply
to You.
c. The disclaimer of warranties and limitation of liability provided above
shall be interpreted in a manner that, to the extent possible, most closely
approximates an absolute disclaimer and waiver of all liability.
Section 6 Term and Termination.
a. This Public License applies for the term of the Copyright and Similar Rights
licensed here. However, if You fail to comply with this Public License, then
Your rights under this Public License terminate automatically.
b. Where Your right to use the Licensed Material has terminated under Section
6(a), it reinstates:
1. automatically as of the date the violation is cured, provided it is cured
within 30 days of Your discovery of the violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any right the
Licensor may have to seek remedies for Your violations of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the Licensed Material
under separate terms or conditions or stop distributing the Licensed Material
at any time; however, doing so will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
Section 7 Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different terms or
conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the Licensed
Material not stated herein are separate from and independent of the terms
and conditions of this Public License.
Section 8 Interpretation.
a. For the avoidance of doubt, this Public License does not, and shall not
be interpreted to, reduce, limit, restrict, or impose conditions on any use
of the Licensed Material that could lawfully be made without permission under
this Public License.
b. To the extent possible, if any provision of this Public License is deemed
unenforceable, it shall be automatically reformed to the minimum extent necessary
to make it enforceable. If the provision cannot be reformed, it shall be severed
from this Public License without affecting the enforceability of the remaining
terms and conditions.
c. No term or condition of this Public License will be waived and no failure
to comply consented to unless expressly agreed to by the Licensor.
d. Nothing in this Public License constitutes or may be interpreted as a limitation
upon, or waiver of, any privileges and immunities that apply to the Licensor
or You, including from the legal processes of any jurisdiction or authority.
Creative Commons is not a party to its public licenses. Notwithstanding, Creative
Commons may elect to apply one of its public licenses to material it publishes
and in those instances will be considered the "Licensor." The text of the
Creative Commons public licenses is dedicated to the public domain under the
CC0 Public Domain Dedication. Except for the limited purpose of indicating
that material is shared under a Creative Commons public license or as otherwise
permitted by the Creative Commons policies published at creativecommons.org/policies,
Creative Commons does not authorize the use of the trademark "Creative Commons"
or any other trademark or logo of Creative Commons without its prior written
consent including, without limitation, in connection with any unauthorized
modifications to any of its public licenses or any other arrangements, understandings,
or agreements concerning use of licensed material. For the avoidance of doubt,
this paragraph does not form part of the public licenses.
Creative Commons may be contacted at creativecommons.org.

View File

@ -1,3 +1,96 @@
# document-server
# ✒️ Document generation microservices
The document generation server responsible for creating pdfs for sponsoring contracts, certificates and more.
## Features
- 📝 HTML templates for pdf generation
- 📚 OpenAPI/Swagger documentation
- ⚡ High-performance with go and gotenberg
## 🚀 Quick Start
```bash
# Install dependencies
go mod download
# Run the server
air
```
## 📖 API Documentation
Swagger UI is available at: `/swagger`
## 🛠️ Development
The project uses:
- 🏃‍♂️ go as the language and build tool
- 🌐 gofiber for the web framework
- 📦 air for live reload
- 📝 swaggo for API documentation
- 📄 gotenberg for HTML to PDF conversion
### 📦 Use docker compose for external dependencies
```shell
docker compose -f docker-compose.dev.yaml up
```
### 🏃 Run via air
> Install air via `go install github.com/air-verse/air@latest`
```shell
# With the default air config
air
# With the config for linux/macOS
air -c .air.linux.toml
# With the config for windows
air -c .air.windows.toml
```
### ✒️ Update the swagger docs
> Install swag via `go install github.com/swaggo/swag/cmd/swag@latest`
```shell
swag init
```
### 🐋 Build container
```shell
# single arch
docker build -t registry.odit.services/lfk/document-server:latest .
# multiarch
docker buildx build --platform=linux/amd64,linux/arm64 -t registry.odit.services/lfk/document-server:latest --push .
```
## ⏱️ Benchmarks
### Barcode Generation
- Requests: 10000
- Concurrency: Unlimited
- Data: `123456789123`
- Width: 1000
- Height: 500
#### No cache (cold start)
| Format | Data | Requests/sec | Slowest | Fastest | Average |
| ------- | -------------- | ------------ | ------- | ------- | ------- |
| Code128 | `123456789123` | 763.3996 | 0.7995 | 0.0172 | 0.0654 |
| EAN13 | `123456789123` | 767.1170 | 0.7607 | 0.0171 | 0.0651 |
| QR | `123456789123` | 683.8472 | 0.6528 | 0.0178 | 0.0730 |
#### Redis cache (warm start)
| Format | Data | Requests/sec | Slowest | Fastest | Average |
| ------- | -------------- | ------------ | ------- | ------- | ------- |
| Code128 | `123456789123` | 15611.5521 | 0.0965 | 0.0006 | 0.0032 |
| EAN13 | `123456789123` | 14985.4401 | 0.0925 | 0.0006 | 0.0033 |
| QR | `123456789123` | 14306.2540 | 0.1143 | 0.0005 | 0.0035 |

9
docker-compose.dev.yaml Normal file
View File

@ -0,0 +1,9 @@
services:
gotenberg:
image: gotenberg/gotenberg:8
ports:
- "3001:3000"
redis:
image: docker.dragonflydb.io/dragonflydb/dragonfly
ports:
- "6379:6379"

15
docker-compose.yaml Normal file
View File

@ -0,0 +1,15 @@
services:
document-server:
build: .
ports:
- "3000:3000"
volumes:
- ./docker.env:/.env
gotenberg:
image: gotenberg/gotenberg:8
ports:
- "3001:3000"
redis:
image: docker.dragonflydb.io/dragonflydb/dragonfly
ports:
- "6379:6379"

18
docker.env Normal file
View File

@ -0,0 +1,18 @@
PORT=3000
PRODUCION=false
APIKEY=lfk
EVENTNAME=Lauf für Kaya! 2025
CURRENCYSYMBOL=
GOTENBERG_BASEURL=http://gotenberg:3000
REDIS_ADDR=redis:6379
CARD_SUBTITLE=Kaya ist cool
CARD_BARCODEFORMAT=ean13
# CARD_BARCODEPREFIX=
SPONSOING_RECEIPTMINIMUM=10
SPONSORING_DISCLAIMER=Kaya ist cool, aber pass auf, dass du nicht zu viel Geld sammelst!
SPONSORING_BARCODEFORMAT=code128
# SPONSORING_BARCODEPREFIX=
CERTIFICATE_FOOTER=Kaya ist cool, danke für deine Unterstützung!

437
docs/docs.go Normal file
View File

@ -0,0 +1,437 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"termsOfService": "https://lauf-fuer-kaya.de/datenschutz",
"contact": {
"name": "ODIT.Services UG (haftungsbeschränkt)",
"url": "https://odit.services",
"email": "info@odit.services"
},
"license": {
"name": "CC BY-NC-SA 4.0"
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/v1/barcodes/{type}/{content}": {
"get": {
"description": "Generate barcodes based on the provided data",
"produces": [
"image/png"
],
"tags": [
"barcodes"
],
"summary": "Generate barcodes",
"parameters": [
{
"enum": [
"ean13",
"code128"
],
"type": "string",
"description": "Barcode type",
"name": "type",
"in": "path",
"required": true
},
{
"minLength": 1,
"type": "string",
"description": "Barcode content",
"name": "content",
"in": "path",
"required": true
},
{
"maximum": 10000,
"minimum": 1,
"type": "integer",
"default": 1000,
"description": "Barcode width",
"name": "width",
"in": "query"
},
{
"maximum": 10000,
"minimum": 1,
"type": "integer",
"default": 1000,
"description": "Barcode height",
"name": "height",
"in": "query"
},
{
"maximum": 100,
"minimum": 0,
"type": "integer",
"default": 10,
"description": "Padding around the barcode (included in image size)",
"name": "padding",
"in": "query"
}
],
"responses": {}
}
},
"/v1/pdfs/cards": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Generate cards based on the provided data",
"consumes": [
"application/json"
],
"produces": [
"application/pdf"
],
"tags": [
"pdfs"
],
"summary": "Generate runner cards",
"parameters": [
{
"description": "Card data",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.CardRequest"
}
}
],
"responses": {}
}
},
"/v1/pdfs/certificates": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Generate certificates based on the provided data",
"consumes": [
"application/json"
],
"produces": [
"application/pdf"
],
"tags": [
"pdfs"
],
"summary": "Generate runner certificates",
"parameters": [
{
"description": "Certificate data",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.CertificateRequest"
}
}
],
"responses": {}
}
},
"/v1/pdfs/contracts": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Generate a contract based on the provided data",
"consumes": [
"application/json"
],
"produces": [
"application/pdf"
],
"tags": [
"pdfs"
],
"summary": "Generate a contract",
"parameters": [
{
"description": "Contract data",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.ContractRequest"
}
}
],
"responses": {}
}
}
},
"definitions": {
"models.Card": {
"type": "object",
"required": [
"code",
"id",
"runner"
],
"properties": {
"code": {
"type": "string"
},
"enabled": {
"type": "boolean",
"default": true
},
"id": {
"type": "integer"
},
"runner": {
"$ref": "#/definitions/models.Runner"
}
}
},
"models.CardRequest": {
"type": "object",
"required": [
"cards",
"locale"
],
"properties": {
"cards": {
"type": "array",
"items": {
"$ref": "#/definitions/models.Card"
}
},
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
}
}
},
"models.CertificateRequest": {
"type": "object",
"required": [
"locale",
"runners"
],
"properties": {
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
},
"runners": {
"type": "array",
"items": {
"$ref": "#/definitions/models.RunnerWithDonations"
}
}
}
},
"models.ContractRequest": {
"type": "object",
"required": [
"locale",
"runners"
],
"properties": {
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
},
"runners": {
"type": "array",
"items": {
"$ref": "#/definitions/models.Runner"
}
}
}
},
"models.DistanceDonation": {
"type": "object",
"required": [
"amount_per_distance",
"donor",
"id"
],
"properties": {
"amount": {
"type": "integer"
},
"amount_per_distance": {
"type": "integer"
},
"donor": {
"$ref": "#/definitions/models.Donor"
},
"id": {
"type": "integer"
},
"paid_amount": {
"type": "integer"
}
}
},
"models.Donor": {
"type": "object",
"required": [
"first_name",
"id",
"last_name"
],
"properties": {
"first_name": {
"type": "string"
},
"id": {
"type": "integer"
},
"last_name": {
"type": "string"
},
"middle_name": {
"type": "string"
}
}
},
"models.Group": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"parent_group": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
}
}
}
}
},
"models.Runner": {
"type": "object",
"required": [
"first_name",
"group",
"id",
"last_name"
],
"properties": {
"first_name": {
"type": "string"
},
"group": {
"$ref": "#/definitions/models.Group"
},
"id": {
"type": "integer"
},
"last_name": {
"type": "string"
},
"middle_name": {
"type": "string"
}
}
},
"models.RunnerWithDonations": {
"type": "object",
"required": [
"distance",
"first_name",
"group",
"id",
"last_name"
],
"properties": {
"distance": {
"type": "integer"
},
"distance_donations": {
"type": "array",
"items": {
"$ref": "#/definitions/models.DistanceDonation"
}
},
"first_name": {
"type": "string"
},
"group": {
"$ref": "#/definitions/models.Group"
},
"id": {
"type": "integer"
},
"last_name": {
"type": "string"
},
"middle_name": {
"type": "string"
},
"total_donations": {
"type": "integer"
},
"total_per_distance": {
"type": "integer"
}
}
}
},
"securityDefinitions": {
"ApiKeyAuth": {
"type": "apiKey",
"name": "key",
"in": "query"
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "LfK Document Server API",
Description: "This is the API documentation for the LfK Document Server - a tool for pdf generation.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

410
docs/swagger.json Normal file
View File

@ -0,0 +1,410 @@
{
"swagger": "2.0",
"info": {
"description": "This is the API documentation for the LfK Document Server - a tool for pdf generation.",
"title": "LfK Document Server API",
"termsOfService": "https://lauf-fuer-kaya.de/datenschutz",
"contact": {
"name": "ODIT.Services UG (haftungsbeschränkt)",
"url": "https://odit.services",
"email": "info@odit.services"
},
"license": {
"name": "CC BY-NC-SA 4.0"
}
},
"paths": {
"/v1/barcodes/{type}/{content}": {
"get": {
"description": "Generate barcodes based on the provided data",
"produces": [
"image/png"
],
"tags": [
"barcodes"
],
"summary": "Generate barcodes",
"parameters": [
{
"enum": [
"ean13",
"code128"
],
"type": "string",
"description": "Barcode type",
"name": "type",
"in": "path",
"required": true
},
{
"minLength": 1,
"type": "string",
"description": "Barcode content",
"name": "content",
"in": "path",
"required": true
},
{
"maximum": 10000,
"minimum": 1,
"type": "integer",
"default": 1000,
"description": "Barcode width",
"name": "width",
"in": "query"
},
{
"maximum": 10000,
"minimum": 1,
"type": "integer",
"default": 1000,
"description": "Barcode height",
"name": "height",
"in": "query"
},
{
"maximum": 100,
"minimum": 0,
"type": "integer",
"default": 10,
"description": "Padding around the barcode (included in image size)",
"name": "padding",
"in": "query"
}
],
"responses": {}
}
},
"/v1/pdfs/cards": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Generate cards based on the provided data",
"consumes": [
"application/json"
],
"produces": [
"application/pdf"
],
"tags": [
"pdfs"
],
"summary": "Generate runner cards",
"parameters": [
{
"description": "Card data",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.CardRequest"
}
}
],
"responses": {}
}
},
"/v1/pdfs/certificates": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Generate certificates based on the provided data",
"consumes": [
"application/json"
],
"produces": [
"application/pdf"
],
"tags": [
"pdfs"
],
"summary": "Generate runner certificates",
"parameters": [
{
"description": "Certificate data",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.CertificateRequest"
}
}
],
"responses": {}
}
},
"/v1/pdfs/contracts": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Generate a contract based on the provided data",
"consumes": [
"application/json"
],
"produces": [
"application/pdf"
],
"tags": [
"pdfs"
],
"summary": "Generate a contract",
"parameters": [
{
"description": "Contract data",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.ContractRequest"
}
}
],
"responses": {}
}
}
},
"definitions": {
"models.Card": {
"type": "object",
"required": [
"code",
"id",
"runner"
],
"properties": {
"code": {
"type": "string"
},
"enabled": {
"type": "boolean",
"default": true
},
"id": {
"type": "integer"
},
"runner": {
"$ref": "#/definitions/models.Runner"
}
}
},
"models.CardRequest": {
"type": "object",
"required": [
"cards",
"locale"
],
"properties": {
"cards": {
"type": "array",
"items": {
"$ref": "#/definitions/models.Card"
}
},
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
}
}
},
"models.CertificateRequest": {
"type": "object",
"required": [
"locale",
"runners"
],
"properties": {
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
},
"runners": {
"type": "array",
"items": {
"$ref": "#/definitions/models.RunnerWithDonations"
}
}
}
},
"models.ContractRequest": {
"type": "object",
"required": [
"locale",
"runners"
],
"properties": {
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
},
"runners": {
"type": "array",
"items": {
"$ref": "#/definitions/models.Runner"
}
}
}
},
"models.DistanceDonation": {
"type": "object",
"required": [
"amount_per_distance",
"donor",
"id"
],
"properties": {
"amount": {
"type": "integer"
},
"amount_per_distance": {
"type": "integer"
},
"donor": {
"$ref": "#/definitions/models.Donor"
},
"id": {
"type": "integer"
},
"paid_amount": {
"type": "integer"
}
}
},
"models.Donor": {
"type": "object",
"required": [
"first_name",
"id",
"last_name"
],
"properties": {
"first_name": {
"type": "string"
},
"id": {
"type": "integer"
},
"last_name": {
"type": "string"
},
"middle_name": {
"type": "string"
}
}
},
"models.Group": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"parent_group": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
}
}
}
}
},
"models.Runner": {
"type": "object",
"required": [
"first_name",
"group",
"id",
"last_name"
],
"properties": {
"first_name": {
"type": "string"
},
"group": {
"$ref": "#/definitions/models.Group"
},
"id": {
"type": "integer"
},
"last_name": {
"type": "string"
},
"middle_name": {
"type": "string"
}
}
},
"models.RunnerWithDonations": {
"type": "object",
"required": [
"distance",
"first_name",
"group",
"id",
"last_name"
],
"properties": {
"distance": {
"type": "integer"
},
"distance_donations": {
"type": "array",
"items": {
"$ref": "#/definitions/models.DistanceDonation"
}
},
"first_name": {
"type": "string"
},
"group": {
"$ref": "#/definitions/models.Group"
},
"id": {
"type": "integer"
},
"last_name": {
"type": "string"
},
"middle_name": {
"type": "string"
},
"total_donations": {
"type": "integer"
},
"total_per_distance": {
"type": "integer"
}
}
}
},
"securityDefinitions": {
"ApiKeyAuth": {
"type": "apiKey",
"name": "key",
"in": "query"
}
}
}

278
docs/swagger.yaml Normal file
View File

@ -0,0 +1,278 @@
definitions:
models.Card:
properties:
code:
type: string
enabled:
default: true
type: boolean
id:
type: integer
runner:
$ref: '#/definitions/models.Runner'
required:
- code
- id
- runner
type: object
models.CardRequest:
properties:
cards:
items:
$ref: '#/definitions/models.Card'
type: array
locale:
enum:
- en
- de
type: string
required:
- cards
- locale
type: object
models.CertificateRequest:
properties:
locale:
enum:
- en
- de
type: string
runners:
items:
$ref: '#/definitions/models.RunnerWithDonations'
type: array
required:
- locale
- runners
type: object
models.ContractRequest:
properties:
locale:
enum:
- en
- de
type: string
runners:
items:
$ref: '#/definitions/models.Runner'
type: array
required:
- locale
- runners
type: object
models.DistanceDonation:
properties:
amount:
type: integer
amount_per_distance:
type: integer
donor:
$ref: '#/definitions/models.Donor'
id:
type: integer
paid_amount:
type: integer
required:
- amount_per_distance
- donor
- id
type: object
models.Donor:
properties:
first_name:
type: string
id:
type: integer
last_name:
type: string
middle_name:
type: string
required:
- first_name
- id
- last_name
type: object
models.Group:
properties:
name:
type: string
parent_group:
properties:
name:
type: string
required:
- name
type: object
required:
- name
type: object
models.Runner:
properties:
first_name:
type: string
group:
$ref: '#/definitions/models.Group'
id:
type: integer
last_name:
type: string
middle_name:
type: string
required:
- first_name
- group
- id
- last_name
type: object
models.RunnerWithDonations:
properties:
distance:
type: integer
distance_donations:
items:
$ref: '#/definitions/models.DistanceDonation'
type: array
first_name:
type: string
group:
$ref: '#/definitions/models.Group'
id:
type: integer
last_name:
type: string
middle_name:
type: string
total_donations:
type: integer
total_per_distance:
type: integer
required:
- distance
- first_name
- group
- id
- last_name
type: object
info:
contact:
email: info@odit.services
name: ODIT.Services UG (haftungsbeschränkt)
url: https://odit.services
description: This is the API documentation for the LfK Document Server - a tool
for pdf generation.
license:
name: CC BY-NC-SA 4.0
termsOfService: https://lauf-fuer-kaya.de/datenschutz
title: LfK Document Server API
paths:
/v1/barcodes/{type}/{content}:
get:
description: Generate barcodes based on the provided data
parameters:
- description: Barcode type
enum:
- ean13
- code128
in: path
name: type
required: true
type: string
- description: Barcode content
in: path
minLength: 1
name: content
required: true
type: string
- default: 1000
description: Barcode width
in: query
maximum: 10000
minimum: 1
name: width
type: integer
- default: 1000
description: Barcode height
in: query
maximum: 10000
minimum: 1
name: height
type: integer
- default: 10
description: Padding around the barcode (included in image size)
in: query
maximum: 100
minimum: 0
name: padding
type: integer
produces:
- image/png
responses: {}
summary: Generate barcodes
tags:
- barcodes
/v1/pdfs/cards:
post:
consumes:
- application/json
description: Generate cards based on the provided data
parameters:
- description: Card data
in: body
name: data
required: true
schema:
$ref: '#/definitions/models.CardRequest'
produces:
- application/pdf
responses: {}
security:
- ApiKeyAuth: []
summary: Generate runner cards
tags:
- pdfs
/v1/pdfs/certificates:
post:
consumes:
- application/json
description: Generate certificates based on the provided data
parameters:
- description: Certificate data
in: body
name: data
required: true
schema:
$ref: '#/definitions/models.CertificateRequest'
produces:
- application/pdf
responses: {}
security:
- ApiKeyAuth: []
summary: Generate runner certificates
tags:
- pdfs
/v1/pdfs/contracts:
post:
consumes:
- application/json
description: Generate a contract based on the provided data
parameters:
- description: Contract data
in: body
name: data
required: true
schema:
$ref: '#/definitions/models.ContractRequest'
produces:
- application/pdf
responses: {}
security:
- ApiKeyAuth: []
summary: Generate a contract
tags:
- pdfs
securityDefinitions:
ApiKeyAuth:
in: query
name: key
type: apiKey
swagger: "2.0"

69
go.mod Normal file
View File

@ -0,0 +1,69 @@
module git.odit.services/lfk/document-server
go 1.23.2
toolchain go1.23.3
require (
github.com/gofiber/fiber/v2 v2.52.5
github.com/gofiber/swagger v1.1.0
github.com/swaggo/swag v1.16.4
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/pkcs7 v0.2.0 // indirect
github.com/hhrutter/tiff v1.0.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/makiuchi-d/gozxing v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oxplot/papersizes v0.0.0-20181201065918-90a3a5ae1915 // indirect
github.com/pdfcpu/pdfcpu v0.10.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/redis/go-redis/v9 v9.7.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/swaggo/files/v2 v2.0.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.61.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/image v0.26.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/tools v0.27.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

162
go.sum Normal file
View File

@ -0,0 +1,162 @@
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/swagger v1.1.0 h1:ff3rg1fB+Rp5JN/N8jfxTiZtMKe/9tB9QDc79fPiJKQ=
github.com/gofiber/swagger v1.1.0/go.mod h1:pRZL0Np35sd+lTODTE5The0G+TMHfNY+oC4hM2/i5m8=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I=
github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8=
github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/oxplot/papersizes v0.0.0-20181201065918-90a3a5ae1915 h1:4WzMzgExTgBfuUQ/HegMf+jcHtH+c3fl7eySUQUbfzg=
github.com/oxplot/papersizes v0.0.0-20181201065918-90a3a5ae1915/go.mod h1:LJRTnhoARxQgMyT7T9L+ZzwR4OrmyHTy5LPxZEzE1CM=
github.com/pdfcpu/pdfcpu v0.10.2 h1:DB2dWuoq0eF0QwHjgyLirYKLTCzFOoZdmmIUSu72aL0=
github.com/pdfcpu/pdfcpu v0.10.2/go.mod h1:Q2Z3sqdRqHTdIq1mPAUl8nfAoim8p3c1ASOaQ10mCpE=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files/v2 v2.0.1 h1:XCVJO/i/VosCDsJu1YLpdejGsGnBE9deRMpjN4pJLHk=
github.com/swaggo/files/v2 v2.0.1/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
github.com/valyala/fasthttp v1.61.0 h1:VV08V0AfoRaFurP1EWKvQQdPTZHiUzaVoulX1aBDgzU=
github.com/valyala/fasthttp v1.61.0/go.mod h1:wRIV/4cMwUPWnRcDno9hGnYZGh78QzODFfo1LTUhBog=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

73
handlers/barcode.go Normal file
View File

@ -0,0 +1,73 @@
package handlers
import (
"strconv"
"github.com/gofiber/fiber/v2"
)
// GenerateBarcode godoc
//
// @Summary Generate barcodes
// @Description Generate barcodes based on the provided data
// @Tags barcodes
// @Param type path string true "Barcode type" Enums(ean13, code128)
// @Param content path string true "Barcode content" MinLength(1)
// @Param width query int false "Barcode width" Minimum(1) Maximum(10000) default(1000)
// @Param height query int false "Barcode height" Minimum(1) Maximum(10000) default(1000)
// @Param padding query int false "Padding around the barcode (included in image size)" Minimum(0) Maximum(100) default(10)
// @Produce image/png
// @Router /v1/barcodes/{type}/{content} [get]
func (h *DefaultHandler) GenerateBarcode(c *fiber.Ctx) error {
logger := h.Logger.Named("GenerateBarcode")
// Get the type and content from the URL
barcodeType := c.Params("type")
barcodeContent := c.Params("content")
// Get the width and height from the query parameters
widthStr := c.Query("width", "1000")
heightStr := c.Query("height", "1000")
paddingStr := c.Query("padding", "10")
// Convert width and height to integers
width, err := strconv.Atoi(widthStr)
if err != nil {
logger.Errorw("Invalid width parameter", "width", widthStr, "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid width parameter",
})
}
height, err := strconv.Atoi(heightStr)
if err != nil {
logger.Errorw("Invalid height parameter", "height", heightStr, "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid height parameter",
})
}
padding, err := strconv.Atoi(paddingStr)
if err != nil {
logger.Errorw("Invalid padding parameter", "padding", paddingStr, "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid padding parameter",
})
}
logger = logger.With("type", barcodeType, "content", barcodeContent, "width", width, "height", height, "padding", padding)
// Generate the barcode
logger.Info("Generating barcode")
barcode, err := h.BarcodeService.GenerateBarcode(barcodeType, barcodeContent, width, height, padding)
if err != nil {
logger.Errorw("Failed to generate barcode", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
logger.Info("Barcode generated")
c.Set(fiber.HeaderContentType, "image/png")
return c.Send(barcode.Bytes())
}

203
handlers/card.go Normal file
View File

@ -0,0 +1,203 @@
package handlers
import (
"fmt"
"os"
"slices"
"git.odit.services/lfk/document-server/models"
"github.com/gofiber/fiber/v2"
"github.com/pdfcpu/pdfcpu/pkg/api"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
)
// GenerateCard godoc
//
// @Summary Generate runner cards
// @Description Generate cards based on the provided data
// @Tags pdfs
// @Accept json
// @Param data body models.CardRequest true "Card data"
// @Produce application/pdf
// @Security ApiKeyAuth
// @Router /v1/pdfs/cards [post]
func (h *DefaultHandler) GenerateCard(c *fiber.Ctx) error {
logger := h.Logger.Named("GenerateCard")
cardRequest := new(models.CardRequest)
if err := c.BodyParser(cardRequest); err != nil {
logger.Errorw("Invalid request", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
if !slices.Contains([]string{"en", "de"}, cardRequest.Locale) {
logger.Errorw("Invalid locale", "locale", cardRequest.Locale)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid locale",
})
}
logger = logger.With("locale", cardRequest.Locale)
templateString, err := h.StaticService.GetTemplate(cardRequest.Locale, "card")
if err != nil {
logger.Errorw("Template not found", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Template not found",
})
}
template, err := h.Templater.StringToTemplate(templateString)
if err != nil {
logger.Errorw("Error parsing template", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
segmentLength := calculateOptimalSegmentSize(len(cardRequest.Cards))
pdfs := []string{}
for i := 0; i < len(cardRequest.Cards); i += segmentLength {
segment := cardRequest.Cards[i:]
if len(segment) > segmentLength {
segment = cardRequest.Cards[i : i+segmentLength]
}
genConfig := &models.CardTemplateOptions{
CardSegments: splitCardSegments(segment),
EventName: h.Config.EventName,
CardSubtitle: h.Config.CardSubtitle,
BarcodeFormat: h.Config.CardBarcodeFormat,
BarcodePrefix: h.Config.CardBarcodePrefix,
}
logger.Info("Generating card html")
result, err := h.Templater.Execute(template, genConfig)
if err != nil {
logger.Errorw("Error executing template", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
logger.Info("Generated card html")
c.Set(fiber.HeaderContentType, "text/html")
logger.Info("Converting html to pdf")
pdf, err := h.Converter.ToPdf(result, "a4", false)
if err != nil {
logger.Errorw("Error converting html to pdf", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
tempFile, err := os.CreateTemp("", fmt.Sprintf("cards-%d-*.pdf", i/segmentLength))
if err != nil {
logger.Errorw("Error creating temp file", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
defer os.Remove(tempFile.Name()) // Ensure cleanup even on error paths
if _, err := tempFile.Write(pdf); err != nil {
logger.Errorw("Error writing pdf to temp file", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
tempFile.Close()
pdfs = append(pdfs, tempFile.Name())
}
outputFile := "./output.pdf"
conf := model.NewDefaultConfiguration()
err = api.MergeCreateFile(pdfs, outputFile, false, conf)
if err != nil {
logger.Errorw("Failed to merge PDFs", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
// Clean up individual PDF files
for _, file := range pdfs {
if err := os.Remove(file); err != nil {
logger.Warnw("Failed to remove temporary PDF file", "file", file, "error", err)
}
}
// Set headers and return the merged PDF
c.Set(fiber.HeaderContentType, "application/pdf")
c.Set(fiber.HeaderContentDisposition, "attachment; filename=runner-cards.pdf")
pdfBytes, err := os.ReadFile(outputFile)
if err != nil {
logger.Errorw("Failed to read merged PDF", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
os.Remove(outputFile) // Clean up the merged file
logger.Info("Converted html to pdf")
c.Set(fiber.HeaderContentType, "application/pdf")
c.Set(fiber.HeaderContentDisposition, "attachment; filename=runner-cards.pdf")
return c.Send(pdfBytes)
}
func calculateOptimalSegmentSize(totalCards int) int {
if totalCards < 30 {
return 25 // Reduces overhead for really small batches
}
// Base size for small batches
if totalCards < 100 {
return 50
}
// For medium batches
if totalCards < 500 {
return 75
}
// For large batches, be more conservative
return 100
}
func invertCardArrayItemPairs(cards []models.Card) []models.Card {
inverted := make([]models.Card, 0)
for i := 0; i < len(cards); i += 2 {
if i+1 < len(cards) {
inverted = append(inverted, cards[i+1])
}
inverted = append(inverted, cards[i])
}
return inverted
}
func splitCardSegments(cards []models.Card) []models.CardTemplateSegment {
cardSegments := make([]models.CardTemplateSegment, 0)
const currentCards = 0
for i := 0; i < len(cards); i += 10 {
segmentLength := 10
if len(cards)-i < 10 {
segmentLength = len(cards) - i
}
segment := cards[i : i+segmentLength]
if segmentLength%2 != 0 {
segment = append(segment, models.Card{
ID: 0,
Enabled: false,
Runner: models.Runner{},
Code: "",
})
}
cardSegments = append(cardSegments, models.CardTemplateSegment{
Cards: segment,
CardsSwapped: invertCardArrayItemPairs(segment),
})
}
return cardSegments
}

108
handlers/certificate.go Normal file
View File

@ -0,0 +1,108 @@
package handlers
import (
"slices"
"git.odit.services/lfk/document-server/models"
"github.com/gofiber/fiber/v2"
)
// GenerateCertificate godoc
//
// @Summary Generate runner certificates
// @Description Generate certificates based on the provided data
// @Tags pdfs
// @Accept json
// @Param data body models.CertificateRequest true "Certificate data"
// @Produce application/pdf
// @Security ApiKeyAuth
// @Router /v1/pdfs/certificates [post]
func (h *DefaultHandler) GenerateCertificate(c *fiber.Ctx) error {
logger := h.Logger.Named("GenerateCertificate")
certificateRequest := new(models.CertificateRequest)
if err := c.BodyParser(certificateRequest); err != nil {
logger.Errorw("Invalid request", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
if !slices.Contains([]string{"en", "de"}, certificateRequest.Locale) {
logger.Errorw("Invalid locale", "locale", certificateRequest.Locale)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid locale",
})
}
logger = logger.With("locale", certificateRequest.Locale)
templateString, err := h.StaticService.GetTemplate(certificateRequest.Locale, "certificate")
if err != nil {
logger.Errorw("Template not found", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Template not found",
})
}
template, err := h.Templater.StringToTemplate(templateString)
if err != nil {
logger.Errorw("Error parsing template", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
genConfig := &models.CertificateTemplateOptions{
Runners: addUpRunnerDonations(certificateRequest.Runners),
EventName: h.Config.EventName,
Footer: h.Config.CertificateFooter,
CurrencySymbol: h.Config.CurrencySymbol,
Locale: certificateRequest.Locale,
SepaConfig: &models.SepaConfig{
BIC: h.Config.SepaBic,
HolderName: h.Config.SepaName,
IBAN: h.Config.SepaIban,
CurrencyIdentifier: h.Config.CurrencyIdentifier,
},
}
logger.Info("Generating certificate html")
result, err := h.Templater.Execute(template, genConfig)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
logger.Info("Generated card html")
c.Set(fiber.HeaderContentType, "text/html")
logger.Info("Converting html to pdf")
pdf, err := h.Converter.ToPdf(result, "a4", false)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
logger.Info("Converted html to pdf")
c.Set(fiber.HeaderContentType, "application/pdf")
c.Set(fiber.HeaderContentDisposition, "attachment; filename=certificate.pdf")
return c.Send(pdf)
}
func addUpRunnerDonations(runners []models.RunnerWithDonations) []models.RunnerWithDonations {
for i := 0; i < len(runners); i++ {
for j := 0; j < len(runners[i].DistanceDonations); j++ {
runners[i].TotalDonations += runners[i].DistanceDonations[j].Amount
runners[i].TotalPerDistance += runners[i].DistanceDonations[j].AmountPerDistance
}
if runners[i].Group.ParentGroup.Name != "" {
runners[i].CombinedGroupName = runners[i].Group.ParentGroup.Name + " - " + runners[i].Group.Name
} else {
runners[i].CombinedGroupName = runners[i].Group.Name
}
}
return runners
}

96
handlers/contract.go Normal file
View File

@ -0,0 +1,96 @@
package handlers
import (
"slices"
"git.odit.services/lfk/document-server/models"
"github.com/gofiber/fiber/v2"
)
// GenerateContract godoc
//
// @Summary Generate a contract
// @Description Generate a contract based on the provided data
// @Tags pdfs
// @Accept json
// @Param data body models.ContractRequest true "Contract data"
// @Produce application/pdf
// @Security ApiKeyAuth
// @Router /v1/pdfs/contracts [post]
func (h *DefaultHandler) GenerateContract(c *fiber.Ctx) error {
logger := h.Logger.Named("GenerateContract")
contract := new(models.ContractRequest)
if err := c.BodyParser(contract); err != nil {
logger.Errorw("Invalid request", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
if !slices.Contains([]string{"en", "de"}, contract.Locale) {
logger.Errorw("Invalid locale", "locale", contract.Locale)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid locale",
})
}
logger = logger.With("locale", contract.Locale)
templateString, err := h.StaticService.GetTemplate(contract.Locale, "contract")
if err != nil {
logger.Errorw("Template not found", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Template not found",
})
}
template, err := h.Templater.StringToTemplate(templateString)
if err != nil {
logger.Errorw("Error parsing template", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
genConfig := &models.ContractTemplateOptions{
Runners: repeatRunnerArrayItems(contract.Runners, 2),
CurrencySymbol: h.Config.CurrencySymbol,
Disclaimer: h.Config.SponosringDisclaimer,
ReceiptMinimumAmount: h.Config.SponsoringReceiptMinimum,
EventName: h.Config.EventName,
BarcodeFormat: h.Config.SponsoringBarcodeFormat,
BarcodePrefix: h.Config.SponsoringBarcodePrefix,
}
logger.Info("Generating contract html")
result, err := h.Templater.Execute(template, genConfig)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
logger.Info("Generated contract html")
logger.Info("Converting html to pdf")
pdf, err := h.Converter.ToPdf(result, "a5", true)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
logger.Info("Converted html to pdf")
c.Set(fiber.HeaderContentType, "application/pdf")
c.Set(fiber.HeaderContentDisposition, "attachment; filename=runner-contracts.pdf")
return c.Send(pdf)
}
func repeatRunnerArrayItems(runners []models.Runner, duplicates int) []models.Runner {
var duplicatedRunners []models.Runner
for _, runner := range runners {
for i := 0; i < duplicates; i++ {
duplicatedRunners = append(duplicatedRunners, runner)
}
}
return duplicatedRunners
}

24
handlers/handlers.go Normal file
View File

@ -0,0 +1,24 @@
package handlers
import (
"git.odit.services/lfk/document-server/models"
"git.odit.services/lfk/document-server/services"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type Handler interface {
GenerateCard(*fiber.Ctx) error
GenerateContract(*fiber.Ctx) error
GenerateCertificate(*fiber.Ctx) error
GenerateBarcode(*fiber.Ctx) error
}
type DefaultHandler struct {
Config *models.Config
BarcodeService services.BarcodeService
Templater services.Templater
Converter services.Converter
StaticService services.StaticService
Logger *zap.SugaredLogger
}

7
handlers/util.go Normal file
View File

@ -0,0 +1,7 @@
package handlers
import "github.com/gofiber/fiber/v2"
func (h *DefaultHandler) NotFoundHandler(c *fiber.Ctx) error {
return c.Status(404).SendString("Not Found")
}

198
main.go Normal file
View File

@ -0,0 +1,198 @@
package main
import (
"crypto/sha256"
"crypto/subtle"
"os"
"strings"
"git.odit.services/lfk/document-server/docs" // Correct import path for docs
"git.odit.services/lfk/document-server/handlers"
"git.odit.services/lfk/document-server/models"
"git.odit.services/lfk/document-server/services"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/keyauth"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/gofiber/swagger"
"github.com/redis/go-redis/v9"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var (
config *models.Config
logger *zap.SugaredLogger
)
func validateAPIKey(c *fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(config.APIKey))
hashedKey := sha256.Sum256([]byte(key))
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
}
func loadEnv() error {
viper.SetDefault("LOGLEVEL", "INFO")
viper.SetDefault("PRODUCION", false)
viper.SetDefault("PORT", "3000")
viper.SetDefault("APIKEY", "lfk")
viper.SetDefault("EVENTNAME", "Demo Event")
viper.SetDefault("CURRENCYSYMBOL", "€")
viper.SetDefault("CURRENCYIDENTIFIER", "EUR")
viper.SetDefault("CARD_SUBTITLE", "Runner Card")
viper.SetDefault("CARD_BARCODEFORMAT", "ean13")
viper.SetDefault("CARD_BARCODEPREFIX", "")
viper.SetDefault("SPONSORING_RECEIPTMINIMUM", 0)
viper.SetDefault("SPONSORING_DISCLAIMER", "Disclaimer")
viper.SetDefault("SPONSORING_BARCODEFORMAT", "code128")
viper.SetDefault("SPONSORING_BARCODEPREFIX", "")
viper.SetDefault("CERTIFICATE_FOOTER", "Footer")
viper.SetDefault("GOTENBERG_BASEURL", "")
viper.SetDefault("REDIS_ADDR", "")
viper.SetDefault("SEPA_BIC", "")
viper.SetDefault("SEPA_NAME", "")
viper.SetDefault("SEPA_IBAN", "")
// Load .env file
viper.SetConfigFile(".env")
// Load environment variables
viper.AutomaticEnv()
err := viper.ReadInConfig()
if err != nil {
logger.Warn("No .env file found")
}
// Unmarshal the config from file and env into the config struct
err = viper.Unmarshal(&config)
if err != nil {
return err
}
logger.Infow("Loaded config", "config", &config)
return nil
}
func initLogger() error {
logLevel := os.Getenv("LOGLEVEL")
if logLevel == "" {
logLevel = "INFO"
}
var zapLogLevel zapcore.Level
err := zapLogLevel.UnmarshalText([]byte(strings.ToLower(logLevel)))
if err != nil {
zapLogLevel = zapcore.InfoLevel
}
zapConfig := zap.NewProductionConfig()
zapConfig.Level = zap.NewAtomicLevelAt(zapLogLevel)
zapLogger, err := zapConfig.Build()
if err != nil {
return err
}
defer zapLogger.Sync()
logger = zapLogger.Sugar()
logger.Debug("Initialized logger")
return nil
}
// @title LfK Document Server API
// @description This is the API documentation for the LfK Document Server - a tool for pdf generation.
// @license.name CC BY-NC-SA 4.0
// @termsOfService https://lauf-fuer-kaya.de/datenschutz
// @contact.name ODIT.Services UG (haftungsbeschränkt)
// @contact.url https://odit.services
// @contact.email info@odit.services
// @securityDefinitions.apiKey ApiKeyAuth
// @in query
// @name key
func main() {
// Init the logger
err := initLogger()
if err != nil {
return
}
err = loadEnv()
if err != nil {
logger.Error(err)
return
}
var redisClient *redis.Client
if config.RedisAddr != "" {
logger.Infow("Using redis", "redisAddr", config.RedisAddr)
redisClient = redis.NewClient(&redis.Options{
Addr: config.RedisAddr,
})
}
barcodeGenerator := &services.DefaultBarcodeService{
RedisClient: redisClient,
Logger: logger.Named("DefaultBarcodeService"),
}
staticService := &services.DefaultStaticService{
Cache: make(map[string]string),
Logger: logger.Named("DefaultStaticService"),
}
handler := handlers.DefaultHandler{
Config: config,
BarcodeService: barcodeGenerator,
StaticService: staticService,
Templater: &services.DefaultTemplater{
BarcodeService: barcodeGenerator,
StaticService: staticService,
Logger: logger.Named("DefaultTemplater"),
},
Converter: &services.GotenbergConverter{
BaseUrl: config.GotenbergBaseUrl,
Logger: logger.Named("GotenbergConverter"),
},
Logger: logger.Named("DefaultHandler"),
}
logger.Debug("Initialized services")
// Create a new Fiber instance
app := fiber.New(fiber.Config{
Prefork: config.Prod,
})
app.Use(cors.New())
app.Use(requestid.New())
// Swagger documentation route
app.Get("/swagger/*", swagger.HandlerDefault)
v1 := app.Group("/v1")
pdfv1 := v1.Group("/pdfs")
pdfv1.Use(keyauth.New(keyauth.Config{
KeyLookup: "query:key",
Validator: validateAPIKey,
}))
pdfv1.Post("/contracts", handler.GenerateContract)
pdfv1.Post("/cards", handler.GenerateCard)
pdfv1.Post("/certificates", handler.GenerateCertificate)
v1.Get("/barcodes/:type/:content", handler.GenerateBarcode)
logger.Debug("Initialized routes")
app.Use(handler.NotFoundHandler)
docs.SwaggerInfo.BasePath = "/"
logger.Infow("Starting server", "port", config.Port)
logger.Error(app.Listen("0.0.0.0:" + config.Port))
}

26
models/card.go Normal file
View File

@ -0,0 +1,26 @@
package models
type CardRequest struct {
Cards []Card `json:"cards" validate:"required"`
Locale string `json:"locale" enums:"en,de" validate:"required"`
}
type Card struct {
ID int `json:"id" validate:"required"`
Enabled bool `json:"enabled" default:"true"`
Runner Runner `json:"runner" validate:"required"`
Code string `json:"code" validate:"required"`
}
type CardTemplateOptions struct {
CardSegments []CardTemplateSegment `json:"card_segments"`
EventName string `json:"event_name"`
CardSubtitle string `json:"card_subtitle"`
BarcodeFormat string `json:"barcode_format"`
BarcodePrefix string `json:"barcode_prefix"`
}
type CardTemplateSegment struct {
Cards []Card `json:"cards"`
CardsSwapped []Card `json:"cards_swapped"`
}

51
models/certificate.go Normal file
View File

@ -0,0 +1,51 @@
package models
type CertificateRequest struct {
Runners []RunnerWithDonations `json:"runners" validate:"required"`
Locale string `json:"locale" enums:"en,de" validate:"required"`
}
type RunnerWithDonations struct {
ID int `json:"id" validate:"required"`
FirstName string `json:"first_name" validate:"required"`
MiddleName string `json:"middle_name" validate:"optional"`
LastName string `json:"last_name" validate:"required"`
Group Group `json:"group" validate:"required"`
CombinedGroupName string `json:"combined_group_name" validate:"optional"`
Distance int `json:"distance" validate:"required"`
DistanceDonations []DistanceDonation `json:"distance_donations" validate:"optional"`
TotalPerDistance int `json:"total_per_distance" validate:"optional"`
TotalDonations int `json:"total_donations" validate:"optional"`
SelfServiceLink string `json:"self_service_link" validate:"required"`
}
type DistanceDonation struct {
ID int `json:"id" validate:"required"`
Amount int `json:"amount"`
PaidAmount int `json:"paid_amount" validate:"optional"`
AmountPerDistance int `json:"amount_per_distance" validate:"required"`
Donor Donor `json:"donor" validate:"required"`
}
type Donor struct {
ID int `json:"id" validate:"required"`
FirstName string `json:"first_name" validate:"required"`
MiddleName string `json:"middle_name" validate:"optional"`
LastName string `json:"last_name" validate:"required"`
}
type CertificateTemplateOptions struct {
Runners []RunnerWithDonations `json:"runners"`
EventName string `json:"event_name"`
Footer string `json:"footer"`
CurrencySymbol string `json:"currency_symbol"`
Locale string `json:"locale"`
SepaConfig *SepaConfig `json:"sepa_config"`
}
type SepaConfig struct {
IBAN string `json:"iban" validate:"required"`
HolderName string `json:"holder_name" validate:"required"`
BIC string `json:"bic" validate:"required"`
CurrencyIdentifier string `json:"currency_identifier" validate:"required"`
}

24
models/config.go Normal file
View File

@ -0,0 +1,24 @@
package models
type Config struct {
LogLevel string `mapstructure:"LOGLEVEL"`
Prod bool `mapstructure:"PRODUCION"`
Port string `mapstructure:"PORT"`
APIKey string `mapstructure:"APIKEY"`
EventName string `mapstructure:"EVENTNAME"`
CurrencySymbol string `mapstructure:"CURRENCYSYMBOL"`
CurrencyIdentifier string `mapstructure:"CURRENCYIDENTIFIER"`
CardSubtitle string `mapstructure:"CARD_SUBTITLE"`
CardBarcodeFormat string `mapstructure:"CARD_BARCODEFORMAT"`
CardBarcodePrefix string `mapstructure:"CARD_BARCODEPREFIX"`
SponsoringReceiptMinimum string `mapstructure:"SPONSORING_RECEIPTMINIMUM"`
SponosringDisclaimer string `mapstructure:"SPONSORING_DISCLAIMER"`
SponsoringBarcodeFormat string `mapstructure:"SPONSORING_BARCODEFORMAT"`
SponsoringBarcodePrefix string `mapstructure:"SPONSORING_BARCODEPREFIX"`
CertificateFooter string `mapstructure:"CERTIFICATE_FOOTER"`
GotenbergBaseUrl string `mapstructure:"GOTENBERG_BASEURL"`
RedisAddr string `mapstructure:"REDIS_ADDR"`
SepaBic string `mapstructure:"SEPA_BIC"`
SepaName string `mapstructure:"SEPA_NAME"`
SepaIban string `mapstructure:"SEPA_IBAN"`
}

31
models/contract.go Normal file
View File

@ -0,0 +1,31 @@
package models
type ContractRequest struct {
Runners []Runner `json:"runners" validate:"required"`
Locale string `json:"locale" enums:"en,de" validate:"required"`
}
type Runner struct {
ID int `json:"id" validate:"required"`
FirstName string `json:"first_name" validate:"required"`
MiddleName string `json:"middle_name" validate:"optional"`
LastName string `json:"last_name" validate:"required"`
Group Group `json:"group" validate:"required"`
}
type Group struct {
Name string `json:"name" validate:"required"`
ParentGroup struct {
Name string `json:"name" validate:"required"`
} `json:"parent_group" validate:"optional"`
}
type ContractTemplateOptions struct {
Runners []Runner `json:"runners"`
CurrencySymbol string `json:"currency_symbol"`
Disclaimer string `json:"disclaimer"`
ReceiptMinimumAmount string `json:"receipt_minimum_amount"`
EventName string `json:"event_name"`
BarcodeFormat string `json:"barcode_format"`
BarcodePrefix string `json:"barcode_prefix"`
}

129
services/barcode.go Normal file
View File

@ -0,0 +1,129 @@
package services
import (
"bytes"
"context"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"slices"
"time"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/code128"
"github.com/boombuler/barcode/ean"
"github.com/boombuler/barcode/qr"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
type BarcodeService interface {
GenerateBarcode(format string, content string, width int, height int, padding int) (bytes.Buffer, error)
IsTypeSupported(format string) bool
}
type DefaultBarcodeService struct {
RedisClient *redis.Client
Logger *zap.SugaredLogger
}
func (b *DefaultBarcodeService) GenerateBarcode(format string, content string, width int, height int, padding int) (bytes.Buffer, error) {
ctx := context.Background()
logger := b.Logger.Named("GenerateBarcode")
if !b.IsTypeSupported(format) {
logger.Errorw("Unsupported barcode type", "type", format)
return bytes.Buffer{}, fmt.Errorf("unsupported barcode type: %s", format)
}
logger = logger.With("type", format, "content", content, "width", width, "height", height, "padding", padding)
cacheKey := fmt.Sprintf("barcode:%s:%s:%d:%d:%d", format, content, width, height, padding)
if b.RedisClient != nil {
logger.Debugw("Checking cache for barcode", "key", cacheKey)
cachedBarcode, err := b.RedisClient.Get(ctx, cacheKey).Result()
if err == nil {
logger.Infow("Barcode found in cache", "key", cacheKey)
buf := bytes.Buffer{}
buf.Write([]byte(cachedBarcode))
return buf, nil
}
}
var generatedCode barcode.Barcode
var err error
switch format {
case "ean13":
generatedCode, err = ean.Encode(content)
if err != nil {
return bytes.Buffer{}, err
}
break
case "code128":
generatedCode, err = code128.Encode(content)
if err != nil {
return bytes.Buffer{}, err
}
break
case "qr":
// Always use qr.Auto encoding to support all characters in the content
encoding := qr.Auto
// QR code generation with error correction level M and auto encoding
generatedCode, err = qr.Encode(content, qr.M, encoding)
if err != nil {
return bytes.Buffer{}, err
}
break
}
// Create a white background image
bg := image.NewRGBA(image.Rect(0, 0, width, height))
white := color.RGBA{255, 255, 255, 255}
draw.Draw(bg, bg.Bounds(), &image.Uniform{white}, image.Point{}, draw.Src)
logger.Debug("Created white background")
// Calculate the new size for the barcode to fit within the padding
newWidth := width - 2*padding
newHeight := height - 2*padding
// Scale the barcode to the new size
scaledCode, err := barcode.Scale(generatedCode, newWidth, newHeight)
if err != nil {
logger.Errorw("Failed to scale barcode", "error", err)
return bytes.Buffer{}, err
}
logger.Debug("Scaled barcode")
// Draw the barcode on top of the white background with padding
draw.Draw(bg, scaledCode.Bounds().Add(image.Point{padding, padding}), scaledCode, image.Point{}, draw.Over)
logger.Debug("Drew barcode on background")
var buf bytes.Buffer
err = png.Encode(&buf, bg)
if err != nil {
logger.Errorw("Failed to encode barcode to PNG", "error", err)
return bytes.Buffer{}, err
}
logger.Debug("Encoded barcode to PNG")
if b.RedisClient != nil {
err = b.RedisClient.Set(ctx, cacheKey, buf.String(), 10*time.Minute).Err()
logger.Debugw("Cached barcode", "key", cacheKey)
if err != nil {
logger.Errorw("Failed to cache barcode", "error", err)
return bytes.Buffer{}, err
}
}
logger.Info("Generated barcode")
return buf, nil
}
func (b *DefaultBarcodeService) IsTypeSupported(format string) bool {
supportedTypes := []string{"ean13", "code128", "qr"}
return slices.Contains(supportedTypes, format)
}

131
services/converter.go Normal file
View File

@ -0,0 +1,131 @@
package services
import (
"bytes"
"fmt"
"mime/multipart"
"net/http"
"strconv"
"github.com/oxplot/papersizes"
"go.uber.org/zap"
)
type Converter interface {
ToPdf(html string, pageSize string, landscape bool) ([]byte, error)
}
type GotenbergConverter struct {
BaseUrl string
Logger *zap.SugaredLogger
}
func (g *GotenbergConverter) ToPdf(html string, pageSize string, landscape bool) ([]byte, error) {
logger := g.Logger.Named("ToPdf").With("page_size", pageSize, "landscape", landscape, "base_url", g.BaseUrl)
client := &http.Client{}
defer client.CloseIdleConnections()
logger.Debug("Created HTTP client")
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("files", "index.html")
if err != nil {
logger.Errorw("Failed to create form file", "error", err)
return nil, err
}
_, err = part.Write([]byte(html))
if err != nil {
logger.Errorw("Failed to write to form file", "error", err)
return nil, err
}
size := papersizes.FromName(pageSize)
if size == nil {
logger.Errorw("Invalid page size", "size", pageSize)
return nil, fmt.Errorf("invalid page size: %s", pageSize)
}
err = writer.WriteField("paperWidth", strconv.Itoa(size.Width)+"mm")
if err != nil {
logger.Errorw("Failed to write paper width", "error", err)
return nil, err
}
err = writer.WriteField("paperHeight", strconv.Itoa(size.Height)+"mm")
if err != nil {
logger.Errorw("Failed to write paper height", "error", err)
return nil, err
}
err = writer.WriteField("landscape", strconv.FormatBool(landscape))
if err != nil {
logger.Errorw("Failed to write landscape", "error", err)
return nil, err
}
err = writer.WriteField("marginTop", "0")
if err != nil {
logger.Errorw("Failed to write margin top", "error", err)
return nil, err
}
err = writer.WriteField("marginBottom", "0")
if err != nil {
logger.Errorw("Failed to write margin bottom", "error", err)
return nil, err
}
err = writer.WriteField("marginLeft", "0")
if err != nil {
logger.Errorw("Failed to write margin left", "error", err)
return nil, err
}
err = writer.WriteField("marginRight", "0")
if err != nil {
logger.Errorw("Failed to write margin right", "error", err)
return nil, err
}
err = writer.WriteField("preferCssPageSize", "true")
if err != nil {
logger.Errorw("Failed to write prefer css page size", "error", err)
return nil, err
}
err = writer.Close()
if err != nil {
logger.Errorw("Failed to close writer", "error", err)
return nil, err
}
logger.Debug("Created form data")
logger.Debug("Creating HTTP request")
req, err := http.NewRequest("POST", g.BaseUrl+"/forms/chromium/convert/html", body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
logger.Debug("Sending HTTP request")
resp, err := client.Do(req)
if err != nil {
logger.Errorw("Failed to send request", "error", err)
return nil, err
}
logger.Debug("Received HTTP response")
defer resp.Body.Close()
data := new(bytes.Buffer)
_, err = data.ReadFrom(resp.Body)
if err != nil {
logger.Errorw("Failed to read response body", "error", err)
return nil, err
}
logger.Debug("Returning PDF data")
return data.Bytes(), nil
}

142
services/templater.go Normal file
View File

@ -0,0 +1,142 @@
package services
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"html/template"
"math"
"strings"
"go.uber.org/zap"
)
type Templater interface {
Execute(template *template.Template, data interface{}) (string, error)
StringToTemplate(templateString string) (*template.Template, error)
}
type DefaultTemplater struct {
BarcodeService BarcodeService
StaticService StaticService
Logger *zap.SugaredLogger
}
func idToEan13(id string, prefix string) (string, error) {
if len(id) > 12 {
return "", errors.New("id too long")
}
for len(id) < 11 {
id = "0" + id
}
id = prefix + id
return id, nil
}
func (t *DefaultTemplater) GenerateEPC(iban string, bic string, name string, title string, amount int, currency string) (string, error) {
var err error
code := fmt.Sprintf(`BCD
002
1
INST
%s
%s
%s
%s%.2f
%s
`, bic, name, iban, currency, float64(amount)/100, title,
)
buf, err := t.BarcodeService.GenerateBarcode("qr", code, 500, 500, 0)
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
}
func (t *DefaultTemplater) GenerateBarcode(code string, format string, prefix string) (string, error) {
var err error
if format == "ean13" {
code, err = idToEan13(code, prefix)
if err != nil {
return "", err
}
}
buf, err := t.BarcodeService.GenerateBarcode(format, code, 1000, 500, 0)
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
}
func (t *DefaultTemplater) SelectSponsorImage(id int) (string, error) {
logger := t.Logger.Named("SelectSponsorImage")
sponsors, err := t.StaticService.ListFilesInStaticSubFolder("images/sponsors")
if err != nil {
logger.Errorw("Failed to list sponsors", "error", err)
return "", err
}
logger.Debugw("Selected sponsor", "sponsors", sponsors, "id", id, "selected", sponsors[id%len(sponsors)])
return t.StaticService.GetImage("sponsors/" + strings.TrimSuffix(sponsors[id%len(sponsors)], ".base64")), nil
}
func (t *DefaultTemplater) LoadImage(name string) (string, error) {
return t.StaticService.GetImage(name), nil
}
func (t *DefaultTemplater) FormatUnit(unit string, locale string, amount int) (string, error) {
var formatted string
var seperator string
switch locale {
case "de":
seperator = " "
default:
seperator = ""
}
switch unit {
case "kilometer":
if amount < 1000 {
formatted = fmt.Sprintf("%d%sm", amount, seperator)
} else if (amount % 1000) == 0 {
formatted = fmt.Sprintf("%d%skm", amount/1000, seperator)
} else {
kilometers := math.Floor(float64(amount) / 1000)
meters := amount - int(kilometers)*1000
formatted = fmt.Sprintf("%d%skm %d%sm", int(kilometers), seperator, meters, seperator)
}
case "euro":
formatted = fmt.Sprintf("%.2f", float64(amount)/100)
default:
return "", errors.New("unknown unit")
}
if locale == "de" {
return strings.Replace(formatted, ".", ",", -1), nil
}
return formatted, nil
}
func (t *DefaultTemplater) StringToTemplate(templateString string) (*template.Template, error) {
return template.New("template").Funcs(template.FuncMap{
"barcode": t.GenerateBarcode,
"sponsorLogo": t.SelectSponsorImage,
"formatUnit": t.FormatUnit,
"loadImage": t.LoadImage,
"epcCode": t.GenerateEPC,
}).Parse(templateString)
}
func (t *DefaultTemplater) Execute(baseTemplate *template.Template, data interface{}) (string, error) {
resultBuffer := new(bytes.Buffer)
err := baseTemplate.Execute(resultBuffer, data)
if err != nil {
return "", err
}
return resultBuffer.String(), nil
}

72
services/templates.go Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,84 @@
<html>
<head>
<meta charset="utf8">
<title>Läuferkarten</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
padding: 1.2cm 2cm 1.2cm 2cm
}
body.A4 .sheet {
width: 210mm;
height: 296mm
}
.runnercard {
border: 1px solid;
height: 5.5cm;
overflow: hidden;
}
</style>
</head>
<body class="A4 landscape">
{{ range .CardSegments }}
<div class="sheet">
<div class="columns is-multiline">
{{ range .Cards }}
<div class="column is-half runnercard">
{{ if ne .Code "" }}
<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;">{{ $.CardSubtitle }}</p>
<p style="font-size: small;">Mit Unterstützung von:</p>
<div class="columns" style="height: 6rem; overflow: hidden;">
<div class="column is-half">
<!--SPONSOR LOGO HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="data:image/png;base64,{{ sponsorLogo .ID }}" />
</div>
<div class="column is-half">
<!--BARCODE HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 5rem;"
src="data:image/png;base64,{{ barcode .Code $.BarcodeFormat $.BarcodePrefix }}" />
<p style="font-size: 0.6rem; text-align: center; margin: 0; padding: 0;">{{ .Code }}</p>
</div>
</div>
{{ if ne .Runner.FirstName "" }}
<p>{{ .Runner.LastName }}, {{ .Runner.FirstName }} {{ .Runner.MiddleName }}</p>
<p>{{ if ne .Runner.Group.ParentGroup.Name "" -}}{{ .Runner.Group.ParentGroup.Name }}/{{end -}}{{ .Runner.Group.Name }}</p>
{{ else }}
<p>Läufer:in</p>
{{ end}}
{{ end}}
</div>
{{ end }}
</div>
</div>
<div class="sheet">
<div class="columns is-multiline">
{{ range .CardsSwapped }}
<div class="column is-half runnercard" style="justify-content: center; align-items: center; text-align: center;">
{{ if ne .Code "" }}
<!--SPONSOR LOGO FIRST-->
<div style="height: 2cm; padding: 0 0 1cm 0">
<img style="object-fit: cover; max-height: 2cm;" src="data:image/png;base64,{{ sponsorLogo .ID }}" />
</div>
<img style="object-fit: cover; max-height: 6rem; position: relative;"
src="data:image/png;base64,{{ barcode .Code $.BarcodeFormat $.BarcodePrefix }}" />
<p style="font-size: 1rem; text-align: center; margin: 0; padding: 0;">{{ .Code }}</p>
{{ end }}
</div>
{{ end }}
</div>
</div>
{{ end}}
</body>
</html>

View File

@ -0,0 +1,79 @@
<html>
<head>
<meta charset="utf8">
<title>Runner cards</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
padding: 1.2cm 2cm 1.2cm 2cm
}
body.A4 .sheet {
width: 210mm;
height: 296mm
}
.runnercard {
border: 1px solid;
height: 5.5cm;
overflow: hidden;
}
</style>
</head>
<body class="A4 landscape">
{{ range .CardSegments }}
<div class="sheet">
<div class="columns is-multiline">
{{ range .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;">{{ $.CardSubtitle }}</p>
<p style="font-size: small;">Supported by:</p>
<div class="columns" style="height: 6rem; overflow: hidden;">
<div class="column is-half">
<!--SPONSOR LOGO HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="data:image/png;base64,{{ sponsorLogo .ID }}" />
</div>
<div class="column is-half">
<!--BARCODE HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 5rem;"
src="data:image/png;base64,{{ barcode .Code $.BarcodeFormat $.BarcodePrefix }}" />
<p style="font-size: 0.6rem; text-align: center; margin: 0; padding: 0;">{{ .Code }}</p>
</div>
</div>
{{ if ne .Runner.FirstName ""}}
<p>{{ .Runner.LastName }}, {{ .Runner.FirstName }} {{ .Runner.MiddleName }}</p>
<p>{{ if ne .Runner.Group.ParentGroup.Name "" -}}{{ .Runner.Group.ParentGroup.Name }}/{{end -}}{{ .Runner.Group.Name }}</p>
{{ else }}
<p>Runner</p>
{{ end}}
</div>
{{ end }}
</div>
</div>
<div class="sheet">
<div class="columns is-multiline">
{{ range .CardsSwapped }}
<div class="column is-half runnercard" style="justify-content: center; align-items: center; text-align: center;">
<!--SPONSOR LOGO FIRST-->
<div style="height: 2cm; padding: 0 0 1cm 0">
<img style="object-fit: cover; max-height: 2cm;" src="data:image/png;base64,{{ sponsorLogo .ID }}" />
</div>
<img style="object-fit: cover; max-height: 6rem; position: relative;" src="data:image/png;base64,{{ barcode .Code $.BarcodeFormat $.BarcodePrefix }}" />
<p style="font-size: 1rem; text-align: center; margin: 0; padding: 0;">{{ .Code }}</p>
</div>
{{ end }}
</div>
</div>
{{ end}}
</body>
</html>

View File

@ -0,0 +1,119 @@
<html>
<head>
<meta charset="utf8">
<title>Läuferurkunde</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
padding: 1.2cm 2cm 1.2cm 2cm;
background-image: url("data:image/png;base64,{{ loadImage "certificate_background" }}");
background-repeat: no-repeat;
background-size: 11cm;
background-position: 5cm 5cm;
color: #000;
/* border-style: solid; */
}
h1,
h2,
h3,
p {
color: black;
}
body.A4 .sheet {
width: 210mm;
height: 296mm
}
.certificate-footer {
position: absolute;
bottom: 0cm;
}
td {
border: 1px solid black;
font-size: large;
font-weight: 600;
}
</style>
</head>
<body class="A4 landscape">
{{ range .Runners }}
<article class="sheet">
<header class="content has-text-centered">
<p style="font-size: 3cm; font-weight: bold;">Urkunde</p>
</header>
<main class="content has-text-centered" style="padding-top: 3cm;">
<p style="width: 50%; font-size: 4vw; font-weight: bold; margin-bottom: 0; display: inline;">{{ .FirstName }}
{{ .MiddleName }} {{ .LastName }}
</p>
<p style="font-size: 1cm; margin-bottom: 0;">hat beim {{ $.EventName }}</p>
<p style="font-size: 2cm; font-weight: bold; margin-bottom: 0;">{{formatUnit "kilometer" $.Locale .Distance}}</p>
<p style="font-size: 1cm;">für den guten Zweck zurückgelegt.</p>
</main>
<footer class="certificate-footer">
<img src="data:image/png;base64,{{ loadImage "certificate_footer" }}">
</footer>
</article>
<article class="sheet">
<header class="content has-text-centered">
<p style="font-size: 2.5cm; font-weight: bold;">Sponsorings</p>
</header>
<main>
<table style="border: solid; width: 17cm;">
<thead>
<td class=".td-head">Sponsor:in</td>
<td>Betrag/km</td>
<td>Gesamtbetrag</td>
</thead>
<tbody>
{{ range .DistanceDonations}}
<tr>
<td>{{ .Donor.FirstName }} {{ .Donor.MiddleName }} {{ .Donor.LastName }}</td>
<td>{{ formatUnit "euro" $.Locale .AmountPerDistance }} {{ $.CurrencySymbol }}</td>
<td>{{ formatUnit "euro" $.Locale .Amount }} {{ $.CurrencySymbol }}</td>
</tr>
{{ end }}
</tbody>
<tfoot>
<td>Gesamt</td>
<td>{{ formatUnit "euro" $.Locale .TotalPerDistance }} {{ $.CurrencySymbol }}</td>
<td>{{ formatUnit "euro" $.Locale .TotalDonations }} {{ $.CurrencySymbol }}</td>
</table>
</main>
<footer class="certificate-footer">
<table style="border-collapse: collapse; border: none; width: 17cm;">
<thead>
<tr>
<th style="border: none; width: 50%; text-align: center;">Link zu deinen Rundenzeiten</th>
<th style="border: none; width: 50%; text-align: center;">Spende überweisen</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: none; text-align: center;">
<img src="data:image/png;base64,{{ barcode .SelfServiceLink "qr" "" }}" style="height: 2.5cm; padding: 0.2cm">
</td>
<td style="border: none; text-align: center;">
<img src="data:image/png;base64,{{ epcCode $.SepaConfig.IBAN $.SepaConfig.BIC $.SepaConfig.HolderName (print "Spende LfK " .ID ", " .FirstName " " .LastName ", " .CombinedGroupName) .TotalDonations $.SepaConfig.CurrencyIdentifier}}" style="height: 2.5cm; padding: 0.2cm">
</td>
</tr>
</tbody>
</table>
<p style="width: 17cm; text-align: center;">
Sponsoren überweisen ihre Beträge bitte auf unser Konto: {{ $.SepaConfig.HolderName }} | IBAN: {{ $.SepaConfig.IBAN }} | BIC: {{ $.SepaConfig.BIC }} | Vz: "Spende LfK {{.ID}}, {{ .FirstName }} {{ .LastName }}, {{.CombinedGroupName}}"
</p>
</footer>
</article>
{{ end }}
</body>
</html>

View File

@ -0,0 +1,120 @@
<html>
<head>
<meta charset="utf8">
<title>Runner certificate</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
padding: 1.2cm 2cm 1.2cm 2cm;
background-image: url("data:image/png;base64,{{ loadImage "certificate_background" }}");
background-repeat: no-repeat;
background-size: 11cm;
background-position: 5cm 5cm;
color: #000;
/* border-style: solid; */
}
h1,
h2,
h3,
p {
color: black;
}
body.A4 .sheet {
width: 210mm;
height: 296mm
}
.certificate-footer {
position: absolute;
bottom: 0cm;
}
td {
border: 1px solid black;
font-size: large;
font-weight: 600;
}
</style>
</head>
<body class="A4 landscape">
{{ range .Runners }}
<article class="sheet">
<header class="content has-text-centered">
<p style="font-size: 3cm; font-weight: bold;">Certificate</p>
</header>
<main class="content has-text-centered" style="padding-top: 3cm;">
<p style="width: 50%; font-size: 4vw; font-weight: bold; margin-bottom: 0; display: inline;">{{ .FirstName }}
{{ .MiddleName }} {{ .LastName }}
</p>
<p style="font-size: 1cm; margin-bottom: 0;">Ran</p>
<p style="font-size: 2cm; font-weight: bold; margin-bottom: 0;">{{formatUnit "kilometer" $.Locale .Distance}}</p>
<p style="font-size: 1cm;">for our good cause at the {{ $.EventName }}.</p>
</main>
<footer class="certificate-footer">
<img src="data:image/png;base64,{{ loadImage "certificate_footer" }}">
</footer>
</article>
<article class="sheet">
<header class="content has-text-centered">
<p style="font-size: 2.5cm; font-weight: bold;">Donations</p>
</header>
<main>
<table style="border: solid; width: 17cm;">
<thead>
<td class=".td-head">Donor</td>
<td>Amount / km</td>
<td>Total</td>
</thead>
<tbody>
{{ range .DistanceDonations}}
<tr>
<td>{{ .Donor.FirstName }} {{ .Donor.MiddleName }} {{ .Donor.LastName }}</td>
<td>{{ formatUnit "euro" $.Locale .AmountPerDistance }} {{ $.CurrencySymbol }}</td>
<td>{{ formatUnit "euro" $.Locale .Amount }} {{ $.CurrencySymbol }}</td>
</tr>
{{ end }}
</tbody>
<tfoot>
<td>Combined</td>
<td>{{ formatUnit "euro" $.Locale .TotalPerDistance }} {{ $.CurrencySymbol }}</td>
<td>{{ formatUnit "euro" $.Locale .TotalDonations }} {{ $.CurrencySymbol }}</td>
</tfoot>
</table>
</main>
<footer class="certificate-footer">
<table style="border-collapse: collapse; border: none; width: 17cm;">
<thead>
<tr>
<th style="border: none; width: 50%; text-align: center;">Link to your lap times</th>
<th style="border: none; width: 50%; text-align: center;">Transfer donation via SEPA</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: none; text-align: center;">
<img src="data:image/png;base64,{{ barcode .SelfServiceLink "qr" "" }}" style="height: 2.5cm; padding: 0.2cm">
</td>
<td style="border: none; text-align: center;">
<img src="data:image/png;base64,{{ epcCode $.SepaConfig.IBAN $.SepaConfig.BIC $.SepaConfig.HolderName (print "Spende LfK " .ID ", " .FirstName " " .LastName ", " .CombinedGroupName) .TotalDonations $.SepaConfig.CurrencyIdentifier}}" style="height: 2.5cm; padding: 0.2cm">
</td>
</tr>
</tbody>
</table>
<p style="width: 17cm; text-align: center;">
Donors, please transfer your donation to our account: {{ $.SepaConfig.HolderName }} | IBAN: {{ $.SepaConfig.IBAN }} | BIC: {{ $.SepaConfig.BIC }} | Ref: "Spende LfK {{.ID}}, {{ .FirstName }} {{ .LastName }}, {{.CombinedGroupName}}"
</p>
</footer>
</article>
{{ end }}
</body>
</html>

View File

@ -0,0 +1,124 @@
<html>
<head>
<meta charset="utf8">
<title>Sponsoring contract</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
}
body.A5.landscape .sheet {
width: 210mm;
height: 147mm
}
.column {
margin-bottom: -20;
}
</style>
</head>
<body class="A5 landscape">
{{ range .Runners }}
<div class="sheet">
<img id="header_img" width="100%" src="data:image/png;base64,{{ loadImage "sponsoringheader" }}" />
<div style=" padding: 0 1rem 0 1rem;">
<div class="columns">
<div class="column is-10">
<div class="columns" style="padding-bottom: 0;">
<div class="column is-two-fifths">
<p style="font-size: large; font-weight: bold;">Sponsoringerklärung</p>
</div>
<div class="column">
<p style="font-size: x-small; vertical-align: revert; margin-top: auto;">Bitte in DRUCKBUCHSTABEN
schreiben
</p>
</div>
</div>
<p>Ich bin/ Wir sind bereit anlässlich des {{ $.EventName }}</p>
<div class="columns">
<div class="column is-9">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{ .FirstName }}
{{ .MiddleName }}</span>
<p style="font-size: x-small; display: block;">Vorname</p>
</div>
<div class="column is-3">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{ .ID }}</span>
<p style="font-size: x-small; display: block;">ID</p>
</div>
</div>
</div>
<div class="column">
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="data:image/png;base64,{{ barcode (printf "%d" .ID) $.BarcodeFormat $.BarcodePrefix }}" />
</div>
</div>
<div class="columns" style="padding-top: 1rem;">
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{ .LastName }}</span>
<p style="font-size: x-small; display: block;">Nachname</p>
</div>
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;"><p>{{ if ne .Group.ParentGroup.Name "" -}}{{ .Group.ParentGroup.Name }}/ {{end -}}{{ .Group.Name }}</p></span>
<p style="font-size: x-small; display: block;">Team/Klasse</p>
</div>
</div>
<p style="margin-top: -0.5rem">mit einem Betrag von _____ {{ $.CurrencySymbol }} pro gelaufenem Kilometer zu
unterstützen.</p>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Nachname</p>
</div>
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Vorname</p>
</div>
</div>
<p style="font-size: medium; margin-top: -0.5rem;">Adresse (Sponsor)</p>
<p style="font-size: x-small;">(Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen -
Spendenquittungen können erst ab einem Gesamtbetrag von {{ $.ReceiptMinimumAmount }}{{ $.CurrencySymbol }}
ausgestellt werden)</p>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-8">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Straße</p>
</div>
<div class="column is-4">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Hausnummer</p>
</div>
</div>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-4">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Postleitzahl</p>
</div>
<div class="column is-8">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Stadt</p>
</div>
</div>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-7">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Ort, Datum</p>
</div>
<div class="column is-5">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Unterschrift</p>
</div>
</div>
<p style="font-size: xx-small; overflow: hidden; height: 4rem; text-align: center;"> {{ $.Disclaimer }}</p>
</div>
</div>
{{ end }}
</body>
</html>

View File

@ -0,0 +1,120 @@
<html>
<head>
<meta charset="utf8">
<title>Sponsoring contract</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
}
body.A5.landscape .sheet {
width: 210mm;
height: 147mm
}
.column {
margin-bottom: -20;
}
</style>
</head>
<body class="A5 landscape">
{{ range .Runners }}
<div class="sheet">
<img id="header_img" width="100%" src="data:image/png;base64,{{ loadImage "sponsoringheader" }}" />
<div style=" padding: 0 1rem 0 1rem;">
<div class="columns">
<div class="column is-10">
<div class="columns" style="padding-bottom: 0;">
<div class="column is-two-fifths">
<p style="font-size: large; font-weight: bold;">Sponsoring contract</p>
</div>
<div class="column">
<p style="font-size: x-small; vertical-align: revert; margin-top: auto;">Please write in BLOCK LETTERS.
</p>
</div>
</div>
<p> On the occasion of the {{ $.EventName }} I/We want to support </p>
<div class="columns">
<div class="column is-9">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{ .FirstName }}
{{ .MiddleName }}</span>
<p style="font-size: x-small; display: block;">First name</p>
</div>
<div class="column is-3">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{ .ID }}</span>
<p style="font-size: x-small; display: block;">ID</p>
</div>
</div>
</div>
<div class="column">
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="data:image/png;base64,{{ barcode (printf "%d" .ID) $.BarcodeFormat $.BarcodePrefix }}"/>
</div>
</div>
<div class="columns" style="padding-top: 1rem;">
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{ .LastName }}</span>
<p style="font-size: x-small; display: block;">Last Name</p>
</div>
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;"><p>{{ if ne .Group.ParentGroup.Name "" -}}{{ .Group.ParentGroup.Name }}/ {{end -}}{{ .Group.Name }}</p></span>
<p style="font-size: x-small; display: block;">Team/class</p>
</div>
</div>
<p style="margin-top: -0.5rem">with the amount of _____{{ $.CurrencySymbol }} per kilometer run.</p>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Last name</p>
</div>
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">First name</p>
</div>
</div>
<p style="font-size: medium; margin-top: -0.5rem;">Address (Sponsor)</p>
<p style="font-size: x-small;">(You have to provide an address if you want a donation receipt - Donation receipts can't be issued for total donation amounts under {{ $.ReceiptMinimumAmount }}{{ $.CurrencySymbol }})</p>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-8">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Street</p>
</div>
<div class="column is-4">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">House number</p>
</div>
</div>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-4">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Postal code</p>
</div>
<div class="column is-8">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">City</p>
</div>
</div>
<div class="columns" style="margin-top: -1rem;">
<div class="column is-7">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Location, Date</p>
</div>
<div class="column is-5">
<span style="border-bottom: 1px solid; width: 100%; display: block;"></span>
<p style="font-size: x-small; display: block;">Signature</p>
</div>
</div>
<p style="font-size: xx-small; overflow: hidden; height: 4rem; text-align: center;">{{ $.Disclaimer }}</p>
</div>
</div>
{{ end }}
</body>
</html>