Initialized proper project structure

This commit is contained in:
Robin Dittmar
2026-07-03 11:39:58 +02:00
parent d98e8e9244
commit 5325e04a30
9 changed files with 159 additions and 52 deletions
+32 -52
View File
@@ -1,67 +1,47 @@
package main package main
import ( import (
"encoding/json" "context"
"fmt" "errors"
"log" "log"
"net/http" "net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/robindittmar/dttmr-api/internal/api/router"
) )
type ApiResponse struct {
Method string `json:"method"`
Url string `json:"url"`
Proto string `json:"proto"`
Header map[string]string `json:"header"`
Host string `json:"host"`
RemoteAddr string `json:"remoteAddr"`
Form map[string]string `json:"form"`
}
type HealthResponse struct {
Status string `json:"status"`
}
func main() { func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { cfg := router.Config{}
var resp ApiResponse mux := router.NewMux(cfg)
resp.Method = r.Method srv := &http.Server{
resp.Url = r.URL.String() Addr: ":8080",
resp.Proto = r.Proto Handler: mux,
resp.Header = make(map[string]string) ReadTimeout: 5 * time.Second,
resp.Host = r.Host WriteTimeout: 10 * time.Second,
resp.RemoteAddr = r.RemoteAddr IdleTimeout: 120 * time.Second,
resp.Form = make(map[string]string) }
for k, v := range r.Header { go func() {
resp.Header[k] = v[0] log.Printf("Listening on %s\n", srv.Addr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("Failed to start server: %v", err)
} }
}()
if err := r.ParseForm(); err == nil { quit := make(chan os.Signal, 1)
for k, v := range r.Form { signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
resp.Form[k] = v[0] <-quit
} log.Println("Shutting down server...")
} else {
log.Print(err)
}
err := json.NewEncoder(w).Encode(resp) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
if err != nil { defer cancel()
log.Println(err)
return
}
})
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { if err := srv.Shutdown(ctx); err != nil {
resp := HealthResponse{Status: "ok"} log.Fatalf("Server forced to shutdown: %v", err)
}
err := json.NewEncoder(w).Encode(resp) log.Println("Server shutdown successful!")
if err != nil {
log.Println(err)
return
}
})
fmt.Println("Listening on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
} }
+44
View File
@@ -0,0 +1,44 @@
package handler
import (
"log"
"net/http"
"github.com/robindittmar/dttmr-api/internal/api/response"
)
type apiResponse struct {
Method string `json:"method"`
Url string `json:"url"`
Proto string `json:"proto"`
Header map[string]string `json:"header"`
Host string `json:"host"`
RemoteAddr string `json:"remoteAddr"`
Form map[string]string `json:"form"`
}
func DefaultHandler(w http.ResponseWriter, r *http.Request) {
var resp apiResponse
resp.Method = r.Method
resp.Url = r.URL.String()
resp.Proto = r.Proto
resp.Header = make(map[string]string)
resp.Host = r.Host
resp.RemoteAddr = r.RemoteAddr
resp.Form = make(map[string]string)
for k, v := range r.Header {
resp.Header[k] = v[0]
}
if err := r.ParseForm(); err == nil {
for k, v := range r.Form {
resp.Form[k] = v[0]
}
} else {
log.Println(err)
}
response.JSON(w, http.StatusOK, resp)
}
+18
View File
@@ -0,0 +1,18 @@
package handler
import (
"net/http"
"github.com/robindittmar/dttmr-api/internal/api/response"
)
type healthResponse struct {
Status string `json:"status"`
}
func HealthHandler(w http.ResponseWriter, r *http.Request) {
resp := healthResponse{
Status: "OK",
}
response.JSON(w, http.StatusOK, resp)
}
+10
View File
@@ -0,0 +1,10 @@
package middleware
import (
"net/http"
)
func WithTelemetry(next http.Handler) http.Handler {
//return otelhttp.NewHandler(next, "dttmr-api")
return next
}
+33
View File
@@ -0,0 +1,33 @@
package response
import (
"encoding/json"
"log"
"net/http"
)
func JSON(w http.ResponseWriter, status int, data any) {
payload, err := json.Marshal(data)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write([]byte(`{"error": "internal server error: failed to marshal response"}`))
if err != nil {
log.Println(err)
return
}
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, err = w.Write(payload)
if err != nil {
log.Println(err)
return
}
}
func Error(w http.ResponseWriter, status int, message string) {
JSON(w, status, map[string]string{"error": message})
}
+22
View File
@@ -0,0 +1,22 @@
package router
import (
"net/http"
"github.com/robindittmar/dttmr-api/internal/api/handler"
"github.com/robindittmar/dttmr-api/internal/api/middleware"
)
type Config struct{}
func NewMux(cfg Config) http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", handler.DefaultHandler)
mux.HandleFunc("GET /health", handler.HealthHandler)
var httpHandler http.Handler = mux
httpHandler = middleware.WithTelemetry(httpHandler)
return httpHandler
}
View File
View File