Files
moteva/server/internal/modules/sharing/memory_store.go
T

247 lines
6.7 KiB
Go

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
}