diff --git a/.vscode/launch.json b/.vscode/launch.json index cf198a1..8db3887 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,13 +1,26 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Debug golang", - "type": "go", - "request": "attach", - "mode": "remote", - "port": 4001, - "host": "127.0.0.1" - } - ] -} + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "env": { + "TITLE": "DEV", + "PUBLIC_URL": "http://localhost:4000", + "WEATHER_KEY": "3722ce75e9330aaefde1cb3eb1b8b030", + "APP_VERSION": "v0.0.1-DEV", + "OIDC_CLIENT_ID": "home", + "OIDC_CLIENT_SECRET": "PkfS5S7BkiEeqX3Km7BGxsBrmH6MOzjqcpODTz2akxMCMFHv8TAvIfyWgTlKof85", + "OIDC_REDIRECT_URI": "http://localhost:4000/auth/callback", + "OIDC_ISSUER": "https://sso.unjx.de/auth/v1", + "SESSION_KEY": "49cda749cb5eaa6c38f371c530808ca8", + } + } + ] +} \ No newline at end of file diff --git a/components/application.templ b/components/application.templ index 2f72b67..9a0efa6 100644 --- a/components/application.templ +++ b/components/application.templ @@ -1,8 +1,6 @@ package components -import ( - "gitlab.unjx.de/flohoss/godash/services" -) +import "gitlab.unjx.de/flohoss/godash/services" templ Application(application services.Application) { diff --git a/components/system.templ b/components/system.templ index 76e1c6a..2600e1e 100644 --- a/components/system.templ +++ b/components/system.templ @@ -1,9 +1,9 @@ package components import ( - "html/template" - "gitlab.unjx.de/flohoss/godash/services" "fmt" + "gitlab.unjx.de/flohoss/godash/services" + "html/template" ) var BarTemplate = template.Must(template.New("bar").Parse("
")) @@ -20,7 +20,7 @@ templ System(icon string, infoPre string, infoPost string, extraInfo string, per
{ extraInfo }
{ infoPre }{ infoPost }
- @templ.FromGoHTML(BarTemplate, Bar{Id:percentageId, Percentage:percentage}) + @templ.FromGoHTML(BarTemplate, Bar{Id: percentageId, Percentage: percentage})
@@ -42,18 +42,18 @@ templ Uptime(extraInfo string, id string, uptime services.Uptime) {
{ fmt.Sprintf("%d",uptime.Days) } days - @templ.FromGoHTML(countDownTemplate, Countdown{Id:"uptimeHours", Value:uptime.Hours}) + @templ.FromGoHTML(countDownTemplate, Countdown{Id: "uptimeHours", Value: uptime.Hours}) hours - @templ.FromGoHTML(countDownTemplate, Countdown{Id:"uptimeMinutes", Value:uptime.Minutes}) + @templ.FromGoHTML(countDownTemplate, Countdown{Id: "uptimeMinutes", Value: uptime.Minutes}) min - @templ.FromGoHTML(countDownTemplate, Countdown{Id:"uptimeSeconds", Value:uptime.Seconds}) + @templ.FromGoHTML(countDownTemplate, Countdown{Id: "uptimeSeconds", Value: uptime.Seconds}) sec
- @templ.FromGoHTML(BarTemplate, Bar{Id:id, Percentage:float64(uptime.Percentage)}) + @templ.FromGoHTML(BarTemplate, Bar{Id: id, Percentage: float64(uptime.Percentage)})
diff --git a/components/user.templ b/components/user.templ new file mode 100644 index 0000000..26ef43d --- /dev/null +++ b/components/user.templ @@ -0,0 +1,11 @@ +package components + +import "gitlab.unjx.de/flohoss/godash/services" + +templ User(user services.User) { +
+
+ +
+
+} diff --git a/components/weather.templ b/components/weather.templ index d2c350b..8234483 100644 --- a/components/weather.templ +++ b/components/weather.templ @@ -1,8 +1,8 @@ package components import ( - "gitlab.unjx.de/flohoss/godash/services" "fmt" + "gitlab.unjx.de/flohoss/godash/services" ) func getIcon(icon string) string { diff --git a/go.mod b/go.mod index e8d8817..d498b24 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,15 @@ module gitlab.unjx.de/flohoss/godash go 1.22 require ( - github.com/a-h/templ v0.2.747 + github.com/a-h/templ v0.2.778 + github.com/alexedwards/scs/v2 v2.8.0 github.com/caarlos0/env/v10 v10.0.0 - github.com/go-playground/validator/v10 v10.22.0 + github.com/coreos/go-oidc/v3 v3.11.0 + github.com/go-playground/validator/v10 v10.22.1 github.com/r3labs/sse/v2 v2.10.0 - github.com/shirou/gopsutil/v4 v4.24.7 - github.com/zitadel/oidc/v3 v3.28.1 + github.com/shirou/gopsutil/v4 v4.24.8 + github.com/thanhpk/randstr v1.0.6 + golang.org/x/oauth2 v0.23.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -20,20 +23,17 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7 // indirect - github.com/muhlemmer/gu v0.3.1 // indirect + github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/numcpus v0.8.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zitadel/schema v1.3.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index de452f1..771e896 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,15 @@ -github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U= -github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8= -github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg= -github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4= +github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM= +github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= +github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw= +github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA= github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18= +github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= +github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= -github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= @@ -23,11 +23,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.21.0 h1:4fZA11ovvtkdgaeev9RGWPgc1uj3H8W+rNYyH/ySBb0= -github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= -github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -39,17 +36,11 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -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/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7 h1:5RK988zAqB3/AN3opGfRpoQgAVqr6/A5+qRTi67VUZY= -github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= -github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= -github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= +github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= @@ -57,10 +48,8 @@ github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktE github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/shirou/gopsutil/v4 v4.24.5 h1:gGsArG5K6vmsh5hcFOHaPm87UD003CaDMkAOweSQjhM= -github.com/shirou/gopsutil/v4 v4.24.5/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= -github.com/shirou/gopsutil/v4 v4.24.7 h1:V9UGTK4gQ8HvcnPKf6Zt3XHyQq/peaekfxpJ2HSocJk= -github.com/shirou/gopsutil/v4 v4.24.7/go.mod h1:0uW/073rP7FYLOkvxolUQM5rMOLTNmRXnFKafpb71rw= +github.com/shirou/gopsutil/v4 v4.24.8 h1:pVQjIenQkIhqO81mwTaXjTzOMT7d3TZkf43PlVFHENI= +github.com/shirou/gopsutil/v4 v4.24.8/go.mod h1:wE0OrJtj4dG+hYkxqDH3QiBICdKSf04/npcvLLc/oRg= 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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -69,40 +58,31 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o= +github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zitadel/oidc/v3 v3.28.1 h1:PsbFm5CzEMQq9HBXUNJ8yvnWmtVYxpwV5Cinj7TTsHo= -github.com/zitadel/oidc/v3 v3.28.1/go.mod h1:WmDFu3dZ9YNKrIoZkmxjGG8QyUR4PbbhsVVSY+rpojM= -github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= -github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/handlers/app.handlers.go b/handlers/app.handlers.go index 368ece3..5ca2c05 100644 --- a/handlers/app.handlers.go +++ b/handlers/app.handlers.go @@ -38,10 +38,6 @@ type AppHandler struct { } func (bh *AppHandler) appHandler(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.Redirect(w, r, "/", http.StatusFound) - return - } bookmarks := bh.bookmarkService.GetAllBookmarks() staticSystem := bh.systemService.GetStaticInformation() liveSystem := bh.systemService.GetLiveInformation() @@ -49,5 +45,12 @@ func (bh *AppHandler) appHandler(w http.ResponseWriter, r *http.Request) { titlePage := bh.env.Title - home.HomeIndex(titlePage, bh.env.Version, home.Home(titlePage, bookmarks, staticSystem, liveSystem, weather)).Render(r.Context(), w) + user := services.User{ + Name: w.Header().Get("X-User-Name"), + Email: w.Header().Get("X-User-Email"), + } + gravatar := services.NewGravatarFromEmail(user.Email) + user.Gravatar = gravatar.GetURL() + + home.HomeIndex(titlePage, bh.env.Version, home.Home(titlePage, user, bookmarks, staticSystem, liveSystem, weather)).Render(r.Context(), w) } diff --git a/handlers/auth.handlers.go b/handlers/auth.handlers.go new file mode 100644 index 0000000..dbf6096 --- /dev/null +++ b/handlers/auth.handlers.go @@ -0,0 +1,155 @@ +package handlers + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "io" + "log/slog" + "net/http" + "os" + "time" + + "github.com/alexedwards/scs/v2" + "github.com/coreos/go-oidc/v3/oidc" + "github.com/thanhpk/randstr" + "golang.org/x/oauth2" + + "gitlab.unjx.de/flohoss/godash/internal/env" + "gitlab.unjx.de/flohoss/godash/services" +) + +func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value string) { + c := &http.Cookie{ + Name: name, + Value: value, + MaxAge: int(time.Hour.Seconds()), + Secure: r.TLS != nil, + HttpOnly: true, + } + http.SetCookie(w, c) +} + +func generateCodeVerifier() (string, error) { + verifierLength := 64 + verifier := make([]byte, verifierLength) + + _, err := rand.Read(verifier) + if err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(verifier), nil +} + +func generateCodeChallenge(verifier string) string { + hash := sha256.New() + _, _ = io.WriteString(hash, verifier) + sha := hash.Sum(nil) + return base64.RawURLEncoding.EncodeToString(sha) +} + +func NewAuthHandler(env *env.Config) *AuthHandler { + ctx := context.Background() + + oidcProvider, err := oidc.NewProvider(ctx, env.OIDCIssuer) + if err != nil { + slog.Error("Failed to get oidc provider", "err", err.Error()) + os.Exit(1) + } + + oauth2Config := &oauth2.Config{ + ClientID: env.OIDCClientID, + ClientSecret: env.OIDCClientSecret, + Endpoint: oidcProvider.Endpoint(), + RedirectURL: env.OIDCRedirectURI, + Scopes: env.OIDCScopes, + } + + codeVerifier, err := generateCodeVerifier() + if err != nil { + slog.Error("Error generating code verifier", "err", err.Error()) + os.Exit(1) + } + codeChallenge := generateCodeChallenge(codeVerifier) + authCodeOptions := []oauth2.AuthCodeOption{ + oauth2.SetAuthURLParam("redirect_uri", env.OIDCRedirectURI), + oauth2.SetAuthURLParam("code_challenge", codeChallenge), + oauth2.SetAuthURLParam("code_challenge_method", "S256"), + oauth2.SetAuthURLParam("code_verifier", codeVerifier), + } + + sessionManager := scs.New() + sessionManager.Lifetime = 24 * time.Hour + + return &AuthHandler{ + ctx: ctx, + oidcProvider: oidcProvider, + oauth2Config: oauth2Config, + authCodeOptions: authCodeOptions, + SessionManager: sessionManager, + } +} + +type AuthHandler struct { + ctx context.Context + oidcProvider *oidc.Provider + oauth2Config *oauth2.Config + authCodeOptions []oauth2.AuthCodeOption + SessionManager *scs.SessionManager +} + +func (ah *AuthHandler) handleCallback(w http.ResponseWriter, r *http.Request) { + state, err := r.Cookie("state") + if err != nil { + http.Error(w, "state not found", http.StatusBadRequest) + return + } + if r.URL.Query().Get("state") != state.Value { + http.Error(w, "state did not match", http.StatusBadRequest) + return + } + + oauth2Token, err := ah.oauth2Config.Exchange(ah.ctx, r.URL.Query().Get("code"), ah.authCodeOptions...) + if err != nil { + http.Error(w, "failed to exchange token: "+err.Error(), http.StatusInternalServerError) + return + } + + ah.SessionManager.Put(r.Context(), "access_token", oauth2Token.AccessToken) + + http.Redirect(w, r, "/", http.StatusFound) +} + +func (ah *AuthHandler) handleLogout(w http.ResponseWriter, r *http.Request) { + ah.SessionManager.Clear(r.Context()) + http.Redirect(w, r, "/", http.StatusFound) +} + +func (ah *AuthHandler) handleLogin(w http.ResponseWriter, r *http.Request) { + state := randstr.String(16) + setCallbackCookie(w, r, "state", state) + http.Redirect(w, r, ah.oauth2Config.AuthCodeURL(state, ah.authCodeOptions...), http.StatusFound) +} + +func (ah *AuthHandler) AuthMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + accessToken := ah.SessionManager.GetString(r.Context(), "access_token") + if accessToken == "" { + ah.handleLogin(w, r) + return + } + + userInfo, err := ah.oidcProvider.UserInfo(ah.ctx, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})) + if err != nil { + ah.handleLogin(w, r) + return + } + var userClaims services.User + userInfo.Claims(&userClaims) + w.Header().Set("X-User-Name", userClaims.Name) + w.Header().Set("X-User-Email", userClaims.Email) + + next.ServeHTTP(w, r) + }) +} diff --git a/handlers/routes.go b/handlers/routes.go index b84a0a4..1bce187 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -6,14 +6,17 @@ import ( "github.com/r3labs/sse/v2" ) -func SetupRoutes(router *http.ServeMux, sse *sse.Server, appHandler *AppHandler) { - router.HandleFunc("GET /sse", sse.ServeHTTP) +func SetupRoutes(router *http.ServeMux, sse *sse.Server, appHandler *AppHandler, authHandler *AuthHandler) { + router.Handle("GET /sse", authHandler.AuthMiddleware(http.HandlerFunc(sse.ServeHTTP))) fsAssets := http.FileServer(http.Dir("assets")) - router.Handle("GET /assets/", http.StripPrefix("/assets/", fsAssets)) + router.Handle("GET /assets/", authHandler.AuthMiddleware((http.StripPrefix("/assets/", fsAssets)))) icons := http.FileServer(http.Dir("storage/icons")) - router.Handle("GET /icons/", http.StripPrefix("/icons/", icons)) + router.Handle("GET /icons/", authHandler.AuthMiddleware(http.StripPrefix("/icons/", icons))) - router.HandleFunc("GET /", appHandler.appHandler) + router.HandleFunc("GET /auth/logout", http.HandlerFunc(authHandler.handleLogout)) + router.HandleFunc("GET /auth/callback", authHandler.handleCallback) + + router.Handle("GET /", authHandler.AuthMiddleware(http.HandlerFunc(appHandler.appHandler))) } diff --git a/internal/env/env.go b/internal/env/env.go index 9a01257..bf3ea5c 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -8,16 +8,23 @@ import ( ) type Config struct { - TimeZone string `env:"TZ" envDefault:"Etc/UTC" validate:"timezone"` - Title string `env:"TITLE" envDefault:"goDash"` - Port int `env:"PORT" envDefault:"4000" validate:"min=1024,max=49151"` - Version string `env:"APP_VERSION"` - LocationLatitude float32 `env:"LOCATION_LATITUDE" envDefault:"48.780331609463815" validate:"latitude"` - LocationLongitude float32 `env:"LOCATION_LONGITUDE" envDefault:"9.177968320179422" validate:"longitude"` - WeatherKey string `env:"WEATHER_KEY"` - WeatherUnits string `env:"WEATHER_UNITS" envDefault:"metric"` - WeatherLanguage string `env:"WEATHER_LANG" envDefault:"en" validate:"bcp47_language_tag"` - WeatherDigits bool `env:"WEATHER_DIGITS" envDefault:"false"` + TimeZone string `env:"TZ" envDefault:"Etc/UTC" validate:"timezone"` + Title string `env:"TITLE" envDefault:"goDash"` + Port int `env:"PORT" envDefault:"4000" validate:"min=1024,max=49151"` + Version string `env:"APP_VERSION"` + LocationLatitude float32 `env:"LOCATION_LATITUDE" envDefault:"48.780331609463815" validate:"latitude"` + LocationLongitude float32 `env:"LOCATION_LONGITUDE" envDefault:"9.177968320179422" validate:"longitude"` + WeatherKey string `env:"WEATHER_KEY"` + WeatherUnits string `env:"WEATHER_UNITS" envDefault:"metric"` + WeatherLanguage string `env:"WEATHER_LANG" envDefault:"en" validate:"bcp47_language_tag"` + WeatherDigits bool `env:"WEATHER_DIGITS" envDefault:"false"` + OIDCClientID string `env:"OIDC_CLIENT_ID"` + OIDCClientSecret string `env:"OIDC_CLIENT_SECRET"` + OIDCRedirectURI string `env:"OIDC_REDIRECT_URI"` + OIDCScopes []string `env:"OIDC_SCOPES" envSeparator:"," envDefault:"openid,email,profile"` + OIDCIssuer string `env:"OIDC_ISSUER"` + OIDCResponseMode string `env:"OIDC_RESPONSE_MODE"` + SessionKey string `env:"SESSION_KEY,unset"` } var errParse = errors.New("error parsing environment variables") diff --git a/main.go b/main.go index fde344c..f4f45c4 100644 --- a/main.go +++ b/main.go @@ -30,11 +30,12 @@ func main() { b := services.NewBookmarkService() appHandler := handlers.NewAppHandler(env, s, w, b) - handlers.SetupRoutes(router, sse, appHandler) + authHandler := handlers.NewAuthHandler(env) + handlers.SetupRoutes(router, sse, appHandler, authHandler) lis := fmt.Sprintf(":%d", env.Port) slog.Info("server listening, press ctrl+c to stop", "addr", "http://localhost"+lis) - err = http.ListenAndServe(lis, router) + err = http.ListenAndServe(lis, authHandler.SessionManager.LoadAndSave(router)) if !errors.Is(err, http.ErrServerClosed) { slog.Error("server terminated", "error", err) os.Exit(1) diff --git a/services/claims.services.go b/services/claims.services.go new file mode 100644 index 0000000..81dc7d7 --- /dev/null +++ b/services/claims.services.go @@ -0,0 +1,71 @@ +package services + +import ( + "crypto/sha256" + "encoding/hex" + "net/url" + "strconv" + "strings" +) + +type User struct { + Name string `json:"name"` + Email string `json:"email"` + Gravatar string `json:"gravatar"` +} + +const ( + defaultScheme = "https" + defaultHostname = "www.gravatar.com" +) + +func NewGravatarFromEmail(email string) Gravatar { + hasher := sha256.Sum256([]byte(strings.TrimSpace(email))) + hash := hex.EncodeToString(hasher[:]) + + g := NewGravatar() + g.Hash = hash + return g +} + +func NewGravatar() Gravatar { + return Gravatar{ + Scheme: defaultScheme, + Host: defaultHostname, + } +} + +type Gravatar struct { + Scheme string + Host string + Hash string + Default string + Rating string + Size int +} + +func (g Gravatar) GetURL() string { + path := "/avatar/" + g.Hash + + v := url.Values{} + if g.Size > 0 { + v.Add("s", strconv.Itoa(g.Size)) + } + + if g.Rating != "" { + v.Add("r", g.Rating) + } + + if g.Default != "" { + v.Add("d", g.Default) + } + + url := url.URL{ + Scheme: g.Scheme, + Host: g.Host, + Path: path, + RawQuery: v.Encode(), + } + + return url.String() +} diff --git a/views/home/home.templ b/views/home/home.templ index 8b4cb19..e5dc163 100644 --- a/views/home/home.templ +++ b/views/home/home.templ @@ -2,22 +2,22 @@ package home import ( "fmt" - + "gitlab.unjx.de/flohoss/godash/components" "gitlab.unjx.de/flohoss/godash/services" "gitlab.unjx.de/flohoss/godash/views/layout" - "gitlab.unjx.de/flohoss/godash/components" ) -templ Home(title string, bookmarks *services.Bookmarks, static *services.StaticInformation, live *services.LiveInformation, weather *services.OpenWeather) { +templ Home(title string, user services.User, bookmarks *services.Bookmarks, static *services.StaticInformation, live *services.LiveInformation, weather *services.OpenWeather) {
@components.Weather(weather) + @components.User(user)
- @components.System("icon-[bi--cpu]",static.CPU.Name,"",static.CPU.Threads,"systemCpuPercentage","",live.CPU) - @components.System("icon-[bi--nvme]",live.Disk.Value,fmt.Sprintf(" | %s", static.Disk.Total),static.Disk.Partitions,"systemDiskPercentage","systemDiskValue",live.Disk.Percentage) - @components.System("icon-[bi--memory]",live.Ram.Value,fmt.Sprintf(" | %s", static.Ram.Total),static.Ram.Swap,"systemRamPercentage","systemRamValue",live.Ram.Percentage) - @components.Uptime(static.Host.Architecture,"systemUptimePercentage",live.Uptime) + @components.System("icon-[bi--cpu]", static.CPU.Name, "", static.CPU.Threads, "systemCpuPercentage", "", live.CPU) + @components.System("icon-[bi--nvme]", live.Disk.Value, fmt.Sprintf(" | %s", static.Disk.Total), static.Disk.Partitions, "systemDiskPercentage", "systemDiskValue", live.Disk.Percentage) + @components.System("icon-[bi--memory]", live.Ram.Value, fmt.Sprintf(" | %s", static.Ram.Total), static.Ram.Swap, "systemRamPercentage", "systemRamValue", live.Ram.Percentage) + @components.Uptime(static.Host.Architecture, "systemUptimePercentage", live.Uptime)
for _, a := range bookmarks.Applications {