all repos

onasty @ 86dba8ea0dc14c9a08dbb0baa9b0441168638328

a one-time notes service

onasty/internal/transport/http/apiv1/note.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat(api): get note metadata (#148)..., 11 months ago
1
package apiv1
2
3
import (
4
	"errors"
5
	"io"
6
	"net/http"
7
	"time"
8
9
	"github.com/gin-gonic/gin"
10
	"github.com/olexsmir/onasty/internal/dtos"
11
	"github.com/olexsmir/onasty/internal/service/notesrv"
12
)
13
14
type createNoteRequest struct {
15
	Content              string    `json:"content"`
16
	Slug                 string    `json:"slug"`
17
	Password             string    `json:"password"`
18
	BurnBeforeExpiration bool      `json:"burn_before_expiration"`
19
	ExpiresAt            time.Time `json:"expires_at"`
20
}
21
22
type createNoteResponse struct {
23
	Slug string `json:"slug"`
24
}
25
26
func (a *APIV1) createNoteHandler(c *gin.Context) {
27
	var req createNoteRequest
28
	if err := c.ShouldBindJSON(&req); err != nil {
29
		newError(c, http.StatusBadRequest, "invalid request")
30
		return
31
	}
32
33
	// TODO: burn_before_expiration shouldn't be set if user has not set or specified expires_at
34
35
	slug, err := a.notesrv.Create(c.Request.Context(), dtos.CreateNote{
36
		Content:              req.Content,
37
		UserID:               a.getUserID(c),
38
		Slug:                 req.Slug,
39
		Password:             req.Password,
40
		BurnBeforeExpiration: req.BurnBeforeExpiration,
41
		CreatedAt:            time.Now(),
42
		ExpiresAt:            req.ExpiresAt,
43
	}, a.getUserID(c))
44
	if err != nil {
45
		errorResponse(c, err)
46
		return
47
	}
48
49
	c.JSON(http.StatusCreated, createNoteResponse{slug})
50
}
51
52
type getNoteBySlugRequest struct {
53
	Password string `json:"password"`
54
}
55
56
type getNoteBySlugResponse struct {
57
	Content   string    `json:"content"`
58
	ReadAt    time.Time `json:"read_at,omitzero"`
59
	CreatedAt time.Time `json:"created_at"`
60
	ExpiresAt time.Time `json:"expires_at,omitzero"`
61
}
62
63
func (a *APIV1) getNoteBySlugHandler(c *gin.Context) {
64
	var req getNoteBySlugRequest
65
	if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
66
		newError(c, http.StatusBadRequest, "invalid request")
67
		return
68
	}
69
70
	note, err := a.notesrv.GetBySlugAndRemoveIfNeeded(
71
		c.Request.Context(),
72
		notesrv.GetNoteBySlugInput{
73
			Slug:     c.Param("slug"),
74
			Password: req.Password,
75
		},
76
	)
77
	if err != nil {
78
		errorResponse(c, err)
79
		return
80
	}
81
82
	status := http.StatusOK
83
	if !note.ReadAt.IsZero() {
84
		status = http.StatusNotFound
85
	}
86
87
	c.JSON(status, getNoteBySlugResponse{
88
		Content:   note.Content,
89
		ReadAt:    note.ReadAt,
90
		CreatedAt: note.CreatedAt,
91
		ExpiresAt: note.ExpiresAt,
92
	})
93
}
94
95
type getNoteMetadataBySlugResponse struct {
96
	CreatedAt   time.Time `json:"created_at"`
97
	HasPassword bool      `json:"has_password"`
98
}
99
100
func (a *APIV1) getNoteMetadataByIDHandler(c *gin.Context) {
101
	meta, err := a.notesrv.GetNoteMetadataBySlug(c.Request.Context(), c.Param("slug"))
102
	if err != nil {
103
		errorResponse(c, err)
104
		return
105
	}
106
107
	c.JSON(http.StatusOK, getNoteMetadataBySlugResponse{
108
		CreatedAt:   meta.CreatedAt,
109
		HasPassword: meta.HasPassword,
110
	})
111
}
112
113
type getNotesResponse struct {
114
	Content              string    `json:"content"`
115
	Slug                 string    `json:"slug"`
116
	BurnBeforeExpiration bool      `json:"burn_before_expiration"`
117
	HasPassword          bool      `json:"has_password"`
118
	CreatedAt            time.Time `json:"created_at"`
119
	ExpiresAt            time.Time `json:"expires_at,omitzero"`
120
	ReadAt               time.Time `json:"read_at,omitzero"`
121
}
122
123
func (a *APIV1) getNotesHandler(c *gin.Context) {
124
	notes, err := a.notesrv.GetAllByAuthorID(c.Request.Context(), a.getUserID(c))
125
	if err != nil {
126
		errorResponse(c, err)
127
		return
128
	}
129
130
	var response []getNotesResponse
131
	for _, note := range notes {
132
		response = append(response, getNotesResponse{
133
			Content:              note.Content,
134
			Slug:                 note.Slug,
135
			BurnBeforeExpiration: note.BurnBeforeExpiration,
136
			HasPassword:          note.HasPassword,
137
			CreatedAt:            note.CreatedAt,
138
			ExpiresAt:            note.ExpiresAt,
139
			ReadAt:               note.ReadAt,
140
		})
141
	}
142
143
	c.JSON(http.StatusOK, response)
144
}
145
146
type updateNoteRequest struct {
147
	ExpiresAt            *time.Time `json:"expires_at,omitempty"`
148
	BurnBeforeExpiration *bool      `json:"burn_before_expiration,omitempty"`
149
}
150
151
func (a *APIV1) updateNoteHandler(c *gin.Context) {
152
	var req updateNoteRequest
153
	if err := c.ShouldBindJSON(&req); err != nil {
154
		newError(c, http.StatusBadRequest, "invalid request")
155
		return
156
	}
157
158
	// TODO: burn_before_expiration shouldn't be set if user has not set or specified expires_at
159
160
	if err := a.notesrv.UpdateExpirationTimeSettings(
161
		c.Request.Context(),
162
		dtos.PatchNote{
163
			BurnBeforeExpiration: req.BurnBeforeExpiration,
164
			ExpiresAt:            req.ExpiresAt,
165
		},
166
		c.Param("slug"),
167
		a.getUserID(c),
168
	); err != nil {
169
		errorResponse(c, err)
170
		return
171
	}
172
173
	c.Status(http.StatusOK)
174
}
175
176
func (a *APIV1) deleteNoteHandler(c *gin.Context) {
177
	if err := a.notesrv.DeleteBySlug(
178
		c.Request.Context(),
179
		c.Param("slug"),
180
		a.getUserID(c),
181
	); err != nil {
182
		errorResponse(c, err)
183
		return
184
	}
185
186
	c.Status(http.StatusNoContent)
187
}
188
189
type setNotePasswordRequest struct {
190
	Password string `json:"password"`
191
}
192
193
func (a *APIV1) setNotePasswordHandler(c *gin.Context) {
194
	var req setNotePasswordRequest
195
	if err := c.ShouldBindJSON(&req); err != nil {
196
		newError(c, http.StatusBadRequest, "invalid request")
197
		return
198
	}
199
200
	if err := a.notesrv.UpdatePassword(
201
		c.Request.Context(),
202
		c.Param("slug"),
203
		req.Password,
204
		a.getUserID(c),
205
	); err != nil {
206
		errorResponse(c, err)
207
		return
208
	}
209
210
	c.Status(http.StatusOK)
211
}