package storage import ( "fmt" "io/fs" "os" "path/filepath" "regexp" "github.com/sirupsen/logrus" "markdown-editor/internal/logger" ) var ( allowedExt = regexp.MustCompile(`(?i)\.md$`) ) type Storage struct { dataDir string log *logrus.Logger } func NewStorage(dataDir string) (*Storage, error) { log := logger.GetLogger() // Create data directory if it doesn't exist if err := os.MkdirAll(dataDir, 0755); err != nil { return nil, fmt.Errorf("failed to create data directory: %w", err) } return &Storage{ dataDir: dataDir, log: log, }, nil } // File represents a markdown file type File struct { Name string `json:"name"` Content string `json:"content"` } // ListFiles returns all .md files in the data directory func (s *Storage) ListFiles() ([]string, error) { s.log.WithField("operation", "list_files").Info("Listing markdown files") files := []string{} err := filepath.WalkDir(s.dataDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if !d.IsDir() && allowedExt.MatchString(d.Name()) { files = append(files, d.Name()) } return nil }) if err != nil { return nil, fmt.Errorf("failed to walk directory: %w", err) } return files, nil } // GetFile reads a markdown file by name func (s *Storage) GetFile(name string) (*File, error) { s.log.WithField("operation", "get_file").WithField("filename", name).Info("Getting file") path := filepath.Join(s.dataDir, name) if !allowedExt.MatchString(path) { return nil, fmt.Errorf("invalid file extension: only .md files allowed") } data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("file not found: %s", name) } return nil, fmt.Errorf("failed to read file: %w", err) } return &File{ Name: name, Content: string(data), }, nil } // CreateFile creates a new markdown file func (s *Storage) CreateFile(name string, content string) error { s.log.WithField("operation", "create_file").WithField("filename", name).Info("Creating file") path := filepath.Join(s.dataDir, name) if !allowedExt.MatchString(path) { return fmt.Errorf("invalid file extension: only .md files allowed") } // Check if file already exists if _, err := os.Stat(path); err == nil { return fmt.Errorf("file already exists: %s", name) } if err := os.WriteFile(path, []byte(content), 0644); err != nil { return fmt.Errorf("failed to write file: %w", err) } return nil } // UpdateFile updates an existing markdown file func (s *Storage) UpdateFile(name string, content string) error { s.log.WithField("operation", "update_file").WithField("filename", name).Info("Updating file") path := filepath.Join(s.dataDir, name) if !allowedExt.MatchString(path) { return fmt.Errorf("invalid file extension: only .md files allowed") } if err := os.WriteFile(path, []byte(content), 0644); err != nil { return fmt.Errorf("failed to write file: %w", err) } return nil } // DeleteFile deletes a markdown file func (s *Storage) DeleteFile(name string) error { s.log.WithField("operation", "delete_file").WithField("filename", name).Info("Deleting file") path := filepath.Join(s.dataDir, name) if !allowedExt.MatchString(path) { return fmt.Errorf("invalid file extension: only .md files allowed") } if err := os.Remove(path); err != nil { return fmt.Errorf("failed to delete file: %w", err) } return nil }