detached 7caefd0

Move the tty related logic into pkg utils

commit
7caefd03d8ae5725c353631f557c1e0983975c1b
author
bnema · · 2023-09-04 12:52 UTC
parent
090f2a0
signature
unsigned
4 files · +134 -53
M cmd/main.go +3 -53
@@ -1,12 +1,7 @@
11 package main
22
33 import (
4- "net/http"
54 "os"
6- "os/exec"
7- "os/signal"
8- "syscall"
9- "time"
105
116 "github.com/labstack/echo/v4"
127 "gogs.bnema.dev/gordon-echo/internal/app"
@@ -15,18 +10,6 @@ import (
1510 "gogs.bnema.dev/gordon-echo/pkg/utils"
1611 )
1712
18-func setRawMode() {
19- cmd := exec.Command("stty", "raw", "-echo")
20- cmd.Stdin = os.Stdin
21- _ = cmd.Run()
22-}
23-
24-func restoreTerminal() {
25- cmd := exec.Command("stty", "-raw", "echo")
26- cmd.Stdin = os.Stdin
27- _ = cmd.Run()
28-}
29-
3013 func main() {
3114 // Port = os.Getenv("PORT") or fallback to default port (1323)
3215 port := os.Getenv("PORT")
@@ -50,42 +33,9 @@ func main() {
5033 e.Logger = utils.NewEchoLoggerWrapper(gordon.APPLogger)
5134 e = routes.ConfigureRouter(e, gordon)
5235
53- // Start the server in a separate goroutine
54- go func() {
36+ startFunc := func() error {
5537 gordon.APPLogger.Info().Msgf("Server is listening on port %s", port)
56- if err := e.Start(":" + port); err != nil && err != http.ErrServerClosed {
57- gordon.APPLogger.Fatal().Err(err).Msg("Failed to start the server")
58- }
59- }()
60-
61- // Catch SIGINT and SIGTERM signals without raw mode initially
62- c := make(chan os.Signal, 1)
63- signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
64- <-c
65-
66- gordon.APPLogger.Warn().Msg("Graceful shutdown initiated")
67- gordon.APPLogger.Warn().Msg("Server will shutdown in 5 seconds press ENTER to cancel")
68-
69- // Now, set raw mode
70- setRawMode()
71- defer restoreTerminal() // Ensure terminal is restored even if program exits unexpectedly
72-
73- // Channel to signal a key press
74- keypress := make(chan struct{})
75-
76- // Goroutine to listen for a key press
77- go func() {
78- var b []byte = make([]byte, 1)
79- os.Stdin.Read(b)
80- keypress <- struct{}{}
81- }()
82-
83- // Wait for either a key press or the 5-second timer
84- select {
85- case <-time.After(5 * time.Second):
86- gordon.APPLogger.Info().Msg("Server shutdown")
87- case <-keypress:
88- gordon.APPLogger.Info().Msg("Shutdown cancelled by user")
38+ return e.Start(":" + port)
8939 }
90-
40+ utils.RunAppCatchSIGINT(startFunc, &gordon.APPLogger.Logger)
9141 }
@@ -1,12 +1,7 @@
1 package main1 package main
2 2
3 import (3 import (
4- "net/http"
5 "os"4 "os"
6- "os/exec"
7- "os/signal"
8- "syscall"
9- "time"
10 5
11 "github.com/labstack/echo/v4"6 "github.com/labstack/echo/v4"
12 "gogs.bnema.dev/gordon-echo/internal/app"7 "gogs.bnema.dev/gordon-echo/internal/app"
@@ -15,18 +10,6 @@ import (
15 "gogs.bnema.dev/gordon-echo/pkg/utils"10 "gogs.bnema.dev/gordon-echo/pkg/utils"
16 )11 )
17 12
18-func setRawMode() {
19- cmd := exec.Command("stty", "raw", "-echo")
20- cmd.Stdin = os.Stdin
21- _ = cmd.Run()
22-}
23-
24-func restoreTerminal() {
25- cmd := exec.Command("stty", "-raw", "echo")
26- cmd.Stdin = os.Stdin
27- _ = cmd.Run()
28-}
29-
30 func main() {13 func main() {
31 // Port = os.Getenv("PORT") or fallback to default port (1323)14 // Port = os.Getenv("PORT") or fallback to default port (1323)
32 port := os.Getenv("PORT")15 port := os.Getenv("PORT")
@@ -50,42 +33,9 @@ func main() {
50 e.Logger = utils.NewEchoLoggerWrapper(gordon.APPLogger)33 e.Logger = utils.NewEchoLoggerWrapper(gordon.APPLogger)
51 e = routes.ConfigureRouter(e, gordon)34 e = routes.ConfigureRouter(e, gordon)
52 35
53- // Start the server in a separate goroutine36+ startFunc := func() error {
54- go func() {
55 gordon.APPLogger.Info().Msgf("Server is listening on port %s", port)37 gordon.APPLogger.Info().Msgf("Server is listening on port %s", port)
56- if err := e.Start(":" + port); err != nil && err != http.ErrServerClosed {38+ return e.Start(":" + port)
57- gordon.APPLogger.Fatal().Err(err).Msg("Failed to start the server")
58- }
59- }()
60-
61- // Catch SIGINT and SIGTERM signals without raw mode initially
62- c := make(chan os.Signal, 1)
63- signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
64- <-c
65-
66- gordon.APPLogger.Warn().Msg("Graceful shutdown initiated")
67- gordon.APPLogger.Warn().Msg("Server will shutdown in 5 seconds press ENTER to cancel")
68-
69- // Now, set raw mode
70- setRawMode()
71- defer restoreTerminal() // Ensure terminal is restored even if program exits unexpectedly
72-
73- // Channel to signal a key press
74- keypress := make(chan struct{})
75-
76- // Goroutine to listen for a key press
77- go func() {
78- var b []byte = make([]byte, 1)
79- os.Stdin.Read(b)
80- keypress <- struct{}{}
81- }()
82-
83- // Wait for either a key press or the 5-second timer
84- select {
85- case <-time.After(5 * time.Second):
86- gordon.APPLogger.Info().Msg("Server shutdown")
87- case <-keypress:
88- gordon.APPLogger.Info().Msg("Shutdown cancelled by user")
89 }39 }
90-40+ utils.RunAppCatchSIGINT(startFunc, &gordon.APPLogger.Logger)
91 }41 }
M logs/app.log +19 -0
@@ -28,3 +28,22 @@
2828 {"level":"warn","time":"2023-09-04T12:40:12+02:00","message":"Graceful shutdown initiated"}
2929 {"level":"warn","time":"2023-09-04T12:40:12+02:00","message":"Server will shutdown in 5 seconds press ENTER to cancel"}
3030 {"level":"info","time":"2023-09-04T12:40:17+02:00","message":"Server shutdown"}
31+{"level":"info","time":"2023-09-04T14:05:05+02:00","message":"Server is listening on port 1323"}
32+{"level":"info","time":"2023-09-04T14:05:15+02:00","message":"Server is listening on port 1323"}
33+{"level":"info","time":"2023-09-04T14:39:43+02:00","message":"Server is listening on port 1323"}
34+{"level":"info","time":"2023-09-04T14:39:43+02:00","message":"Server is listening on port 1323"}
35+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:39:43+02:00","message":"Failed to start the server"}
36+{"level":"info","time":"2023-09-04T14:42:19+02:00","message":"Server is listening on port 1323"}
37+{"level":"info","time":"2023-09-04T14:42:19+02:00","message":"Server is listening on port 1323"}
38+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:42:19+02:00","message":"Failed to start the server"}
39+{"level":"info","time":"2023-09-04T14:45:00+02:00","message":"Server is listening on port 1323"}
40+{"level":"info","time":"2023-09-04T14:45:00+02:00","message":"Server is listening on port 1323"}
41+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:45:00+02:00","message":"Failed to start the server"}
42+{"level":"info","time":"2023-09-04T14:50:37+02:00","message":"Server is listening on port 1323"}
43+{"level":"info","time":"2023-09-04T14:50:37+02:00","message":"Server is listening on port 1323"}
44+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:50:37+02:00","message":"Failed to start the server"}
45+{"level":"info","time":"2023-09-04T14:50:51+02:00","message":"Server is listening on port 1323"}
46+{"level":"info","time":"2023-09-04T14:51:08+02:00","message":"Server is listening on port 1323"}
47+{"level":"warn","time":"2023-09-04T14:51:10+02:00","message":"Graceful shutdown initiated"}
48+{"level":"warn","time":"2023-09-04T14:51:10+02:00","message":"Server will shutdown in 5 seconds press ENTER to cancel"}
49+{"level":"info","time":"2023-09-04T14:51:15+02:00","message":"Server shutdown"}
@@ -28,3 +28,22 @@
28 {"level":"warn","time":"2023-09-04T12:40:12+02:00","message":"Graceful shutdown initiated"}28 {"level":"warn","time":"2023-09-04T12:40:12+02:00","message":"Graceful shutdown initiated"}
29 {"level":"warn","time":"2023-09-04T12:40:12+02:00","message":"Server will shutdown in 5 seconds press ENTER to cancel"}29 {"level":"warn","time":"2023-09-04T12:40:12+02:00","message":"Server will shutdown in 5 seconds press ENTER to cancel"}
30 {"level":"info","time":"2023-09-04T12:40:17+02:00","message":"Server shutdown"}30 {"level":"info","time":"2023-09-04T12:40:17+02:00","message":"Server shutdown"}
31+{"level":"info","time":"2023-09-04T14:05:05+02:00","message":"Server is listening on port 1323"}
32+{"level":"info","time":"2023-09-04T14:05:15+02:00","message":"Server is listening on port 1323"}
33+{"level":"info","time":"2023-09-04T14:39:43+02:00","message":"Server is listening on port 1323"}
34+{"level":"info","time":"2023-09-04T14:39:43+02:00","message":"Server is listening on port 1323"}
35+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:39:43+02:00","message":"Failed to start the server"}
36+{"level":"info","time":"2023-09-04T14:42:19+02:00","message":"Server is listening on port 1323"}
37+{"level":"info","time":"2023-09-04T14:42:19+02:00","message":"Server is listening on port 1323"}
38+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:42:19+02:00","message":"Failed to start the server"}
39+{"level":"info","time":"2023-09-04T14:45:00+02:00","message":"Server is listening on port 1323"}
40+{"level":"info","time":"2023-09-04T14:45:00+02:00","message":"Server is listening on port 1323"}
41+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:45:00+02:00","message":"Failed to start the server"}
42+{"level":"info","time":"2023-09-04T14:50:37+02:00","message":"Server is listening on port 1323"}
43+{"level":"info","time":"2023-09-04T14:50:37+02:00","message":"Server is listening on port 1323"}
44+{"level":"fatal","error":"listen tcp :1323: bind: address already in use","time":"2023-09-04T14:50:37+02:00","message":"Failed to start the server"}
45+{"level":"info","time":"2023-09-04T14:50:51+02:00","message":"Server is listening on port 1323"}
46+{"level":"info","time":"2023-09-04T14:51:08+02:00","message":"Server is listening on port 1323"}
47+{"level":"warn","time":"2023-09-04T14:51:10+02:00","message":"Graceful shutdown initiated"}
48+{"level":"warn","time":"2023-09-04T14:51:10+02:00","message":"Server will shutdown in 5 seconds press ENTER to cancel"}
49+{"level":"info","time":"2023-09-04T14:51:15+02:00","message":"Server shutdown"}
M logs/http.log +47 -0
@@ -18,3 +18,50 @@
1818 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}
1919 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}
2020 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}
21+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:05:22+02:00","message":"request"}
22+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:05:23+02:00","message":"request"}
23+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
24+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/js/custom.js","status":404,"time":"2023-09-04T14:05:25+02:00","message":"request"}
25+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/css/tailwind.css","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
26+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/htmx.min.js","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
27+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/alpine.min.js","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
28+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/iconoir.css","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
29+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/imgs/gordon-mascot-hq-trsp.png","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
30+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
31+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
32+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
33+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
34+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/css/tailwind.css","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
35+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/alpine.min.js","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
36+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/iconoir.css","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
37+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/htmx.min.js","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
38+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/js/custom.js","status":404,"time":"2023-09-04T14:31:47+02:00","message":"request"}
39+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/imgs/gordon-mascot-hq-trsp.png","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
40+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
41+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
42+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
43+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:22+02:00","message":"request"}
44+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:22+02:00","message":"request"}
45+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:42+02:00","message":"request"}
46+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:42+02:00","message":"request"}
47+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:42+02:00","message":"request"}
48+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:46+02:00","message":"request"}
49+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:47+02:00","message":"request"}
50+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:47+02:00","message":"request"}
51+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:48+02:00","message":"request"}
52+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:49+02:00","message":"request"}
53+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:49+02:00","message":"request"}
54+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:53+02:00","message":"request"}
55+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:54+02:00","message":"request"}
56+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:54+02:00","message":"request"}
57+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:55+02:00","message":"request"}
58+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:33:13+02:00","message":"request"}
59+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/css/tailwind.css","status":200,"time":"2023-09-04T14:33:13+02:00","message":"request"}
60+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/iconoir.css","status":200,"time":"2023-09-04T14:33:13+02:00","message":"request"}
61+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/js/custom.js","status":404,"time":"2023-09-04T14:33:13+02:00","message":"request"}
62+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/alpine.min.js","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
63+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/htmx.min.js","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
64+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
65+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
66+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/imgs/gordon-mascot-hq-trsp.png","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
67+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
@@ -18,3 +18,50 @@
18 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}18 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}
19 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}19 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}
20 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}20 {"level":"info","type":"http","remote_ip":"::1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T11:57:48+02:00","message":"request"}
21+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:05:22+02:00","message":"request"}
22+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:05:23+02:00","message":"request"}
23+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
24+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/js/custom.js","status":404,"time":"2023-09-04T14:05:25+02:00","message":"request"}
25+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/css/tailwind.css","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
26+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/htmx.min.js","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
27+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/alpine.min.js","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
28+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/iconoir.css","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
29+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/imgs/gordon-mascot-hq-trsp.png","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
30+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
31+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
32+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T14:05:25+02:00","message":"request"}
33+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
34+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/css/tailwind.css","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
35+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/alpine.min.js","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
36+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/iconoir.css","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
37+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/htmx.min.js","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
38+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/js/custom.js","status":404,"time":"2023-09-04T14:31:47+02:00","message":"request"}
39+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/imgs/gordon-mascot-hq-trsp.png","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
40+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
41+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
42+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T14:31:47+02:00","message":"request"}
43+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:22+02:00","message":"request"}
44+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:22+02:00","message":"request"}
45+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:42+02:00","message":"request"}
46+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:42+02:00","message":"request"}
47+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:42+02:00","message":"request"}
48+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:46+02:00","message":"request"}
49+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:47+02:00","message":"request"}
50+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:47+02:00","message":"request"}
51+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:48+02:00","message":"request"}
52+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:49+02:00","message":"request"}
53+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:49+02:00","message":"request"}
54+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:53+02:00","message":"request"}
55+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:54+02:00","message":"request"}
56+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:32:54+02:00","message":"request"}
57+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:32:55+02:00","message":"request"}
58+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/admin","status":200,"time":"2023-09-04T14:33:13+02:00","message":"request"}
59+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/css/tailwind.css","status":200,"time":"2023-09-04T14:33:13+02:00","message":"request"}
60+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/iconoir.css","status":200,"time":"2023-09-04T14:33:13+02:00","message":"request"}
61+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/js/custom.js","status":404,"time":"2023-09-04T14:33:13+02:00","message":"request"}
62+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/alpine.min.js","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
63+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/libs/htmx.min.js","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
64+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/LilitaOne/LilitaOne-Regular.ttf","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
65+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
66+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/assets/imgs/gordon-mascot-hq-trsp.png","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
67+{"level":"info","type":"http","remote_ip":"127.0.0.1","method":"GET","URI":"/favicon.ico","status":200,"time":"2023-09-04T14:33:14+02:00","message":"request"}
A pkg/utils/terminal.go +65 -0
@@ -0,0 +1,65 @@
1+package utils
2+
3+import (
4+ "net/http"
5+ "os"
6+ "os/exec"
7+ "os/signal"
8+ "syscall"
9+ "time"
10+
11+ "github.com/rs/zerolog"
12+)
13+
14+// SetRawMode sets the terminal to raw mode.
15+func SetRawMode() {
16+ cmd := exec.Command("stty", "raw", "-echo")
17+ cmd.Stdin = os.Stdin
18+ _ = cmd.Run()
19+}
20+
21+// RestoreTerminal restores the terminal settings.
22+func RestoreTerminal() {
23+ cmd := exec.Command("stty", "-raw", "echo")
24+ cmd.Stdin = os.Stdin
25+ _ = cmd.Run()
26+}
27+
28+func RunAppCatchSIGINT(startServerFunc func() error, logger *zerolog.Logger) {
29+ // Start the server in a separate goroutine
30+ go func() {
31+ if err := startServerFunc(); err != nil && err != http.ErrServerClosed {
32+ logger.Fatal().Err(err).Msg("Failed to start the server")
33+ }
34+ }()
35+
36+ // Catch SIGINT and SIGTERM signals without raw mode initially
37+ c := make(chan os.Signal, 1)
38+ signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
39+ <-c
40+
41+ logger.Warn().Msg("Graceful shutdown initiated")
42+ logger.Warn().Msg("Server will shutdown in 5 seconds press ENTER to cancel")
43+
44+ // Now, set raw mode
45+ SetRawMode()
46+ defer RestoreTerminal()
47+
48+ // Channel to signal a key press
49+ keypress := make(chan struct{})
50+
51+ // Goroutine to listen for a key press
52+ go func() {
53+ var b []byte = make([]byte, 1)
54+ os.Stdin.Read(b)
55+ keypress <- struct{}{}
56+ }()
57+
58+ // Wait for either a key press or the 5-second timer
59+ select {
60+ case <-time.After(5 * time.Second):
61+ logger.Info().Msg("Server shutdown")
62+ case <-keypress:
63+ logger.Info().Msg("Shutdown cancelled by user")
64+ }
65+}
@@ -0,0 +1,65 @@
1+package utils
2+
3+import (
4+ "net/http"
5+ "os"
6+ "os/exec"
7+ "os/signal"
8+ "syscall"
9+ "time"
10+
11+ "github.com/rs/zerolog"
12+)
13+
14+// SetRawMode sets the terminal to raw mode.
15+func SetRawMode() {
16+ cmd := exec.Command("stty", "raw", "-echo")
17+ cmd.Stdin = os.Stdin
18+ _ = cmd.Run()
19+}
20+
21+// RestoreTerminal restores the terminal settings.
22+func RestoreTerminal() {
23+ cmd := exec.Command("stty", "-raw", "echo")
24+ cmd.Stdin = os.Stdin
25+ _ = cmd.Run()
26+}
27+
28+func RunAppCatchSIGINT(startServerFunc func() error, logger *zerolog.Logger) {
29+ // Start the server in a separate goroutine
30+ go func() {
31+ if err := startServerFunc(); err != nil && err != http.ErrServerClosed {
32+ logger.Fatal().Err(err).Msg("Failed to start the server")
33+ }
34+ }()
35+
36+ // Catch SIGINT and SIGTERM signals without raw mode initially
37+ c := make(chan os.Signal, 1)
38+ signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
39+ <-c
40+
41+ logger.Warn().Msg("Graceful shutdown initiated")
42+ logger.Warn().Msg("Server will shutdown in 5 seconds press ENTER to cancel")
43+
44+ // Now, set raw mode
45+ SetRawMode()
46+ defer RestoreTerminal()
47+
48+ // Channel to signal a key press
49+ keypress := make(chan struct{})
50+
51+ // Goroutine to listen for a key press
52+ go func() {
53+ var b []byte = make([]byte, 1)
54+ os.Stdin.Read(b)
55+ keypress <- struct{}{}
56+ }()
57+
58+ // Wait for either a key press or the 5-second timer
59+ select {
60+ case <-time.After(5 * time.Second):
61+ logger.Info().Msg("Server shutdown")
62+ case <-keypress:
63+ logger.Info().Msg("Shutdown cancelled by user")
64+ }
65+}

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