Compare commits
	
		
			515 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						f902c61490
	
				 | 
					
					
						|||
| 
						
						
							
						
						11e8cc5b1d
	
				 | 
					
					
						|||
| 
						
						
							
						
						84155b7404
	
				 | 
					
					
						|||
| 
						
						
							
						
						45b37197ec
	
				 | 
					
					
						|||
| 
						
						
							
						
						f65848924c
	
				 | 
					
					
						|||
| 
						
						
							
						
						98d584867e
	
				 | 
					
					
						|||
| 
						
						
							
						
						376e8de1a4
	
				 | 
					
					
						|||
| 
						
						
							
						
						2911391fb9
	
				 | 
					
					
						|||
| 
						
						
							
						
						6d2e0241c9
	
				 | 
					
					
						|||
| 
						
						
							
						
						afc5b1f0c6
	
				 | 
					
					
						|||
| 
						
						
							
						
						4a76ee469b
	
				 | 
					
					
						|||
| 
						
						
							
						
						b58bf700df
	
				 | 
					
					
						|||
| 
						
						
							
						
						efd3a35802
	
				 | 
					
					
						|||
| 
						
						
							
						
						0f7e44a42a
	
				 | 
					
					
						|||
| 
						
						
							
						
						f90e5d75fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						31d4ec5f27
	
				 | 
					
					
						|||
| 
						
						
							
						
						d61d4d6e7e
	
				 | 
					
					
						|||
| 
						
						
							
						
						606ce6b940
	
				 | 
					
					
						|||
| 
						
						
							
						
						750fa70332
	
				 | 
					
					
						|||
| 
						
						
							
						
						7d503edbc9
	
				 | 
					
					
						|||
| 
						
						
							
						
						5c9235df8d
	
				 | 
					
					
						|||
| 
						
						
							
						
						11ea0858bb
	
				 | 
					
					
						|||
| 
						
						
							
						
						4d57cf827d
	
				 | 
					
					
						|||
| 
						
						
							
						
						df9f7fdc13
	
				 | 
					
					
						|||
| 
						
						
							
						
						cdd2b5e250
	
				 | 
					
					
						|||
| 
						
						
							
						
						94b766f106
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2e94f715b
	
				 | 
					
					
						|||
| 
						
						
							
						
						f64daaf817
	
				 | 
					
					
						|||
| 
						
						
							
						
						b4bb732303
	
				 | 
					
					
						|||
| 
						
						
							
						
						3dee3e72af
	
				 | 
					
					
						|||
| 
						
						
							
						
						f5914e5c38
	
				 | 
					
					
						|||
| 
						
						
							
						
						5a5a7179e9
	
				 | 
					
					
						|||
| 
						
						
							
						
						6c57d63891
	
				 | 
					
					
						|||
| 
						
						
							
						
						b502e2fbd5
	
				 | 
					
					
						|||
| 
						
						
							
						
						c9475d0093
	
				 | 
					
					
						|||
| 
						
						
							
						
						1cc19e0085
	
				 | 
					
					
						|||
| 
						
						
							
						
						b792806481
	
				 | 
					
					
						|||
| 
						
						
							
						
						ea6a4a7080
	
				 | 
					
					
						|||
| 
						
						
							
						
						de6fe4991c
	
				 | 
					
					
						|||
| 
						
						
							
						
						1d068b2655
	
				 | 
					
					
						|||
| 
						
						
							
						
						ef25adf5ed
	
				 | 
					
					
						|||
| 
						
						
							
						
						c09c00ec68
	
				 | 
					
					
						|||
| 
						
						
							
						
						1f4981b0a9
	
				 | 
					
					
						|||
| 
						
						
							
						
						c9f28612be
	
				 | 
					
					
						|||
| 
						
						
							
						
						c2d192d8a3
	
				 | 
					
					
						|||
| 
						
						
							
						
						31734596f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						850fa0a760
	
				 | 
					
					
						|||
