claude-web-chat/projects.go
djuka 6c0ca3a96f
All checks were successful
Tests / unit-tests (push) Successful in 25s
Dodato kreiranje projekta kroz UI i change-password template
- CreateProject() sa validacijom imena (regex, duplikati, prazno)
- POST /projects/create ruta sa AuthMiddleware
- Modal forma na projects stranici za unos imena
- 10 unit testova za CreateProject
- change-password.html template i testovi
- Ažuriran TESTING.md sa novom sekcijom

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 06:27:10 +00:00

89 lines
1.9 KiB
Go

package main
import (
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
)
type Project struct {
Name string
Path string
Description string
HasReadme bool
}
// ListProjects returns a sorted list of projects from the given directory.
// Only directories are considered projects. Hidden directories (starting with .)
// are excluded.
func ListProjects(projectsPath string) ([]Project, error) {
entries, err := os.ReadDir(projectsPath)
if err != nil {
return nil, err
}
var projects []Project
for _, e := range entries {
if !e.IsDir() {
continue
}
name := e.Name()
if strings.HasPrefix(name, ".") {
continue
}
p := Project{
Name: name,
Path: filepath.Join(projectsPath, name),
}
// Try to read description from README.md first line
readmePath := filepath.Join(p.Path, "README.md")
if data, err := os.ReadFile(readmePath); err == nil {
p.HasReadme = true
lines := strings.SplitN(string(data), "\n", 3)
for _, line := range lines {
line = strings.TrimSpace(line)
line = strings.TrimLeft(line, "# ")
if line != "" {
p.Description = line
break
}
}
}
projects = append(projects, p)
}
sort.Slice(projects, func(i, j int) bool {
return projects[i].Name < projects[j].Name
})
return projects, nil
}
var validProjectName = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_-]*$`)
// CreateProject creates a new empty project directory.
// Name must contain only alphanumeric characters, hyphens, and underscores.
func CreateProject(projectsPath, name string) error {
if name == "" {
return fmt.Errorf("ime projekta ne može biti prazno")
}
if !validProjectName.MatchString(name) {
return fmt.Errorf("ime projekta može sadržati samo slova, brojeve, '-' i '_'")
}
path := filepath.Join(projectsPath, name)
if _, err := os.Stat(path); err == nil {
return fmt.Errorf("projekat '%s' već postoji", name)
}
return os.MkdirAll(path, 0755)
}