Compare commits

...

8 Commits

18 changed files with 720 additions and 34 deletions

46
.air.toml Normal file
View File

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

5
docker-compose.yaml Normal file
View File

@ -0,0 +1,5 @@
services:
gotenberg:
image: gotenberg/gotenberg:8
ports:
- "3001:3000"

View File

@ -47,6 +47,13 @@ const docTemplate = `{
"models.Contract": { "models.Contract": {
"type": "object", "type": "object",
"properties": { "properties": {
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
},
"runners": { "runners": {
"type": "array", "type": "array",
"items": { "items": {

View File

@ -38,6 +38,13 @@
"models.Contract": { "models.Contract": {
"type": "object", "type": "object",
"properties": { "properties": {
"locale": {
"type": "string",
"enum": [
"en",
"de"
]
},
"runners": { "runners": {
"type": "array", "type": "array",
"items": { "items": {

View File

@ -1,6 +1,11 @@
definitions: definitions:
models.Contract: models.Contract:
properties: properties:
locale:
enum:
- en
- de
type: string
runners: runners:
items: items:
$ref: '#/definitions/models.Runner' $ref: '#/definitions/models.Runner'

19
go.mod
View File

@ -1,18 +1,22 @@
module git.odit.services/lfk/document-server module git.odit.services/lfk/document-server
go 1.22.1 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 ( require (
github.com/KyleBanks/depth v1.2.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.2.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference 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/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/gofiber/fiber/v2 v2.52.5 // indirect
github.com/gofiber/swagger v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.17.11 // indirect
@ -20,16 +24,13 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/oxplot/papersizes v0.0.0-20181201065918-90a3a5ae1915 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/swaggo/files/v2 v2.0.1 // indirect github.com/swaggo/files/v2 v2.0.1 // indirect
github.com/swaggo/swag v1.16.4 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.57.0 // indirect github.com/valyala/fasthttp v1.57.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/tools v0.27.0 // indirect golang.org/x/tools v0.27.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

41
go.sum
View File

@ -1,13 +1,8 @@
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28=
github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 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/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
@ -21,16 +16,15 @@ github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yG
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= 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 h1:ff3rg1fB+Rp5JN/N8jfxTiZtMKe/9tB9QDc79fPiJKQ=
github.com/gofiber/swagger v1.1.0/go.mod h1:pRZL0Np35sd+lTODTE5The0G+TMHfNY+oC4hM2/i5m8= github.com/gofiber/swagger v1.1.0/go.mod h1:pRZL0Np35sd+lTODTE5The0G+TMHfNY+oC4hM2/i5m8=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 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/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@ -39,42 +33,43 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 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/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 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/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/swaggo/files/v2 v2.0.1 h1:XCVJO/i/VosCDsJu1YLpdejGsGnBE9deRMpjN4pJLHk= 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/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 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= 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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg= github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE= github.com/valyala/fasthttp v1.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 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 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= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= 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.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= 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/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -2,8 +2,11 @@ package handlers
import ( import (
"log" "log"
"slices"
"git.odit.services/lfk/document-server/models" "git.odit.services/lfk/document-server/models"
"git.odit.services/lfk/document-server/services"
"git.odit.services/lfk/document-server/templates"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -22,6 +25,51 @@ func GenerateContract(c *fiber.Ctx) error {
"error": err.Error(), "error": err.Error(),
}) })
} }
log.Println(contract.Runners[0].Group.ParentGroup) if !slices.Contains([]string{"en", "de"}, contract.Locale) {
return c.SendString("Contract generated") return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid locale",
})
}
var duplicates int = 2
var duplicatedRunners []models.Runner
for _, runner := range contract.Runners {
for i := 0; i < duplicates; i++ {
duplicatedRunners = append(duplicatedRunners, runner)
}
}
contract.Runners = duplicatedRunners
generator := services.DefaultTemplater{}
template, err := templates.GetTemplate(contract.Locale, "contract")
if err != nil {
log.Println(err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Template not found",
})
}
result, err := generator.Execute(template, &services.ContractTemplateOptions{
Runners: contract.Runners,
CurrencySymbol: "€",
Disclaimer: "This is a disclaimer",
ReceiptMinimumAmount: 10,
EventName: "Event name",
SponsoringHeader: templates.ImageSponsoringHeaderBase64,
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
converter := services.GotenbergConverter{BaseUrl: "http://localhost:3001"}
pdf, err := converter.ToPdf(result, "a5", true)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
c.Set(fiber.HeaderContentType, "application/pdf")
return c.Send(pdf)
} }

View File

@ -2,6 +2,7 @@ package models
type Contract struct { type Contract struct {
Runners []Runner `json:"runners"` Runners []Runner `json:"runners"`
Locale string `json:"locale" enums:"en,de"`
} }
type Runner struct { type Runner struct {

107
services/converter.go Normal file
View File

@ -0,0 +1,107 @@
package services
import (
"bytes"
"fmt"
"mime/multipart"
"net/http"
"strconv"
"github.com/oxplot/papersizes"
)
type Converter interface {
ToPdf(html string, pageSize string, landscape bool) ([]byte, error)
}
type GotenbergConverter struct {
BaseUrl string
}
func (g *GotenbergConverter) ToPdf(html string, pageSize string, landscape bool) ([]byte, error) {
client := &http.Client{}
defer client.CloseIdleConnections()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("files", "index.html")
if err != nil {
return nil, err
}
_, err = part.Write([]byte(html))
if err != nil {
return nil, err
}
size := papersizes.FromName(pageSize)
if size == nil {
return nil, fmt.Errorf("invalid page size: %s", pageSize)
}
err = writer.WriteField("paperWidth", strconv.Itoa(size.Width)+"mm")
if err != nil {
return nil, err
}
err = writer.WriteField("paperHeight", strconv.Itoa(size.Height)+"mm")
if err != nil {
return nil, err
}
err = writer.WriteField("landscape", strconv.FormatBool(landscape))
if err != nil {
return nil, err
}
err = writer.WriteField("marginTop", "0")
if err != nil {
return nil, err
}
err = writer.WriteField("marginBottom", "0")
if err != nil {
return nil, err
}
err = writer.WriteField("marginLeft", "0")
if err != nil {
return nil, err
}
err = writer.WriteField("marginRight", "0")
if err != nil {
return nil, err
}
err = writer.WriteField("preferCssPageSize", "true")
if err != nil {
return nil, err
}
err = writer.Close()
if err != nil {
return nil, err
}
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())
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data := new(bytes.Buffer)
_, err = data.ReadFrom(resp.Body)
if err != nil {
return nil, err
}
return data.Bytes(), nil
}

35
services/templater.go Normal file
View File

@ -0,0 +1,35 @@
package services
import (
"bytes"
"html/template"
"git.odit.services/lfk/document-server/models"
)
type Templater interface {
Execute(template *template.Template, data interface{}) (string, error)
}
type DefaultTemplater struct {
}
type ContractTemplateOptions struct {
Runners []models.Runner `json:"runners"`
CurrencySymbol string `json:"currency_symbol"`
Disclaimer string `json:"disclaimer"`
ReceiptMinimumAmount int `json:"receipt_minimum_amount"`
EventName string `json:"event_name"`
SponsoringHeader string `json:"base_url"`
}
func (t *DefaultTemplater) Execute(template *template.Template, data interface{}) (string, error) {
resultBuffer := new(bytes.Buffer)
err := template.Execute(resultBuffer, data)
if err != nil {
return "", err
}
return resultBuffer.String(), nil
}

71
templates/card/de.html Normal file
View File

@ -0,0 +1,71 @@
<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;
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">
<div class="sheet">
<div class="columns is-multiline">
{{#each cards}}
<div class="column is-half runnercard">
<p class="title is-5" style="text-align: center; padding-bottom: 0; margin-top: -0.75rem;">{{../eventname}}</p>
<p style="text-align: center; margin-top: -1.5rem; font-size: small;">{{../card_subtitle}}</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="{{--sponsor this.id}}" />
</div>
<div class="column is-half">
<!--BARCODE HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="{{--bc this.code ../codeformat}}" />
</div>
</div>
<p>{{this.runner.lastname}}, {{this.runner.firstname}} {{this.runner.middlename}}</p>
<p>{{this.runner.group.fullName}}</p>
</div>
{{/each}}
</div>
</div>
<div class="sheet">
<div class="columns is-multiline">
{{#each cards_swapped}}
<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 2.25cm 0">
<img style="object-fit: cover; max-height: 2cm;" src="{{--sponsor this.id}}" />
</div>
<img style="object-fit: cover; max-height: 2.5cm; position: relative;" src="{{--bc this.code ../codeformat}}" />
</div>
{{/each}}
</div>
</div>
</body>
</html>

71
templates/card/en.html Normal file
View File

@ -0,0 +1,71 @@
<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;
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">
<div class="sheet">
<div class="columns is-multiline">
{{#each cards}}
<div class="column is-half runnercard">
<p class="title is-5" style="text-align: center; padding-bottom: 0; margin-top: -0.75rem;">{{../eventname}}</p>
<p style="text-align: center; margin-top: -1.5rem; font-size: small;">{{../card_subtitle}}</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="{{--sponsor this.id}}" />
</div>
<div class="column is-half">
<!--BARCODE HERE-->
<img style="vertical-align: revert; margin-top: auto; object-fit: cover; max-height: 2cm;"
src="{{--bc this.code ../codeformat}}" />
</div>
</div>
<p>{{this.runner.lastname}}, {{this.runner.firstname}} {{this.runner.middlename}}</p>
<p>{{this.runner.group.fullName}}</p>
</div>
{{/each}}
</div>
</div>
<div class="sheet">
<div class="columns is-multiline">
{{#each cards_swapped}}
<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 2.25cm 0">
<img style="object-fit: cover; max-height: 2cm;" src="{{--sponsor this.id}}" />
</div>
<img style="object-fit: cover; max-height: 2.5cm; position: relative;" src="{{--bc this.code ../codeformat}}" />
</div>
{{/each}}
</div>
</div>
</body>
</html>

124
templates/contract/de.html Normal file
View File

@ -0,0 +1,124 @@
<html>
<head>
<meta charset="utf8">
<title>Sponsoring contract</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
}
body.A5.landscape .sheet {
width: 210mm;
height: 147mm
}
.column {
margin-bottom: -20;
}
</style>
</head>
<body class="A5 landscape">
{{ range .Runners }}
<div class="sheet">
<img id="header_img" width="100%" src="data:image/png;base64,{{ $.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="{{/* TODO: BARCODE*/}}" />
</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;">{{ .Group.Name }}</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
templates/contract/en.html Normal file
View File

@ -0,0 +1,120 @@
<html>
<head>
<meta charset="utf8">
<title>Sponsoring contract</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
}
body.A5.landscape .sheet {
width: 210mm;
height: 147mm
}
.column {
margin-bottom: -20;
}
</style>
</head>
<body class="A5 landscape">
{{#each runners}}
<div class="sheet">
<img id="header_img" width="100%" src="sponsoringheader.png" />
<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;">{{this.firstname}}
{{this.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;">{{this.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="{{--bc this.id ../codeformat}}" />
</div>
</div>
<div class="columns" style="padding-top: 1rem;">
<div class="column is-6">
<span style="border-bottom: 1px solid; width: 100%; display: block;">{{this.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;">{{this.group.fullName}}</span>
<p style="font-size: x-small; display: block;">Team/class</p>
</div>
</div>
<p style="margin-top: -0.5rem">with the amount of _____{{currency_symbol}} 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 {{sponsoring_receipt_minimum_amount}}{{currency_symbol}})</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>
{{/each}}
</body>
</html>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

42
templates/templates.go Normal file
View File

@ -0,0 +1,42 @@
package templates
import (
_ "embed"
"fmt"
"html/template"
)
//go:embed card/en.html
var CardTemplateEn string
//go:embed card/de.html
var CardTemplateDe string
//go:embed contract/en.html
var ContractTemplateEn string
//go:embed contract/de.html
var ContractTemplateDe string
//go:embed images/sponsoringheader.base64
var ImageSponsoringHeaderBase64 string
func GetTemplate(locale, templateName string) (*template.Template, error) {
switch templateName {
case "card":
switch locale {
case "en":
return template.New("card").Parse(CardTemplateEn)
case "de":
return template.New("card").Parse(CardTemplateDe)
}
case "contract":
switch locale {
case "en":
return template.New("contract").Parse(ContractTemplateEn)
case "de":
return template.New("contract").Parse(ContractTemplateDe)
}
}
return nil, fmt.Errorf("template not found with name %s and locale %s", templateName, locale)
}