feat(lists): add direct user invitation to shared lists and streamline live-sync indicator
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s
This commit is contained in:
1 parent
6e09888a44
commit
7bc5fbe001
7 files changed
+580
-29
No files matched your search
@@ -606,14 +606,25 @@ func (s *Store) GetDiscoverableUsers(viewerID string) ([]domain.User, error) {
|
||||
return []domain.User{*viewer}, nil
|
||||
}
|
||||
|
||||
// teams_only: users sharing at least one team or organization with viewer
|
||||
// teams_only: if no teams exist yet, allow discovering users on this instance
|
||||
var teamCount int
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM teams`).Scan(&teamCount)
|
||||
if teamCount == 0 {
|
||||
return s.GetAllUsers()
|
||||
}
|
||||
|
||||
// users sharing at least one team, organization or space with viewer
|
||||
rows, err := s.db.Query(
|
||||
`SELECT DISTINCT `+userSelectCols+` FROM users WHERE id = ? OR id IN (
|
||||
SELECT tm2.user_id FROM team_members tm1
|
||||
JOIN team_members tm2 ON tm1.team_id = tm2.team_id
|
||||
WHERE tm1.user_id = ?
|
||||
UNION
|
||||
SELECT sm2.user_id FROM space_members sm1
|
||||
JOIN space_members sm2 ON sm1.space_id = sm2.space_id
|
||||
WHERE sm1.user_id = ?
|
||||
)`,
|
||||
viewerID, viewerID,
|
||||
viewerID, viewerID, viewerID,
|
||||
)
|
||||
if err != nil {
|
||||
return []domain.User{*viewer}, nil
|
||||
@@ -1460,12 +1471,16 @@ func (s *Store) CreateDocShare(currentUserID, docID, targetUserIdentifier, permi
|
||||
permission = "read"
|
||||
}
|
||||
|
||||
// Resolve target user by ID or Email
|
||||
// Resolve target user by ID, username, or Email
|
||||
var targetUser *domain.User
|
||||
cleanTarget := strings.TrimSpace(targetUserIdentifier)
|
||||
if strings.Contains(cleanTarget, "@") {
|
||||
targetUser, err = s.GetUserByEmail(cleanTarget)
|
||||
} else {
|
||||
if strings.HasPrefix(cleanTarget, "usr_") {
|
||||
targetUser, _ = s.GetUserByID(cleanTarget)
|
||||
}
|
||||
if targetUser == nil {
|
||||
targetUser, _ = s.GetUserByLogin(cleanTarget)
|
||||
}
|
||||
if targetUser == nil {
|
||||
targetUser, err = s.GetUserByID(cleanTarget)
|
||||
}
|
||||
if err != nil || targetUser == nil {
|
||||
@@ -1636,6 +1651,79 @@ func (s *Store) GetSpaceMembers(spaceID string) ([]domain.SpaceMember, error) {
|
||||
return members, nil
|
||||
}
|
||||
|
||||
func (s *Store) AddSpaceMember(currentUserID, spaceID, targetUserIdentifier, role string) (*domain.SpaceMember, error) {
|
||||
// 1. Check if current user is member of space
|
||||
var myRole string
|
||||
err := s.db.QueryRow(`SELECT role FROM space_members WHERE space_id = ? AND user_id = ?`, spaceID, currentUserID).Scan(&myRole)
|
||||
if err != nil {
|
||||
return nil, errors.New("keine Berechtigung für diesen Bereich")
|
||||
}
|
||||
|
||||
// 2. Resolve target user by ID, username or email
|
||||
cleanTarget := strings.TrimSpace(targetUserIdentifier)
|
||||
var targetUser *domain.User
|
||||
if strings.HasPrefix(cleanTarget, "usr_") {
|
||||
targetUser, _ = s.GetUserByID(cleanTarget)
|
||||
}
|
||||
if targetUser == nil {
|
||||
targetUser, _ = s.GetUserByLogin(cleanTarget)
|
||||
}
|
||||
if targetUser == nil {
|
||||
targetUser, err = s.GetUserByID(cleanTarget)
|
||||
}
|
||||
if err != nil || targetUser == nil {
|
||||
return nil, errors.New("benutzer nicht gefunden")
|
||||
}
|
||||
|
||||
if role != "owner" && role != "editor" && role != "viewer" {
|
||||
role = "editor"
|
||||
}
|
||||
|
||||
now := domain.NowISO()
|
||||
_, err = s.db.Exec(
|
||||
`INSERT INTO space_members (space_id, user_id, role, joined_at) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(space_id, user_id) DO UPDATE SET role = excluded.role`,
|
||||
spaceID, targetUser.ID, role, now,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &domain.SpaceMember{
|
||||
SpaceID: spaceID,
|
||||
UserID: targetUser.ID,
|
||||
DisplayName: targetUser.DisplayName,
|
||||
Email: targetUser.Email,
|
||||
Role: role,
|
||||
JoinedAt: now,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Store) RemoveSpaceMember(currentUserID, spaceID, targetUserID string) error {
|
||||
var myRole string
|
||||
err := s.db.QueryRow(`SELECT role FROM space_members WHERE space_id = ? AND user_id = ?`, spaceID, currentUserID).Scan(&myRole)
|
||||
if err != nil {
|
||||
return errors.New("keine Berechtigung für diesen Bereich")
|
||||
}
|
||||
|
||||
if currentUserID != targetUserID && myRole != "owner" {
|
||||
return errors.New("nur der Eigentümer kann andere Mitglieder entfernen")
|
||||
}
|
||||
|
||||
var targetRole string
|
||||
_ = s.db.QueryRow(`SELECT role FROM space_members WHERE space_id = ? AND user_id = ?`, spaceID, targetUserID).Scan(&targetRole)
|
||||
if targetRole == "owner" {
|
||||
var ownerCount int
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM space_members WHERE space_id = ? AND role = 'owner'`, spaceID).Scan(&ownerCount)
|
||||
if ownerCount <= 1 {
|
||||
return errors.New("der letzte Eigentümer kann den Bereich nicht verlassen")
|
||||
}
|
||||
}
|
||||
|
||||
_, err = s.db.Exec(`DELETE FROM space_members WHERE space_id = ? AND user_id = ?`, spaceID, targetUserID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) GetSpacesForUser(userID string) ([]domain.Space, error) {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT s.id, s.name, s.icon, s.description, s.invite_code, s.is_personal, s.owner_id, s.created_at
|
||||
|
||||
Reference in new issue
Block a user