| 
						
						
							
						
						1c0a9860fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						7c32f1aad2
	
				 | 
					
					
						|||
| 
						
						
							
						
						b9e550d6f5
	
				 | 
					
					
						|||
| 
						
						
							
						
						2a4f126377
	
				 | 
					
					
						|||
| 
						
						
							
						
						5c932158e9
	
				 | 
					
					
						|||
| 
						
						
							
						
						1296c9e399
	
				 | 
					
					
						|||
| 
						
						
							
						
						0f2452eca0
	
				 | 
					
					
						|||
| 
						
						
							
						
						b6cc98a165
	
				 | 
					
					
						|||
| 
						
						
							
						
						c5da33f10f
	
				 | 
					
					
						|||
| 
						
						
							
						
						69f4de6739
	
				 | 
					
					
						|||
| 
						
						
							
						
						649ac2a3c2
	
				 | 
					
					
						|||
| 
						
						
							
						
						5587fdaaa8
	
				 | 
					
					
						|||
| 
						
						
							
						
						8f676f08a9
	
				 | 
					
					
						|||
| 28de60d375 | |||
| 
						
						
							
						
						d19029b5ad
	
				 | 
					
					
						|||
| 
						
						
							
						
						1dfd96869d
	
				 | 
					
					
						|||
| 
						
						
							
						
						a1ba28cacb
	
				 | 
					
					
						|||
| 
						
						
							
						
						d2bace87af
	
				 | 
					
					
						|||
| 
						
						
							
						
						9d507b9572
	
				 | 
					
					
						|||
| 
						
						
							
						
						e89c17806f
	
				 | 
					
					
						|||
| 
						
						
							
						
						924f76a100
	
				 | 
					
					
						|||
| 
						
						
							
						
						99ec0933ea
	
				 | 
					
					
						|||
| 
						
						
							
						
						cceca7f5e1
	
				 | 
					
					
						|||
| 
						
						
							
						
						54d294a8b4
	
				 | 
					
					
						|||
| 
						
						
							
						
						41291b9200
	
				 | 
					
					
						|||
| 
						
						
							
						
						4faf76a073
	
				 | 
					
					
						|||
| 
						
						
							
						
						715eb8e1cb
	
				 | 
					
					
						|||
| 
						
						
							
						
						2686bee1d1
	
				 | 
					
					
						|||
| 
						
						
							
						
						57a3777891
	
				 | 
					
					
						|||
| 
						
						
							
						
						f6dc33edb4
	
				 | 
					
					
						|||
| 
						
						
							
						
						e2cd445aeb
	
				 | 
					
					
						|||
| 
						
						
							
						
						f5debf58fc
	
				 | 
					
					
						|||
| 
						
						
							
						
						11a9b51197
	
				 | 
					
					
						|||
| 
						
						
							
						
						c5dc4f7e79
	
				 | 
					
					
						|||
| 
						
						
							
						
						f9f30e96c7
	
				 | 
					
					
						|||
| 
						
						
							
						
						eff3354867
	
				 | 
					
					
						|||
| 
						
						
							
						
						2bfff006ed
	
				 | 
					
					
						|||
| 
						
						
							
						
						53eab5db94
	
				 | 
					
					
						|||
| 
						
						
							
						
						af73b35b18
	
				 | 
					
					
						|||
| 
						
						
							
						
						692f378eab
	
				 | 
					
					
						|||
| 
						
						
							
						
						12f61e373f
	
				 | 
					
					
						|||
| 
						
						
							
						
						79a0062f60
	
				 | 
					
					
						|||
| 
						
						
							
						
						561e7151c6
	
				 | 
					
					
						|||
| 
						
						
							
						
						2108c88001
	
				 | 
					
					
						|||
| 
						
						
							
						
						2537235ce6
	
				 | 
					
					
						|||
| 
						
						
							
						
						4deda8adf1
	
				 | 
					
					
						|||
