all repos

onasty @ 450a6a93f5f70a4a975b11d8e232fa4f2ce294fc

a one-time notes service

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

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat(note): return `burn_before_expiration` (#149), 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
	BurnBeforeExpiration bool      `json:"burn_before_expiration"`
60
	CreatedAt            time.Time `json:"created_at"`
61
	ExpiresAt            time.Time `json:"expires_at,omitzero"`
62
}
63
64
func (a *APIV1) getNoteBySlugHandler(c *gin.Context) {
65
	var req getNoteBySlugRequest
66
	if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
67
		newError(c, http.StatusBadRequest, "invalid request")
68
		return
69
	}
70
71
	note, err := a.notesrv.GetBySlugAndRemoveIfNeeded(
72
		c.Request.Context(),
73
		notesrv.GetNoteBySlugInput{
74
			Slug:     c.Param("slug"),
75
			Password: req.Password,
76
		},
77
	)
78
	if err != nil {
79
		errorResponse(c, err)
80
		return
81
	}
82
83
	status := http.StatusOK
84
	if !note.ReadAt.IsZero() {
85
		status = http.StatusNotFound
86
	}
87
88
	c.JSON(status, getNoteBySlugResponse{
89
		Content:              note.Content,
90
		ReadAt:               note.ReadAt,
91
		CreatedAt:            note.CreatedAt,
92
		ExpiresAt:            note.ExpiresAt,
93
		BurnBeforeExpiration: note.BurnBeforeExpiration,
94
	})
95
}
96
97
type getNoteMetadataBySlugResponse struct {
98
	CreatedAt   time.Time `json:"created_at"`
99
	HasPassword bool      `json:"has_password"`
100
}
101
102
func (a *APIV1) getNoteMetadataByIDHandler(c *gin.Context) {
103
	meta, err := a.notesrv.GetNoteMetadataBySlug(c.Request.Context(), c.Param("slug"))
104
	if err != nil {
105
		errorResponse(c, err)
106
		return
107
	}
108
109
	c.JSON(http.StatusOK, getNoteMetadataBySlugResponse{
110
		CreatedAt:   meta.CreatedAt,
111
		HasPassword: meta.HasPassword,
112
	})
113
}
114
115
type getNotesResponse struct {
116
	Content              string    `json:"content"`
117
	Slug                 string    `json:"slug"`
118
	BurnBeforeExpiration bool      `json:"burn_before_expiration"`
119
	HasPassword          bool      `json:"has_password"`
120
	CreatedAt            time.Time `json:"created_at"`
121
	ExpiresAt            time.Time `json:"expires_at,omitzero"`
122
	ReadAt               time.Time `json:"read_at,omitzero"`
123
}
124
125
func (a *APIV1) getNotesHandler(c *gin.Context) {
126
	notes, err := a.notesrv.GetAllByAuthorID(c.Request.Context(), a.getUserID(c))
127
	if err != nil {
128
		errorResponse(c, err)
129
		return
130
	}
131
132
	var response []getNotesResponse
133
	for _, note := range notes {
134
		response = append(response, getNotesResponse{
135
			Content:              note.Content,
136
			Slug:                 note.Slug,
137
			BurnBeforeExpiration: note.BurnBeforeExpiration,
138
			HasPassword:          note.HasPassword,
139
			CreatedAt:            note.CreatedAt,
140
			ExpiresAt:            note.ExpiresAt,
141
			ReadAt:               note.ReadAt,
142
		})
143
	}
144
145
	c.JSON(http.StatusOK, response)
146
}
147
148
type updateNoteRequest struct {
149
	ExpiresAt            *time.Time `json:"expires_at,omitempty"`
150
	BurnBeforeExpiration *bool      `json:"burn_before_expiration,omitempty"`
151
}
152
153
func (a *APIV1) updateNoteHandler(c *gin.Context) {
154
	var req updateNoteRequest
155
	if err := c.ShouldBindJSON(&req); err != nil {
156
		newError(c, http.StatusBadRequest, "invalid request")
157
		return
158
	}
159
160
	// TODO: burn_before_expiration shouldn't be set if user has not set or specified expires_at
161
162
	if err := a.notesrv.UpdateExpirationTimeSettings(
163
		c.Request.Context(),
164
		dtos.PatchNote{
165
			BurnBeforeExpiration: req.BurnBeforeExpiration,
166
			ExpiresAt:            req.ExpiresAt,
167
		},
168
		c.Param("slug"),
169
		a.getUserID(c),
170
	); err != nil {
171
		errorResponse(c, err)
172
		return
173
	}
174
175
	c.Status(http.StatusOK)
176
}
177
178
func (a *APIV1) deleteNoteHandler(c *gin.Context) {
179
	if err := a.notesrv.DeleteBySlug(
180
		c.Request.Context(),
181
		c.Param("slug"),
182
		a.getUserID(c),
183
	); err != nil {
184
		errorResponse(c, err)
185
		return
186
	}
187
188
	c.Status(http.StatusNoContent)
189
}
190
191
type setNotePasswordRequest struct {
192
	Password string `json:"password"`
193
}
194
195
func (a *APIV1) setNotePasswordHandler(c *gin.Context) {
196
	var req setNotePasswordRequest
197
	if err := c.ShouldBindJSON(&req); err != nil {
198
		newError(c, http.StatusBadRequest, "invalid request")
199
		return
200
	}
201
202
	if err := a.notesrv.UpdatePassword(
203
		c.Request.Context(),
204
		c.Param("slug"),
205
		req.Password,
206
		a.getUserID(c),
207
	); err != nil {
208
		errorResponse(c, err)
209
		return
210
	}
211
212
	c.Status(http.StatusOK)
213
}