package paste import ( "crypto/rand" "errors" "sync" "time" ) var ErrNotFound = errors.New("paste not found") type Paste struct { ID string `json:"id"` Title string `json:"title"` Content string `json:"content"` Language string `json:"language"` CreatedAt time.Time `json:"created_at"` ExpiresAt *time.Time `json:"expires_at"` PasswordHash string `json:"-"` Protected bool `json:"protected"` } type Store struct { mu sync.RWMutex pastes map[string]*Paste } func NewStore() *Store { return &Store{pastes: make(map[string]*Paste)} } func (s *Store) Create(p *Paste) error { id, err := generateID() if err != nil { return err } p.ID = id p.CreatedAt = time.Now().UTC() s.mu.Lock() s.pastes[id] = p s.mu.Unlock() return nil } func (s *Store) Get(id string) (*Paste, error) { s.mu.RLock() p, ok := s.pastes[id] s.mu.RUnlock() if !ok { return nil, ErrNotFound } return p, nil } func (s *Store) Delete(id string) error { s.mu.Lock() delete(s.pastes, id) s.mu.Unlock() return nil } func (s *Store) ForEach(fn func(p *Paste) error) error { s.mu.RLock() ids := make([]string, 0, len(s.pastes)) for id := range s.pastes { ids = append(ids, id) } s.mu.RUnlock() for _, id := range ids { s.mu.RLock() p, ok := s.pastes[id] s.mu.RUnlock() if !ok { continue } if err := fn(p); err != nil { return err } } return nil } const base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" func generateID() (string, error) { b := make([]byte, 8) if _, err := rand.Read(b); err != nil { return "", err } result := make([]byte, 8) for i, v := range b { result[i] = base62Chars[int(v)%62] } return string(result), nil }