| 
						
						
							
						
						d910a722df
	
				 | 
					
					
						|||
| 
						
						
							
						
						e10448f1e3
	
				 | 
					
					
						|||
| 
						
						
							
						
						f880e9f10c
	
				 | 
					
					
						|||
| 
						
						
							
						
						b179541532
	
				 | 
					
					
						|||
| 
						
						
							
						
						5098d27ae9
	
				 | 
					
					
						|||
| 
						
						
							
						
						d8fb9ea2fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						5dbe7816cd
	
				 | 
					
					
						|||
| 
						
						
							
						
						1657a10dec
	
				 | 
					
					
						|||
| 
						
						
							
						
						7d22a32cb4
	
				 | 
					
					
						|||
| 
						
						
							
						
						ed4941b403
	
				 | 
					
					
						|||
| 
						
						
							
						
						c227c291c9
	
				 | 
					
					
						|||
| 
						
						
							
						
						389922f22d
	
				 | 
					
					
						|||
| 
						
						
							
						
						670290b60b
	
				 | 
					
					
						|||
| 
						
						
							
						
						3345571bd8
	
				 | 
					
					
						|||
| 
						
						
							
						
						d51e78a442
	
				 | 
					
					
						|||
| 
						
						
							
						
						bd70ac4542
	
				 | 
					
					
						|||
| 
						
						
							
						
						62f04f7d1c
	
				 | 
					
					
						|||
| 
						
						
							
						
						8812bf2410
	
				 | 
					
					
						|||
| 
						
						
							
						
						323c0b0ff9
	
				 | 
					
					
						|||
| 
						
						
							
						
						e9c28efd47
	
				 | 
					
					
						|||
| 
						
						
							
						
						e5f9eff54f
	
				 | 
					
					
						|||
| 
						
						
							
						
						382a799038
	
				 | 
					
					
						|||
| 
						
						
							
						
						f4f5c8b63a
	
				 | 
					
					
						|||
| 
						
						
							
						
						c0dd30f08c
	
				 | 
					
					
						|||
| 
						
						
							
						
						3eb914d640
	
				 | 
					
					
						|||
| 
						
						
							
						
						ba7e02fa30
	
				 | 
					
					
						|||
| 
						
						
							
						
						56e09dafb9
	
				 | 
					
					
						|||
| 
						
						
							
						
						145ebd8346
	
				 | 
					
					
						|||
| 
						
						
							
						
						13e9c88a8e
	
				 | 
					
					
						|||
| 
						
						
							
						
						7d571ad46b
	
				 | 
					
					
						|||
| 
						
						
							
						
						491d31e2e9
	
				 | 
					
					
						|||
| 
						
						
							
						
						3f35d35016
	
				 | 
					
					
						|||
| 
						
						
							
						
						aeeb7b3448
	
				 | 
					
					
						|||
| 
						
						
							
						
						f9b471b59e
	
				 | 
					
					
						|||
| 
						
						
							
						
						8f3faee573
	
				 | 
					
					
						|||
| 
						
						
							
						
						6ff2ba702b
	
				 | 
					
					
						|||
| 
						
						
							
						
						4ecefe8534
	
				 | 
					
					
						|||
| 
						
						
							
						
						ab9295ee23
	
				 | 
					
					
						|||
| 
						
						
							
						
						29d4e21656
	
				 | 
					
					
						|||
| 
						
						
							
						
						490f24422b
	
				 | 
					
					
						|||
| 
						
						
							
						
						67eb761a99
	
				 | 
					
					
						|||
| 
						
						
							
						
						22978e5bbc
	
				 | 
					
					
						|||
| 
						
						
							
						
						0dbd7311ab
	
				 | 
					
					
						|||
| 
						
						
							
						
						cbda10e2a1
	
				 | 
					
					
						|||
| 
						
						
							
						
						ee3c443216
	
				 | 
					
					
						|||
| 
						
						
							
						
						bbe01cd231
	
				 | 
					
					
						|||
