changes + add uploader
Some checks failed
build / build (push) Successful in 41s
release-tag / release-image (push) Has been cancelled

This commit is contained in:
Tijl 2024-08-29 12:21:38 +02:00
parent 52b91be870
commit 7e1c36c966
Signed by: tijl
GPG Key ID: DAE24BFCD722F053
30 changed files with 528 additions and 90 deletions

1
.gitignore vendored
View File

@ -8,4 +8,5 @@ config.yaml
config.dev.yaml config.dev.yaml
blog/ blog/
.data/ .data/
data/

11
go.mod
View File

@ -4,6 +4,7 @@ go 1.22.5
require ( require (
github.com/coreos/go-oidc/v3 v3.11.0 github.com/coreos/go-oidc/v3 v3.11.0
github.com/enescakir/emoji v1.0.0
github.com/gofiber/contrib/fiberzerolog v1.0.2 github.com/gofiber/contrib/fiberzerolog v1.0.2
github.com/gofiber/fiber/v2 v2.52.5 github.com/gofiber/fiber/v2 v2.52.5
github.com/gofiber/template/html/v2 v2.1.2 github.com/gofiber/template/html/v2 v2.1.2
@ -20,11 +21,10 @@ require (
require ( require (
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/enescakir/emoji v1.0.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect github.com/gofiber/utils v1.1.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect
@ -33,21 +33,14 @@ require (
github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/compress v1.17.9 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
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.15 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.55.0 // indirect github.com/valyala/fasthttp v1.55.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/atomic v1.7.0 // indirect go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.25.0 // indirect golang.org/x/crypto v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect

26
go.sum
View File

@ -25,8 +25,6 @@ github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0= github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/contrib/fiberzerolog v1.0.2 h1:LMa/luarQVeINoRwZLHtLQYepLPDIwUNB5OmdZKk+s8= github.com/gofiber/contrib/fiberzerolog v1.0.2 h1:LMa/luarQVeINoRwZLHtLQYepLPDIwUNB5OmdZKk+s8=
github.com/gofiber/contrib/fiberzerolog v1.0.2/go.mod h1:aTPsgArSgxRWcUeJ/K6PiICz3mbQENR1QOR426QwOoQ= github.com/gofiber/contrib/fiberzerolog v1.0.2/go.mod h1:aTPsgArSgxRWcUeJ/K6PiICz3mbQENR1QOR426QwOoQ=
@ -42,9 +40,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -70,8 +67,6 @@ 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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 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-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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@ -92,8 +87,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
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.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@ -102,19 +95,11 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 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/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
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.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
@ -125,8 +110,6 @@ github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
@ -139,12 +122,8 @@ golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@ -152,7 +131,6 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -12,7 +12,6 @@ import (
"git.tijl.dev/tijl/tijl.dev-core/internal/utils" "git.tijl.dev/tijl/tijl.dev-core/internal/utils"
"git.tijl.dev/tijl/tijl.dev-core/modules/db" "git.tijl.dev/tijl/tijl.dev-core/modules/db"
"git.tijl.dev/tijl/tijl.dev-core/modules/i18n" "git.tijl.dev/tijl/tijl.dev-core/modules/i18n"
log "git.tijl.dev/tijl/tijl.dev-core/modules/logger"
"git.tijl.dev/tijl/tijl.dev-core/modules/web" "git.tijl.dev/tijl/tijl.dev-core/modules/web"
"github.com/enescakir/emoji" "github.com/enescakir/emoji"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -148,8 +147,8 @@ func questionHandler(c *fiber.Ctx, newGame NullableUUID, prevError NullableStrin
data["Answers"] = shuffledAnswers data["Answers"] = shuffledAnswers
data["Flag"] = flag data["Flag"] = flag
data["Errors"] = gameSession.QuestionsErrors data["Errors"] = gameSession.QuestionsErrors
log.Debug().Msg(prevError.String)
data["PreviousError"] = prevError.String data["PreviousError"] = prevError.String
data["ShortcutKeys"] = []string{"d", "f", "h", "j", "k"}
return c.Render("apps/flags/question", data, "layouts/base") return c.Render("apps/flags/question", data, "layouts/base")
} }
@ -202,8 +201,14 @@ func startNewGameHandler(c *fiber.Ctx) error {
} }
func createSharedGameData(c *fiber.Ctx, tags []string, maxQuestions int, seconds int) error { func createSharedGameData(c *fiber.Ctx, tags []string, maxQuestions int, seconds int) error {
uid, err := user.GetSession(c)
if err != nil {
return c.SendStatus(http.StatusUnauthorized)
}
shareKey := utils.RandString(4) shareKey := utils.RandString(4)
_, err := db.Queries.AppFlagsNewSharedData(context.TODO(), queries.AppFlagsNewSharedDataParams{ _, err = db.Queries.AppFlagsNewSharedData(context.TODO(), queries.AppFlagsNewSharedDataParams{
Uid: uid,
ShareKey: shareKey, ShareKey: shareKey,
Tags: tags, Tags: tags,
Questions: int32(maxQuestions), Questions: int32(maxQuestions),

View File

@ -0,0 +1,68 @@
package uploader
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha512"
"encoding/hex"
"fmt"
"io"
)
func generateEncryptionKey() ([]byte, error) {
key := make([]byte, 32) // AES-256 32-byte key
if _, err := rand.Read(key); err != nil {
return nil, err
}
return key, nil
}
func encryptData(data []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, data, nil), nil
}
func decryptData(encryptedData []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(encryptedData) < nonceSize {
return nil, fmt.Errorf("encrypted data is too short")
}
nonce, ciphertext := encryptedData[:nonceSize], encryptedData[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
func hashKey(key []byte) string {
hash := sha512.Sum512(key)
return hex.EncodeToString(hash[:])
}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,196 @@
package uploader
import (
"context"
"database/sql"
"embed"
"encoding/hex"
"encoding/json"
"net/http"
"os"
"path/filepath"
"strconv"
"time"
"git.tijl.dev/tijl/tijl.dev-core/internal/config"
"git.tijl.dev/tijl/tijl.dev-core/internal/queries"
"git.tijl.dev/tijl/tijl.dev-core/internal/user"
"git.tijl.dev/tijl/tijl.dev-core/modules/db"
"git.tijl.dev/tijl/tijl.dev-core/modules/i18n"
"git.tijl.dev/tijl/tijl.dev-core/modules/web"
"github.com/gofiber/fiber/v2"
)
//go:embed locales/*
var Embed embed.FS
func Setup() {
i18n.RegisterTranslations(Embed, "locales")
var uploadDir string = filepath.Join(config.Config.DataLocation, "./uploader")
web.RegisterAppSetupFunc(func(a *fiber.App) {
a.Get("/app/uploader", func(c *fiber.Ctx) error {
data := *web.Common(c)
data["Title"] = "tmp"
return c.Render("apps/uploader/index", data, "layouts/base")
})
a.Get("/app/uploader/:key", func(c *fiber.Ctx) error {
encryptionKey, err := hex.DecodeString(c.Params("key"))
if err != nil {
return err
}
storageKey := hashKey(encryptionKey)
row, err := db.Queries.AppUploaderGet(context.TODO(), storageKey)
if err != nil {
return c.Next()
}
if row.Expire.UnixMilli() < time.Now().UnixMilli() {
return c.Next()
}
accessCount, err := db.Queries.AppUploaderAccessCount(context.TODO(), row.ID)
if err != nil {
return err
}
if accessCount >= int64(row.MaxVisits) {
return c.Next()
}
createLog := queries.AppUploaderAccessCreateParams{
FileID: row.ID,
IpAddress: c.IP(),
Agent: string(c.Context().UserAgent()),
}
user, err := user.GetSession(c)
if err == nil {
createLog.Uid = sql.NullString{
Valid: true,
String: user,
}
}
db.Queries.AppUploaderAccessCreate(context.TODO(), createLog)
encryptedContent, err := os.ReadFile(filepath.Join(uploadDir, row.ID.String()))
if err != nil {
return err
}
encryptedMetadata, err := os.ReadFile(filepath.Join(uploadDir, row.ID.String()+".meta"))
if err != nil {
return err
}
decryptedContent, err := decryptData(encryptedContent, encryptionKey)
if err != nil {
return err
}
decryptedMetadata, err := decryptData(encryptedMetadata, encryptionKey)
if err != nil {
return err
}
var metadata map[string]interface{}
if err := json.Unmarshal(decryptedMetadata, &metadata); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to parse metadata")
}
c.Set("Content-Disposition", "attachment; filename="+metadata["filename"].(string))
c.Set("Content-Type", metadata["content_type"].(string))
return c.Send(decryptedContent)
})
a.Post("/app/uploader", func(c *fiber.Ctx) error {
uid, err := user.GetSession(c)
if err != nil {
return c.SendStatus(http.StatusUnauthorized)
}
expireDays, err := strconv.Atoi(c.FormValue("expire_days"))
if err != nil {return err}
maxDownloads, err := strconv.Atoi(c.FormValue("max_downloads"))
if err != nil {return err}
file, err := c.FormFile("file")
if err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Failed to read file")
}
src, err := file.Open()
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to open file")
}
defer src.Close()
// Read file content into a byte slice
fileContent := make([]byte, file.Size)
if _, err := src.Read(fileContent); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to read file content")
}
// Generate encryption key
encryptionKey, err := generateEncryptionKey()
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to generate encryption key")
}
// Encrypt the file content
encryptedContent, err := encryptData(fileContent, encryptionKey)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to encrypt file content")
}
storageKey := hashKey(encryptionKey)
id, err := db.Queries.AppUploaderCreate(context.TODO(), queries.AppUploaderCreateParams{
Uid: uid,
FileCrypto: storageKey,
Expire: time.Now().AddDate(0, 0, expireDays),
MaxVisits: int32(maxDownloads),
})
if err != nil {
return err
}
// Save encrypted file content
if err := os.WriteFile(filepath.Join(uploadDir, id.String()), encryptedContent, 0644); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to save encrypted file")
}
metadata := map[string]interface{}{
"filename": file.Filename,
"content_type": file.Header.Get("Content-Type"),
}
metadataBytes, err := json.Marshal(metadata)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to marshal metadata")
}
encryptedMetadata, err := encryptData(metadataBytes, encryptionKey)
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to encrypt metadata")
}
if err := os.WriteFile(filepath.Join(uploadDir, id.String()+".meta"), encryptedMetadata, 0644); err != nil {
return c.Status(fiber.StatusInternalServerError).SendString("Failed to save encrypted metadata")
}
data := *web.Common(c)
data["Title"] = "tmp"
data["Key"] = hex.EncodeToString(encryptionKey)
return c.Render("apps/uploader/uploaded", data, "layouts/base")
})
}, 1000)
}

View File

@ -31,7 +31,7 @@ func Load() {
type ConfigType struct { type ConfigType struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
BlogLocation string `yaml:"blog_location"` DataLocation string `yaml:"data_location"`
UrlBase string `yaml:"url_base"` UrlBase string `yaml:"url_base"`
JsonLogging bool `yaml:"log_json"` JsonLogging bool `yaml:"log_json"`
DB string `yaml:"database"` DB string `yaml:"database"`

View File

@ -10,6 +10,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"git.tijl.dev/tijl/tijl.dev-core/internal/config"
"git.tijl.dev/tijl/tijl.dev-core/modules/i18n" "git.tijl.dev/tijl/tijl.dev-core/modules/i18n"
log "git.tijl.dev/tijl/tijl.dev-core/modules/logger" log "git.tijl.dev/tijl/tijl.dev-core/modules/logger"
"git.tijl.dev/tijl/tijl.dev-core/modules/web" "git.tijl.dev/tijl/tijl.dev-core/modules/web"
@ -48,7 +49,7 @@ func LoadPosts() {
), ),
) )
files, err := filepath.Glob("blog/*.md") files, err := filepath.Glob(filepath.Join(config.Config.DataLocation, "blog/*.md"))
if err != nil { if err != nil {
log.Error().Err(err).Msg("error reading folder") log.Error().Err(err).Msg("error reading folder")
} }

View File

@ -32,7 +32,7 @@ VALUES ($1, $2, $3)
DO UPDATE SET errors = app_flags_games_answers.errors + EXCLUDED.errors; DO UPDATE SET errors = app_flags_games_answers.errors + EXCLUDED.errors;
-- name: AppFlagsNewSharedData :one -- name: AppFlagsNewSharedData :one
INSERT INTO app_flags_games_shared_data (share_key, game_seed, tags, questions, seconds) VALUES ($1, $2, $3, $4, $5) RETURNING game_seed; INSERT INTO app_flags_games_shared_data (uid, share_key, game_seed, tags, questions, seconds) VALUES ($1, $2, $3, $4, $5, $6) RETURNING game_seed;
-- name: AppFlagsGetSharedData :one -- name: AppFlagsGetSharedData :one
SELECT * FROM app_flags_games_shared_data WHERE share_key = $1 LIMIT 1; SELECT * FROM app_flags_games_shared_data WHERE share_key = $1 LIMIT 1;

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.25.0
// source: app_flags.sql // source: app_flags.sql
package queries package queries
@ -93,7 +93,7 @@ func (q *Queries) AppFlagsGetGame(ctx context.Context, gameID uuid.UUID) (AppFla
} }
const appFlagsGetSharedData = `-- name: AppFlagsGetSharedData :one const appFlagsGetSharedData = `-- name: AppFlagsGetSharedData :one
SELECT id, share_key, game_seed, questions, tags, seconds, created_at FROM app_flags_games_shared_data WHERE share_key = $1 LIMIT 1 SELECT id, uid, share_key, game_seed, questions, tags, seconds, created_at FROM app_flags_games_shared_data WHERE share_key = $1 LIMIT 1
` `
func (q *Queries) AppFlagsGetSharedData(ctx context.Context, shareKey string) (AppFlagsGamesSharedDatum, error) { func (q *Queries) AppFlagsGetSharedData(ctx context.Context, shareKey string) (AppFlagsGamesSharedDatum, error) {
@ -101,6 +101,7 @@ func (q *Queries) AppFlagsGetSharedData(ctx context.Context, shareKey string) (A
var i AppFlagsGamesSharedDatum var i AppFlagsGamesSharedDatum
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Uid,
&i.ShareKey, &i.ShareKey,
&i.GameSeed, &i.GameSeed,
&i.Questions, &i.Questions,
@ -112,10 +113,11 @@ func (q *Queries) AppFlagsGetSharedData(ctx context.Context, shareKey string) (A
} }
const appFlagsNewSharedData = `-- name: AppFlagsNewSharedData :one const appFlagsNewSharedData = `-- name: AppFlagsNewSharedData :one
INSERT INTO app_flags_games_shared_data (share_key, game_seed, tags, questions, seconds) VALUES ($1, $2, $3, $4, $5) RETURNING game_seed INSERT INTO app_flags_games_shared_data (uid, share_key, game_seed, tags, questions, seconds) VALUES ($1, $2, $3, $4, $5, $6) RETURNING game_seed
` `
type AppFlagsNewSharedDataParams struct { type AppFlagsNewSharedDataParams struct {
Uid string
ShareKey string ShareKey string
GameSeed uuid.UUID GameSeed uuid.UUID
Tags []string Tags []string
@ -125,6 +127,7 @@ type AppFlagsNewSharedDataParams struct {
func (q *Queries) AppFlagsNewSharedData(ctx context.Context, arg AppFlagsNewSharedDataParams) (uuid.UUID, error) { func (q *Queries) AppFlagsNewSharedData(ctx context.Context, arg AppFlagsNewSharedDataParams) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, appFlagsNewSharedData, row := q.db.QueryRowContext(ctx, appFlagsNewSharedData,
arg.Uid,
arg.ShareKey, arg.ShareKey,
arg.GameSeed, arg.GameSeed,
pq.Array(arg.Tags), pq.Array(arg.Tags),

View File

@ -0,0 +1,12 @@
-- name: AppUploaderCreate :one
INSERT INTO app_uploader_files (uid, file_crypto, expire, max_visits) VALUES ($1, $2, $3, $4) RETURNING id;
-- name: AppUploaderGet :one
SELECT * FROM app_uploader_files WHERE file_crypto = $1 LIMIT 1;
-- name: AppUploaderAccessCreate :exec
INSERT INTO app_uploader_files_access (file_id, uid, ip_address, agent) VALUES ($1, $2, $3, $4);
-- name: AppUploaderAccessCount :one
SELECT COUNT(*) AS file_accessors FROM app_uploader_files_access WHERE file_id = $1;

View File

@ -0,0 +1,87 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// source: app_uploader.sql
package queries
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const appUploaderAccessCount = `-- name: AppUploaderAccessCount :one
SELECT COUNT(*) AS file_accessors FROM app_uploader_files_access WHERE file_id = $1
`
func (q *Queries) AppUploaderAccessCount(ctx context.Context, fileID uuid.UUID) (int64, error) {
row := q.db.QueryRowContext(ctx, appUploaderAccessCount, fileID)
var file_accessors int64
err := row.Scan(&file_accessors)
return file_accessors, err
}
const appUploaderAccessCreate = `-- name: AppUploaderAccessCreate :exec
INSERT INTO app_uploader_files_access (file_id, uid, ip_address, agent) VALUES ($1, $2, $3, $4)
`
type AppUploaderAccessCreateParams struct {
FileID uuid.UUID
Uid sql.NullString
IpAddress string
Agent string
}
func (q *Queries) AppUploaderAccessCreate(ctx context.Context, arg AppUploaderAccessCreateParams) error {
_, err := q.db.ExecContext(ctx, appUploaderAccessCreate,
arg.FileID,
arg.Uid,
arg.IpAddress,
arg.Agent,
)
return err
}
const appUploaderCreate = `-- name: AppUploaderCreate :one
INSERT INTO app_uploader_files (uid, file_crypto, expire, max_visits) VALUES ($1, $2, $3, $4) RETURNING id
`
type AppUploaderCreateParams struct {
Uid string
FileCrypto string
Expire time.Time
MaxVisits int32
}
func (q *Queries) AppUploaderCreate(ctx context.Context, arg AppUploaderCreateParams) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, appUploaderCreate,
arg.Uid,
arg.FileCrypto,
arg.Expire,
arg.MaxVisits,
)
var id uuid.UUID
err := row.Scan(&id)
return id, err
}
const appUploaderGet = `-- name: AppUploaderGet :one
SELECT id, uid, file_crypto, created_at, expire, max_visits FROM app_uploader_files WHERE file_crypto = $1 LIMIT 1
`
func (q *Queries) AppUploaderGet(ctx context.Context, fileCrypto string) (AppUploaderFile, error) {
row := q.db.QueryRowContext(ctx, appUploaderGet, fileCrypto)
var i AppUploaderFile
err := row.Scan(
&i.ID,
&i.Uid,
&i.FileCrypto,
&i.CreatedAt,
&i.Expire,
&i.MaxVisits,
)
return i, err
}

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.25.0
package queries package queries

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.25.0
package queries package queries
@ -33,6 +33,7 @@ type AppFlagsGamesAnswer struct {
type AppFlagsGamesSharedDatum struct { type AppFlagsGamesSharedDatum struct {
ID int32 ID int32
Uid string
ShareKey string ShareKey string
GameSeed uuid.UUID GameSeed uuid.UUID
Questions int32 Questions int32
@ -41,6 +42,23 @@ type AppFlagsGamesSharedDatum struct {
CreatedAt time.Time CreatedAt time.Time
} }
type AppUploaderFile struct {
ID uuid.UUID
Uid string
FileCrypto string
CreatedAt time.Time
Expire time.Time
MaxVisits int32
}
type AppUploaderFilesAccess struct {
FileID uuid.UUID
Uid sql.NullString
IpAddress string
Agent string
AccessTime time.Time
}
type Session struct { type Session struct {
ID int32 ID int32
Uid string Uid string

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.25.0
// source: sessions.sql // source: sessions.sql
package queries package queries

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.25.0
// source: users.sql // source: users.sql
package queries package queries

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.27.0 // sqlc v1.25.0
// source: util.sql // source: util.sql
package queries package queries

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"git.tijl.dev/tijl/tijl.dev-core/internal/apps/flags" "git.tijl.dev/tijl/tijl.dev-core/internal/apps/flags"
"git.tijl.dev/tijl/tijl.dev-core/internal/apps/uploader"
"git.tijl.dev/tijl/tijl.dev-core/internal/assets" "git.tijl.dev/tijl/tijl.dev-core/internal/assets"
"git.tijl.dev/tijl/tijl.dev-core/internal/config" "git.tijl.dev/tijl/tijl.dev-core/internal/config"
"git.tijl.dev/tijl/tijl.dev-core/internal/handlers" "git.tijl.dev/tijl/tijl.dev-core/internal/handlers"
@ -43,6 +44,7 @@ func Listen() {
// setup apps // setup apps
flags.Setup() flags.Setup()
uploader.Setup()
// setup web // setup web
webinternal.Load() webinternal.Load()

View File

@ -1,6 +1,6 @@
CREATE TABLE users ( CREATE TABLE users (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
uid VARCHAR UNIQUE NOT NULL, -- username as unique identifier uid VARCHAR NOT NULL UNIQUE, -- username as unique identifier
email VARCHAR NOT NULL, email VARCHAR NOT NULL,
email_verified BOOLEAN NOT NULL, email_verified BOOLEAN NOT NULL,
full_name VARCHAR NOT NULL, full_name VARCHAR NOT NULL,

View File

@ -0,0 +1,3 @@
DROP TABLE IF EXISTS app_flags_games;
DROP TABLE IF EXISTS app_flags_games_answers;
DROP TABLE IF EXISTS app_flags_games_shared_data;

View File

@ -13,13 +13,15 @@ CREATE TABLE app_flags_games (
); );
CREATE TABLE app_flags_games_shared_data ( CREATE TABLE app_flags_games_shared_data (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY UNIQUE,
uid VARCHAR NOT NULL,
share_key VARCHAR NOT NULL, share_key VARCHAR NOT NULL,
game_seed UUID NOT NULL DEFAULT gen_random_uuid(), game_seed UUID NOT NULL DEFAULT gen_random_uuid(),
questions INT DEFAULT 0 NOT NULL, questions INT DEFAULT 0 NOT NULL,
tags VARCHAR[] NOT NULL, tags VARCHAR[] NOT NULL,
seconds INT NOT NULL, seconds INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (uid) REFERENCES users (uid)
); );
CREATE TABLE app_flags_games_answers ( CREATE TABLE app_flags_games_answers (

View File

@ -0,0 +1,22 @@
CREATE TABLE app_uploader_files (
id UUID PRIMARY KEY UNIQUE NOT NULL DEFAULT gen_random_uuid(),
uid VARCHAR NOT NULL,
file_crypto VARCHAR NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
-- settings
expire TIMESTAMP NOT NULL,
max_visits INT NOT NULL,
FOREIGN KEY (uid) REFERENCES users (uid)
);
CREATE TABLE app_uploader_files_access (
file_id UUID NOT NULL,
-- info about user who accessed
uid VARCHAR DEFAULT NULL, -- link to logged in user
ip_address VARCHAR NOT NULL,
agent VARCHAR NOT NULL,
access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (file_id) REFERENCES app_uploader_files (id),
FOREIGN KEY (uid) REFERENCES users (uid)
);

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{ {
"name": "tijldev-next", "name": "tijldev-core",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "tijldev-next", "name": "tijldev-core",
"dependencies": { "dependencies": {
"htmx.org": "^1.9.12" "htmx.org": "^1.9.12"
}, },

View File

@ -18,15 +18,17 @@
<span style="font-size: 105px;">{{.Flag}}</span> <span style="font-size: 105px;">{{.Flag}}</span>
</div> </div>
<div hx-boost="true" class="flex justify-center flex-wrap"> <div hx-boost="true" id="answers" class="flex justify-center flex-wrap">
{{range .Answers}} {{range $index, $answer := .Answers}}
<form method="post" class="m-1"> <form method="post" class="m-1">
<input class="hidden" name="type" value="answer" /> <input class="hidden" name="type" value="answer" />
<input class="hidden" name="answer" value="{{.}}" /> <input class="hidden" name="answer" value="{{.}}" />
{{if eq . $.PreviousError}} {{if eq . $.PreviousError}}
<button disabled type="submit" class="btn btn-lg btn-error rounded-2xl">{{index $.T .}}</button> <button disabled type="submit" class="btn btn-lg btn-error rounded-2xl">{{index $.T $answer}}<kbd
class="hidden lg:block kbd kbd-sm">{{index $.ShortcutKeys $index}}</kbd></button>
{{else}} {{else}}
<button type="submit" class="btn btn-lg rounded-2xl">{{index $.T .}}</button> <button type="submit" class="btn btn-lg rounded-2xl">{{index $.T $answer}} <kbd
class="hidden lg:block kbd kbd-sm">{{index $.ShortcutKeys $index}}</kbd></button>
{{end}} {{end}}
</form> </form>
{{end}} {{end}}
@ -55,3 +57,32 @@
startCountdown(); startCountdown();
</script> </script>
{{end}} {{end}}
<script>
document.addEventListener('DOMContentLoaded', function () {
const keyMap = {
'd': 0,
'f': 1,
'h': 2,
'j': 3,
'k': 4
};
function triggerButton(key) {
const index = keyMap[key];
if (index !== undefined) {
const buttons = document.querySelectorAll('#answers button');
if (buttons[index]) {
buttons[index].click();
}
}
}
document.addEventListener('keydown', function (e) {
const key = e.key.toLowerCase();
if (keyMap.hasOwnProperty(key)) {
triggerButton(key);
}
});
});
</script>

View File

@ -1 +1,7 @@
<div>{{.ShareKey}}</div> <div>
<div class="text-center text-2xl my-4">{{.ShareKey}}</div>
<form hx-boost="true" method="get" class="justify-center flex mt-4">
<button type="submit" class="btn text-center">OK</button>
</form>
</div>

View File

@ -1,45 +1,40 @@
<div hx-boost="true"> <div hx-boost="true"> <div id="error-message" class="text-red-500 mb-4"></div>
<div id="error-message" class="text-red-500 mb-4"></div>
<form method="post" hx-on="htmx:responseError: <form method="post" hx-on="htmx:responseError:
document.getElementById('error-message').innerHTML = '{{.T.error_long}}: ' + event.detail.xhr.responseText; document.getElementById('error-message').innerHTML = '{{.T.error_long}}:
htmx.trigger(this, 'htmx:swap', { target: '#error-message', swap: 'innerHTML' });"> ' + event.detail.xhr.responseText; htmx.trigger(this, 'htmx:swap', {
target: '#error-message', swap: 'innerHTML' });">
<input type="text" class="hidden" name="type" value="start" /> <input type="text" class="hidden" name="type" value="start" />
<div> <div> {{range .SupportedTags}} <label class="cursor-pointer label">
{{range .SupportedTags}} <span class="label-text">{{index $.T .}}</span> <input
<label class="cursor-pointer label"> name="tags" value="{{.}}" type="checkbox" class="checkbox
<span class="label-text">{{index $.T .}}</span> checkbox-primary" />
<input name="tags" value="{{.}}" type="checkbox" class="checkbox checkbox-primary" /> </label> {{end}}
</div>
{{if .SignedIn}}
<label class="cursor-pointer label mt-5"> <span
class="label-text">Share</span> <input name="share" type="checkbox"
class="checkbox checkbox-primary" />
</label> </label>
{{end}} {{end}}
<div> <label>
<span>max questions</span> <input value="0" placeholder="max
questions" name="max_questions" type="number"
class="input input-bordered" /> </label>
</div> </div>
<label class="cursor-pointer label mt-5"> <div> <label>
<span class="label-text">Share</span> <span>time limit</span> <input value="0" placeholder="seconds
<input name="share" type="checkbox" class="checkbox checkbox-primary" /> time limit" name="seconds" type="number"
</label> class="input input-bordered" /> </label>
<div>
<label>
<span>max questions</span>
<input value="0" placeholder="max questions" name="max_questions" type="number"
class="input input-bordered" />
</label>
</div> </div>
<div> <button type="submit" class="btn btn-primary">Submit</button> </form>
<label> <form class="mt-4" method="post"> <input type="text" class="hidden"
<span>time limit</span> name="type" value="shared" /> <input type="text" class="input
<input value="0" placeholder="seconds time limit" name="seconds" type="number" input-bordered" name="sharekey" placeholder="sharekey" /> <button
class="input input-bordered" /> type="submit" class="btn btn-primary">Shared</button>
</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<form class="mt-4" method="post">
<input type="text" class="hidden" name="type" value="shared" />
<input type="text" class="input input-bordered" name="sharekey" placeholder="sharekey" />
<button type="submit" class="btn btn-primary">Shared</button>
</form> </form>
</div> </div>

View File

@ -0,0 +1,12 @@
<form enctype="multipart/form-data" method="post" hx-boost="true">
<label>
<span>Max downloaders</span>
<input name="max_downloads" required type="number" value="1" placeholder="Max downloaders" class="input input-bordered" />
</label>
<label>
<span>Expire in (days)</span>
<input name="expire_days" required type="number" value="1" placeholder="Expire in days from now" class="input input-bordered" />
</label>
<input required type="file" name="file" class="file-input file-input-bordered w-full max-w-xs" />
<input class="btn" type="submit" value="Upload">
</form>

View File

@ -0,0 +1 @@
<div>{{.Key}}</div>