feat: initial implementation of go-kite

Core framework:
- Kite router with full HTTP method support (GET, POST, PUT, DELETE, HEAD, OPTIONS, CONNECT, TRACE)
- Nestable route groups with prefix and middleware inheritance
- Global, group, and route-level middleware with onion ordering
- Centralized error, not-found, and method-not-allowed handlers
- Graceful shutdown on SIGTERM/SIGINT with configurable timeout
- Configurable server read, write, and idle timeouts
- Context with typed request/response helpers, value store, cookie, form, and body binding support
- Response writer wrapper for status code tracking

Middleware:
- Logger with configurable output, format, and skip function
- Recovery with configurable panic handler
- RequestID with configurable header and generator, forwards incoming IDs
- CORS with configurable origins, methods, headers, credentials, and max age
- MaxBodySize with configurable byte limit

Docs:
- README with quickstart, routing, middleware, context API reference, and TLS guide
This commit is contained in:
2026-04-23 20:25:13 +02:00
parent 23ae03a92b
commit dd92f23e0b
18 changed files with 1126 additions and 10 deletions

51
middleware/logger.go Normal file
View File

@@ -0,0 +1,51 @@
package middleware
import (
"fmt"
"io"
"log"
"os"
"time"
"git.trcreatives.at/trcreatives/go-kite"
)
type LoggerConfig struct {
Output io.Writer
Format func(method, path string, statusCode int, latency time.Duration) string
Skip func(ctx *kite.Context) bool
}
func DefaultLoggerFormat(method, path string, statusCode int, latency time.Duration) string {
return fmt.Sprintf("[Kite] %s %s -> %d in %s", method, path, statusCode, latency)
}
func Logger() kite.Middleware {
return LoggerWithConfig(LoggerConfig{})
}
func LoggerWithConfig(cfg LoggerConfig) kite.Middleware {
if cfg.Output == nil {
cfg.Output = os.Stdout
}
if cfg.Format == nil {
cfg.Format = DefaultLoggerFormat
}
logger := log.New(cfg.Output, "", log.LstdFlags)
return func(h kite.Handler) kite.Handler {
return func(ctx *kite.Context) error {
if cfg.Skip != nil && cfg.Skip(ctx) {
return h(ctx)
}
start := time.Now()
err := h(ctx)
logger.Println(cfg.Format(ctx.GetMethod(), ctx.GetPath(), ctx.GetStatusCode(), time.Since(start)))
return err
}
}
}