package handlers import ( "context" "os" "Yimaru-Backend/internal/domain" "github.com/gofiber/fiber/v2" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) // GetLogsHandler godoc // @Summary Retrieve application logs with filtering and pagination // @Description Fetches application logs from MongoDB with pagination, level filtering, and search // @Tags Logs // @Produce json // @Param level query string false "Filter logs by level (debug, info, warn, error, dpanic, panic, fatal)" // @Param search query string false "Search term to match against message or fields" // @Param page query int false "Page number for pagination (default: 1)" default(1) // @Param limit query int false "Number of items per page (default: 50, max: 100)" default(50) // @Success 200 {object} domain.LogResponse "Paginated list of application logs" // @Failure 400 {object} domain.ErrorResponse "Invalid request parameters" // @Failure 500 {object} domain.ErrorResponse "Internal server error" // @Router /api/v1/logs [get] func GetLogsHandler(appCtx context.Context) fiber.Handler { return func(c *fiber.Ctx) error { // Get query parameters levelFilter := c.Query("level") searchTerm := c.Query("search") page := c.QueryInt("page", 1) limit := c.QueryInt("limit", 50) // Validate pagination parameters if page < 1 { page = 1 } if limit < 1 || limit > 100 { limit = 50 } // Calculate skip value for pagination skip := (page - 1) * limit client, err := mongo.Connect(appCtx, options.Client().ApplyURI(os.Getenv("MONGODB_URL"))) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "MongoDB connection failed: "+err.Error()) } defer client.Disconnect(appCtx) collection := client.Database("logdb").Collection("applogs") // Build filter filter := bson.M{} // Add level filter if specified if levelFilter != "" { validLevels := map[string]bool{ "debug": true, "info": true, "warn": true, "error": true, "dpanic": true, "panic": true, "fatal": true, } if !validLevels[levelFilter] { return fiber.NewError(fiber.StatusBadRequest, "Invalid log level specified") } filter["level"] = levelFilter } // Add search filter if specified if searchTerm != "" { filter["$or"] = []bson.M{ {"message": bson.M{"$regex": searchTerm, "$options": "i"}}, {"fields": bson.M{"$elemMatch": bson.M{"$regex": searchTerm, "$options": "i"}}}, } } // Find options with pagination and sorting opts := options.Find(). SetSort(bson.D{{Key: "timestamp", Value: -1}}). SetSkip(int64(skip)). SetLimit(int64(limit)) // Get total count for pagination metadata total, err := collection.CountDocuments(appCtx, filter) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to count logs: "+err.Error()) } // Find logs cursor, err := collection.Find(appCtx, filter, opts) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch logs: "+err.Error()) } defer cursor.Close(appCtx) var logs []domain.LogEntry = make([]domain.LogEntry, 0) if err := cursor.All(appCtx, &logs); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Cursor decoding error: "+err.Error()) } // Calculate pagination metadata totalPages := int(total) / limit if int(total)%limit != 0 { totalPages++ } // Prepare response response := domain.LogResponse{ Message: "Logs fetched successfully", Data: logs, Pagination: domain.Pagination{ Total: int(total), TotalPages: totalPages, CurrentPage: page, Limit: limit, }, } return c.JSON(response) } }