Files
MyClub/internal/controllers/docs_controller.go
T
Tomáš Dvořák 35d0954afd dev day #62
2025-10-16 17:10:13 +02:00

143 lines
3.4 KiB
Go

package controllers
import (
"io/ioutil"
"net/http"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
type DocsController struct {
DocsPath string
}
func NewDocsController(docsPath string) *DocsController {
return &DocsController{
DocsPath: docsPath,
}
}
// GetDocFile serves a specific documentation file
func (dc *DocsController) GetDocFile(c *gin.Context) {
// Get the requested file path from the URL
docPath := c.Param("filepath")
// Security: Prevent directory traversal
if strings.Contains(docPath, "..") {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file path"})
return
}
// Build full path
fullPath := filepath.Join(dc.DocsPath, docPath)
// Check if file exists and is a markdown file
if !strings.HasSuffix(fullPath, ".md") {
c.JSON(http.StatusBadRequest, gin.H{"error": "Only markdown files are allowed"})
return
}
// Read the file
content, err := ioutil.ReadFile(fullPath)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Documentation file not found"})
return
}
// Return the content as plain text
c.Header("Content-Type", "text/markdown; charset=utf-8")
c.String(http.StatusOK, string(content))
}
// ListDocFiles returns a list of available documentation files
func (dc *DocsController) ListDocFiles(c *gin.Context) {
files, err := ioutil.ReadDir(dc.DocsPath)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read documentation directory"})
return
}
var docFiles []map[string]interface{}
for _, file := range files {
if !file.IsDir() && strings.HasSuffix(file.Name(), ".md") {
docFiles = append(docFiles, map[string]interface{}{
"name": file.Name(),
"path": "/DOCS/" + file.Name(),
"size": file.Size(),
"modified_at": file.ModTime(),
})
}
}
c.JSON(http.StatusOK, gin.H{
"files": docFiles,
"total": len(docFiles),
})
}
// SearchDocs searches through documentation files
func (dc *DocsController) SearchDocs(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Search query is required"})
return
}
query = strings.ToLower(query)
files, err := ioutil.ReadDir(dc.DocsPath)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read documentation directory"})
return
}
var results []map[string]interface{}
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), ".md") {
continue
}
fullPath := filepath.Join(dc.DocsPath, file.Name())
content, err := ioutil.ReadFile(fullPath)
if err != nil {
continue
}
contentLower := strings.ToLower(string(content))
nameLower := strings.ToLower(file.Name())
// Check if query matches filename or content
if strings.Contains(nameLower, query) || strings.Contains(contentLower, query) {
// Find context around match
index := strings.Index(contentLower, query)
start := 0
end := len(content)
if index > 100 {
start = index - 100
}
if index+len(query)+200 < len(content) {
end = index + len(query) + 200
}
excerpt := string(content[start:end])
results = append(results, map[string]interface{}{
"name": file.Name(),
"path": "/DOCS/" + file.Name(),
"excerpt": excerpt,
"matches": strings.Count(contentLower, query),
})
}
}
c.JSON(http.StatusOK, gin.H{
"results": results,
"total": len(results),
"query": query,
})
}