search added

This commit is contained in:
nahakubuilde
2025-08-25 18:02:41 +01:00
parent 240b71e321
commit da71deb7e2
3 changed files with 238 additions and 1 deletions

View File

@@ -547,3 +547,104 @@ func (h *Handlers) TreeAPIHandler(c *gin.Context) {
c.JSON(http.StatusOK, notesTree)
}
// SearchHandler performs a simple full-text search across markdown and allowed text files
// within the notes directory, honoring skipped directories.
// GET /api/search?q=term
func (h *Handlers) SearchHandler(c *gin.Context) {
query := strings.TrimSpace(c.Query("q"))
if query == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "missing query"})
return
}
// Case-insensitive search
qLower := strings.ToLower(query)
type Snippet struct {
Line int `json:"line"`
Preview string `json:"preview"`
}
type Result struct {
Path string `json:"path"`
Type string `json:"type"` // "md" or "text"
Snippets []Snippet `json:"snippets"`
}
results := make([]Result, 0, 32)
// Walk the notes directory
err := filepath.Walk(h.config.NotesDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil // skip on error
}
if info.IsDir() {
// Compute relative dir path to check skip list
rel, _ := filepath.Rel(h.config.NotesDir, path)
rel = filepath.ToSlash(rel)
if rel == "." {
rel = ""
}
if utils.IsPathInSkippedDirs(rel, h.config.NotesDirSkip) {
return filepath.SkipDir
}
return nil
}
// Compute relative file path
relPath, _ := filepath.Rel(h.config.NotesDir, path)
relPath = filepath.ToSlash(relPath)
// Skip disallowed files
ext := strings.ToLower(filepath.Ext(relPath))
isMD := ext == ".md"
if !isMD && !models.IsAllowedFile(relPath, h.config.AllowedFileExtensions) {
return nil
}
// Read file content (limit size to prevent huge memory usage)
data, readErr := os.ReadFile(path)
if readErr != nil {
return nil
}
content := string(data)
contentLower := strings.ToLower(content)
if !strings.Contains(contentLower, qLower) {
return nil
}
// Build snippets: show up to 3 matches with 2 lines of context
lines := strings.Split(content, "\n")
linesLower := strings.Split(contentLower, "\n")
snippets := make([]Snippet, 0, 3)
for i := 0; i < len(linesLower) && len(snippets) < 3; i++ {
if strings.Contains(linesLower[i], qLower) {
start := i - 2
if start < 0 {
start = 0
}
end := i + 2
if end >= len(lines) {
end = len(lines) - 1
}
// Join preview lines
preview := strings.Join(lines[start:end+1], "\n")
snippets = append(snippets, Snippet{Line: i + 1, Preview: preview})
}
}
rtype := "text"
if isMD {
rtype = "md"
}
results = append(results, Result{Path: relPath, Type: rtype, Snippets: snippets})
return nil
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"query": query, "results": results})
}

View File

@@ -96,6 +96,7 @@ func (s *Server) setupRoutes() {
// API routes
s.router.GET("/api/tree", h.TreeAPIHandler)
s.router.GET("/api/search", h.SearchHandler)
}
func (s *Server) setupStaticFiles() {