package api import ( "net/http" "markdownhub/internal/files" "github.com/google/uuid" ) func (s *Server) handleShareFile(w http.ResponseWriter, r *http.Request) { var req struct { Path string `json:"path"` UserID string `json:"user_id"` Username string `json:"username"` Level string `json:"level"` // "ro" or "rw" } if err := decodeBody(r, &req); err != nil || req.Path == "" || req.Level == "" { writeJSON(w, 400, map[string]string{"error": "path, user_id/username, and level required"}) return } if req.Level != "ro" && req.Level != "rw" { writeJSON(w, 400, map[string]string{"error": "level must be 'ro' or 'rw'"}) return } ownerID := getUserID(r) // Resolve username to user_id if needed targetUserID := req.UserID if targetUserID == "" && req.Username != "" { err := s.db.QueryRow("SELECT id FROM users WHERE username = ?", req.Username).Scan(&targetUserID) if err != nil { writeJSON(w, 404, map[string]string{"error": "user not found"}) return } } if targetUserID == "" { writeJSON(w, 400, map[string]string{"error": "user_id or username required"}) return } // Get or create file record fileID := "" err := s.db.QueryRow("SELECT id FROM files WHERE owner_id = ? AND path = ?", ownerID, req.Path).Scan(&fileID) if err != nil { // Create file record fileID = uuid.New().String() s.db.Exec("INSERT INTO files (id, owner_id, path) VALUES (?, ?, ?)", fileID, ownerID, req.Path) } // Upsert permission permID := uuid.New().String() _, err = s.db.Exec( `INSERT INTO permissions (id, file_id, user_id, level, granted_by) VALUES (?, ?, ?, ?, ?) ON CONFLICT(file_id, user_id) DO UPDATE SET level = ?`, permID, fileID, targetUserID, req.Level, ownerID, req.Level, ) if err != nil { writeJSON(w, 500, map[string]string{"error": "share failed"}) return } writeJSON(w, 200, map[string]string{"status": "shared"}) } func (s *Server) handleUnshareFile(w http.ResponseWriter, r *http.Request) { var req struct { Path string `json:"path"` UserID string `json:"user_id"` } if err := decodeBody(r, &req); err != nil || req.Path == "" || req.UserID == "" { writeJSON(w, 400, map[string]string{"error": "path and user_id required"}) return } ownerID := getUserID(r) fileID := "" s.db.QueryRow("SELECT id FROM files WHERE owner_id = ? AND path = ?", ownerID, req.Path).Scan(&fileID) if fileID == "" { writeJSON(w, 404, map[string]string{"error": "file not found"}) return } s.db.Exec("DELETE FROM permissions WHERE file_id = ? AND user_id = ?", fileID, req.UserID) writeJSON(w, 200, map[string]string{"status": "unshared"}) } func (s *Server) handleListSharedFiles(w http.ResponseWriter, r *http.Request) { userID := getUserID(r) // Files shared WITH me rows, err := s.db.Query(` SELECT f.path, f.owner_id, u.username, p.level FROM permissions p JOIN files f ON f.id = p.file_id JOIN users u ON u.id = f.owner_id WHERE p.user_id = ? `, userID) if err != nil { writeJSON(w, 200, []files.FileInfo{}) return } defer rows.Close() var shared []map[string]string for rows.Next() { var path, ownerID, ownerName, level string rows.Scan(&path, &ownerID, &ownerName, &level) shared = append(shared, map[string]string{ "path": path, "owner": ownerName, "level": level, "owner_id": ownerID, }) } // Files I shared with others rows2, err := s.db.Query(` SELECT f.path, p.user_id, u.username, p.level FROM permissions p JOIN files f ON f.id = p.file_id JOIN users u ON u.id = p.user_id WHERE f.owner_id = ? `, userID) if err == nil { defer rows2.Close() for rows2.Next() { var path, sharedWith, sharedWithName, level string rows2.Scan(&path, &sharedWith, &sharedWithName, &level) shared = append(shared, map[string]string{ "path": path, "shared_with": sharedWithName, "level": level, "type": "outgoing", }) } } if shared == nil { shared = []map[string]string{} } writeJSON(w, 200, shared) }