### Install and Use go-eagle CLI for Project Scaffolding Source: https://context7.com/limitcool/starter/llms.txt Install the eagle scaffolding tool globally and use it to create a new project from the Starter template. Copy the example configuration for local development and run migrations before starting the server. ```bash # Install the eagle scaffolding tool go install github.com/go-eagle/eagle/cmd/eagle@latest # Create a new project based on Starter eagle new my-app -r https://github.com/limitcool/starter cd my-app # Copy the example config for local development cp example.yaml dev.yaml # Run database migrations (creates tables + seeds admin user) go run main.go migrate # Start the HTTP server go run main.go server # Output: # INFO Application starting version=dev # INFO HTTP server started address=http://localhost:8080 mode=debug ``` -------------------------------- ### Copying Example Configuration for Production Source: https://github.com/limitcool/starter/blob/main/README_EN.md To set up the production environment, copy the example configuration file and rename it to 'prod.yaml'. ```bash # Production environment cp example.yaml prod.yaml ``` -------------------------------- ### Copying Example Configuration for Development Source: https://github.com/limitcool/starter/blob/main/README_EN.md Before running the application, copy the example configuration file to 'dev.yaml' for the development environment. ```bash # Development environment cp example.yaml dev.yaml ``` -------------------------------- ### Copying Example Configuration for Test Environment Source: https://github.com/limitcool/starter/blob/main/README_EN.md For the testing environment, copy the example configuration file and rename it to 'test.yaml'. ```bash # Test environment cp example.yaml test.yaml ``` -------------------------------- ### Install and Create Project (Bash) Source: https://github.com/limitcool/starter/blob/main/README_EN.md Command to install the eagle CLI and create a new project using the starter template. ```bash go install github.com/go-eagle/eagle/cmd/eagle@latest eagle new -r https://github.com/limitcool/starter ``` -------------------------------- ### Example File Paths Source: https://github.com/limitcool/starter/blob/main/docs/file-storage-guide.md Illustrates various file path examples based on the defined structure, showing how different file types and purposes are organized. ```text - **头像**: `private/users/avatars/user_123/uuid.jpg` - **横幅**: `public/content/banners/2025/06/uuid.png` - **文档**: `private/documents/general/2025/06/uuid.pdf` - **临时文件**: `private/temp/2025/06/18/uuid.tmp` ``` -------------------------------- ### Server Command Options (Bash) Source: https://github.com/limitcool/starter/blob/main/README_EN.md Demonstrates how to start the HTTP server with different configuration options like port and config file. ```bash # Start the server with default configuration ./ server # Start the server with a specified port ./ server --port 9000 # Start the server with a specified configuration file ./ server --config custom.yaml ``` -------------------------------- ### Logger Setup and Usage Source: https://context7.com/limitcool/starter/llms.txt Demonstrates how to set up and use the structured logger for different logging levels and contexts. ```APIDOC ## Logger — `logger.Setup` / `logger.Info` / `logger.LogErrorWithStack` Structured logging abstraction over `uber-go/zap` and `charmbracelet/log`. Configure via the YAML `Log` section; call the package-level functions anywhere. ```go import ( "context" "github.com/limitcool/starter/internal/pkg/logger" "github.com/limitcool/starter/pkg/logconfig" ) // Initialize once at startup (called automatically by server/migrate commands) logger.Setup(logconfig.LogConfig{ Level: logconfig.LogLevelInfo, Output: []string{"console", "file"}, Format: "json", StackTraceEnabled: true, StackTraceLevel: "error", MaxStackFrames: 10, FileConfig: logconfig.FileConfig{ Path: "logs/app.log", MaxSize: 100, }, }) // Structured key-value logging logger.Info("User logged in", "user_id", 42, "ip", "192.168.1.1") logger.Warn("Rate limit approaching", "user_id", 42, "count", 95) logger.Error("DB query failed", "error", err, "query", "SELECT ...") // Context-aware logging (preserves trace_id, request_id) ctx := context.WithValue(context.Background(), "trace_id", "abc-123") logger.InfoContext(ctx, "Processing order", "order_id", 999) // Error logging with full stack trace and error chain logger.LogErrorWithStack("Payment processing failed", err, "order_id", 999, "amount", 99.99, ) // Output (JSON): // {"level":"ERROR","msg":"Payment processing failed", // "error":"connection refused","error_code":3000, // "error_chain":"...[handler/payment.go:42]", // "stack_trace":"\ngithub.com/...","order_id":999,"amount":99.99} ``` ``` -------------------------------- ### gRPC Configuration Example Source: https://github.com/limitcool/starter/blob/main/README.md Example configuration for enabling and customizing gRPC services, including port, health checks, and reflection. ```yaml GRPC: Enabled: true Port: 9000 HealthCheck: true Reflection: true ``` -------------------------------- ### Basic Application Commands (Bash) Source: https://github.com/limitcool/starter/blob/main/README_EN.md Common commands for interacting with the application, including help, version, server start, and migration. ```bash # View help information ./ --help # View version information ./ version # Start the server ./ server # Execute database migration ./ migrate ``` -------------------------------- ### Implement gRPC Controller Source: https://github.com/limitcool/starter/blob/main/README.md Example Go code for implementing a gRPC controller for the SystemService. This includes the GetSystemInfo method. ```go // internal/controller/system_grpc.go package controller import ( "context" "time" pb "github.com/limitcool/starter/internal/proto/gen/v1" // ... ) // SystemGRPCController gRPC系统控制器 type SystemGRPCController struct { pb.UnimplementedSystemServiceServer // ... } // GetSystemInfo 获取系统信息 func (c *SystemGRPCController) GetSystemInfo(ctx context.Context, req *pb.SystemInfoRequest) (*pb.SystemInfoResponse, error) { // 实现业务逻辑 return &pb.SystemInfoResponse{ AppName: c.config.App.Name, Version: "1.0.0", Mode: c.config.App.Mode, ServerTime: time.Now().Unix(), }, nil } ``` -------------------------------- ### Use gRPC Client to Call Service Source: https://github.com/limitcool/starter/blob/main/README.md Example Go code for establishing a gRPC connection and calling the GetSystemInfo service using a generated client. ```go // 创建 gRPC 连接 conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure()) if err != nil { log.Fatalf("Failed to connect: %v", err) } deffer conn.Close() // 创建客户端 client := pb.NewSystemServiceClient(conn) // 调用服务 resp, err := client.GetSystemInfo(context.Background(), &pb.SystemInfoRequest{ RequestId: "test-request", }) ``` -------------------------------- ### Create User Repository Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Example of creating a specific repository for the User model, initializing a generic repository with a custom error code for 'not found' scenarios. ```go // 创建用户仓库 func NewUserRepo(db *gorm.DB) *UserRepo { genericRepo := NewGenericRepo[model.User](db) genericRepo.SetErrorCode(errorx.ErrorUserNotFoundCode) // 设置错误码 return &UserRepo{ DB: db, genericRepo: genericRepo, } } ``` -------------------------------- ### Register gRPC Service in App Container Source: https://github.com/limitcool/starter/blob/main/README.md Example Go code demonstrating how to initialize and register a gRPC server and its services within the application container. ```go // 在应用容器中初始化 gRPC 服务 func (a *App) initGRPCServer() error { // 创建 gRPC 服务器 grpcServer := grpc.NewServer() // 注册服务 systemController := NewSystemGRPCController(a.config) pb.RegisterSystemServiceServer(grpcServer, systemController) a.grpcServer = grpcServer return nil } ``` -------------------------------- ### Method Naming Convention Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Illustrates the use of CamelCase for method names, which should clearly express their functionality, such as GetByID, Create, Update, and Delete. ```go GetByID ``` ```go Create ``` ```go Update ``` ```go Delete ``` -------------------------------- ### Define gRPC Service in Proto Source: https://github.com/limitcool/starter/blob/main/README.md Example of a Protocol Buffers file defining a SystemService with GetSystemInfo RPC. This file is located in internal/proto/v1. ```protobuf syntax = "proto3"; package internal.proto.v1; option go_package = "internal/proto/gen/v1;protov1"; // SystemService 系统服务 service SystemService { // GetSystemInfo 获取系统信息 rpc GetSystemInfo(SystemInfoRequest) returns (SystemInfoResponse) {} } // SystemInfoRequest 系统信息请求 message SystemInfoRequest { // 请求ID string request_id = 1; } // SystemInfoResponse 系统信息响应 message SystemInfoResponse { // 应用名称 string app_name = 1; // 应用版本 string version = 2; // 运行模式 string mode = 3; // 服务器时间 int64 server_time = 4; } ``` -------------------------------- ### Create Cached Repository Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Example of creating a cached repository for users. It involves creating a cache instance and then wrapping a base repository with caching logic. ```go // 创建缓存工厂 cacheFactory := cache.GetFactory() // 创建内存缓存 userCache, err := cacheFactory.Create("user_cache", cache.Memory, cache.WithExpiration(5*time.Minute), cache.WithMaxEntries(1000), ) if err != nil { panic(err) } // 创建基础用户仓库 baseUserRepo := repository.NewUserRepo(db) // 创建缓存用户仓库 cachedUserRepo := repository.NewCachedRepo(baseUserRepo, userCache, "user", 5*time.Minute) // 或者使用辅助函数 cachedUserRepo, err := repository.WithCache(baseUserRepo, "user_cache", "user", 5*time.Minute) if err != nil { panic(err) } ``` -------------------------------- ### Project Directory Structure Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Illustrates the standard directory structure for organizing project components like API definitions, controllers, services, repositories, models, and internal packages. ```tree internal/ ├── api/ # API定义 ├── controller/ # 控制器层 ├── services/ # 服务层 ├── repository/ # 仓库层 ├── model/ # 模型层 ├── pkg/ # 内部包 └── ... ``` -------------------------------- ### Variable Naming Convention Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Demonstrates the use of camelCase for variable names, which should clearly indicate their purpose, such as userID, adminUser, and fileRepo. ```go userID ``` ```go adminUser ``` ```go fileRepo ``` -------------------------------- ### Logging Usage Example in Go Source: https://github.com/limitcool/starter/blob/main/README_EN.md Demonstrates how to log messages at different levels (Info, Warn, Error, Debug) using the logger package. Ensure the logger package is imported before use. ```go // Import the logger package import "github.com/limitcool/starter/internal/pkg/logger" // Log at different levels func example() { // Log info message logger.Info("This is an info message", "user", "admin", "action", "login") // Log warning message logger.Warn("This is a warning message", "memory", "90%") // Log error message err := someFunction() if err != nil { logger.Error("Operation failed", "error", err, "operation", "someFunction") } // Log debug message logger.Debug("Detailed debug information", "request", req, "response", resp) } ``` -------------------------------- ### Logging Configuration in YAML Source: https://github.com/limitcool/starter/blob/main/README_EN.md YAML configuration for setting up the logging system. This example specifies the log driver, level, output methods, format, and file-specific configurations. ```yaml Log: Driver: charm # Log driver: charm, zap Level: info # Log level: debug, info, warn, error Output: [console, file] # Output methods: console, file Format: text # Log format: text, json FileConfig: Path: ./logs/app.log # Log file path MaxSize: 100 # Maximum size of each log file (MB) MaxAge: 7 # Days to retain log files MaxBackups: 10 # Maximum number of old log files to retain Compress: true # Whether to compress old log files ``` -------------------------------- ### Constructing and Using a Redis Cache Client Source: https://context7.com/limitcool/starter/llms.txt Demonstrates how to create a Redis cache client with custom expiration settings and perform basic cache operations like Set, Get, and GetMulti. ```go import ( "context" "encoding/json" "time" "github.com/limitcool/starter/internal/pkg/cache" "github.com/go-redis/redis/v8" ) // Construct a Redis cache directly client := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) c := cache.NewRedisCache(client, cache.WithExpiration(10*time.Minute), ) ctx := context.Background() // Set data, _ := json.Marshal(map[string]any{"name": "alice", "age": 30}) if err := c.Set(ctx, "user:42", data, 5*time.Minute); err != nil { log.Fatal(err) } // Get raw, err := c.Get(ctx, "user:42") if err != nil { log.Fatal("cache miss:", err) // cache.ErrNotFound on miss } var user map[string]any json.Unmarshal(raw, &user) // {"name":"alice","age":30} // Batch operations items := map[string][]byte{ "user:1": []byte(`{"name":"bob"}`), "user:2": []byte(`{"name":"carol"}`), } c.SetMulti(ctx, items, 10*time.Minute) results, _ := c.GetMulti(ctx, []string{"user:1", "user:2", "user:99"}) // results["user:99"] is absent (cache miss, no error) // Atomic counter newVal, _ := c.Incr(ctx, "page_views", 1) fmt.Println("Views:", newVal) // Cache-aside with stampede/penetration protection result, err := c.(*cache.RedisCache).GetWithProtection(ctx, "product:99", func(ctx context.Context) (any, error) { // Called only on cache miss, with a distributed lock held return loadProductFromDB(ctx, 99) }, 5*time.Minute, ) ``` -------------------------------- ### gRPC Controller Implementation in Go Source: https://github.com/limitcool/starter/blob/main/README_EN.md Example of implementing a gRPC controller for the SystemService. Business logic is implemented within the GetSystemInfo method. Ensure the controller is suffixed with '_grpc.go'. ```go // internal/controller/system_grpc.go package controller import ( "context" "time" pb "github.com/limitcool/starter/internal/proto/gen/v1" // ... ) // SystemGRPCController gRPC system controller type SystemGRPCController struct { pb.UnimplementedSystemServiceServer // ... } // GetSystemInfo get system information func (c *SystemGRPCController) GetSystemInfo(ctx context.Context, req *pb.SystemInfoRequest) (*pb.SystemInfoResponse, error) { // Implement business logic return &pb.SystemInfoResponse{ AppName: c.config.App.Name, Version: "1.0.0", Mode: c.config.App.Mode, ServerTime: time.Now().Unix(), }, nil } ``` -------------------------------- ### File Naming Convention Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Demonstrates the use of snake_case for file names, reflecting their primary type or function, with specific prefixes for different modules like admin or user. ```go admin_user_repo.go ``` ```go admin_system_repo.go ``` ```go user_repo.go ``` -------------------------------- ### Example Language Resource File Content Source: https://github.com/limitcool/starter/blob/main/README_EN.md Language resource files are in JSON format and contain key-value pairs for different language strings. ```json { "error.success": "Success", "error.common.invalid_params": "Invalid request parameters", "error.user.user_not_found": "User not found" } ``` -------------------------------- ### Example Language Resource File Source: https://github.com/limitcool/starter/blob/main/README.md Define translations for error messages in JSON format for each supported language. This file is located in the 'locales' directory. ```json { "error.success": "Success", "error.common.invalid_params": "Invalid request parameters", "error.user.user_not_found": "User not found" } ``` -------------------------------- ### Error Handling Usage Examples in Go Source: https://github.com/limitcool/starter/blob/main/README.md Demonstrates how to use the custom error types in the Model layer when database errors occur, and how to handle these errors in the Handler layer. ```go // 在 Model 层 func (r *UserRepo) GetByID(ctx context.Context, id uint) (*User, error) { var user User if err := r.DB.First(&user, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errorx.NewError(errorx.ErrorUserNotFound, "用户不存在") } return nil, errorx.NewError(errorx.ErrorDatabase, "数据库错误").WithError(err) } return &user, nil } // 在 Handler 层 func (h *UserHandler) GetUser(c *gin.Context) { id := cast.ToUint(c.Param("id")) user, err := h.userRepo.GetByID(c.Request.Context(), id) if err != nil { logger.Error("获取用户失败", "error", err, "id", id) response.Error(c, err) return } response.Success(c, user) } ``` -------------------------------- ### Get CPU Profile Source: https://github.com/limitcool/starter/blob/main/README.md Capture a CPU profile for a specified duration (e.g., 30 seconds) and save it to a file. This helps in identifying CPU-bound bottlenecks. ```bash curl http://localhost:8080/debug/pprof/profile?seconds=30 > cpu.prof ``` -------------------------------- ### Type Naming Convention Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Shows the use of CamelCase for type names, which should clearly indicate their function and module affiliation, such as AdminUser, AdminUserRepo, AdminUserService, and AdminUserController. ```go AdminUser ``` ```go AdminUserRepo ``` ```go AdminUserService ``` ```go AdminUserController ``` -------------------------------- ### Join Query: Join Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Example of performing a JOIN query between two tables, specifying the join type, tables, join condition, selected fields, and an optional WHERE clause. ```go // 连接查询 results, err := userRepo.Join(ctx, "LEFT", "orders", "users.id = orders.user_id", []string{"users.id", "users.username", "COUNT(orders.id) as order_count"}, "users.status = ?", 1, ) ``` -------------------------------- ### Database Table Naming Convention Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Shows the use of snake_case for database table names, which should reflect the data type they contain, such as admin_user, user, and file. ```sql admin_user ``` ```sql user ``` ```sql file ``` -------------------------------- ### Implement RouterInitializer for Custom Route Group Source: https://context7.com/limitcool/starter/llms.txt Implement the `handler.RouterInitializer` interface to define custom route groups. Pass the handler to `app.New` or register it in `app.initRouter`. This example shows how to set up routes for products, including public, authenticated, and admin-only endpoints. ```go package handler import ( "github.com/gin-gonic/gin" "github.com/limitcool/starter/internal/api/response" "github.com/limitcool/starter/internal/middleware" ) // ProductHandler satisfies RouterInitializer type ProductHandler struct { *BaseHandler app AppContext } func NewProductHandler(app AppContext) *ProductHandler { return &ProductHandler{ BaseHandler: NewBaseHandler(app.GetDB(), app.GetConfig()), app: app, } } func (h *ProductHandler) InitRouters(g *gin.RouterGroup, root *gin.Engine) { products := g.Group("/products") // Public products.GET("", h.ListProducts) products.GET("/:id", h.GetProduct) // Authenticated auth := products.Group("", middleware.JWTAuth(h.Config)) auth.POST("", h.CreateProduct) // Admin-only admin := auth.Group("", middleware.AdminCheck()) admin.DELETE("/:id", h.DeleteProduct) } func (h *ProductHandler) ListProducts(c *gin.Context) { // Query DB using h.DB (gorm.DB) var products []Product if err := h.DB.Find(&products).Error; err != nil { response.Error(c, errspec.ErrInternal.New(c.Request.Context())) return } response.Success(c, products) } ``` -------------------------------- ### Manage Application with Cobra CLI Commands Source: https://context7.com/limitcool/starter/llms.txt The application integrates with Cobra for command-line interface management. Common commands include starting the HTTP server with optional port and configuration overrides, running database migrations, performing a fresh migration wipe and re-run (for development only), and rolling back the last migration batch. ```bash # Start the HTTP server (override port at runtime) ./myapp server --port 9000 --config prod.yaml # Run all pending database migrations ./myapp migrate # Wipe and re-run all migrations (DANGEROUS — dev only) ./myapp migrate --fresh # Roll back the last migration batch ./myapp migrate rollback ``` -------------------------------- ### Starter Application Configuration (`example.yaml`) Source: https://context7.com/limitcool/starter/llms.txt This YAML file controls all application behavior. It includes settings for the application name and port, JWT authentication secrets and expiration, database driver and name, Redis configuration, logging levels and output, file storage type and path, admin credentials, internationalization settings, and pprof port. ```yaml APP: Name: MyApp Port: 8080 JwtAuth: AccessSecret: uOvKLmVfztaXGpNYd4Z0I1SiT7MweJhl AccessExpire: 2592000 # seconds (30 days) RefreshSecret: uOvKLmVfztaXGpNYd4Z0I1SiT7MweJhl RefreshExpire: 2592000 Driver: sqlite3 # mysql | postgres | sqlite3 Database: Enabled: true DBName: app # For SQLite this is the filename Redis: "default": Enabled: false Addr: localhost:6379 Password: "" DB: 0 PoolSize: 100 Log: Level: debug # debug | info | warn | error Output: ["console"] # console | file Format: text # text | json FileConfig: Path: logs/app.log MaxSize: 10 # MB Storage: Enabled: true Type: local # local | s3 | oss Local: Path: storage URL: http://localhost:8080/static Admin: Username: admin Password: admin123 Nickname: System Admin I18n: Enabled: true DefaultLanguage: zh-CN ResourcesPath: locales Pprof: Enabled: false Port: 0 # 0 = use main server port; non-zero = separate port ``` -------------------------------- ### Initialize Application with `app.New` Source: https://context7.com/limitcool/starter/llms.txt The `app.New` function is the central factory for wiring application components in a specific order: database, Redis, file storage, router, HTTP server, and pprof. Required components will halt startup on failure, while optional ones will log a warning and continue. ```go package main import ( "github.com/limitcool/starter/configs" "github.com/limitcool/starter/internal/app" "github.com/limitcool/starter/internal/pkg/logger" ) func main() { cfg := &configs.Config{ App: configs.App{Port: 8080, Name: "MyApp", Mode: "debug"}, Driver: configs.DriverSqlite, Database: configs.Database{Enabled: true, DBName: "app.db"}, JwtAuth: configs.JwtAuth{ AccessSecret: "my-secret-key", AccessExpire: 86400, }, } application, err := app.New(cfg) if err != nil { logger.Error("Failed to start", "error", err) return } // Blocks until SIGINT/SIGTERM, then gracefully shuts down // with a 30-second timeout for in-flight requests. if err := application.Run(); err != nil { logger.Error("Run error", "error", err) } } ``` -------------------------------- ### GET /public/files/:id — Get Public File Info Source: https://context7.com/limitcool/starter/llms.txt Publicly accessible endpoint to retrieve file metadata by ID. No authentication is required. ```APIDOC ## GET /public/files/:id — Get Public File Info ### Description Publicly accessible endpoint (no auth required) to retrieve file metadata by ID. ### Method GET ### Endpoint `/public/files/:id` ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the file to retrieve information for. ### Request Example ```bash curl http://localhost:8080/public/files/01HX... ``` ### Response #### Success Response (200) - **code** (integer) - Response code. - **data** (object) - File metadata. - **id** (string) - The unique identifier for the file. - **original_name** (string) - The original name of the file. - **url** (string) - The URL to access the file. - **size** (integer) - The size of the file in bytes. - **mime_type** (string) - The MIME type of the file. - **is_public** (boolean) - Indicates if the file is public. - **usage** (string) - The usage type of the file. #### Response Example ```json { "code": 0, "data": { "id": "01HX...", "original_name": "avatar.png", "url": "http://localhost:8080/static/public/avatars/2/20240425/abc123.png", "size": 48210, "mime_type": "image/png", "is_public": true, "usage": "avatar" } } ``` ``` -------------------------------- ### gRPC Client Usage in Go Source: https://github.com/limitcool/starter/blob/main/README_EN.md Demonstrates how to establish a gRPC connection, create a client, and call a service method. Ensure the correct host and port are used for the connection. ```go // Create gRPC connection conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure()) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer conn.Close() // Create client client := pb.NewSystemServiceClient(conn) // Call service resp, err := client.GetSystemInfo(context.Background(), &pb.SystemInfoRequest{ RequestId: "test-request", }) ``` -------------------------------- ### GET /api/v1/admin/files/:id/download — Get Download URL (Admin) Source: https://context7.com/limitcool/starter/llms.txt Generates a fresh presigned download URL for a stored file. Returns a permanent URL for public files and a time-limited signed URL for private files. ```APIDOC ## GET /api/v1/admin/files/:id/download — Get Download URL (Admin) ### Description Generates a fresh presigned download URL for a stored file. For public files, returns a permanent URL; for private files, returns a time-limited signed URL. ### Method GET ### Endpoint `/api/v1/admin/files/:id/download` ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the file to download. ### Request Example ```bash curl http://localhost:8080/api/v1/admin/files/01HX.../download \ -H "Authorization: Bearer $ADMIN_TOKEN" ``` ### Response #### Success Response (200) - **code** (integer) - Response code. - **data** (object) - File download details. - **file_id** (string) - The unique identifier for the file. - **filename** (string) - The name of the file. - **download_url** (string) - The presigned URL for downloading the file. - **is_public** (boolean) - Indicates if the file is public. - **size** (integer) - The size of the file in bytes. - **storage_type** (string) - The type of storage used. #### Response Example ```json { "code": 0, "data": { "file_id": "01HX...", "filename": "report.pdf", "download_url": "https://my-bucket.s3.amazonaws.com/private/documents/1/.../report.pdf?X-Amz-Expires=3600&...", "is_public": false, "size": 204800, "storage_type": "s3" } } ``` ``` -------------------------------- ### Initialize and Use FileStorage Interface Source: https://context7.com/limitcool/starter/llms.txt Configure and create a FileStorage instance from application configuration. Supports multiple backends like S3, Aliyun OSS, and MinIO. ```go import ( "context" "os" "github.com/limitcool/starter/configs" "github.com/limitcool/starter/internal/filestore" ) // Create storage from config (used automatically by App) cfg := configs.Config{ Storage: configs.Storage{ Enabled: true, Type: "s3", S3: configs.S3Storage{ AccessKey: os.Getenv("AWS_ACCESS_KEY"), SecretKey: os.Getenv("AWS_SECRET_KEY"), Region: "us-east-1", Bucket: "my-app-files", }, }, } storage, err := filestore.NewFileStorage(cfg) if err != nil { log.Fatal(err) } ctx := context.Background() // Generate a presigned upload URL (15-min expiry) uploadURL, method, err := storage.GetUploadURL(ctx, "documents/user-1/report.pdf", "application/pdf", false, // private file ) // method == "PUT", uploadURL is a signed S3 URL // Check if upload completed exists, _ := storage.FileExists(ctx, "documents/user-1/report.pdf", false) // Generate a time-limited download URL downloadURL, _ := storage.GetDownloadURL(ctx, "documents/user-1/report.pdf", false) // Direct upload (suitable for local storage / server-side streaming) f, _ := os.Open("/tmp/upload.png") defer f.Close() storage.UploadFile(ctx, "images/user-1/photo.png", f, true) // Delete storage.DeleteFile(ctx, "documents/user-1/report.pdf", false) fmt.Println("Backend:", storage.GetStorageType()) // "s3" ``` -------------------------------- ### GET /health — Health Check Source: https://context7.com/limitcool/starter/llms.txt Unauthenticated endpoint to check the liveness of the service. ```APIDOC ## GET /health — Health Check ### Description Unauthenticated liveness probe endpoint. ### Method GET ### Endpoint `/health` ### Response #### Success Response (200) - **status** (string) - Indicates the health status (e.g., "ok"). #### Response Example ```json {"status":"ok"} ``` ``` -------------------------------- ### Get Public File Info Source: https://context7.com/limitcool/starter/llms.txt Publicly accessible endpoint to retrieve file metadata by ID without authentication. ```bash curl http://localhost:8080/public/files/01HX... ``` ```json { "code": 0, "data": { "id": "01HX...", "original_name": "avatar.png", "url": "http://localhost:8080/static/public/avatars/2/20240425/abc123.png", "size": 48210, "mime_type": "image/png", "is_public": true, "usage": "avatar" } } ``` -------------------------------- ### Configure and Use Structured Logger Source: https://context7.com/limitcool/starter/llms.txt Set up the structured logger with various output formats and levels. Use package-level functions for logging messages with context and stack traces. ```go import ( "context" "github.com/limitcool/starter/internal/pkg/logger" "github.com/limitcool/starter/pkg/logconfig" ) // Initialize once at startup (called automatically by server/migrate commands) logger.Setup(logconfig.LogConfig{ Level: logconfig.LogLevelInfo, Output: []string{"console", "file"}, Format: "json", StackTraceEnabled: true, StackTraceLevel: "error", MaxStackFrames: 10, FileConfig: logconfig.FileConfig{ Path: "logs/app.log", MaxSize: 100, }, }) // Structured key-value logging logger.Info("User logged in", "user_id", 42, "ip", "192.168.1.1") logger.Warn("Rate limit approaching", "user_id", 42, "count", 95) logger.Error("DB query failed", "error", err, "query", "SELECT ...") // Context-aware logging (preserves trace_id, request_id) ctx := context.WithValue(context.Background(), "trace_id", "abc-123") logger.InfoContext(ctx, "Processing order", "order_id", 999) // Error logging with full stack trace and error chain logger.LogErrorWithStack("Payment processing failed", err, "order_id", 999, "amount", 99.99, ) // Output (JSON): // {"level":"ERROR","msg":"Payment processing failed", // "error":"connection refused","error_code":3000, // "error_chain":"...[handler/payment.go:42]", // "stack_trace":"\ngithub.com/...","order_id":999,"amount":99.99} ``` -------------------------------- ### Enum Naming Convention Example Source: https://github.com/limitcool/starter/blob/main/docs/naming_convention.md Illustrates the use of CamelCase for enum types and enum values, such as UserType, UserTypeAdminUser, and UserTypeUser. ```go UserType ``` ```go UserTypeAdminUser ``` ```go UserTypeUser ``` -------------------------------- ### Apply RegularUserCheck Middleware Source: https://github.com/limitcool/starter/blob/main/README.md Example of applying the RegularUserCheck middleware to a route group for regular users only. This middleware ensures the user is not an administrator. ```go // 仅普通用户接口 regularUserGroup := router.Group("/api/v1/regular") regularUserGroup.Use(middleware.RegularUserCheck()) { regularUserGroup.POST("/feedback", handler.SubmitFeedback) // 其他仅普通用户接口... } ``` -------------------------------- ### Apply UserCheck Middleware Source: https://github.com/limitcool/starter/blob/main/README.md Example of applying the UserCheck middleware to a route group for any logged-in user. This middleware only verifies authentication status. ```go // 普通用户接口 userGroup := router.Group("/api/v1/user") userGroup.Use(middleware.UserCheck()) { userGroup.GET("/profile", handler.GetUserProfile) // 其他用户接口... } ``` -------------------------------- ### Get Authenticated User Info Source: https://context7.com/limitcool/starter/llms.txt Retrieves the profile information of the currently logged-in user. Requires a valid JWT in the `Authorization` header. ```APIDOC ## GET /api/v1/user/info — Get Authenticated User Info Returns the profile of the currently logged-in user. Requires a valid Bearer JWT in the `Authorization` header. ### Method GET ### Endpoint /api/v1/user/info ### Headers - **Authorization** (string) - Required - Bearer token for authentication. ### Request Example ```bash TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." curl http://localhost:8080/api/v1/user/info \ -H "Authorization: Bearer $TOKEN" ``` ### Response #### Success Response - **code** (integer) - 0 for success. - **message** (string) - "success". - **data** (object) - Contains the user's profile information. - **id** (integer) - The unique identifier for the user. - **username** (string) - The user's username. - **nickname** (string) - The user's nickname. - **email** (string) - The user's email address. - **is_admin** (boolean) - Indicates if the user is an administrator. - **enabled** (boolean) - Indicates if the user account is enabled. - **last_login_at** (string) - Timestamp of the last login. - **last_login_ip** (string) - IP address of the last login. #### Response Example ```json { "code": 0, "message": "success", "data": { "id": 2, "username": "alice", "nickname": "Alice", "email": "alice@example.com", "is_admin": false, "enabled": true, "last_login_at": "2024-04-25T10:05:00Z", "last_login_ip": "127.0.0.1" } } ``` #### Error Responses - **401 Missing / Invalid Token** ```json { "code": 2003, "message": "user not logged in", "data": {} } ``` ``` -------------------------------- ### Registering gRPC Services in Go Source: https://github.com/limitcool/starter/blob/main/README_EN.md Shows how to initialize and register gRPC services within the application container. This involves creating a new gRPC server and registering the implemented controllers. ```go // Initialize gRPC server in application container func (a *App) initGRPCServer() error { // Create gRPC server grpcServer := grpc.NewServer() // Register services systemController := NewSystemGRPCController(a.config) pb.RegisterSystemServiceServer(grpcServer, systemController) a.grpcServer = grpcServer return nil } ``` -------------------------------- ### Apply AdminCheck Middleware Source: https://github.com/limitcool/starter/blob/main/README.md Example of applying the AdminCheck middleware to a route group for administrator-only access. This middleware checks the 'is_admin' field in JWT. ```go // 管理员接口 adminGroup := router.Group("/api/v1/admin") adminGroup.Use(middleware.AdminCheck()) { adminGroup.GET("/users", handler.ListUsers) // 其他管理员接口... } ``` -------------------------------- ### Advanced Querying with Repository Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Shows how to perform advanced queries like pagination, aggregation, and grouping using the repository pattern. ```go // 分页查询 users, total, err := userRepo.GetPage(ctx, 1, 10, "status = ?", 1) if err != nil { // 处理错误 } fmt.Printf("总数: %d, 当前页数据: %d\n", total, len(users)) // 聚合查询 activeCount, err := userRepo.AggregateField(ctx, repository.Count, "id", "status = ?", 1) if err != nil { // 处理错误 } fmt.Printf("活跃用户数: %f\n", activeCount) // 分组查询 results, err := userRepo.GroupBy(ctx, []string{"status"}, []string{"status", "COUNT(*) as count"}, "", ) if err != nil { // 处理错误 } for _, result := range results { fmt.Printf("状态: %v, 数量: %v\n", result["status"], result["count"]) } ``` -------------------------------- ### Print Version Information Source: https://context7.com/limitcool/starter/llms.txt Displays the application's version, Git commit hash, build date, and Go version. This information is set during the build process. ```bash ./myapp version ``` -------------------------------- ### Aggregation Query: AggregateField Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Example of using AggregateField to perform aggregate functions like COUNT or SUM on a specific field, optionally filtered by a condition. ```go // 聚合查询 count, err := userRepo.AggregateField(ctx, repository.Count, "id", "status = ?", 1) sum, err := userRepo.AggregateField(ctx, repository.Sum, "amount", "user_id = ?", userID) ``` -------------------------------- ### Get System Settings (Admin Only) Source: https://context7.com/limitcool/starter/llms.txt Retrieves application metadata. This endpoint is protected and requires administrator privileges. Non-admins will receive a 403 Forbidden response. ```APIDOC ## GET /api/v1/admin/settings — Get System Settings (Admin Only) Returns application metadata. Protected by `JWTAuth` + `AdminCheck` middleware; non-admins receive a 403. ### Method GET ### Endpoint /api/v1/admin/settings ### Headers - **Authorization** (string) - Required - Bearer token for authentication (must be an admin user). ### Request Example ```bash ADMIN_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." # is_admin=true curl http://localhost:8080/api/v1/admin/settings \ -H "Authorization: Bearer $ADMIN_TOKEN" ``` ### Response #### Success Response - **code** (integer) - 0 for success. - **message** (string) - "success". - **data** (object) - Contains system settings. - **app_name** (string) - The name of the application. - **app_version** (string) - The version of the application. - **app_mode** (string) - The current mode of the application (e.g., "debug"). #### Response Example ```json { "code": 0, "message": "success", "data": { "app_name": "MyApp", "app_version": "1.0.0", "app_mode": "debug" } } ``` #### Error Responses - **403 Regular user token (HTTP 403)** ```json { "code": 1007, "message": "access denied", "data": {} } ``` ``` -------------------------------- ### Application Container Management (Go) Source: https://github.com/limitcool/starter/blob/main/README_EN.md Shows the structure of an App type that manages component lifecycles and initialization order. ```go type App struct { config *configs.Config db *gorm.DB handlers *Handlers router *gin.Engine server *http.Server } func New(config *configs.Config) (*App, error) { app := &App{config: config} // Initialize components in order if err := app.initDatabase(); err != nil { return nil, err } // ... other component initialization return app, nil } ``` -------------------------------- ### Aggregation Query: GroupBy Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Demonstrates how to perform a GROUP BY query, specifying fields to group by, fields to select, and an optional condition. ```go // 分组查询 results, err := userRepo.GroupBy(ctx, []string{"status"}, []string{"status", "COUNT(*) as count"}, "created_at > ?", startTime, ) ``` -------------------------------- ### Running Server in Development Environment Source: https://github.com/limitcool/starter/blob/main/README_EN.md Execute the server in the development environment by setting the APP_ENV variable to 'dev' or 'development' before running the application. ```bash # Run the server in development environment APP_ENV=dev ./ server ``` -------------------------------- ### Request API with Language Preference Source: https://github.com/limitcool/starter/blob/main/README_EN.md Use the `Accept-Language` header to specify the desired language for API responses. This example shows requests for English and Chinese responses. ```bash # Request English response curl -X POST "http://localhost:8080/api/v1/user/login" \ -H "Accept-Language: en-US" \ -H "Content-Type: application/json" \ -d '{"username": "test", "password": "wrong"}' ``` ```bash # Request Chinese response curl -X POST "http://localhost:8080/api/v1/user/login" \ -H "Accept-Language: zh-CN" \ -H "Content-Type: application/json" \ -d '{"username": "test", "password": "wrong"}' ``` -------------------------------- ### Implementing Caching with Repository Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Demonstrates how to integrate caching with the repository pattern for improved performance. Cache is automatically updated or invalidated on create, update, and delete operations. ```go // 创建缓存用户仓库 cachedUserRepo, err := repository.WithCache(userRepo, "user_cache", "user", 5*time.Minute) if err != nil { // 处理错误 } // 获取用户(首次会从数据库获取并缓存) user, err := cachedUserRepo.GetByID(ctx, 1) if err != nil { // 处理错误 } // 再次获取用户(从缓存获取) user, err = cachedUserRepo.GetByID(ctx, 1) if err != nil { // 处理错误 } // 更新用户(会自动更新缓存) user.Email = "updated@example.com" if err := cachedUserRepo.Update(ctx, user); err != nil { // 处理错误 } // 删除用户(会自动删除缓存) if err := cachedUserRepo.Delete(ctx, 1); err != nil { // 处理错误 } ``` -------------------------------- ### Dependency Injection Constructors (Go) Source: https://github.com/limitcool/starter/blob/main/README_EN.md Illustrates how dependencies are injected through constructors in different layers (Model, Handler, Router) using Uber fx. ```go // Model layer func NewUserRepo(db *gorm.DB) *UserRepo { // ... } // Handler layer func NewUserHandler(db *gorm.DB, config *configs.Config) *UserHandler { // ... } // Router layer func NewRouter(userHandler *handler.UserHandler) *gin.Engine { // ... } ``` -------------------------------- ### Basic Repository Operations Source: https://github.com/limitcool/starter/blob/main/docs/repository.md Illustrates fundamental CRUD operations using the repository pattern: creating, retrieving, updating, and deleting entities. ```go // 创建用户仓库 userRepo := repository.NewUserRepo(db) // 创建用户 user := &model.User{ Username: "admin", Password: "password", Email: "admin@example.com", } if err := userRepo.Create(ctx, user); err != nil { // 处理错误 } // 获取用户 user, err := userRepo.GetByID(ctx, 1) if err != nil { // 处理错误 } // 更新用户 user.Email = "new_email@example.com" if err := userRepo.Update(ctx, user); err != nil { // 处理错误 } // 删除用户 if err := userRepo.Delete(ctx, 1); err != nil { // 处理错误 } ```