feat(workspace): synchronize folders with server and add folder deletion
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
This commit is contained in:
1 parent
1be0909344
commit
c4b2a00348
5 files changed
+382
-49
No files matched your search
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -1435,6 +1436,135 @@ func (s *Store) DeleteDocument(userID, docID string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) GetFolders(userID string) ([]string, error) {
|
||||
folderSet := make(map[string]bool)
|
||||
|
||||
// 1. Collect from SQLite documents (owned by user or shared with user)
|
||||
rows, err := s.db.Query(
|
||||
`SELECT DISTINCT folder_path FROM documents
|
||||
WHERE (user_id = ? OR id IN (SELECT doc_id FROM doc_shares WHERE user_id = ?))
|
||||
AND is_archived = 0 AND folder_path != ''`,
|
||||
userID, userID,
|
||||
)
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
var fp string
|
||||
if err := rows.Scan(&fp); err == nil && fp != "" {
|
||||
clean := filepath.ToSlash(filepath.Clean(fp))
|
||||
parts := strings.Split(clean, "/")
|
||||
var cur string
|
||||
for _, p := range parts {
|
||||
if p != "" && p != "." {
|
||||
if cur == "" {
|
||||
cur = p
|
||||
} else {
|
||||
cur = cur + "/" + p
|
||||
}
|
||||
folderSet[cur] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = rows.Close()
|
||||
}
|
||||
|
||||
// 2. Scan physical directories on disk in docDir/userID
|
||||
if s.docDir != "" {
|
||||
userDir := filepath.Join(s.docDir, userID)
|
||||
if fi, err := os.Stat(userDir); err == nil && fi.IsDir() {
|
||||
_ = filepath.Walk(userDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if path == userDir {
|
||||
return nil
|
||||
}
|
||||
rel, err := filepath.Rel(userDir, path)
|
||||
if err == nil && rel != "" && rel != "." {
|
||||
clean := filepath.ToSlash(filepath.Clean(rel))
|
||||
parts := strings.Split(clean, "/")
|
||||
var cur string
|
||||
for _, p := range parts {
|
||||
if p != "" && p != "." {
|
||||
if cur == "" {
|
||||
cur = p
|
||||
} else {
|
||||
cur = cur + "/" + p
|
||||
}
|
||||
folderSet[cur] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(folderSet))
|
||||
for f := range folderSet {
|
||||
result = append(result, f)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Store) CreateFolder(userID, folderPath string) (string, error) {
|
||||
clean := filepath.ToSlash(filepath.Clean(strings.TrimSpace(folderPath)))
|
||||
if clean == "" || clean == "." || strings.HasPrefix(clean, "/") || strings.Contains(clean, "..") {
|
||||
return "", fmt.Errorf("ungültiger Ordnerpfad")
|
||||
}
|
||||
|
||||
if s.docDir != "" {
|
||||
targetDir := filepath.Join(s.docDir, userID, clean)
|
||||
if err := os.MkdirAll(targetDir, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return clean, nil
|
||||
}
|
||||
|
||||
func (s *Store) DeleteFolder(userID, folderPath string) ([]string, error) {
|
||||
clean := filepath.ToSlash(filepath.Clean(strings.TrimSpace(folderPath)))
|
||||
if clean == "" || clean == "." || strings.HasPrefix(clean, "/") || strings.Contains(clean, "..") {
|
||||
return nil, fmt.Errorf("ungültiger Ordnerpfad")
|
||||
}
|
||||
|
||||
// Find all documents in this folder and subfolders
|
||||
rows, err := s.db.Query(
|
||||
`SELECT id FROM documents
|
||||
WHERE user_id = ? AND (folder_path = ? OR folder_path LIKE ?)`,
|
||||
userID, clean, clean+"/%",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var deletedDocIDs []string
|
||||
for rows.Next() {
|
||||
var id string
|
||||
if err := rows.Scan(&id); err == nil {
|
||||
deletedDocIDs = append(deletedDocIDs, id)
|
||||
}
|
||||
}
|
||||
_ = rows.Close()
|
||||
|
||||
// Delete from SQLite, pins, shares
|
||||
for _, id := range deletedDocIDs {
|
||||
_, _ = s.db.Exec(`DELETE FROM doc_pins WHERE doc_id = ?`, id)
|
||||
_, _ = s.db.Exec(`DELETE FROM doc_shares WHERE doc_id = ?`, id)
|
||||
_, _ = s.db.Exec(`DELETE FROM documents WHERE id = ? AND user_id = ?`, id, userID)
|
||||
}
|
||||
|
||||
// Remove physical folder and subdirectories from disk
|
||||
if s.docDir != "" {
|
||||
targetDir := filepath.Join(s.docDir, userID, clean)
|
||||
_ = os.RemoveAll(targetDir)
|
||||
}
|
||||
|
||||
return deletedDocIDs, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetDocShares(docID string) ([]domain.DocShare, error) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT s.id, s.doc_id, s.user_id, u.email, u.display_name, s.permission, s.created_by, s.created_at
|
||||
|
||||
Reference in new issue
Block a user