updated docs

2026-01-21 19:20:52 +11:00
parent 65c020c133
commit 7f7c036292
4 changed files with 575 additions and 5 deletions

@@ -1,4 +1,4 @@
# EZConf - v0.1.0
# EZConf - v0.1.1
A unified configuration management system for loading and managing environment-based configurations across multiple Go packages.
@@ -59,8 +59,13 @@ func main() {
hwsauth.NewEZConfIntegration(),
)
// Load everything
if err := loader.Load(); err != nil {
// Parse the envvars from the config structs
if err := loader.ParseEnvVars(); err != nil {
log.Fatal(err)
}
// Load the configs
if err := loader.LoadConfigs(); err != nil {
log.Fatal(err)
}
@@ -262,6 +267,8 @@ for name, cfg := range allConfigs {
```
### Printing Environment Variables
Printing the environment variables requires `loader.ParseEnvVars` to have been called.
If passing `true` to show the values, `loader.LoadConfigs` must also be called
```go
// Print variable names and descriptions (no values)
@@ -275,6 +282,7 @@ if err := loader.PrintEnvVarsStdout(false); err != nil {
// DATABASE_URL # Database connection string (required)
// Print with current values
// Requires loader.LoadConfigs to be called
if err := loader.PrintEnvVarsStdout(true); err != nil {
log.Fatal(err)
}
@@ -285,8 +293,6 @@ if err := loader.PrintEnvVarsStdout(true); err != nil {
// DATABASE_URL=postgres://localhost/db # Database connection string (required)
```
**Note:** `PrintEnvVarsStdout()` and `PrintEnvVars()` will return an error if called before `Load()`. Make sure to call `Load()` first.
### Writing to Custom Writer
```go

@@ -22,6 +22,12 @@ Unified configuration management system for loading and managing environment-bas
### [TMDB](TMDB.md)
Go client library for The Movie Database (TMDB) API. Features automatic rate limiting, retry logic with exponential backoff, movie search, detailed movie information, cast/crew data, and convenient image URL helpers.
### [env](env.md)
Environment variable utilities for Go applications with type safety and default values. Supports all basic Go types including strings, integers (all sizes), unsigned integers (all sizes), booleans, and time.Duration values with comprehensive boolean parsing.
### [cookies](cookies.md)
HTTP cookie utilities for Go web applications with security best practices. Provides secure cookie setting, proper deletion, pagefrom tracking for post-login redirects, and host validation for referer-based redirects.
## Installation
```bash

313
cookies.md Normal file

@@ -0,0 +1,313 @@
# cookies v1.0.0
HTTP cookie utilities for Go web applications with security best practices.
## Installation
```bash
go get git.haelnorr.com/h/golib/cookies
```
## Key Concepts and Features
The cookies package provides secure utilities for handling HTTP cookies in Go web applications. All cookies are set with the HttpOnly flag by default to prevent XSS attacks.
### Core Functions
- **SetCookie**: Set secure cookies with configurable path and max-age
- **DeleteCookie**: Properly delete cookies by setting expiration in the past
- **SetPageFrom**: Track the referring page for post-login redirects
- **CheckPageFrom**: Retrieve and delete pagefrom tracking cookie
## Quick Start
```go
package main
import (
"net/http"
"git.haelnorr.com/h/golib/cookies"
)
func loginHandler(w http.ResponseWriter, r *http.Request) {
// Set pagefrom cookie before redirecting to login
cookies.SetPageFrom(w, r, "example.com")
http.Redirect(w, r, "/login", http.StatusFound)
}
func postLoginHandler(w http.ResponseWriter, r *http.Request) {
// Set session cookie after successful login
cookies.SetCookie(w, "session", "/", "user-session-id", 3600)
// Redirect back to the original page
redirectTo := cookies.CheckPageFrom(w, r)
http.Redirect(w, r, redirectTo, http.StatusFound)
}
func logoutHandler(w http.ResponseWriter, r *http.Request) {
// Delete session cookie
cookies.DeleteCookie(w, "session", "/")
http.Redirect(w, r, "/login", http.StatusFound)
}
```
## Configuration
### Cookie Security
All cookies set by this package include:
- **HttpOnly**: Prevents JavaScript access (XSS protection)
- **Configurable Path**: Restricts cookie to specific paths
- **Configurable Max-Age**: Controls cookie lifetime
### Environment Variables
The package doesn't require specific environment variables, but commonly used ones include:
```go
// ENV COOKIE_DOMAIN: Cookie domain (optional)
// ENV COOKIE_SECURE: Use HTTPS-only cookies (default: true in production)
// ENV TRUSTED_HOST: Host for pagefrom validation (recommended)
```
## Detailed Usage
### Setting Cookies
```go
// Basic session cookie (1 hour)
cookies.SetCookie(w, "session", "/", "abc123", 3600)
// Persistent cookie (30 days)
cookies.SetCookie(w, "preferences", "/", "dark-mode", 2592000)
// Temporary cookie (browser session)
cookies.SetCookie(w, "temp", "/", "data", 0)
// Short-lived cookie (5 minutes)
cookies.SetCookie(w, "csrf", "/", "token", 300)
```
### Deleting Cookies
```go
// Delete session cookie
cookies.DeleteCookie(w, "session", "/")
// Delete cookie with specific path
cookies.DeleteCookie(w, "preferences", "/user")
// Delete admin cookie
cookies.DeleteCookie(w, "admin_token", "/admin")
```
### Pagefrom Tracking
The pagefrom functionality helps maintain user experience by remembering where users were before authentication:
```go
// Middleware to track pagefrom before auth
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if needsAuth(r) && !isAuthenticated(r) {
// Track where user was going
cookies.SetPageFrom(w, r, "example.com")
http.Redirect(w, r, "/login", http.StatusFound)
return
}
next.ServeHTTP(w, r)
})
}
// After successful login
func loginSuccessHandler(w http.ResponseWriter, r *http.Request) {
// Authenticate user...
// Get original destination
redirectTo := cookies.CheckPageFrom(w, r)
// Default to dashboard if no pagefrom
if redirectTo == "/" {
redirectTo = "/dashboard"
}
http.Redirect(w, r, redirectTo, http.StatusFound)
}
```
### Host Validation
Pagefrom tracking includes host validation to prevent open redirects:
```go
// Only allows redirects from trusted host
cookies.SetPageFrom(w, r, "example.com")
// This will be rejected and redirect to "/"
// Referer: http://evil.com/steal-data
// This will be accepted
// Referer: http://example.com/dashboard
```
## Integration
### With HWS (Web Server)
```go
import "git.haelnorr.com/h/golib/hws"
import "git.haelnorr.com/h/golib/cookies"
func main() {
server := hws.NewServer()
// Add auth middleware
server.Use(authMiddleware)
// Login routes
server.Get("/login", loginHandler)
server.Post("/login", postLoginHandler)
server.Get("/logout", logoutHandler)
server.Start()
}
func authMiddleware(ctx *hws.Context) {
if !isAuthenticated(ctx.Request) {
cookies.SetPageFrom(ctx.ResponseWriter, ctx.Request, "example.com")
ctx.Redirect("/login", http.StatusFound)
return
}
ctx.Next()
}
```
### With HWSAuth (Authentication)
```go
import "git.haelnorr.com/h/golib/hwsauth"
import "git.haelnorr.com/h/golib/cookies"
func loginHandler(ctx *hws.Context) {
// Validate credentials...
if valid {
// Create JWT token
token, err := hwsauth.CreateToken(userID)
if err != nil {
ctx.Error("Failed to create token", http.StatusInternalServerError)
return
}
// Set cookie with token
cookies.SetCookie(ctx.ResponseWriter, "auth", "/", token, 3600)
// Redirect to original destination
redirectTo := cookies.CheckPageFrom(ctx.ResponseWriter, ctx.Request)
ctx.Redirect(redirectTo, http.StatusFound)
}
}
```
## Best Practices
1. **Use HttpOnly**: All cookies are HttpOnly by default for security
2. **Set appropriate paths**: Restrict cookies to necessary paths
3. **Use reasonable max-age**: Don't keep cookies longer than needed
4. **Validate pagefrom**: Always use trusted host validation
5. **Delete properly**: Use DeleteCookie instead of setting empty values
```go
// Good: Specific path and reasonable max-age
cookies.SetCookie(w, "session", "/", sessionID, 3600)
// Good: Proper deletion
cookies.DeleteCookie(w, "session", "/")
// Avoid: Too broad or too persistent
cookies.SetCookie(w, "data", "/", "value", 31536000) // 1 year
```
### Security Considerations
1. **HTTPS in Production**: Always use HTTPS for cookies with sensitive data
2. **SameSite**: Consider SameSite attribute for CSRF protection
3. **Domain Restriction**: Set domain to restrict cookies to your domain
4. **Path Restriction**: Use specific paths when possible
```go
// Production example with additional security
func setSecureCookie(w http.ResponseWriter, name, value string) {
cookie := &http.Cookie{
Name: name,
Value: value,
Path: "/",
Domain: "example.com",
MaxAge: 3600,
HttpOnly: true,
Secure: true, // HTTPS only
SameSite: http.SameSiteStrictMode,
}
http.SetCookie(w, cookie)
}
```
## Troubleshooting
### Common Issues
1. **Cookie not setting**: Check that response hasn't been written yet
2. **Cookie not deleting**: Ensure path matches original cookie path
3. **Pagefrom not working**: Verify trusted host matches referer host
4. **Redirect loops**: Check authentication logic carefully
### Debug Tips
```go
// Debug cookie headers
func debugCookies(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Request cookies: %v\n", r.Cookies())
// Check response headers after setting cookies
headers := w.Header()
fmt.Printf("Set-Cookie headers: %v\n", headers["Set-Cookie"])
}
```
### Testing Pagefrom
```go
// Test pagefrom flow manually
func testPagefromFlow() {
// 1. Set pagefrom
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/protected", nil)
r.Header.Set("Referer", "http://example.com/dashboard")
cookies.SetPageFrom(w, r, "example.com")
// 2. Check pagefrom
w2 := httptest.NewRecorder()
r2 := httptest.NewRequest("GET", "/login", nil)
// Add the cookie from step 1
for _, cookie := range w.Result().Cookies() {
r2.AddCookie(cookie)
}
redirectTo := cookies.CheckPageFrom(w2, r2)
fmt.Printf("Redirect to: %s\n", redirectTo) // Should be "/dashboard"
}
```
## See Also
- [HWS](HWS.md) - Web server framework for building applications
- [HWSAuth](HWSAuth.md) - JWT authentication system
- [env](env.md) - Environment variable configuration
## Links
- [GoDoc API](https://pkg.go.dev/git.haelnorr.com/h/golib/cookies)
- [Source Code](https://git.haelnorr.com/h/golib/src/branch/main/cookies)
- [Issue Tracker](https://git.haelnorr.com/h/golib/issues)

245
env.md Normal file

@@ -0,0 +1,245 @@
# env v1.0.0
Environment variable utilities for Go applications with type safety and default values.
## Installation
```bash
go get git.haelnorr.com/h/golib/env
```
## Key Concepts and Features
The env package provides type-safe functions for reading environment variables with automatic fallback to default values. All functions handle missing environment variables gracefully by returning the provided default value.
### Supported Types
- **String**: Basic string values
- **Integers**: int, int8, int16, int32, int64
- **Unsigned Integers**: uint, uint8, uint16, uint32, uint64
- **Boolean**: Flexible boolean parsing with multiple truthy/falsy values
- **Duration**: time.Duration values in nanoseconds
## Quick Start
```go
package main
import (
"fmt"
"time"
"git.haelnorr.com/h/golib/env"
)
func main() {
// String values
host := env.String("HOST", "localhost")
// Integer values (all sizes supported)
port := env.Int("PORT", 8080)
timeout := env.Int64("TIMEOUT_SECONDS", 30)
// Unsigned integer values
maxConnections := env.UInt("MAX_CONNECTIONS", 100)
// Boolean values (supports many formats)
debug := env.Bool("DEBUG", false)
// Duration values
requestTimeout := env.Duration("REQUEST_TIMEOUT", 30*time.Second)
fmt.Printf("Server: %s:%d\n", host, port)
fmt.Printf("Debug: %v\n", debug)
fmt.Printf("Timeout: %v\n", requestTimeout)
}
```
## Configuration
### Environment Variables
All functions follow the pattern: `FunctionName("ENV_VAR_NAME", defaultValue)`
#### String Values
```go
// ENV HOST: Server hostname (default: localhost)
host := env.String("HOST", "localhost")
// ENV DATABASE_URL: Database connection string (required)
dbURL := env.String("DATABASE_URL", "")
```
#### Integer Values
```go
// ENV PORT: Server port (default: 8080)
port := env.Int("PORT", 8080)
// ENV MAX_CONNECTIONS: Maximum database connections (default: 10)
maxConn := env.Int16("MAX_CONNECTIONS", 10)
```
#### Boolean Values
```go
// ENV DEBUG: Enable debug mode (default: false)
debug := env.Bool("DEBUG", false)
// ENV PRODUCTION: Production mode flag (default: false)
prod := env.Bool("PRODUCTION", false)
```
#### Duration Values
```go
// ENV TIMEOUT: Request timeout in seconds (default: 30)
timeout := env.Duration("TIMEOUT", 30*time.Second)
```
### Boolean Parsing
The boolean parser supports many common truthy and falsy values:
**Truthy values**: true, t, yes, y, on, 1, enable, enabled, active, affirmative
**Falsy values**: false, f, no, n, off, 0, disable, disabled, inactive, negative
All values are case-insensitive and whitespace is trimmed.
## Detailed Usage
### String Functions
```go
// Basic string with default
apiKey := env.String("API_KEY", "default-key")
// Empty string as default
optional := env.String("OPTIONAL", "")
```
### Integer Functions
All integer types are supported with consistent behavior:
```go
// Different integer sizes
port := env.Int("PORT", 8080)
smallNum := env.Int8("SMALL_NUM", 42)
mediumNum := env.Int16("MEDIUM_NUM", 1000)
largeNum := env.Int32("LARGE_NUM", 100000)
veryLarge := env.Int64("VERY_LARGE", 1000000000)
```
### Unsigned Integer Functions
```go
// Unsigned integers (reject negative values)
maxUsers := env.UInt("MAX_USERS", 1000)
byteValue := env.UInt8("BYTE_VALUE", 255)
shortValue := env.UInt16("SHORT_VALUE", 65535)
normalValue := env.UInt32("NORMAL_VALUE", 4294967295)
bigValue := env.UInt64("BIG_VALUE", 18446744073709551615)
```
### Boolean Functions
```go
// Boolean with various input formats
enabled := env.Bool("ENABLED", false)
verbose := env.Bool("VERBOSE", true)
maintenance := env.Bool("MAINTENANCE", false)
```
### Duration Functions
```go
// Duration in nanoseconds (from integer env var)
timeout := env.Duration("TIMEOUT", 30*time.Second)
heartbeat := env.Duration("HEARTBEAT", 5*time.Second)
```
## Integration
The env package integrates well with other golib packages:
### With HWS (Web Server)
```go
import "git.haelnorr.com/h/golib/env"
import "git.haelnorr.com/h/golib/hws"
func main() {
port := env.Int("PORT", 8080)
debug := env.Bool("DEBUG", false)
server := hws.NewServer()
server.SetPort(port)
server.SetDebug(debug)
server.Start()
}
```
### With Database Packages
```go
import "git.haelnorr.com/h/golib/env"
func getDBConfig() (host string, port int, timeout time.Duration) {
host = env.String("DB_HOST", "localhost")
port = env.Int("DB_PORT", 5432)
timeout = env.Duration("DB_TIMEOUT", 30*time.Second)
return
}
```
## Best Practices
1. **Use meaningful defaults**: Provide sensible defaults that work in development
2. **Document required variables**: Use comments to indicate which variables are required
3. **Prefer specific types**: Use the most appropriate integer size for your use case
4. **Handle validation**: For complex validation, parse the env var then validate separately
5. **Group related config**: Consider using a struct to group related configuration
```go
type ServerConfig struct {
Host string
Port int
Debug bool
Timeout time.Duration
}
func LoadServerConfig() *ServerConfig {
return &ServerConfig{
Host: env.String("HOST", "localhost"),
Port: env.Int("PORT", 8080),
Debug: env.Bool("DEBUG", false),
Timeout: env.Duration("TIMEOUT", 30*time.Second),
}
}
```
## Troubleshooting
### Common Issues
1. **Parsing errors**: All functions return the default value on parsing errors
2. **Type mismatches**: Ensure environment variables contain valid values for the target type
3. **Missing variables**: Always provide a sensible default value
### Debug Tips
```go
// Debug environment variable loading
func debugEnvVars() {
fmt.Printf("HOST: %s\n", env.String("HOST", "not-set"))
fmt.Printf("PORT: %d\n", env.Int("PORT", 0))
fmt.Printf("DEBUG: %v\n", env.Bool("DEBUG", false))
}
```
## See Also
- [HWS](HWS.md) - Web server framework that uses env for configuration
- [EZConf](EZConf.md) - Configuration management system
- [HWSAuth](HWSAuth.md) - Authentication package with env-based configuration
## Links
- [GoDoc API](https://pkg.go.dev/git.haelnorr.com/h/golib/env)
- [Source Code](https://git.haelnorr.com/h/golib/src/branch/main/env)
- [Issue Tracker](https://git.haelnorr.com/h/golib/issues)