From 322826d06e425297fcbbc976a0566868a74f2d87 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Sun, 21 Dec 2025 22:16:52 +0100 Subject: feat(backend): record every jump between pages --- backend/router.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'backend/router.go') diff --git a/backend/router.go b/backend/router.go index b7403cf..7f9de5c 100644 --- a/backend/router.go +++ b/backend/router.go @@ -2,6 +2,7 @@ package backend import ( "context" + "database/sql" "embed" "fmt" "io/fs" @@ -45,7 +46,7 @@ func SetupLogger(debug bool) { slog.SetDefault(logger) } -func NewRouter(debug bool, cfg *Config, assets fs.FS) *chi.Mux { +func NewRouter(debug bool, cfg *Config, db *sql.DB, assets fs.FS) *chi.Mux { r := chi.NewRouter() logLevel := slog.LevelWarn @@ -70,7 +71,7 @@ func NewRouter(debug bool, cfg *Config, assets fs.FS) *chi.Mux { r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // prevent tracking - w.Header().Add("Referrer-Policy", "no-referrer") + w.Header().Add("Referrer-Policy", "same-origin") // prevent iframe w.Header().Add("X-Frame-Options", "deny") // prevent bad content being parsed @@ -94,6 +95,15 @@ func NewRouter(debug bool, cfg *Config, assets fs.FS) *chi.Mux { next.ServeHTTP(w, r.WithContext(ctx)) }) }) + // stats + r.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if err := UpdateStats(r.Context(), db, r); err != nil { + slog.Error("updating stats", "error", err) + } + next.ServeHTTP(w, r) + }) + }) return r } -- cgit v1.2.3 From 93c4f9047426f9f56940ed2b733dcde2d98b0c0e Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 22 Dec 2025 13:36:18 +0100 Subject: perf(backend): decrease render time when request has referer header Made the update stats async --- backend/router.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'backend/router.go') diff --git a/backend/router.go b/backend/router.go index 7f9de5c..14abc06 100644 --- a/backend/router.go +++ b/backend/router.go @@ -22,6 +22,7 @@ const ( configKey = "config" assetsFSKey = "assets_fs" debugKey = "debug" + dbKey = "db" ) //go:embed templates @@ -87,20 +88,27 @@ func NewRouter(debug bool, cfg *Config, db *sql.DB, assets fs.FS) *chi.Mux { }) }) // context + setContext := func(ctx context.Context) context.Context { + ctx = context.WithValue(ctx, configKey, cfg) + ctx = context.WithValue(ctx, assetsFSKey, assets) + ctx = context.WithValue(ctx, debugKey, debug) + return context.WithValue(ctx, dbKey, db) + } r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := context.WithValue(r.Context(), configKey, cfg) - ctx = context.WithValue(ctx, assetsFSKey, assets) - ctx = context.WithValue(ctx, debugKey, debug) - next.ServeHTTP(w, r.WithContext(ctx)) + next.ServeHTTP(w, r.WithContext(setContext(r.Context()))) }) }) // stats r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if err := UpdateStats(r.Context(), db, r); err != nil { - slog.Error("updating stats", "error", err) - } + go func(r *http.Request) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + if err := UpdateStats(setContext(ctx), r); err != nil { + slog.Error("updating stats", "error", err) + } + }(r) next.ServeHTTP(w, r) }) }) -- cgit v1.2.3 From 84af6427d8205b1882b9f9df11ce394f96d6b792 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 22 Dec 2025 15:07:55 +0100 Subject: feat(backend): admin dashboard --- backend/router.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'backend/router.go') diff --git a/backend/router.go b/backend/router.go index 14abc06..f2ed775 100644 --- a/backend/router.go +++ b/backend/router.go @@ -2,6 +2,8 @@ package backend import ( "context" + "crypto/sha256" + "crypto/subtle" "database/sql" "embed" "fmt" @@ -23,6 +25,7 @@ const ( assetsFSKey = "assets_fs" debugKey = "debug" dbKey = "db" + loginKey = "login" ) //go:embed templates @@ -112,6 +115,21 @@ func NewRouter(debug bool, cfg *Config, db *sql.DB, assets fs.FS) *chi.Mux { next.ServeHTTP(w, r) }) }) + // login + r.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, pass, ok := r.BasicAuth() + ctx := r.Context() + if ok { + cfg := ctx.Value(configKey).(*Config) + passHash := sha256.Sum256([]byte(pass)) + rightPassHash := sha256.Sum256([]byte(cfg.AdminPassword)) + ok = subtle.ConstantTimeCompare(passHash[:], rightPassHash[:]) == 1 + } + ctx = context.WithValue(ctx, loginKey, ok) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + }) return r } -- cgit v1.2.3 From cbd5c09c5e1403709d4aabf91051443f147689e5 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 22 Dec 2025 18:34:02 +0100 Subject: refactor(backend): move db related in new package --- backend/router.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'backend/router.go') diff --git a/backend/router.go b/backend/router.go index f2ed775..161433d 100644 --- a/backend/router.go +++ b/backend/router.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "git.anhgelus.world/anhgelus/small-web/backend/storage" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/httplog/v3" @@ -24,7 +25,6 @@ const ( configKey = "config" assetsFSKey = "assets_fs" debugKey = "debug" - dbKey = "db" loginKey = "login" ) @@ -95,7 +95,7 @@ func NewRouter(debug bool, cfg *Config, db *sql.DB, assets fs.FS) *chi.Mux { ctx = context.WithValue(ctx, configKey, cfg) ctx = context.WithValue(ctx, assetsFSKey, assets) ctx = context.WithValue(ctx, debugKey, debug) - return context.WithValue(ctx, dbKey, db) + return context.WithValue(ctx, storage.DBKey, db) } r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -108,7 +108,7 @@ func NewRouter(debug bool, cfg *Config, db *sql.DB, assets fs.FS) *chi.Mux { go func(r *http.Request) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - if err := UpdateStats(setContext(ctx), r); err != nil { + if err := storage.UpdateStats(setContext(ctx), r, cfg.Domain); err != nil { slog.Error("updating stats", "error", err) } }(r) -- cgit v1.2.3 From 9ad7b0e67c90697893f188323a32590fbecd5a65 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Mon, 22 Dec 2025 18:51:42 +0100 Subject: fix(storage): does not store stats if request fails --- backend/router.go | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'backend/router.go') diff --git a/backend/router.go b/backend/router.go index 161433d..1a2f024 100644 --- a/backend/router.go +++ b/backend/router.go @@ -91,28 +91,11 @@ func NewRouter(debug bool, cfg *Config, db *sql.DB, assets fs.FS) *chi.Mux { }) }) // context - setContext := func(ctx context.Context) context.Context { - ctx = context.WithValue(ctx, configKey, cfg) - ctx = context.WithValue(ctx, assetsFSKey, assets) - ctx = context.WithValue(ctx, debugKey, debug) - return context.WithValue(ctx, storage.DBKey, db) - } - r.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - next.ServeHTTP(w, r.WithContext(setContext(r.Context()))) - }) - }) - // stats r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - go func(r *http.Request) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - if err := storage.UpdateStats(setContext(ctx), r, cfg.Domain); err != nil { - slog.Error("updating stats", "error", err) - } - }(r) - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext( + setContext(r.Context(), cfg, debug, db, assets), + )) }) }) // login @@ -179,3 +162,23 @@ func HandleStaticFiles(r *chi.Mux, path string, root fs.FS) { http.StripPrefix(pathPrefix, http.FileServerFS(root)).ServeHTTP(w, req) }) } + +func UpdateStats(r *http.Request) { + ctx := r.Context() + cfg := ctx.Value(configKey).(*Config) + debug := ctx.Value(debugKey).(bool) + db := ctx.Value(storage.DBKey).(*sql.DB) + assets := ctx.Value(assetsFSKey).(fs.FS) + + ctx2, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + if err := storage.UpdateStats(setContext(ctx2, cfg, debug, db, assets), r, cfg.Domain); err != nil { + slog.Error("updating stats", "error", err) + } +} +func setContext(ctx context.Context, cfg *Config, debug bool, db *sql.DB, assets fs.FS) context.Context { + ctx = context.WithValue(ctx, configKey, cfg) + ctx = context.WithValue(ctx, assetsFSKey, assets) + ctx = context.WithValue(ctx, debugKey, debug) + return context.WithValue(ctx, storage.DBKey, db) +} -- cgit v1.2.3