-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
89727e8
commit 21b7fae
Showing
6 changed files
with
359 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
label: Authentication | ||
position: 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
--- | ||
title: "CORS" | ||
sidebar_position: 5 | ||
--- | ||
|
||
# CORS Middleware Configuration and Usage | ||
|
||
This document explains how to configure and use the CORS (Cross-Origin Resource Sharing) middleware in the Go REST API Boilerplate project. The middleware is implemented using the `gin-contrib/cors` package and is designed to be flexible and configurable. | ||
|
||
## Overview | ||
|
||
The CORS middleware allows you to control which origins can access your API. It's crucial for securing your API while still allowing legitimate cross-origin requests. | ||
|
||
## Configuration | ||
|
||
The middleware is configured in the `CORSMiddleware()` function, which returns a `gin.HandlerFunc`. Here's how it works: | ||
|
||
1. **Default Configuration**: It starts with the default CORS configuration. | ||
|
||
2. **Allowed Origins**: | ||
- The middleware checks for an environment variable `ALLOWED_ORIGINS`. | ||
- If set, it uses these origins as the allowed origins. | ||
- If not set, it allows any origin that starts with `http://localhost` or `https://localhost`. | ||
|
||
3. **Allowed Methods**: The middleware allows the following HTTP methods: | ||
- GET | ||
- POST | ||
- PUT | ||
- PATCH | ||
- DELETE | ||
- OPTIONS | ||
|
||
4. **Allowed Headers**: The middleware allows the following headers: | ||
- Origin | ||
- Content-Type | ||
- Accept | ||
- Authorization | ||
|
||
## Usage | ||
|
||
To use this middleware in your Gin application, follow these steps: | ||
|
||
1. Import the middleware package: | ||
|
||
```go | ||
import "go-rest-api/internal/api/middleware" | ||
``` | ||
|
||
2. Add the middleware to your Gin router: | ||
|
||
```go | ||
func main() { | ||
router := gin.Default() | ||
|
||
// Apply the CORS middleware | ||
router.Use(middleware.CORSMiddleware()) | ||
|
||
// Your routes go here | ||
// ... | ||
|
||
router.Run() | ||
} | ||
``` | ||
|
||
## Configuration via Environment Variables | ||
|
||
To configure allowed origins using environment variables: | ||
|
||
1. Set the `ALLOWED_ORIGINS` environment variable before running your application. Multiple origins should be comma-separated. | ||
|
||
Example: | ||
```bash | ||
export ALLOWED_ORIGINS="https://example.com,https://api.example.com" | ||
``` | ||
|
||
2. If `ALLOWED_ORIGINS` is not set, the middleware will default to allowing any origin that starts with `http://localhost` or `https://localhost`. | ||
|
||
## Customization | ||
|
||
If you need to customize the CORS settings further: | ||
|
||
1. Modify the `CORSMiddleware()` function in the middleware package. | ||
2. You can adjust allowed methods, headers, or add more sophisticated origin checking logic. | ||
|
||
Example of adding a custom header: | ||
|
||
```go | ||
config.AllowHeaders = append(config.AllowHeaders, "X-Custom-Header") | ||
``` | ||
|
||
## Security Considerations | ||
|
||
1. **Restrict Origins**: In production, always set `ALLOWED_ORIGINS` to a specific list of trusted domains. | ||
2. **Least Privilege**: Only expose the methods and headers that your API actually needs. | ||
3. **Credentials**: If your API requires credentials (cookies, HTTP authentication), you may need to set `config.AllowCredentials = true`. Use this with caution and ensure `AllowOrigins` is not set to `*`. | ||
|
||
## Troubleshooting | ||
|
||
If you're experiencing CORS issues: | ||
|
||
1. Check that the `ALLOWED_ORIGINS` environment variable is set correctly. | ||
2. Ensure that the origin making the request matches exactly with one of the allowed origins (including the protocol, `http://` or `https://`). | ||
3. Verify that the request is using an allowed method and only includes allowed headers. | ||
|
||
## Example | ||
|
||
Here's a complete example of setting up a Gin router with the CORS middleware: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
"go-rest-api/internal/api/middleware" | ||
) | ||
|
||
func main() { | ||
// Set up Gin | ||
router := gin.Default() | ||
|
||
// Apply CORS middleware | ||
router.Use(middleware.CORSMiddleware()) | ||
|
||
// Define a route | ||
router.GET("/api/data", func(c *gin.Context) { | ||
c.JSON(200, gin.H{ | ||
"message": "This is CORS-enabled data", | ||
}) | ||
}) | ||
|
||
// Run the server | ||
router.Run(":8080") | ||
} | ||
``` | ||
|
||
In this example, the CORS middleware will be applied to all routes. The allowed origins will be determined by the `ALLOWED_ORIGINS` environment variable, or default to localhost if not set. | ||
|
||
By following these guidelines, you can effectively implement and customize CORS in your Go REST API, ensuring that your API is accessible to the intended clients while maintaining security. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
label: Deployments | ||
label: Deployments |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
--- | ||
sidebar_position: 2 | ||
sidebar_position: 1 | ||
--- | ||
|
||
# Docker Deployment | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
--- | ||
title: "Logging" | ||
sidebar_position: 3 | ||
--- | ||
|
||
# Logging | ||
|
||
This document explains how to use the custom logger implemented in the Go REST API Boilerplate project. The logger is a singleton wrapper around the `zap` logging library, providing a simple and consistent way to log messages across your application. | ||
|
||
## Overview | ||
|
||
The logger package provides a global `Log` variable of type `*Logger`, which is a wrapper around `zap.Logger`. It's designed as a singleton to ensure that only one instance of the logger is created and used throughout the application. | ||
|
||
## Initialization | ||
|
||
Before using the logger, you need to initialize it. This is typically done at the start of your application: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
logger "go-rest-api/pkg" | ||
) | ||
|
||
func main() { | ||
logger.Init() | ||
// Rest of your application code | ||
} | ||
``` | ||
|
||
The `Init()` function uses a `sync.Once` to ensure that the logger is only initialized once, even if `Init()` is called multiple times. | ||
|
||
## Basic Usage | ||
|
||
After initialization, you can use the logger throughout your application. The package provides several logging methods: | ||
|
||
### Logging at Different Levels | ||
|
||
```go | ||
logger.Info("This is an info message") | ||
logger.Error("This is an error message") | ||
logger.Fatal("This is a fatal message") // This will also call os.Exit(1) | ||
``` | ||
|
||
### Logging with Additional Fields | ||
|
||
You can add structured fields to your log messages: | ||
|
||
```go | ||
logger.Info("User logged in", zap.String("username", "john_doe"), zap.Int("user_id", 12345)) | ||
``` | ||
|
||
|
||
### Creating a Logger with Preset Fields | ||
|
||
If you want to create a logger with some fields already set (useful for adding context to all logs from a particular component): | ||
|
||
```go | ||
componentLogger := logger.With(zap.String("component", "auth_service")) | ||
componentLogger.Info("Starting authentication") | ||
``` | ||
|
||
## Advanced Usage | ||
|
||
### Direct Access to Underlying Zap Logger | ||
|
||
If you need access to the underlying `zap.Logger` for advanced use cases: | ||
|
||
```go | ||
zapLogger := logger.Log.Logger | ||
// Use zapLogger for zap-specific functionality | ||
``` | ||
|
||
### Custom Log Fields | ||
|
||
You can create custom log fields using `zap.Field` constructors: | ||
|
||
```go | ||
customField := zap.Any("custom_data", someComplexStruct) | ||
logger.Info("Message with custom data", customField) | ||
``` | ||
|
||
## Best Practices | ||
|
||
1. **Initialization**: Always call `logger.Init()` at the start of your application. | ||
|
||
2. **Log Levels**: Use appropriate log levels: | ||
- `Info` for general information | ||
- `Error` for error conditions | ||
- `Fatal` for unrecoverable errors (use sparingly as it terminates the program) | ||
|
||
3. **Structured Logging**: Prefer using structured fields over string interpolation for better searchability and analysis of logs. | ||
|
||
4. **Context**: Use `logger.With()` to add context to logs from specific components or request handlers. | ||
|
||
5. **Performance**: The logger is designed to be performant, but avoid excessive logging in hot paths. | ||
|
||
## Customization | ||
|
||
The logger is initialized with a production configuration. If you need to customize the logger (e.g., for development environments), you can modify the `Init()` function in `logger.go`. | ||
|
||
## Examples | ||
|
||
### In HTTP Handlers | ||
|
||
```go | ||
func UserHandler(w http.ResponseWriter, r *http.Request) { | ||
userID := getUserIDFromRequest(r) | ||
requestLogger := logger.With(zap.String("handler", "UserHandler"), zap.Int("user_id", userID)) | ||
|
||
requestLogger.Info("Processing user request") | ||
|
||
// Handler logic... | ||
|
||
if err != nil { | ||
requestLogger.Error("Failed to process user request", zap.Error(err)) | ||
// Handle error... | ||
} | ||
|
||
requestLogger.Info("User request processed successfully") | ||
} | ||
``` | ||
|
||
### In Services | ||
|
||
```go | ||
type AuthService struct { | ||
logger *logger.Logger | ||
} | ||
|
||
func NewAuthService() *AuthService { | ||
return &AuthService{ | ||
logger: logger.With(zap.String("service", "auth")), | ||
} | ||
} | ||
|
||
func (s *AuthService) Authenticate(username, password string) error { | ||
s.logger.Info("Attempting authentication", zap.String("username", username)) | ||
|
||
// Authentication logic... | ||
|
||
if authFailed { | ||
s.logger.Error("Authentication failed", zap.String("username", username)) | ||
return ErrAuthFailed | ||
} | ||
|
||
s.logger.Info("Authentication successful", zap.String("username", username)) | ||
return nil | ||
} | ||
``` | ||
|
||
By following these guidelines and examples, you can effectively use the logger throughout your application to produce consistent, structured logs that will aid in monitoring, debugging, and maintaining your Go REST API. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package logger | ||
|
||
import ( | ||
"os" | ||
"sync" | ||
|
||
"go.uber.org/zap" | ||
"go.uber.org/zap/zapcore" | ||
) | ||
|
||
var ( | ||
Log *Logger | ||
once sync.Once | ||
) | ||
|
||
type Logger struct { | ||
*zap.Logger | ||
} | ||
|
||
func Init() { | ||
once.Do(func() { | ||
config := zap.NewProductionConfig() | ||
config.EncoderConfig.TimeKey = "timestamp" | ||
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder | ||
|
||
logger, err := config.Build() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
Log = &Logger{logger} | ||
}) | ||
} | ||
|
||
func (l *Logger) Info(msg string, fields ...zap.Field) { | ||
l.Logger.Info(msg, fields...) | ||
} | ||
|
||
func (l *Logger) Error(msg string, fields ...zap.Field) { | ||
l.Logger.Error(msg, fields...) | ||
} | ||
|
||
func (l *Logger) Fatal(msg string, fields ...zap.Field) { | ||
l.Logger.Fatal(msg, fields...) | ||
os.Exit(1) | ||
} | ||
|
||
func (l *Logger) With(fields ...zap.Field) *Logger { | ||
return &Logger{l.Logger.With(fields...)} | ||
} | ||
|
||
func Info(msg string, fields ...zap.Field) { | ||
Log.Info(msg, fields...) | ||
} | ||
|
||
func Error(msg string, fields ...zap.Field) { | ||
Log.Error(msg, fields...) | ||
} | ||
|
||
func Fatal(msg string, fields ...zap.Field) { | ||
Log.Fatal(msg, fields...) | ||
} | ||
|
||
func With(fields ...zap.Field) *Logger { | ||
return Log.With(fields...) | ||
} |