package services import ( "context" "net/http" "strconv" "sync" "time" "github.com/gin-gonic/gin" "github.com/wailsapp/wails/v3/pkg/application" ) // User represents a user in the system type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` CreatedAt time.Time `json:"createdAt"` } // GinService implements a Wails service that uses Gin for HTTP handling type GinService struct { ginEngine *gin.Engine users []User nextID int mu sync.RWMutex app *application.App } type EventData struct { Message string `json:"message"` Timestamp string `json:"timestamp"` } // NewGinService creates a new GinService instance func NewGinService() *GinService { // Create a new Gin router ginEngine := gin.New() // Add middlewares ginEngine.Use(gin.Recovery()) ginEngine.Use(LoggingMiddleware()) service := &GinService{ ginEngine: ginEngine, users: []User{ {ID: 1, Name: "Alice", Email: "alice@example.com", CreatedAt: time.Now().Add(-72 * time.Hour)}, {ID: 2, Name: "Bob", Email: "bob@example.com", CreatedAt: time.Now().Add(-48 * time.Hour)}, {ID: 3, Name: "Charlie", Email: "charlie@example.com", CreatedAt: time.Now().Add(-24 * time.Hour)}, }, nextID: 4, } // Define routes service.setupRoutes() return service } // ServiceName returns the name of the service func (s *GinService) ServiceName() string { return "Gin API Service" } // ServiceStartup is called when the service starts func (s *GinService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { // You can access the application instance via ctx s.app = application.Get() // Register an event handler that can be triggered from the frontend s.app.OnEvent("gin-api-event", func(event *application.CustomEvent) { // Log the event data // Parse the event data s.app.Logger.Info("Received event from frontend", "data", event.Data) // You could also emit an event back to the frontend s.app.EmitEvent("gin-api-response", map[string]interface{}{ "message": "Response from Gin API Service", "time": time.Now().Format(time.RFC3339), }) }) return nil } // ServiceShutdown is called when the service shuts down func (s *GinService) ServiceShutdown(ctx context.Context) error { return nil } // ServeHTTP implements the http.Handler interface func (s *GinService) ServeHTTP(w http.ResponseWriter, r *http.Request) { // All other requests go to the Gin router s.ginEngine.ServeHTTP(w, r) } // setupRoutes configures the API routes func (s *GinService) setupRoutes() { // Basic info endpoint s.ginEngine.GET("/info", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "service": "Gin API Service", "version": "1.0.0", "time": time.Now().Format(time.RFC3339), }) }) // Users group users := s.ginEngine.Group("/users") { // Get all users users.GET("", func(c *gin.Context) { s.mu.RLock() defer s.mu.RUnlock() c.JSON(http.StatusOK, s.users) }) // Get user by ID users.GET("/:id", func(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) return } s.mu.RLock() defer s.mu.RUnlock() for _, user := range s.users { if user.ID == id { c.JSON(http.StatusOK, user) return } } c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) }) // import block (ensure this exists in your file) import ( "context" "net/http" "strconv" "strings" "sync" "time" ) // ... // Create a new user users.POST("", func(c *gin.Context) { var newUser User if err := c.ShouldBindJSON(&newUser); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Validate required fields if newUser.Name == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Name is required"}) return } if newUser.Email == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Email is required"}) return } // Basic email validation (consider using a proper validator library in production) if !strings.Contains(newUser.Email, "@") { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email format"}) return } s.mu.Lock() defer s.mu.Unlock() // Set the ID and creation time newUser.ID = s.nextID newUser.CreatedAt = time.Now() s.nextID++ // Add to the users slice s.users = append(s.users, newUser) c.JSON(http.StatusCreated, newUser) // Emit an event to notify about the new user s.app.EmitEvent("user-created", newUser) }) // Delete a user users.DELETE("/:id", func(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) return } s.mu.Lock() defer s.mu.Unlock() for i, user := range s.users { if user.ID == id { // Remove the user from the slice s.users = append(s.users[:i], s.users[i+1:]...) c.JSON(http.StatusOK, gin.H{"message": "User deleted"}) return } } c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) }) } } // LoggingMiddleware is a Gin middleware that logs request details func LoggingMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // Start timer start := time.Now() // Process request c.Next() // Calculate latency latency := time.Since(start) // Log request details statusCode := c.Writer.Status() clientIP := c.ClientIP() method := c.Request.Method path := c.Request.URL.Path // Get the application instance app := application.Get() if app != nil { app.Logger.Info("HTTP Request", "status", statusCode, "method", method, "path", path, "ip", clientIP, "latency", latency, ) } } }