heroagent/pkg/servers/ui/app.go
2025-05-24 09:24:19 +04:00

152 lines
5.2 KiB
Go

package ui
import (
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"git.ourworld.tf/herocode/heroagent/pkg/servers/ui/routes" // Import the routes package
"github.com/gofiber/fiber/v2"
jetadapter "github.com/gofiber/template/jet/v2" // Aliased for clarity
)
// AppConfig holds the configuration for the UI application.
type AppConfig struct {
// Any specific configurations can be added here later
}
// NewApp creates and configures a new Fiber application for the UI.
func NewApp(config AppConfig) *fiber.App {
// Initialize Jet template engine
// Using OSFileSystemLoader to load templates from the filesystem.
// The path is relative to where the application is run.
// Get current working directory and construct absolute path to views
cwd, err := os.Getwd()
if err != nil {
panic("Failed to get current working directory: " + err.Error())
}
viewsPath := filepath.Join(cwd, "pkg", "servers", "ui", "views")
// Validate that the views directory and key template files exist
if _, err := os.Stat(viewsPath); os.IsNotExist(err) {
panic("Views directory does not exist: " + viewsPath)
}
// Check for key template files
baseLayoutPath := filepath.Join(viewsPath, "layouts", "base.jet")
dashboardPath := filepath.Join(viewsPath, "pages", "dashboard.jet")
navbarPath := filepath.Join(viewsPath, "components", "navbar.jet")
sidebarPath := filepath.Join(viewsPath, "components", "sidebar.jet")
requiredFiles := []string{baseLayoutPath, dashboardPath, navbarPath, sidebarPath}
for _, filePath := range requiredFiles {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
panic("Required template file does not exist: " + filePath)
}
}
// Log the views path for debugging
println("Views directory found at:", viewsPath)
println("All required template files exist")
// Create Fiber Jet adapter
engine := jetadapter.New(viewsPath, ".jet")
// Enable template reloading for development
engine.Reload(true)
// No custom functions for now
// Create a new Fiber app with the configured Jet engine and enhanced error handling
app := fiber.New(fiber.Config{
Views: engine,
ErrorHandler: func(c *fiber.Ctx, err error) error {
// Log the detailed error
log.Printf("ERROR: %v", err)
// Check if it's a template rendering error
if err.Error() != "" && (c.Route().Path != "" && c.Method() == "GET") {
// Extract template name and line number from error message
errorMsg := err.Error()
templateInfo := "Unknown template"
lineInfo := "Unknown line"
variableInfo := "Unknown variable"
// Try to extract template name and line number
if strings.Contains(errorMsg, "Jet Runtime Error") {
// Extract template and line number
templateLineRegex := regexp.MustCompile(`"([^"]+)":(\d+)`)
templateMatches := templateLineRegex.FindStringSubmatch(errorMsg)
if len(templateMatches) >= 3 {
templateInfo = templateMatches[1]
lineInfo = templateMatches[2]
}
// Extract variable name
varRegex := regexp.MustCompile(`there is no field or method '([^']+)'`)
varMatches := varRegex.FindStringSubmatch(errorMsg)
if len(varMatches) >= 2 {
variableInfo = varMatches[1]
}
// Log more detailed information
log.Printf("Template Error Details - Template: %s, Line: %s, Variable: %s",
templateInfo, lineInfo, variableInfo)
}
// Create a more detailed error page
errorHTML := fmt.Sprintf(`
<html>
<head>
<title>Template Error</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; padding: 15px; border-radius: 5px; }
.error-details { background-color: #f8f9fa; border: 1px solid #dee2e6; padding: 15px; border-radius: 5px; margin-top: 20px; }
.code-context { background-color: #f0f0f0; padding: 10px; border-radius: 5px; font-family: monospace; }
pre { white-space: pre-wrap; }
.highlight { background-color: #ffeb3b; font-weight: bold; }
</style>
</head>
<body>
<h1>Template Error</h1>
<div class="error-box">
<h3>Error Details:</h3>
<p><strong>Template:</strong> %s</p>
<p><strong>Line:</strong> %s</p>
<p><strong>Missing Variable:</strong> <span class="highlight">%s</span></p>
<pre>%s</pre>
</div>
<div class="error-details">
<h3>Debugging Tips:</h3>
<p>1. Check if the variable <span class="highlight">%s</span> is passed to the template</p>
<p>2. Visit <a href="/debug">/debug</a> to see available template variables</p>
<p>3. Check for typos in variable names</p>
<p>4. Ensure the variable is of the expected type</p>
<p>5. Check the controller that renders this template to ensure all required data is provided</p>
</div>
</body>
</html>
`, templateInfo, lineInfo, variableInfo, errorMsg, variableInfo)
return c.Status(fiber.StatusInternalServerError).Type("html").SendString(errorHTML)
}
// For other errors, use the default error handler
return fiber.DefaultErrorHandler(c, err)
},
})
// Setup static file serving
// Files in ./pkg/servers/ui/static will be accessible via /static URL path
app.Static("/static", "./pkg/servers/ui/static")
// Setup routes
routes.SetupRoutes(app)
return app
}