| 
						
						
							
						
						10c68cfb37
	
				 | 
					
					
						|||
| 
						
						
							
						
						c21076a049
	
				 | 
					
					
						|||
| 
						
						
							
						
						fc774d5af3
	
				 | 
					
					
						|||
| 
						
						
							
						
						dd0b60b2ba
	
				 | 
					
					
						|||
| 
						
						
							
						
						6add09c7df
	
				 | 
					
					
						|||
| 
						
						
							
						
						d064b51e1b
	
				 | 
					
					
						|||
| 
						
						
							
						
						744f567b7c
	
				 | 
					
					
						|||
| 
						
						
							
						
						7dbce9e870
	
				 | 
					
					
						|||
| 
						
						
							
						
						72ed2495f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						05e0c63931
	
				 | 
					
					
						|||
| fcbcec85c5 | |||
| 
						
						
							
						
						b47b2f804f
	
				 | 
					
					
						|||
| 
						
						
							
						
						5f4fc74cd9
	
				 | 
					
					
						|||
| 300b8fd01a | |||
| 8b951dfb3f | |||
| 
						
						
							
						
						517e8a0819
	
				 | 
					
					
						|||
| 
						
						
							
						
						3af4b521d3
	
				 | 
					
					
						|||
| dca2829323 | |||
| 57a84c256a | |||
| b78534e1b0 | |||
| 9b85b54da5 | |||
| 4b5a86282d | |||
| 
						
						
							
						
						c9cb03ea95
	
				 | 
					
					
						|||
| c7f57548f3 | |||
| 8d00307170 | |||
| 5e92b9a48f | |||
| 01e1323555 | |||
| f8465721cd | |||
| 4cea7cb32f | |||
| 
						
						
							
						
						72303b1105
	
				 | 
					
					
						|||
| 451b7fbe05 | |||
| 
						
						
							
						
						2a3322612d
	
				 | 
					
					
						|||
| 4b4d66ae78 | |||
| c935950eb0 | |||
| 573b921197 | |||
| 
						
						
							
						
						274c13e358
	
				 | 
					
					
						|||
