all repos

onasty @ 7ff621dd2cbf09e69e4ec0edf8bf5e4a28b7d637

a one-time notes service

onasty/internal/models/note.go (view raw)

Olexandr Smirnov Olexandr Smirnov
ss2316544@gmail.com
fix: return "invalid slug" error to user (#189)..., 9 months ago
1
package models
2
3
import (
4
	"errors"
5
	"regexp"
6
	"time"
7
8
	"github.com/gofrs/uuid/v5"
9
)
10
11
// read and unread are not allowed because those slugs might and will be interpreted as api routes
12
var notAllowedSlugs = map[string]struct{}{
13
	"read":   {},
14
	"unread": {},
15
}
16
17
var (
18
	ErrNoteContentIsEmpty     = errors.New("note: content is empty")
19
	ErrNoteSlugIsAlreadyInUse = errors.New("note: slug is already in use")
20
	ErrNoteSlugIsInvalid      = errors.New("note: slug is invalid")
21
	ErrNoteExpired            = errors.New("note: expired")
22
	ErrNoteNotFound           = errors.New("note: not found")
23
)
24
25
type Note struct {
26
	ID                   uuid.UUID
27
	Content              string
28
	Slug                 string
29
	Password             string
30
	BurnBeforeExpiration bool
31
	ReadAt               time.Time
32
	CreatedAt            time.Time
33
	ExpiresAt            time.Time
34
}
35
36
var slugPattern = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
37
38
func (n Note) Validate() error {
39
	if n.Content == "" {
40
		return ErrNoteContentIsEmpty
41
	}
42
43
	if !slugPattern.MatchString(n.Slug) {
44
		return ErrNoteSlugIsInvalid
45
	}
46
47
	if n.IsExpired() {
48
		return ErrNoteExpired
49
	}
50
51
	if _, exists := notAllowedSlugs[n.Slug]; exists {
52
		return ErrNoteSlugIsAlreadyInUse
53
	}
54
55
	return nil
56
}
57
58
func (n Note) IsExpired() bool {
59
	return !n.ExpiresAt.IsZero() &&
60
		n.ExpiresAt.Before(time.Now())
61
}
62
63
func (n Note) ShouldBeBurnt() bool {
64
	return !n.ExpiresAt.IsZero() &&
65
		n.BurnBeforeExpiration
66
}
67
68
func (n Note) IsRead() bool {
69
	return !n.ReadAt.IsZero()
70
}