detached 5bc84df

Implement notify new version in WebUI, also configure CORS for the proxy endpoint /version

commit
5bc84dfdaf1d3ad36485d31f02ca1734b3602e40
author
bnema · · 2023-11-05 09:30 UTC
parent
c0fb6fe
signature
unsigned
8 files · +77 -57
M go.mod +0 -3
@@ -18,7 +18,6 @@ require (
1818 )
1919
2020 require (
21- github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
2221 github.com/dustin/go-humanize v1.0.1 // indirect
2322 github.com/inconshreveable/mousetrap v1.1.0 // indirect
2423 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
@@ -28,7 +27,6 @@ require (
2827 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
2928 github.com/rivo/uniseg v0.4.4 // indirect
3029 github.com/spf13/pflag v1.0.5 // indirect
31- github.com/stretchr/testify v1.8.4 // indirect
3230 lukechampine.com/uint128 v1.3.0 // indirect
3331 modernc.org/cc/v3 v3.41.0 // indirect
3432 modernc.org/ccgo/v3 v3.16.15 // indirect
@@ -52,7 +50,6 @@ require (
5250 github.com/gorilla/context v1.1.1 // indirect
5351 github.com/gorilla/css v1.0.0 // indirect
5452 github.com/gorilla/securecookie v1.1.1 // indirect
55- github.com/labstack/echo v3.3.10+incompatible
5653 github.com/labstack/gommon v0.4.0 // indirect
5754 github.com/mattn/go-colorable v0.1.13 // indirect
5855 github.com/mattn/go-isatty v0.0.20 // indirect
@@ -18,7 +18,6 @@ require (
18 )18 )
19 19
20 require (20 require (
21- github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
22 github.com/dustin/go-humanize v1.0.1 // indirect21 github.com/dustin/go-humanize v1.0.1 // indirect
23 github.com/inconshreveable/mousetrap v1.1.0 // indirect22 github.com/inconshreveable/mousetrap v1.1.0 // indirect
24 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect23 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
@@ -28,7 +27,6 @@ require (
28 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect27 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
29 github.com/rivo/uniseg v0.4.4 // indirect28 github.com/rivo/uniseg v0.4.4 // indirect
30 github.com/spf13/pflag v1.0.5 // indirect29 github.com/spf13/pflag v1.0.5 // indirect
31- github.com/stretchr/testify v1.8.4 // indirect
32 lukechampine.com/uint128 v1.3.0 // indirect30 lukechampine.com/uint128 v1.3.0 // indirect
33 modernc.org/cc/v3 v3.41.0 // indirect31 modernc.org/cc/v3 v3.41.0 // indirect
34 modernc.org/ccgo/v3 v3.16.15 // indirect32 modernc.org/ccgo/v3 v3.16.15 // indirect
@@ -52,7 +50,6 @@ require (
52 github.com/gorilla/context v1.1.1 // indirect50 github.com/gorilla/context v1.1.1 // indirect
53 github.com/gorilla/css v1.0.0 // indirect51 github.com/gorilla/css v1.0.0 // indirect
54 github.com/gorilla/securecookie v1.1.1 // indirect52 github.com/gorilla/securecookie v1.1.1 // indirect
55- github.com/labstack/echo v3.3.10+incompatible
56 github.com/labstack/gommon v0.4.0 // indirect53 github.com/labstack/gommon v0.4.0 // indirect
57 github.com/mattn/go-colorable v0.1.13 // indirect54 github.com/mattn/go-colorable v0.1.13 // indirect
58 github.com/mattn/go-isatty v0.0.20 // indirect55 github.com/mattn/go-isatty v0.0.20 // indirect
M go.sum +1 -12 generated file: collapsed
M internal/httpserve/middleware/secure.go +6 -2
@@ -1,6 +1,7 @@
11 package middleware
22
33 import (
4+ "fmt"
45 "log"
56 "os"
67
@@ -12,14 +13,17 @@ import (
1213 "github.com/labstack/echo/v4/middleware"
1314 )
1415
15-func SecureRoutes() echo.MiddlewareFunc {
16+func SecureRoutes(a *server.App) echo.MiddlewareFunc {
17+ proxyURL := a.Config.Build.ProxyURL // <-- https://gordon-proxy.bnema.dev
18+ urlCheckVersion := a.Config.Build.ProxyURL + "/version"
19+ fmt.Println("Proxy URL:", proxyURL)
1620 return middleware.SecureWithConfig(middleware.SecureConfig{
1721 XSSProtection: "1; mode=block",
1822 ContentTypeNosniff: "nosniff",
1923 XFrameOptions: "SAMEORIGIN",
2024 HSTSMaxAge: 3600,
2125 HSTSExcludeSubdomains: false,
22- ContentSecurityPolicy: "default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline'",
26+ ContentSecurityPolicy: "default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self' " + proxyURL + " " + urlCheckVersion,
2327 })
2428 }
2529
@@ -1,6 +1,7 @@
1 package middleware1 package middleware
2 2
3 import (3 import (
4+ "fmt"
4 "log"5 "log"
5 "os"6 "os"
6 7
@@ -12,14 +13,17 @@ import (
12 "github.com/labstack/echo/v4/middleware"13 "github.com/labstack/echo/v4/middleware"
13 )14 )
14 15
15-func SecureRoutes() echo.MiddlewareFunc {16+func SecureRoutes(a *server.App) echo.MiddlewareFunc {
17+ proxyURL := a.Config.Build.ProxyURL // <-- https://gordon-proxy.bnema.dev
18+ urlCheckVersion := a.Config.Build.ProxyURL + "/version"
19+ fmt.Println("Proxy URL:", proxyURL)
16 return middleware.SecureWithConfig(middleware.SecureConfig{20 return middleware.SecureWithConfig(middleware.SecureConfig{
17 XSSProtection: "1; mode=block",21 XSSProtection: "1; mode=block",
18 ContentTypeNosniff: "nosniff",22 ContentTypeNosniff: "nosniff",
19 XFrameOptions: "SAMEORIGIN",23 XFrameOptions: "SAMEORIGIN",
20 HSTSMaxAge: 3600,24 HSTSMaxAge: 3600,
21 HSTSExcludeSubdomains: false,25 HSTSExcludeSubdomains: false,
22- ContentSecurityPolicy: "default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline'",26+ ContentSecurityPolicy: "default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self' " + proxyURL + " " + urlCheckVersion,
23 })27 })
24 }28 }
25 29
M internal/httpserve/router.go +1 -1
@@ -30,7 +30,7 @@ func RegisterRoutes(e *echo.Echo, a *server.App) *echo.Echo {
3030 e.Use(middleware.ErrorHandler)
3131
3232 // Use middlewares
33- e.Use(middleware.SecureRoutes())
33+ e.Use(middleware.SecureRoutes(a))
3434
3535 // Color scheme detection for dark/light mode
3636 e.Use(middleware.ColorSchemeDetection)
@@ -30,7 +30,7 @@ func RegisterRoutes(e *echo.Echo, a *server.App) *echo.Echo {
30 e.Use(middleware.ErrorHandler)30 e.Use(middleware.ErrorHandler)
31 31
32 // Use middlewares32 // Use middlewares
33- e.Use(middleware.SecureRoutes())33+ e.Use(middleware.SecureRoutes(a))
34 34
35 // Color scheme detection for dark/light mode35 // Color scheme detection for dark/light mode
36 e.Use(middleware.ColorSchemeDetection)36 e.Use(middleware.ColorSchemeDetection)
M internal/templating/models/html/fragments/menu.gohtml +45 -16
@@ -1,3 +1,36 @@
1+<script>
2+ function checkVersion(versionData) {
3+ var currentVersion = document
4+ .getElementById("actual-version")
5+ .textContent.trim();
6+ // Extract the numeric part of the version string using a regular expression
7+ var fetchedVersionNumber =
8+ versionData["amd64"]["name"].match(/\d+\.\d+\.\d+/)[0];
9+
10+ if (currentVersion !== fetchedVersionNumber) {
11+ console.log("New version available:", fetchedVersionNumber);
12+ // Show the update-available span by removing the 'hidden' attribute
13+ document.getElementById("update-available").removeAttribute("hidden");
14+ } else {
15+ console.log(
16+ "No new version. Current version is up-to-date:",
17+ currentVersion
18+ );
19+ }
20+ }
21+
22+ function fetchVersionInfo() {
23+ fetch("https://gordon-proxy.bnema.dev/version")
24+ .then((response) => response.json())
25+ .then((data) => {
26+ checkVersion(data); // Pass the parsed JSON data to checkVersion
27+ })
28+ .catch((error) => console.error("Error fetching version:", error));
29+ }
30+
31+ // Run fetchVersionInfo when the window loads
32+ window.onload = fetchVersionInfo;
33+</script>
134 <div
235 id="menubar"
336 class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans">
@@ -7,9 +40,18 @@
740 Gordon
841 </h1>
942 <span
10- class="text-gordonmascot-mblue text-xs md:text-sm"
11- >{{ .BuildVersion }}</span
12- >
43+ id="actual-version"
44+ class="text-gordonmascot-mblue text-xs md:text-sm">
45+ {{ .BuildVersion }}
46+ </span>
47+ <!-- if .LatestVersion is > to BuildVersion show arrow up -->
48+ <span
49+ hidden
50+ id="update-available"
51+ class="text-teal-200 text-xs md:text-sm"
52+ title="A new version is available"
53+ ><a href="{{ .LatestVersionURL }}">󰨦</a>
54+ </span>
1355 </div>
1456 <div class="w-full">
1557 <ul
@@ -35,16 +77,3 @@
3577 >
3678 </div>
3779 </div>
38-
39-<!-- <li class="" hidden>
40- <i class="iconoir-dashboard-speed mr-2 text-3xl"></i>
41- <a href="/admin">Dashboard</a>
42- </li>
43- <li class="flex items-center justify-start w-full text-gray-600" hidden>
44- <i class="iconoir-community mr-2 text-3xl"></i>
45- <a title="Disabled" href="#">Users</a>
46- </li>
47- <li class="flex items-center justify-start w-full text-gray-600" hidden>
48- <i class="iconoir-database-settings mr-2 text-3xl"></i>
49- <a title="Disabled" href="#">Settings</a>
50- </li> -->
@@ -1,3 +1,36 @@
1+<script>
2+ function checkVersion(versionData) {
3+ var currentVersion = document
4+ .getElementById("actual-version")
5+ .textContent.trim();
6+ // Extract the numeric part of the version string using a regular expression
7+ var fetchedVersionNumber =
8+ versionData["amd64"]["name"].match(/\d+\.\d+\.\d+/)[0];
9+
10+ if (currentVersion !== fetchedVersionNumber) {
11+ console.log("New version available:", fetchedVersionNumber);
12+ // Show the update-available span by removing the 'hidden' attribute
13+ document.getElementById("update-available").removeAttribute("hidden");
14+ } else {
15+ console.log(
16+ "No new version. Current version is up-to-date:",
17+ currentVersion
18+ );
19+ }
20+ }
21+
22+ function fetchVersionInfo() {
23+ fetch("https://gordon-proxy.bnema.dev/version")
24+ .then((response) => response.json())
25+ .then((data) => {
26+ checkVersion(data); // Pass the parsed JSON data to checkVersion
27+ })
28+ .catch((error) => console.error("Error fetching version:", error));
29+ }
30+
31+ // Run fetchVersionInfo when the window loads
32+ window.onload = fetchVersionInfo;
33+</script>
1 <div34 <div
2 id="menubar"35 id="menubar"
3 class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans">36 class="w-full h-12 md:h-16 fixed top-0 left-0 bg-gordonmascot-lblue flex flex-row justify-evenly items-center px-4 IBMPlexSans">
@@ -7,9 +40,18 @@
7 Gordon40 Gordon
8 </h1>41 </h1>
9 <span42 <span
10- class="text-gordonmascot-mblue text-xs md:text-sm"43+ id="actual-version"
11- >{{ .BuildVersion }}</span44+ class="text-gordonmascot-mblue text-xs md:text-sm">
12- >45+ {{ .BuildVersion }}
46+ </span>
47+ <!-- if .LatestVersion is > to BuildVersion show arrow up -->
48+ <span
49+ hidden
50+ id="update-available"
51+ class="text-teal-200 text-xs md:text-sm"
52+ title="A new version is available"
53+ ><a href="{{ .LatestVersionURL }}">󰨦</a>
54+ </span>
13 </div>55 </div>
14 <div class="w-full">56 <div class="w-full">
15 <ul57 <ul
@@ -35,16 +77,3 @@
35 >77 >
36 </div>78 </div>
37 </div>79 </div>
38-
39-<!-- <li class="" hidden>
40- <i class="iconoir-dashboard-speed mr-2 text-3xl"></i>
41- <a href="/admin">Dashboard</a>
42- </li>
43- <li class="flex items-center justify-start w-full text-gray-600" hidden>
44- <i class="iconoir-community mr-2 text-3xl"></i>
45- <a title="Disabled" href="#">Users</a>
46- </li>
47- <li class="flex items-center justify-start w-full text-gray-600" hidden>
48- <i class="iconoir-database-settings mr-2 text-3xl"></i>
49- <a title="Disabled" href="#">Settings</a>
50- </li> -->
M internal/templating/models/html/login/index.gohtml +0 -4
@@ -8,10 +8,6 @@
88 <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />
99 <!-- Custom CSS + Tailwind -->
1010 <link rel="stylesheet" href="/assets/css/tailwind.css" />
11- <!-- Iconoir -->
12- <link rel="stylesheet" href="/libs/iconoir.css" />
13- <!-- Alpine -->
14- <script src="/libs/alpine.min.js"></script>
1511 <!-- HTMX min js -->
1612 <script src="/libs/htmx.min.js"></script>
1713 <!-- HTMX SSE -->
@@ -8,10 +8,6 @@
8 <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />8 <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />
9 <!-- Custom CSS + Tailwind -->9 <!-- Custom CSS + Tailwind -->
10 <link rel="stylesheet" href="/assets/css/tailwind.css" />10 <link rel="stylesheet" href="/assets/css/tailwind.css" />
11- <!-- Iconoir -->
12- <link rel="stylesheet" href="/libs/iconoir.css" />
13- <!-- Alpine -->
14- <script src="/libs/alpine.min.js"></script>
15 <!-- HTMX min js -->11 <!-- HTMX min js -->
16 <script src="/libs/htmx.min.js"></script>12 <script src="/libs/htmx.min.js"></script>
17 <!-- HTMX SSE -->13 <!-- HTMX SSE -->
M internal/templating/render/html.go +4 -3
@@ -11,9 +11,10 @@ import (
1111 )
1212
1313 type Renderer struct {
14- Template *template.Template
15- ParseError error
16- BuildVersion string
14+ Template *template.Template
15+ ParseError error
16+ BuildVersion string
17+ LatestVersion string
1718 }
1819
1920 // Render function renders the template with the given data
@@ -11,9 +11,10 @@ import (
11 )11 )
12 12
13 type Renderer struct {13 type Renderer struct {
14- Template *template.Template14+ Template *template.Template
15- ParseError error15+ ParseError error
16- BuildVersion string16+ BuildVersion string
17+ LatestVersion string
17 }18 }
18 19
19 // Render function renders the template with the given data20 // Render function renders the template with the given data
M internal/webui/public/assets/css/tailwind.css +20 -16
@@ -636,10 +636,6 @@ video {
636636 display: none;
637637 }
638638
639-.h-10 {
640- height: 2.5rem;
641-}
642-
643639 .h-12 {
644640 height: 3rem;
645641 }
@@ -680,10 +676,6 @@ video {
680676 width: 41.666667%;
681677 }
682678
683-.w-72 {
684- width: 18rem;
685-}
686-
687679 .w-full {
688680 width: 100%;
689681 }
@@ -772,10 +764,6 @@ video {
772764 border-radius: 0.25rem;
773765 }
774766
775-.rounded-lg {
776- border-radius: 0.5rem;
777-}
778-
779767 .rounded-md {
780768 border-radius: 0.375rem;
781769 }
@@ -1019,11 +1007,31 @@ video {
10191007 color: rgb(241 245 249 / var(--tw-text-opacity));
10201008 }
10211009
1010+.text-teal-600 {
1011+ --tw-text-opacity: 1;
1012+ color: rgb(13 148 136 / var(--tw-text-opacity));
1013+}
1014+
10221015 .text-white {
10231016 --tw-text-opacity: 1;
10241017 color: rgb(255 255 255 / var(--tw-text-opacity));
10251018 }
10261019
1020+.text-teal-300 {
1021+ --tw-text-opacity: 1;
1022+ color: rgb(94 234 212 / var(--tw-text-opacity));
1023+}
1024+
1025+.text-teal-400 {
1026+ --tw-text-opacity: 1;
1027+ color: rgb(45 212 191 / var(--tw-text-opacity));
1028+}
1029+
1030+.text-teal-200 {
1031+ --tw-text-opacity: 1;
1032+ color: rgb(153 246 228 / var(--tw-text-opacity));
1033+}
1034+
10271035 .shadow {
10281036 --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
10291037 --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -1195,10 +1203,6 @@ video {
11951203 margin-left: 1rem;
11961204 }
11971205
1198- .md\:flex {
1199- display: flex;
1200- }
1201-
12021206 .md\:h-16 {
12031207 height: 4rem;
12041208 }
@@ -636,10 +636,6 @@ video {
636 display: none;636 display: none;
637 }637 }
638 638
639-.h-10 {
640- height: 2.5rem;
641-}
642-
643 .h-12 {639 .h-12 {
644 height: 3rem;640 height: 3rem;
645 }641 }
@@ -680,10 +676,6 @@ video {
680 width: 41.666667%;676 width: 41.666667%;
681 }677 }
682 678
683-.w-72 {
684- width: 18rem;
685-}
686-
687 .w-full {679 .w-full {
688 width: 100%;680 width: 100%;
689 }681 }
@@ -772,10 +764,6 @@ video {
772 border-radius: 0.25rem;764 border-radius: 0.25rem;
773 }765 }
774 766
775-.rounded-lg {
776- border-radius: 0.5rem;
777-}
778-
779 .rounded-md {767 .rounded-md {
780 border-radius: 0.375rem;768 border-radius: 0.375rem;
781 }769 }
@@ -1019,11 +1007,31 @@ video {
1019 color: rgb(241 245 249 / var(--tw-text-opacity));1007 color: rgb(241 245 249 / var(--tw-text-opacity));
1020 }1008 }
1021 1009
1010+.text-teal-600 {
1011+ --tw-text-opacity: 1;
1012+ color: rgb(13 148 136 / var(--tw-text-opacity));
1013+}
1014+
1022 .text-white {1015 .text-white {
1023 --tw-text-opacity: 1;1016 --tw-text-opacity: 1;
1024 color: rgb(255 255 255 / var(--tw-text-opacity));1017 color: rgb(255 255 255 / var(--tw-text-opacity));
1025 }1018 }
1026 1019
1020+.text-teal-300 {
1021+ --tw-text-opacity: 1;
1022+ color: rgb(94 234 212 / var(--tw-text-opacity));
1023+}
1024+
1025+.text-teal-400 {
1026+ --tw-text-opacity: 1;
1027+ color: rgb(45 212 191 / var(--tw-text-opacity));
1028+}
1029+
1030+.text-teal-200 {
1031+ --tw-text-opacity: 1;
1032+ color: rgb(153 246 228 / var(--tw-text-opacity));
1033+}
1034+
1027 .shadow {1035 .shadow {
1028 --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);1036 --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
1029 --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);1037 --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -1195,10 +1203,6 @@ video {
1195 margin-left: 1rem;1203 margin-left: 1rem;
1196 }1204 }
1197 1205
1198- .md\:flex {
1199- display: flex;
1200- }
1201-
1202 .md\:h-16 {1206 .md\:h-16 {
1203 height: 4rem;1207 height: 4rem;
1204 }1208 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close