GetInvitationByCode retrieves a pending invitation by its join code. Looks up by SHA-256 hash first (new invitations), then falls back to plaintext lookup for legacy invitations created before hashing.
(code string)
| 686 | // Looks up by SHA-256 hash first (new invitations), then falls back to |
| 687 | // plaintext lookup for legacy invitations created before hashing. |
| 688 | func (s *Store) GetInvitationByCode(code string) (*models.WorkspaceInvitation, error) { |
| 689 | // Hash the provided code for lookup |
| 690 | hash := sha256.Sum256([]byte(code)) |
| 691 | codeHash := hex.EncodeToString(hash[:]) |
| 692 | |
| 693 | var inv models.WorkspaceInvitation |
| 694 | var acceptedAt, expiresAt *string |
| 695 | var createdAt string |
| 696 | |
| 697 | // Try hashed lookup first (new invitations) |
| 698 | err := s.db.QueryRow(s.q(` |
| 699 | SELECT id, workspace_id, email, role, invited_by, code, accepted_at, expires_at, created_at |
| 700 | FROM workspace_invitations WHERE code_hash = ? AND accepted_at IS NULL |
| 701 | `), codeHash).Scan( |
| 702 | &inv.ID, &inv.WorkspaceID, &inv.Email, &inv.Role, &inv.InvitedBy, |
| 703 | &inv.Code, &acceptedAt, &expiresAt, &createdAt, |
| 704 | ) |
| 705 | if err == nil { |
| 706 | inv.CreatedAt = parseTime(createdAt) |
| 707 | inv.AcceptedAt = parseTimePtr(acceptedAt) |
| 708 | inv.ExpiresAt = parseTimePtr(expiresAt) |
| 709 | return &inv, nil |
| 710 | } |
| 711 | if err != sql.ErrNoRows { |
| 712 | return nil, fmt.Errorf("get invitation by code hash: %w", err) |
| 713 | } |
| 714 | |
| 715 | // Fall back to plaintext lookup (legacy invitations) |
| 716 | err = s.db.QueryRow(s.q(` |
| 717 | SELECT id, workspace_id, email, role, invited_by, code, accepted_at, expires_at, created_at |
| 718 | FROM workspace_invitations WHERE code = ? AND accepted_at IS NULL |
| 719 | `), code).Scan( |
| 720 | &inv.ID, &inv.WorkspaceID, &inv.Email, &inv.Role, &inv.InvitedBy, |
| 721 | &inv.Code, &acceptedAt, &expiresAt, &createdAt, |
| 722 | ) |
| 723 | if err == sql.ErrNoRows { |
| 724 | return nil, nil |
| 725 | } |
| 726 | if err != nil { |
| 727 | return nil, fmt.Errorf("get invitation by code: %w", err) |
| 728 | } |
| 729 | |
| 730 | inv.CreatedAt = parseTime(createdAt) |
| 731 | inv.AcceptedAt = parseTimePtr(acceptedAt) |
| 732 | inv.ExpiresAt = parseTimePtr(expiresAt) |
| 733 | return &inv, nil |
| 734 | } |
| 735 | |
| 736 | // AcceptInvitation marks an invitation as accepted. |
| 737 | func (s *Store) AcceptInvitation(id string) error { |