feat: initial commit
This commit is contained in:
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731890469,
|
||||||
|
"narHash": "sha256-D1FNZ70NmQEwNxpSSdTXCSklBH1z2isPR84J6DQrJGs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "5083ec887760adfe12af64830a66807423a859a7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
23
flake.nix
Normal file
23
flake.nix
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
description = "yadunut.dev website";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
flake-utils = {
|
||||||
|
url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {nixpkgs, flake-utils, ...}:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
}; in {
|
||||||
|
devShells = {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs;[
|
||||||
|
go gopls gotools go-tools
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
16
main.go
Normal file
16
main.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.yadunut.dev/yadunut.dev/site"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := site.NewConfig(slog.LevelDebug, 8080)
|
||||||
|
c.Logger.Info("Starting server")
|
||||||
|
if err := site.Run(c); err != nil {
|
||||||
|
c.Logger.Error(fmt.Sprintf("failed with error %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
61
site/handler.go
Normal file
61
site/handler.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package site
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type counter struct {
|
||||||
|
hits map[string]int
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
logger *slog.Logger
|
||||||
|
requestsCounter *counter
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHandler(logger *slog.Logger) *handler {
|
||||||
|
return &handler{
|
||||||
|
logger: logger,
|
||||||
|
requestsCounter: &counter{hits: make(map[string]int)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) LoggingMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.logger.Debug("Received request", "path", r.URL.Path)
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
h.logger.Debug("Finished Request", "path", r.URL.Path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) RequestsCounterMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.requestsCounter.mu.Lock()
|
||||||
|
h.requestsCounter.hits[r.URL.Path] += 1
|
||||||
|
h.requestsCounter.mu.Unlock()
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) Ping(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "Pong")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) Metrics(w http.ResponseWriter, r *http.Request) {
|
||||||
|
h.requestsCounter.mu.Lock()
|
||||||
|
defer h.requestsCounter.mu.Unlock()
|
||||||
|
for path, hits := range h.requestsCounter.hits {
|
||||||
|
fmt.Fprintf(w, "http_requests_total{handler=\"%s\"} %d\n", path, hits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (h *handler) Healthz(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "Ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) NotFound(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Error(w, "Page Not Found", http.StatusNotFound)
|
||||||
|
}
|
||||||
32
site/router.go
Normal file
32
site/router.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package site
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func middlewares(h *handler, next http.Handler) http.Handler {
|
||||||
|
return h.LoggingMiddleware(h.RequestsCounterMiddleware(next))
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRoutes(h *handler) map[string]http.Handler {
|
||||||
|
routes := map[string]http.Handler{
|
||||||
|
"/ping": middlewares(h, http.HandlerFunc(h.Ping)),
|
||||||
|
"/metrics": middlewares(h, http.HandlerFunc(h.Metrics)),
|
||||||
|
"/healthz": middlewares(h, http.HandlerFunc(h.Healthz)),
|
||||||
|
}
|
||||||
|
return routes
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRouter(handler *handler) http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
for pattern, handler := range createRoutes(handler) {
|
||||||
|
mux.Handle(pattern, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if h, pattern := mux.Handler(r); pattern != "" {
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
middlewares(handler, http.HandlerFunc(handler.NotFound)).ServeHTTP(w, r)
|
||||||
|
http.Error(w, "Boo", http.StatusNotFound)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
26
site/site.go
Normal file
26
site/site.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package site
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Logger *slog.Logger
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig(logLevel slog.Level, port int) Config {
|
||||||
|
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||||||
|
return Config{
|
||||||
|
Logger: logger,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(c Config) error {
|
||||||
|
h := newHandler(c.Logger)
|
||||||
|
return http.ListenAndServe(fmt.Sprintf(":%d", c.Port), newRouter(h))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user