| ff0421da2f | |||
| 915baa6efa | |||
| bac004d74e | |||
| b7b7f6a0ae | |||
| 11efdebacf | |||
| 0f2d6f58d6 | |||
| df8bd1133b | |||
| 22fb3edd78 | |||
| ded610f114 | |||
| a4c8dade23 | |||
| b6fc069042 | |||
| 60cc343adf | |||
| 010f2046ad | |||
| c18cb7f135 | |||
| 2e7c3e8a5b | |||
| ac9be793bd | |||
| c18fc4ec93 | |||
| 981bae4786 | |||
| 754d0ca58c | |||
| fa26ed6012 | |||
| cc4a2b4ab4 | |||
| e97e209746 | |||
| 8f30d8933f | |||
| f78037c0f1 | |||
| 3c02e13997 | |||
| d8f3a6ed06 | |||
| 2ee4c06055 | |||
| 76418f65e1 | |||
| a57e0909b9 | |||
| a81db03ba3 | |||
| 7ae4750307 | |||
| f623c0a7cd | |||
| 3fc612488d | |||
| f220e70743 | |||
| d3f7d1a6c9 | |||
| 82159bed53 | |||
| 479e28c46c | |||
| e75f15142e | |||
| cec893032d | |||
| 2278e4ad06 | |||
| 5a98688d60 | |||
| 63c7beb8b9 | |||
| 2a4cfdb2f8 | |||
| a580841973 | |||
| 6b23dea477 | |||
| e0add846bb | |||
| 1d12de7045 | |||
| b43aeec0cf | |||
| 405bb20601 | |||
| ac572f1ea3 | |||
| 7fea1ca78f | |||
| 64fce5bd01 | |||
| 5ba26c4cbf | |||
| b82a32ae3e | |||
| 0af9b81b38 | |||
| 955e11846b | |||
| d1577cd08d | |||
| 6767c3b2d1 | |||
| 2b2195727b | |||
| 3ca2237953 | |||
| 8d6ea4dbf9 | |||
| 8b71608792 | |||
| f1084b59a7 | |||
| c8dc998ecd | |||
| 3df3d26708 | |||
| 922e762aa2 | |||
| c3beb3e103 | |||
| 457ea26cf8 | |||
| c2d2b66f2f | |||
| 289a0d8671 | |||
| ce3053c0ba | |||
| 6608456c68 | |||
| 1cbe5a1614 | |||
| 9584bfed8b | |||
| 0839ff6359 | |||
| 4739193709 | |||
| 0ade57536e | |||
| d17108f4b9 | |||
| 3c42ca3042 | |||
| 073433f308 | |||
| 8bac1fadd6 | |||
| a478081727 | |||
| b1978e796f | |||
| c51ec74d30 | |||
| b8f0d1fa60 | |||
| 96886c74bc | |||
| 8d3cc34395 | |||
| 85519bc2e4 | |||
| ed02306738 | |||
| 31a59500fa | |||
| d01b4a0b99 | |||
| d9919404b5 | |||
| b180e04045 | |||
| 7f28525ec2 | |||
| 677bd86133 | |||
| b612562d34 | |||
| e9d3574599 | |||
| 9dd62cefa9 | |||
| 0797c678c8 | |||
| 5e4d6f44da | |||
| 885765ac71 | |||
| 03ed6d5bc1 | |||
| 4e1e124d0d | |||
| 19fbf50f6f | |||
| 21b5e048ed | |||
| c012b4943d | |||
| 7c23dba493 | |||
| ba566bcc33 | |||
| a386c5bef8 | |||
| 1ca5d3ea07 | |||
| fd8b7e56da | |||
| 55877de2aa | |||
| d1a29c1cbb | |||
| e6f7dd2be8 | |||
| 49590b897e | |||
| a9019e4c67 | |||
| cc6a53b258 | |||
| e0db6f6a78 | |||
| 0fcfb30d5c | |||
| c2909082a2 | |||
| 92c52401b3 | |||
| 7ca7266ea4 | |||
| adf11ab1c3 | |||
| db91661556 | |||
| 9d7d044384 | |||
| 95099c5fbd | |||
| 782861bd93 | |||
| dcde424b77 | |||
| b7c6c6e157 | |||
| 2d031dae03 | |||
| 729f2d7240 | |||
| 454309278e | |||
| 7be211f8b7 | |||
| bdeadd274b | |||
| e306cdb2c8 | |||
| 406add3d51 | |||
| e74b9a4c9d | |||
| 449a96b302 | |||
| 703eaa0e9d | |||
| b4dc1d2be7 | |||
| cf0f5839ee | |||
| 29376a7782 | |||
| 08e858726c | |||
| 68a1b8f3e0 | |||
| 9697d53a15 | |||
| d38923c4ad | |||
| 03c7d590d0 | |||
| 8a90f63b09 | |||
| 491cdb8d71 | |||
| 68572b194e | |||
| 1eb634fe11 | |||
| dbccbf68f4 | |||
| 96204d809e | |||
| 7ac8edb5cf | |||
| 05561db5b8 | |||
| 149bf1849d | |||
| b952ac4d72 | |||
| 5c075bce8b | |||
| b77945a9e4 | |||
| aefe5493b0 | |||
| 75b8b281b8 | |||
| 44f139aa01 | |||
| a1b0a1918d | |||
| 467d8df6db | |||
| fea0b1dc05 | |||
| 7122fe7dbe | |||
| ff36b4871f | |||
| 03f63e3777 | |||
| ecd02a1af7 | |||
| b0d42939a6 | |||
| 502345782f | |||
| 9a7c1d64fd | |||
| 4b79b29ee6 | |||
| edc846ab05 | |||
| 0d27916188 | |||
| 0894446085 | |||
| 4187a8e820 | |||
| e1ec193a4f | |||
| ad9a8a4fe0 | |||
| 6a14232889 | |||
| b6296b8d97 | |||
| bc4d16e6f8 | |||
| e3a45a61ac | |||
| 7f58dd694b | |||
| 68f46a45b5 | |||
| 016f746c7c | |||
| b77bb3ad9d | |||
| b4ebae283b | |||
| 3bb322ede5 | |||
| b92a6f7b2b | |||
| d3a213ce33 | |||
| 8fc6c7176e | |||
| 929ac81515 | |||
| 75d2ac3c5f | |||
| 3e2b011d28 | |||
| 1c06689800 | |||
| a35f8cfd3a | |||
| 27d1d69360 | |||
| 8072d0b194 | |||
| a30600943d | |||
| 123cf8ad48 | |||
| 22b1e0097e | |||
| 7e507d4cc4 | |||
| f7dfd6d0c3 | |||
| cbff3074d1 | |||
| 9755e437c2 | |||
| fe0f45ea92 | |||
| b07c5a96f2 | |||
| dba765cb01 | |||
| 8ca015ecf8 | |||
| 6ab3946c31 | |||
| 603a814ad4 | |||
| 7736ccdc0c | |||
| d1b07f39fd | |||
| 8d4e7a16d1 | |||
| 4fca5afdfa | |||
| ea8028dfd5 | |||
| bc2b6fadd9 | |||
| ee19efa0e6 | |||
| f3de80af78 | |||
| 6a42fe37d0 | |||
| 84259d37d4 | |||
| 140fda11cc | |||
| e92820e12f | |||
| 4773a5f18c | |||
| 06dedc0797 | |||
| 785544ca16 | |||
| 4f191dcb52 | |||
| 1ced0e3175 | |||
| 5e1252545b | |||
| 2d0b7ce79e | |||
| 1962499d18 | |||
| 1e67672ef0 | |||
| e401d0ec72 | |||
| 42443af734 | |||
| c07319b250 | |||
| 388d8a2dc6 | |||
| ee8ba99cc7 | |||
| 47a05facb3 | |||
| f755f4f9fb | |||
| 784d7c656f | |||
| e345c36dd0 | |||
| c617c40e9d | |||
| a9e3360ee2 | |||
| 13776d1ecb | |||
| f69e7779e4 | |||
| 8ec5de4877 | |||
| 7c3813b94d | |||
| b4232e51e0 | |||
| cd51e78282 | |||
| f833ae222e | |||
| 4cd437b6af | |||
| 9be3e789a5 | |||
| 119102aef8 | |||
| cf9cd298b6 | |||
| 05e471878f | |||
| 6f81566cb8 | |||
| a596188c00 | |||
| 75eb925267 | |||
| 19697692bc | |||
| 4acf3e39ce | |||
| 3af76a53e3 | |||
| d58453faf9 | |||
| bd6ec6215d | |||
| 96d863bfc8 | |||
| ba7cedd187 | |||
| 78205ee8c7 | |||
| 041c0ed6bb | |||
| 6121b1e3bf | |||
| 5afd26ea22 | |||
| 4585a83838 | |||
| c07c6aeeab | |||
| 9a115e75b1 | |||
| f4a34dd36b | |||
| 42d6c40d0c | |||
| de432c4481 | |||
| 73915da203 | |||
| 15cd5f6579 | |||
| 95b882aced | |||
| 376087fe66 | |||
| 68e34a9054 | |||
| 465efe0300 | |||
| 782b41756d | |||
| 15f16415c0 | |||
| 82b5f9e86e | |||
| b5e79e51ef | |||
| 8141269dd9 | |||
| 56b72275ac | |||
| df94b1b750 | |||
| 15ff412d75 | |||
| 36396af50a | |||
| 4d45e0f80d | |||
| 6435c8a88e | |||
| 3fb8be22b7 | |||
| 7d5b5750ad | |||
| 64bd1ffc3a | |||
| 3ca38abe93 | |||
| 2c7b0254ec | |||
| f726a6e699 | |||
| a7d4001ad9 | |||
| 4617f2c5bd | |||
| 557cc26f28 | |||
| 8a30265fc6 | |||
| 9f3758de39 | |||
| cfa65e83ca | |||
| 1d73e4ed9c | |||
| 1d1fa50327 | |||
| d08bdfd961 | |||
| aae4f507ea | |||
| cb7325bbf9 | |||
| e29c17a29a | |||
| 63f9523766 | |||
| d291cf0d1b | |||
| d493e74bf3 | |||
| 83c4bd62cb | |||
| 8f250f747a | |||
| 0c4e5a182c | |||
| 48d7cad9f3 | |||
| 185e66fb5b | |||
| cbc421cd83 | |||
| b9d4cc3619 | |||
| 1d62e7f14c | |||
| 4efb62921b | |||
| c2d714116e | |||
| cff70110fd | |||
| 814b564752 | |||
| 915ff92418 | |||
| 94e5e51f8d | |||
| 57afbd4b6c | |||
| 6539fd7855 | |||
| 9d62225478 | |||
| b1747f6374 | |||
| ae466b4d7e | |||
| e1f03788db | |||
| 278c56386b | |||
| 66134c0e1e | |||
| 61c7dbff0b | |||
| 43bb728d0d | |||
| d450ceac74 | |||
| b0427a6d81 | 
							
								
								
									
										46
									
								
								.air.linux.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								.air.linux.toml
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										46
									
								
								.air.windows.toml
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
tmp
 | 
			
		||||
