package sharing import ( "context" "sort" "sync" "time" ) type MemoryStore struct { mu sync.RWMutex policies map[string]PolicyRecord tokens map[string]string members map[string]map[string]MemberRecord visits map[string]map[string]VisitRecord } func NewMemoryStore() *MemoryStore { return &MemoryStore{ policies: make(map[string]PolicyRecord), tokens: make(map[string]string), members: make(map[string]map[string]MemberRecord), visits: make(map[string]map[string]VisitRecord), } } func (s *MemoryStore) GetPolicyByProject(ctx context.Context, projectID string) (PolicyRecord, error) { if err := ctx.Err(); err != nil { return PolicyRecord{}, err } s.mu.RLock() defer s.mu.RUnlock() policy, ok := s.policies[projectID] if !ok { return PolicyRecord{}, ErrNotFound } return clonePolicy(policy), nil } func (s *MemoryStore) GetPolicyByTokenHash(ctx context.Context, tokenHash []byte) (PolicyRecord, error) { if err := ctx.Err(); err != nil { return PolicyRecord{}, err } s.mu.RLock() defer s.mu.RUnlock() projectID, ok := s.tokens[string(tokenHash)] if !ok { return PolicyRecord{}, ErrNotFound } policy, ok := s.policies[projectID] if !ok { return PolicyRecord{}, ErrNotFound } return clonePolicy(policy), nil } func (s *MemoryStore) CreatePolicy(ctx context.Context, policy PolicyRecord) (PolicyRecord, error) { if err := ctx.Err(); err != nil { return PolicyRecord{}, err } s.mu.Lock() defer s.mu.Unlock() if existing, ok := s.policies[policy.ProjectID]; ok { return clonePolicy(existing), nil } policy = clonePolicy(policy) s.policies[policy.ProjectID] = policy s.tokens[string(policy.TokenHash)] = policy.ProjectID return clonePolicy(policy), nil } func (s *MemoryStore) UpdatePolicyPermission(ctx context.Context, projectID string, ownerID string, permission Permission, updatedAt time.Time) (PolicyRecord, error) { if err := ctx.Err(); err != nil { return PolicyRecord{}, err } s.mu.Lock() defer s.mu.Unlock() policy, ok := s.policies[projectID] if !ok { return PolicyRecord{}, ErrNotFound } if policy.OwnerID != ownerID { return PolicyRecord{}, ErrForbidden } policy.LinkPermission = permission policy.Version++ policy.UpdatedAt = updatedAt s.policies[projectID] = policy return clonePolicy(policy), nil } func (s *MemoryStore) DeleteAll(ctx context.Context, projectID string, ownerID string) error { if err := ctx.Err(); err != nil { return err } s.mu.Lock() defer s.mu.Unlock() policy, ok := s.policies[projectID] if !ok { return nil } if policy.OwnerID != ownerID { return ErrForbidden } delete(s.tokens, string(policy.TokenHash)) delete(s.policies, projectID) delete(s.members, projectID) delete(s.visits, projectID) return nil } func (s *MemoryStore) ListMembers(ctx context.Context, projectID string) ([]MemberRecord, error) { if err := ctx.Err(); err != nil { return nil, err } s.mu.RLock() defer s.mu.RUnlock() items := make([]MemberRecord, 0, len(s.members[projectID])) for _, member := range s.members[projectID] { items = append(items, cloneMember(member)) } sort.Slice(items, func(i, j int) bool { return items[i].CreatedAt.Before(items[j].CreatedAt) }) return items, nil } func (s *MemoryStore) CreateMember(ctx context.Context, member MemberRecord) (MemberRecord, error) { if err := ctx.Err(); err != nil { return MemberRecord{}, err } s.mu.Lock() defer s.mu.Unlock() if _, ok := s.policies[member.ProjectID]; !ok { return MemberRecord{}, ErrNotFound } if s.members[member.ProjectID] == nil { s.members[member.ProjectID] = make(map[string]MemberRecord) } for _, existing := range s.members[member.ProjectID] { if existing.IdentifierType == member.IdentifierType && hashesEqual(existing.IdentifierHash, member.IdentifierHash) { return MemberRecord{}, ErrMemberExists } } member = cloneMember(member) s.members[member.ProjectID][member.ID] = member return cloneMember(member), nil } func (s *MemoryStore) UpdateMember(ctx context.Context, projectID string, memberID string, permission Permission, updatedAt time.Time) (MemberRecord, error) { if err := ctx.Err(); err != nil { return MemberRecord{}, err } s.mu.Lock() defer s.mu.Unlock() member, ok := s.members[projectID][memberID] if !ok { return MemberRecord{}, ErrNotFound } member.Permission = permission member.UpdatedAt = updatedAt s.members[projectID][memberID] = member return cloneMember(member), nil } func (s *MemoryStore) DeleteMember(ctx context.Context, projectID string, memberID string) error { if err := ctx.Err(); err != nil { return err } s.mu.Lock() defer s.mu.Unlock() if _, ok := s.members[projectID][memberID]; !ok { return ErrNotFound } delete(s.members[projectID], memberID) return nil } func (s *MemoryStore) CountVisitors(ctx context.Context, projectID string) (int64, error) { if err := ctx.Err(); err != nil { return 0, err } s.mu.RLock() defer s.mu.RUnlock() return int64(len(s.visits[projectID])), nil } func (s *MemoryStore) RecordVisit(ctx context.Context, visit VisitRecord) error { if err := ctx.Err(); err != nil { return err } s.mu.Lock() defer s.mu.Unlock() if _, ok := s.policies[visit.ProjectID]; !ok { return ErrNotFound } if s.visits[visit.ProjectID] == nil { s.visits[visit.ProjectID] = make(map[string]VisitRecord) } key := string(visit.VisitorHash) if current, ok := s.visits[visit.ProjectID][key]; ok { current.VisitCount++ current.LastSeenAt = visit.LastSeenAt if visit.UserID != "" { current.UserID = visit.UserID } s.visits[visit.ProjectID][key] = current return nil } s.visits[visit.ProjectID][key] = cloneVisit(visit) return nil } func (s *MemoryStore) ListVisitors(ctx context.Context, projectID string, limit int64) ([]VisitRecord, error) { if err := ctx.Err(); err != nil { return nil, err } s.mu.RLock() defer s.mu.RUnlock() items := make([]VisitRecord, 0, len(s.visits[projectID])) for _, visit := range s.visits[projectID] { items = append(items, cloneVisit(visit)) } sort.Slice(items, func(i, j int) bool { return items[i].LastSeenAt.After(items[j].LastSeenAt) }) if limit > 0 && int64(len(items)) > limit { items = items[:limit] } return items, nil } func (s *MemoryStore) Close() error { return nil } func clonePolicy(policy PolicyRecord) PolicyRecord { policy.TokenHash = append([]byte(nil), policy.TokenHash...) policy.TokenCiphertext = append([]byte(nil), policy.TokenCiphertext...) return policy } func cloneMember(member MemberRecord) MemberRecord { member.IdentifierHash = append([]byte(nil), member.IdentifierHash...) member.IdentifierCiphertext = append([]byte(nil), member.IdentifierCiphertext...) return member } func cloneVisit(visit VisitRecord) VisitRecord { visit.VisitorHash = append([]byte(nil), visit.VisitorHash...) return visit }