diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..6df777c --- /dev/null +++ b/.air.toml @@ -0,0 +1,52 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + args_bin = [] + bin = "tmp\\main.exe" + cmd = "go build -o ./tmp/main.exe ./cmd/web/main.go" + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html", "css", "gohtml"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + post_cmd = [] + pre_cmd = [] + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + silent = false + time = false + +[misc] + clean_on_exit = false + +[proxy] + app_port = 0 + enabled = false + proxy_port = 0 + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.gitignore b/.gitignore index f45732e..84be3c1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ profile.cov go.work go.work.sum .env +tmp/ diff --git a/cmd/web/main.go b/cmd/web/main.go index a3dd973..29bf195 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -1,7 +1,58 @@ package main -import "fmt" +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/StevanFreeborn/links.stevanfreeborn.com/internal/handlers" + "github.com/StevanFreeborn/links.stevanfreeborn.com/internal/middleware" +) func main() { - fmt.Println("Hello, World!") + mux := http.NewServeMux() + + mux.HandleFunc("/", handlers.Index) + mux.HandleFunc("/css/", handlers.CSS) + + loggingMux := middleware.Logging(mux) + + const addr = ":7777" + + server := &http.Server{ + Addr: addr, + Handler: loggingMux, + } + + go func() { + fmt.Printf("Starting server on %s\n", addr) + + err := server.ListenAndServe() + + if !errors.Is(err, http.ErrServerClosed) { + log.Fatalf("HTTP server error: %v", err) + } + + log.Println("Stopped serving new connections.") + }() + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + <-sigChan + + shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second) + + defer shutdownRelease() + + if err := server.Shutdown(shutdownCtx); err != nil { + log.Fatalf("HTTP shutdown error: %v", err) + } + + log.Println("Graceful shutdown complete.") } diff --git a/internal/assets/css/style.css b/internal/assets/css/style.css new file mode 100644 index 0000000..0aecf4f --- /dev/null +++ b/internal/assets/css/style.css @@ -0,0 +1,54 @@ +/* CSS Resets */ +* { + margin: 0; + padding: 0; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +button { + background: none; + border: none; + outline: none; + cursor: pointer; +} + +a { + text-decoration: none; + color: inherit; +} + +ul { + list-style-type: none; +} + +img { + max-width: 100%; + display: block; +} + +table { + width: 100%; + border-collapse: collapse; +} + +/* Site Styles */ + +:root { + font-size: 16px; +} + +html, +body { + height: 100%; +} + + + + + + diff --git a/internal/assets/main.go b/internal/assets/main.go new file mode 100644 index 0000000..ff9b3f0 --- /dev/null +++ b/internal/assets/main.go @@ -0,0 +1,13 @@ +// Package assets contains embedded static assets for the application. +package assets + +import ( + "embed" +) + +var ( + //go:embed "templates/*" + Templates embed.FS + //go:embed "css/*" + CSS embed.FS +) diff --git a/internal/assets/templates/index.gohtml b/internal/assets/templates/index.gohtml new file mode 100644 index 0000000..5abfd13 --- /dev/null +++ b/internal/assets/templates/index.gohtml @@ -0,0 +1,12 @@ + + + + + + Stevan's Links + + + +

Stevan's Links

+ + diff --git a/internal/handlers/main.go b/internal/handlers/main.go new file mode 100644 index 0000000..2e9178e --- /dev/null +++ b/internal/handlers/main.go @@ -0,0 +1,24 @@ +// Package handlers provides HTTP handlers for the web application. +package handlers + +import ( + "net/http" + "text/template" + + "github.com/StevanFreeborn/links.stevanfreeborn.com/internal/assets" +) + +func Index(w http.ResponseWriter, r *http.Request) { + t, err := template.ParseFS(assets.Templates, "templates/index.gohtml") + + if err != nil { + http.Error(w, "Unable to load template", http.StatusInternalServerError) + return + } + + t.Execute(w, nil) +} + +func CSS(w http.ResponseWriter, r *http.Request) { + http.ServeFileFS(w, r, assets.CSS, r.URL.Path) +} diff --git a/internal/middleware/main.go b/internal/middleware/main.go new file mode 100644 index 0000000..61c0785 --- /dev/null +++ b/internal/middleware/main.go @@ -0,0 +1,16 @@ +// Package middleware provides HTTP middleware for the web application. +package middleware + +import ( + "log" + "net/http" + "time" +) + +func Logging(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + next.ServeHTTP(w, r) + log.Printf("%s %s %s", r.Method, r.RequestURI, time.Since(start)) + }) +} diff --git a/static/style.css b/static/style.css deleted file mode 100644 index e69de29..0000000 diff --git a/templates/index.gohtml b/templates/index.gohtml deleted file mode 100644 index e69de29..0000000