docker-compose.yaml
 | 
			
		||||
.air*.toml
 | 
			
		||||
							
								
								
									
										23
									
								
								.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.env
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										27
									
								
								.gitea/workflows/dev.yaml
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										27
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal 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
									
									
								
							
							
						
						
									
										130
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										15
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
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 scratch
 | 
			
		||||
COPY --from=builder /app/server /server
 | 
			
		||||
COPY static /static
 | 
			
		||||
ADD https://curl.haxx.se/ca/cacert.pem /etc/ssl/certs/ca-certificates.crt
 | 
			
		||||
ENTRYPOINT [ "/server" ]
 | 
			
		||||
							
								
								
									
										362
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										362
									
								
								LICENSE
									
									
									
									
									
								
							@@ -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
 | 
			
		||||
reason–for example, because of any applicable exception or limitation to copyright–then
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										97
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								README.md
									
									
									
									
									
								
							@@ -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
									
								
							
							
						
						
									
										9
									
								
								docker-compose.dev.yaml
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										15
									
								
								docker-compose.yaml
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										18
									
								
								docker.env
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										437
									
								
								docs/docs.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										410
									
								
								docs/swagger.json
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										278
									
								
								docs/swagger.yaml
									
									
									
									
									
										Normal 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"
 | 
			
		||||
							
								
								
									
										61
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
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/josharian/intern v1.0.0 // indirect
 | 
			
		||||
	github.com/klauspost/compress v1.17.11 // 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/pelletier/go-toml/v2 v2.2.2 // 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.57.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/exp v0.0.0-20230905200255-921286631fa9 // indirect
 | 
			
		||||
	golang.org/x/sys v0.27.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.19.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.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										138
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
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/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/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/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/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/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/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/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/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
 | 
			
		||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 | 
			
		||||
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.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
									
								
							
							
						
						
									
										73
									
								
								handlers/barcode.go
									
									
									
									
									
										Normal 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())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								handlers/card.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								handlers/card.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package handlers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"slices"
 | 
			
		||||
 | 
			
		||||
	"git.odit.services/lfk/document-server/models"
 | 
			
		||||
	"github.com/gofiber/fiber/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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(),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	genConfig := &models.CardTemplateOptions{
 | 
			
		||||
		CardSegments:  splitCardSegments(cardRequest.Cards),
 | 
			
		||||
		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(),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	logger.Info("Converted html to pdf")
 | 
			
		||||
 | 
			
		||||
	c.Set(fiber.HeaderContentType, "application/pdf")
 | 
			
		||||
	c.Set(fiber.HeaderContentDisposition, "attachment; filename=runner-cards.pdf")
 | 
			
		||||
	return c.Send(pdf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
									
								
							
							
						
						
									
										108
									
								
								handlers/certificate.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										96
									
								
								handlers/contract.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										24
									
								
								handlers/handlers.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										7
									
								
								handlers/util.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										198
									
								
								main.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										26
									
								
								models/card.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										51
									
								
								models/certificate.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										24
									
								
								models/config.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										31
									
								
								models/contract.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										129
									
								
								services/barcode.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										131
									
								
								services/converter.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								services/templater.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								services/templater.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html/template"
 | 
			
		||||
	"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
 | 
			
		||||
SCT
 | 
			
		||||
%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
 | 
			
		||||
	switch unit {
 | 
			
		||||
	case "kilometer":
 | 
			
		||||
		formatted = fmt.Sprintf("%.3f", float64(amount)/1000)
 | 
			
		||||
	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
									
								
							
							
						
						
									
										72
									
								
								services/templates.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/certificate_background.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/certificate_background.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/certificate_footer.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/certificate_footer.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/error.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/error.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/sponsoringheader.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/sponsoringheader.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/sponsors/atlantis.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/sponsors/atlantis.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/sponsors/herzogspark.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/sponsors/herzogspark.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/sponsors/odit.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/sponsors/odit.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/sponsors/sparkasse.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/sponsors/sparkasse.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/images/sponsors/vrbank.base64
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/sponsors/vrbank.base64
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										84
									
								
								static/templates/card/de.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								static/templates/card/de.html
									
									
									
									
									
										Normal 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>Kein Läufer zugewiesen</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>
 | 
			
		||||
							
								
								
									
										79
									
								
								static/templates/card/en.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								static/templates/card/en.html
									
									
									
									
									
										Normal 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>Blank card</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>
 | 
			
		||||
							
								
								
									
										119
									
								
								static/templates/certificate/de.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								static/templates/certificate/de.html
									
									
									
									
									
										Normal 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}}km</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 "Sponsoring für " .FirstName " " .LastName ", " .CombinedGroupName) .TotalDonations $.SepaConfig.CurrencyIdentifier}}" style="height: 2.5cm; padding: 0.2cm">
 | 
			
		||||
              </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
            <p style="width: 17cm; text-align: center;">
 | 
			
		||||
              {{ $.Footer }}
 | 
			
		||||
            </p>
 | 
			
		||||
    </footer>
 | 
			
		||||
  </article>
 | 
			
		||||
  {{ end }}
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										120
									
								
								static/templates/certificate/en.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								static/templates/certificate/en.html
									
									
									
									
									
										Normal 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}}km</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 "Sponsoring for " .FirstName " " .LastName ", " .CombinedGroupName) .TotalDonations $.SepaConfig.CurrencyIdentifier}}" style="height: 2.5cm; padding: 0.2cm">
 | 
			
		||||
          </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
      <p style="width: 17cm; text-align: center;">
 | 
			
		||||
        {{ $.Footer }}
 | 
			
		||||
      </p>
 | 
			
		||||
    </footer>
 | 
			
		||||
  </article>
 | 
			
		||||
  {{ end }}
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										124
									
								
								static/templates/contract/de.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								static/templates/contract/de.html
									
									
									
									
									
										Normal 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>
 | 
			
		||||
							
								
								
									
										120
									
								
								static/templates/contract/en.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								static/templates/contract/en.html
									
									
									
									
									
										Normal 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>
 | 
			
		||||
		Reference in New Issue
	
	Block a user