17 files changed,
142 insertions(+),
97 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2025-08-28 16:57:36 +0300
Parent:
d2c87a8
jump to
M
cmd/seed/notes.go
··· 13 13 id string 14 14 content string 15 15 slug string 16 - burnBeforeExpiration bool 16 + keepBeforeExpiration bool 17 17 password string 18 18 expiresAt time.Time 19 19 hasAuthor bool ··· 22 22 { //nolint:exhaustruct 23 23 content: "that test note one", 24 24 slug: "one", 25 - burnBeforeExpiration: false, 25 + keepBeforeExpiration: false, 26 26 }, 27 27 { //nolint:exhaustruct 28 28 content: "that test note two", 29 29 slug: "two", 30 - burnBeforeExpiration: true, 30 + keepBeforeExpiration: true, 31 31 password: "", 32 32 expiresAt: time.Now().Add(24 * time.Hour), 33 33 }, ··· 74 74 75 75 err := db.QueryRow( 76 76 ctx, ` 77 - insert into notes (content, slug, burn_before_expiration, password, expires_at) 77 + insert into notes (content, slug, keep_before_expiration, password, expires_at) 78 78 values ($1, $2, $3, $4, $5) 79 79 on conflict (slug) do update set 80 80 content = excluded.content, 81 - burn_before_expiration = excluded.burn_before_expiration, 81 + keep_before_expiration = excluded.keep_before_expiration, 82 82 password = excluded.password, 83 83 expires_at = excluded.expires_at 84 84 returning id`, 85 85 note.content, 86 86 note.slug, 87 - note.burnBeforeExpiration, 87 + note.keepBeforeExpiration, 88 88 passwd, 89 89 note.expiresAt, 90 90 ).Scan(¬esData[i].id)
M
e2e/apiv1_notes_test.go
··· 16 16 Content string `json:"content"` 17 17 Slug string `json:"slug"` 18 18 Password string `json:"password"` 19 - BurnBeforeExpiration bool `json:"burn_before_expiration"` 19 + KeepBeforeExpiration bool `json:"keep_before_expiration"` 20 20 ExpiresAt time.Time `json:"expires_at"` 21 21 } 22 22 apiv1NoteCreateResponse struct { ··· 137 137 }, 138 138 }, 139 139 { 140 - name: "burn before expiration, but without expiration time", 140 + name: "keep before expiration, but without expiration time", 141 141 inp: apiv1NoteCreateRequest{ //nolint:exhaustruct 142 142 Content: e.uuid(), 143 - BurnBeforeExpiration: true, 143 + KeepBeforeExpiration: true, 144 144 }, 145 145 assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) { 146 146 var body errorResponse 147 147 e.readBodyAndUnjsonify(r.Body, &body) 148 148 149 - e.Equal(models.ErrNoteCannotBeBurnt.Error(), body.Message) 149 + e.Equal(models.ErrNoteCannotBeKept.Error(), body.Message) 150 150 }, 151 151 }, 152 152 { ··· 163 163 name: "all possible fields", 164 164 inp: apiv1NoteCreateRequest{ //nolint:exhaustruct 165 165 Content: e.uuid(), 166 - BurnBeforeExpiration: true, 166 + KeepBeforeExpiration: true, 167 167 ExpiresAt: time.Now().Add(time.Hour), 168 168 }, 169 169 assert: func(r *httptest.ResponseRecorder, inp apiv1NoteCreateRequest) { ··· 176 176 e.NotEmpty(dbNote) 177 177 178 178 e.Equal(dbNote.Content, inp.Content) 179 - e.Equal(dbNote.BurnBeforeExpiration, inp.BurnBeforeExpiration) 179 + e.Equal(dbNote.KeepBeforeExpiration, inp.KeepBeforeExpiration) 180 180 e.Equal(dbNote.ExpiresAt.Unix(), inp.ExpiresAt.Unix()) 181 181 }, 182 182 }, ··· 266 266 e.Equal(dbNote2.ExpiresAt.Unix(), bodyRead2.ExpiresAt.Unix()) 267 267 } 268 268 269 -func (e *AppTestSuite) TestNoteV1_Get_ShouldNotBurnBeforeExpiration() { 269 +func (e *AppTestSuite) TestNoteV1_Get_ShouldNotBeKeptBeforeExpiration() { 270 270 // create note 271 271 content := e.uuid() 272 272 httpRespCreated := e.httpRequest( ··· 275 275 e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct 276 276 Content: content, 277 277 ExpiresAt: time.Now().Add(time.Hour), 278 - BurnBeforeExpiration: true, 278 + KeepBeforeExpiration: true, 279 279 }), 280 280 ) 281 281 e.Equal(http.StatusCreated, httpRespCreated.Code) ··· 295 295 dbNote := e.getNoteBySlug(bodyCreated.Slug) 296 296 e.Equal(content, dbNote.Content) 297 297 e.True(dbNote.ReadAt.IsZero()) 298 + 299 + // read note again 300 + httpRespRead2 := e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug, nil) 301 + e.Equal(http.StatusOK, httpRespRead2.Code) 302 + 303 + var body2 apiv1NoteGetResponse 304 + e.readBodyAndUnjsonify(httpRespRead2.Body, &body2) 305 + 306 + dbNote2 := e.getNoteBySlug(bodyCreated.Slug) 307 + e.Equal(content, dbNote2.Content) 308 + e.Equal(body2.Content, dbNote.Content) 309 + e.True(dbNote2.ReadAt.IsZero()) 310 + 311 + e.Equal(bodyRead, body2) 312 + e.Equal(dbNote, dbNote2) 298 313 } 299 314 300 -func (e *AppTestSuite) TestNoteV1_Get_ShouldBurnBeforeExpiration() { 315 +func (e *AppTestSuite) TestNoteV1_Get_ShouldKeepBeforeExpiration_expired() { 301 316 // synctest is used here to ensure proper synchronization and isolation of test execution 302 317 // it still feels wrong to use synctest in e2e test, but it works nonetheless 303 318 synctest.Test(e.T(), func(_ *testing.T) { ··· 309 324 e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct 310 325 Content: content, 311 326 ExpiresAt: time.Now().Add(time.Hour), 312 - BurnBeforeExpiration: true, 327 + KeepBeforeExpiration: true, 328 + }), 329 + ) 330 + e.Equal(http.StatusCreated, httpRespCreated.Code) 331 + 332 + var bodyCreated apiv1NoteCreateResponse 333 + e.readBodyAndUnjsonify(httpRespCreated.Body, &bodyCreated) 334 + 335 + time.Sleep(2 * time.Hour) 336 + 337 + // read note 338 + httpRespRead := e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug, nil) 339 + e.Equal(http.StatusGone, httpRespRead.Code) 340 + 341 + dbNote := e.getNoteBySlug(bodyCreated.Slug) 342 + e.Equal(content, dbNote.Content) 343 + e.True(dbNote.ReadAt.IsZero()) 344 + }) 345 +} 346 + 347 +func (e *AppTestSuite) TestNoteV1_Get_expired() { 348 + // synctest is used here to ensure proper synchronization and isolation of test execution 349 + // it still feels wrong to use synctest in e2e test, but it works nonetheless 350 + synctest.Test(e.T(), func(_ *testing.T) { 351 + // create note 352 + content := e.uuid() 353 + httpRespCreated := e.httpRequest( 354 + http.MethodPost, 355 + "/api/v1/note", 356 + e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct 357 + Content: content, 358 + ExpiresAt: time.Now().Add(time.Hour), 313 359 }), 314 360 ) 315 361 e.Equal(http.StatusCreated, httpRespCreated.Code)
M
e2e/e2e_utils_db_test.go
··· 105 105 "id", 106 106 "content", 107 107 "slug", 108 - "burn_before_expiration", 108 + "keep_before_expiration", 109 109 "password", 110 110 "read_at", 111 111 "created_at", ··· 119 119 var readAt sql.NullTime 120 120 var note models.Note 121 121 err = e.postgresDB.QueryRow(e.ctx, query, args...). 122 - Scan(¬e.ID, ¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.Password, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) 122 + Scan(¬e.ID, ¬e.Content, ¬e.Slug, ¬e.KeepBeforeExpiration, ¬e.Password, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) 123 123 if errors.Is(err, pgx.ErrNoRows) { 124 124 return models.Note{} //nolint:exhaustruct 125 125 }
M
internal/dtos/note.go
··· 10 10 11 11 type GetNote struct { 12 12 Content string 13 - BurnBeforeExpiration bool 13 + KeepBeforeExpiration bool 14 14 ReadAt time.Time 15 15 CreatedAt time.Time 16 16 ExpiresAt time.Time ··· 25 25 Content string 26 26 UserID uuid.UUID 27 27 Slug NoteSlug 28 - BurnBeforeExpiration bool 28 + KeepBeforeExpiration bool 29 29 Password string 30 30 CreatedAt time.Time 31 31 ExpiresAt time.Time ··· 34 34 type NoteDetailed struct { 35 35 Content string 36 36 Slug NoteSlug 37 - BurnBeforeExpiration bool 37 + KeepBeforeExpiration bool 38 38 HasPassword bool 39 39 CreatedAt time.Time 40 40 ExpiresAt time.Time ··· 43 43 44 44 type PatchNote struct { 45 45 ExpiresAt *time.Time 46 - BurnBeforeExpiration *bool 46 + KeepBeforeExpiration *bool 47 47 }
M
internal/models/note.go
··· 18 18 ErrNoteContentIsEmpty = errors.New("note: content is empty") 19 19 ErrNoteSlugIsAlreadyInUse = errors.New("note: slug is already in use") 20 20 ErrNoteSlugIsInvalid = errors.New("note: slug is invalid") 21 - ErrNoteCannotBeBurnt = errors.New( 22 - "note: cannot be burnt before expiration if expiration time is not provided", 21 + ErrNoteCannotBeKept = errors.New( 22 + "note: cannot be kept before expiration if expiration time is not provided", 23 23 ) 24 24 ErrNoteExpired = errors.New("note: expired") 25 25 ErrNoteNotFound = errors.New("note: not found") ··· 30 30 Content string 31 31 Slug string 32 32 Password string 33 - BurnBeforeExpiration bool 33 + KeepBeforeExpiration bool 34 34 ReadAt time.Time 35 35 CreatedAt time.Time 36 36 ExpiresAt time.Time ··· 51 51 return ErrNoteExpired 52 52 } 53 53 54 - if n.BurnBeforeExpiration && n.ExpiresAt.IsZero() { 55 - return ErrNoteCannotBeBurnt 54 + if n.KeepBeforeExpiration && n.ExpiresAt.IsZero() { 55 + return ErrNoteCannotBeKept 56 56 } 57 57 58 58 if _, exists := notAllowedSlugs[n.Slug]; exists { ··· 67 67 n.ExpiresAt.Before(time.Now()) 68 68 } 69 69 70 -func (n Note) ShouldBeBurnt() bool { 70 +func (n Note) ShouldPreserveOnRead() bool { 71 71 return !n.ExpiresAt.IsZero() && 72 - n.BurnBeforeExpiration 72 + n.KeepBeforeExpiration 73 73 } 74 74 75 75 func (n Note) IsRead() bool {
M
internal/models/note_test.go
··· 31 31 n := Note{ 32 32 Slug: "some-slug", 33 33 Password: "some-password", 34 - BurnBeforeExpiration: false, 34 + KeepBeforeExpiration: false, 35 35 } 36 36 assert.EqualError(t, n.Validate(), ErrNoteContentIsEmpty.Error()) 37 37 }) ··· 43 43 } 44 44 assert.EqualError(t, n.Validate(), ErrNoteExpired.Error()) 45 45 }) 46 - t.Run("should fail if burn before expiration is set, and expiration time is not", 46 + t.Run("should fail if keep before expiration is set, and expiration time is not", 47 47 func(t *testing.T) { 48 48 n := Note{ 49 49 Content: "content", 50 - BurnBeforeExpiration: true, 50 + KeepBeforeExpiration: true, 51 51 } 52 52 53 - assert.EqualError(t, n.Validate(), ErrNoteCannotBeBurnt.Error()) 53 + assert.EqualError(t, n.Validate(), ErrNoteCannotBeKept.Error()) 54 54 }, 55 55 ) 56 56 t.Run("should not fail if slug is not provided", func(t *testing.T) { ··· 90 90 } 91 91 92 92 //nolint:exhaustruct 93 -func TestNote_ShouldBeBurnt(t *testing.T) { 94 - t.Run("should be burnt", func(t *testing.T) { 93 +func TestNote_ShouldPreserveOnRead(t *testing.T) { 94 + t.Run("should keep", func(t *testing.T) { 95 95 note := Note{ 96 - BurnBeforeExpiration: true, 96 + KeepBeforeExpiration: true, 97 97 ExpiresAt: time.Now().Add(time.Hour), 98 98 } 99 - assert.True(t, note.ShouldBeBurnt()) 99 + assert.True(t, note.ShouldPreserveOnRead()) 100 100 }) 101 - t.Run("should not be burnt", func(t *testing.T) { 101 + t.Run("should not be kept", func(t *testing.T) { 102 102 note := Note{ 103 - BurnBeforeExpiration: true, 103 + KeepBeforeExpiration: true, 104 104 ExpiresAt: time.Time{}, 105 105 } 106 - assert.False(t, note.ShouldBeBurnt()) 106 + assert.False(t, note.ShouldPreserveOnRead()) 107 107 }) 108 - t.Run("could not be burnt when expiration and shouldBurn set to false", func(t *testing.T) { 108 + t.Run("cannot be kept when expiration and shouldKeep set to false", func(t *testing.T) { 109 109 note := Note{ 110 - BurnBeforeExpiration: false, 110 + KeepBeforeExpiration: false, 111 111 ExpiresAt: time.Time{}, 112 112 } 113 - assert.False(t, note.ShouldBeBurnt()) 113 + assert.False(t, note.ShouldPreserveOnRead()) 114 114 }) 115 115 } 116 116
M
internal/service/notesrv/notesrv.go
··· 42 42 // GetAllUnreadByAuthorID returns all notes that ARE UNREAD and authored by author id. 43 43 GetAllUnreadByAuthorID(ctx context.Context, authorID uuid.UUID) ([]dtos.NoteDetailed, error) 44 44 45 - // UpdateExpirationTimeSettings updates expiresAt and burnBeforeExpiration. 45 + // UpdateExpirationTimeSettings updates expiresAt and keepBeforeExpiration. 46 46 // If notes is not found returns [models.ErrNoteNotFound]. 47 47 UpdateExpirationTimeSettings( 48 48 ctx context.Context, ··· 99 99 Content: inp.Content, 100 100 Slug: inp.Slug, 101 101 Password: inp.Password, 102 - BurnBeforeExpiration: inp.BurnBeforeExpiration, 102 + KeepBeforeExpiration: inp.KeepBeforeExpiration, 103 103 CreatedAt: inp.CreatedAt, 104 104 ExpiresAt: inp.ExpiresAt, 105 105 } ··· 135 135 136 136 respNote := dtos.GetNote{ 137 137 Content: note.Content, 138 - BurnBeforeExpiration: note.BurnBeforeExpiration, 138 + KeepBeforeExpiration: note.KeepBeforeExpiration, 139 139 ReadAt: note.ReadAt, 140 140 CreatedAt: note.CreatedAt, 141 141 ExpiresAt: note.ExpiresAt, ··· 143 143 144 144 // since not every note should be burn before expiration 145 145 // we return early if it's not 146 - // TODO: fix naming 147 - if note.ShouldBeBurnt() { 146 + if note.ShouldPreserveOnRead() { 148 147 return respNote, nil 149 148 } 150 149 ··· 270 269 resNotes = append(resNotes, dtos.NoteDetailed{ 271 270 Content: note.Content, 272 271 Slug: note.Slug, 273 - BurnBeforeExpiration: note.BurnBeforeExpiration, 272 + KeepBeforeExpiration: note.KeepBeforeExpiration, 274 273 HasPassword: note.Password != "", 275 274 CreatedAt: note.CreatedAt, 276 275 ExpiresAt: note.ExpiresAt,
M
internal/store/psql/noterepo/noterepo.go
··· 48 48 password string, 49 49 ) (models.Note, error) 50 50 51 - // UpdateExpirationTimeSettingsBySlug patches note by updating expiresAt and burnBeforeExpiration if one is passwd 51 + // UpdateExpirationTimeSettingsBySlug patches note by updating expiresAt and keepBeforeExpiration if one is passwd 52 52 // Returns [models.ErrNoteNotFound] if note is not found. 53 53 UpdateExpirationTimeSettingsBySlug( 54 54 ctx context.Context, ··· 91 91 func (s *NoteRepo) Create(ctx context.Context, inp models.Note) error { 92 92 query, args, err := pgq. 93 93 Insert("notes"). 94 - Columns("content", "slug", "password", "burn_before_expiration", "created_at", "expires_at"). 95 - Values(inp.Content, inp.Slug, inp.Password, inp.BurnBeforeExpiration, inp.CreatedAt, inp.ExpiresAt). 94 + Columns("content", "slug", "password", "keep_before_expiration", "created_at", "expires_at"). 95 + Values(inp.Content, inp.Slug, inp.Password, inp.KeepBeforeExpiration, inp.CreatedAt, inp.ExpiresAt). 96 96 SQL() 97 97 if err != nil { 98 98 return err ··· 108 108 109 109 func (s *NoteRepo) GetBySlug(ctx context.Context, slug dtos.NoteSlug) (models.Note, error) { 110 110 query, args, err := pgq. 111 - Select("content", "slug", "burn_before_expiration", "read_at", "created_at", "expires_at"). 111 + Select("content", "slug", "keep_before_expiration", "read_at", "created_at", "expires_at"). 112 112 From("notes"). 113 113 Where("(password is null or password = '')"). 114 114 Where(pgq.Eq{"slug": slug}). ··· 120 120 var note models.Note 121 121 var readAt sql.NullTime 122 122 err = s.db.QueryRow(ctx, query, args...). 123 - Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) 123 + Scan(¬e.Content, ¬e.Slug, ¬e.KeepBeforeExpiration, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) 124 124 if errors.Is(err, pgx.ErrNoRows) { 125 125 return models.Note{}, models.ErrNoteNotFound 126 126 } ··· 158 158 authorID uuid.UUID, 159 159 ) ([]models.Note, error) { 160 160 query := `--sql 161 -select n.content, n.slug, n.burn_before_expiration, n.password, n.read_at, n.created_at, n.expires_at 161 +select n.content, n.slug, n.keep_before_expiration, n.password, n.read_at, n.created_at, n.expires_at 162 162 from notes n 163 163 inner join notes_authors na on n.id = na.note_id 164 164 where na.user_id = $1` ··· 171 171 authorID uuid.UUID, 172 172 ) ([]models.Note, error) { 173 173 query := `--sql 174 -select n.content, n.slug, n.burn_before_expiration, n.password, n.read_at, n.created_at, n.expires_at 174 +select n.content, n.slug, n.keep_before_expiration, n.password, n.read_at, n.created_at, n.expires_at 175 175 from notes n 176 176 inner join notes_authors na on n.id = na.note_id 177 177 where na.user_id = $1 ··· 185 185 authorID uuid.UUID, 186 186 ) ([]models.Note, error) { 187 187 query := `--sql 188 -select n.content, n.slug, n.burn_before_expiration, n.password, n.read_at, n.created_at, n.expires_at 188 +select n.content, n.slug, n.keep_before_expiration, n.password, n.read_at, n.created_at, n.expires_at 189 189 from notes n 190 190 inner join notes_authors na on n.id = na.note_id 191 191 where na.user_id = $1 ··· 214 214 passwd string, 215 215 ) (models.Note, error) { 216 216 query, args, err := pgq. 217 - Select("content", "slug", "burn_before_expiration", "read_at", "created_at", "expires_at"). 217 + Select("content", "slug", "keep_before_expiration", "read_at", "created_at", "expires_at"). 218 218 From("notes"). 219 219 Where(pgq.Eq{ 220 220 "slug": slug, ··· 228 228 var note models.Note 229 229 var readAt sql.NullTime 230 230 err = s.db.QueryRow(ctx, query, args...). 231 - Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) 231 + Scan(¬e.Content, ¬e.Slug, ¬e.KeepBeforeExpiration, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) 232 232 233 233 if errors.Is(err, pgx.ErrNoRows) { 234 234 return models.Note{}, models.ErrNoteNotFound ··· 247 247 ) error { 248 248 query := `--sql 249 249 update notes n 250 -set burn_before_expiration = COALESCE($1, n.burn_before_expiration), 250 +set keep_before_expiration = COALESCE($1, n.keep_before_expiration), 251 251 expires_at = COALESCE($2, n.expires_at) 252 252 from notes_authors na 253 253 where n.slug = $3 ··· 255 255 and na.note_id = n.id` 256 256 257 257 ct, err := s.db.Exec(ctx, query, 258 - patch.BurnBeforeExpiration, patch.ExpiresAt, 258 + patch.KeepBeforeExpiration, patch.ExpiresAt, 259 259 slug, authorID.String()) 260 260 if err != nil { 261 261 return err ··· 392 392 for rows.Next() { 393 393 var note models.Note 394 394 var readAt sql.NullTime 395 - if err := rows.Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.Password, 395 + if err := rows.Scan(¬e.Content, ¬e.Slug, ¬e.KeepBeforeExpiration, ¬e.Password, 396 396 &readAt, ¬e.CreatedAt, ¬e.ExpiresAt); err != nil { 397 397 return nil, err 398 398 }
M
internal/transport/http/apiv1/note.go
··· 13 13 Content string `json:"content"` 14 14 Slug string `json:"slug"` 15 15 Password string `json:"password"` 16 - BurnBeforeExpiration bool `json:"burn_before_expiration"` 16 + KeepBeforeExpiration bool `json:"keep_before_expiration"` 17 17 ExpiresAt time.Time `json:"expires_at"` 18 18 } 19 19 ··· 28 28 return 29 29 } 30 30 31 - // TODO: burn_before_expiration shouldn't be set if user has not set or specified expires_at 32 - 33 31 slug, err := a.notesrv.Create(c.Request.Context(), dtos.CreateNote{ 34 32 Content: req.Content, 35 33 UserID: a.getUserID(c), 36 34 Slug: req.Slug, 37 35 Password: req.Password, 38 - BurnBeforeExpiration: req.BurnBeforeExpiration, 36 + KeepBeforeExpiration: req.KeepBeforeExpiration, 39 37 CreatedAt: time.Now(), 40 38 ExpiresAt: req.ExpiresAt, 41 39 }, a.getUserID(c)) ··· 50 48 type getNoteBySlugResponse struct { 51 49 Content string `json:"content"` 52 50 ReadAt time.Time `json:"read_at,omitzero"` 53 - BurnBeforeExpiration bool `json:"burn_before_expiration"` 51 + KeepBeforeExpiration bool `json:"keep_before_expiration"` 54 52 CreatedAt time.Time `json:"created_at"` 55 53 ExpiresAt time.Time `json:"expires_at,omitzero"` 56 54 } ··· 78 76 ReadAt: note.ReadAt, 79 77 CreatedAt: note.CreatedAt, 80 78 ExpiresAt: note.ExpiresAt, 81 - BurnBeforeExpiration: note.BurnBeforeExpiration, 79 + KeepBeforeExpiration: note.KeepBeforeExpiration, 82 80 }) 83 81 } 84 82 ··· 115 113 ReadAt: note.ReadAt, 116 114 CreatedAt: note.CreatedAt, 117 115 ExpiresAt: note.ExpiresAt, 118 - BurnBeforeExpiration: note.BurnBeforeExpiration, 116 + KeepBeforeExpiration: note.KeepBeforeExpiration, 119 117 }) 120 118 } 121 119 ··· 140 138 type getNotesResponse struct { 141 139 Content string `json:"content"` 142 140 Slug string `json:"slug"` 143 - BurnBeforeExpiration bool `json:"burn_before_expiration"` 141 + KeepBeforeExpiration bool `json:"keep_before_expiration"` 144 142 HasPassword bool `json:"has_password"` 145 143 CreatedAt time.Time `json:"created_at"` 146 144 ExpiresAt time.Time `json:"expires_at,omitzero"` ··· 179 177 180 178 type updateNoteRequest struct { 181 179 ExpiresAt *time.Time `json:"expires_at,omitempty"` 182 - BurnBeforeExpiration *bool `json:"burn_before_expiration,omitempty"` 180 + KeepBeforeExpiration *bool `json:"keep_before_expiration,omitempty"` 183 181 } 184 182 185 183 func (a *APIV1) updateNoteHandler(c *gin.Context) { ··· 189 187 return 190 188 } 191 189 192 - // TODO: burn_before_expiration shouldn't be set if user has not set or specified expires_at 193 - 194 190 if err := a.notesrv.UpdateExpirationTimeSettings( 195 191 c.Request.Context(), 196 192 dtos.PatchNote{ 197 - BurnBeforeExpiration: req.BurnBeforeExpiration, 193 + KeepBeforeExpiration: req.KeepBeforeExpiration, 198 194 ExpiresAt: req.ExpiresAt, 199 195 }, 200 196 c.Param("slug"), ··· 250 246 response = append(response, getNotesResponse{ 251 247 Content: note.Content, 252 248 Slug: note.Slug, 253 - BurnBeforeExpiration: note.BurnBeforeExpiration, 249 + KeepBeforeExpiration: note.KeepBeforeExpiration, 254 250 HasPassword: note.HasPassword, 255 251 CreatedAt: note.CreatedAt, 256 252 ExpiresAt: note.ExpiresAt,
M
internal/transport/http/apiv1/response.go
··· 32 32 // notes 33 33 errors.Is(err, notesrv.ErrNotePasswordNotProvided) || 34 34 errors.Is(err, models.ErrNoteContentIsEmpty) || 35 - errors.Is(err, models.ErrNoteCannotBeBurnt) || 35 + errors.Is(err, models.ErrNoteCannotBeKept) || 36 36 errors.Is(err, models.ErrNoteSlugIsAlreadyInUse) || 37 37 errors.Is(err, models.ErrNoteSlugIsInvalid) { 38 38 newError(c, http.StatusBadRequest, err.Error())
A
migrations/20250828132538_notes_rename_burnBeforeExpiration_to_KeepBeforeExpiration.down.sql
··· 1 +ALTER TABLE notes 2 + RENAME COLUMN keep_before_expiration TO burn_before_expiration ;
A
migrations/20250828132538_notes_rename_burnBeforeExpiration_to_KeepBeforeExpiration.up.sql
··· 1 +ALTER TABLE notes 2 + RENAME COLUMN burn_before_expiration TO keep_before_expiration;
M
web/src/Api/Note.elm
··· 15 15 , slug : Maybe String 16 16 , password : Maybe String 17 17 , expiresAt : Posix 18 - , burnBeforeExpiration : Bool 18 + , keepBeforeExpiration : Bool 19 19 } 20 20 -> Effect msg 21 21 create options = ··· 34 34 [ ( "content", E.string options.content ) 35 35 , encodeMaybe "slug" E.string options.slug 36 36 , encodeMaybe "password" E.string options.password 37 - , ( "burn_before_expiration", E.bool options.burnBeforeExpiration ) 37 + , ( "keep_before_expiration", E.bool options.keepBeforeExpiration ) 38 38 , if options.expiresAt == Time.millisToPosix 0 then 39 39 ( "expires_at", E.null ) 40 40
M
web/src/Data/Note.elm
··· 17 17 type alias Note = 18 18 { content : String 19 19 , readAt : Maybe Posix 20 - , burnBeforeExpiration : Bool 20 + , keepBeforeExpiration : Bool 21 21 , createdAt : Posix 22 22 , expiresAt : Maybe Posix 23 23 } ··· 28 28 D.map5 Note 29 29 (D.field "content" D.string) 30 30 (D.maybe (D.field "read_at" Iso8601.decoder)) 31 - (D.field "burn_before_expiration" D.bool) 31 + (D.field "keep_before_expiration" D.bool) 32 32 (D.field "created_at" Iso8601.decoder) 33 33 (D.maybe (D.field "expires_at" Iso8601.decoder)) 34 34
M
web/src/Pages/Home_.elm
··· 42 42 , slug : Maybe String 43 43 , password : Maybe String 44 44 , expirationTime : Maybe Int 45 - , dontBurnBeforeExpiration : Bool 45 + , keepBeforeExpiration : Bool 46 46 , apiError : Maybe Api.Error 47 47 , userClickedCopyLink : Bool 48 48 , now : Maybe Posix ··· 61 61 , slug = Nothing 62 62 , password = Nothing 63 63 , expirationTime = Nothing 64 - , dontBurnBeforeExpiration = True 64 + , keepBeforeExpiration = True 65 65 , userClickedCopyLink = False 66 66 , apiError = Nothing 67 67 , now = Nothing ··· 117 117 , content = model.content 118 118 , slug = model.slug 119 119 , password = model.password 120 - , burnBeforeExpiration = not model.dontBurnBeforeExpiration 120 + , keepBeforeExpiration = model.keepBeforeExpiration 121 121 , expiresAt = expiresAt 122 122 } 123 123 ) ··· 165 165 else 166 166 ( { model | expirationTime = String.toInt expirationTime }, Effect.none ) 167 167 168 - UserClickedCheckbox burnBeforeExpiration -> 169 - ( { model | dontBurnBeforeExpiration = burnBeforeExpiration }, Effect.none ) 168 + UserClickedCheckbox keepBeforeExpiration -> 169 + ( { model | keepBeforeExpiration = keepBeforeExpiration }, Effect.none ) 170 170 171 171 ApiCreateNoteResponded (Ok response) -> 172 172 ( { model | pageVariant = NoteCreated response.slug, slug = Just response.slug, apiError = Nothing }, Effect.none ) ··· 287 287 ] 288 288 , H.div [ A.class "space-y-6" ] 289 289 [ viewExpirationTimeSelector 290 - , viewBurnBeforeExpirationCheckbox (isCheckBoxDisabled model.expirationTime) 290 + , viewKeepBeforeExpirationCheckbox (isCheckBoxDisabled model.expirationTime) 291 291 ] 292 292 ] 293 293 , H.div [ A.class "flex justify-end" ] ··· 341 341 ] 342 342 343 343 344 -viewBurnBeforeExpirationCheckbox : Bool -> Html Msg 345 -viewBurnBeforeExpirationCheckbox isDisabled = 344 +viewKeepBeforeExpirationCheckbox : Bool -> Html Msg 345 +viewKeepBeforeExpirationCheckbox isDisabled = 346 346 H.div [ A.class "space-y-2" ] 347 347 [ H.div [ A.class "flex items-start space-x-3" ] 348 348 [ H.input 349 349 [ E.onCheck UserClickedCheckbox 350 - , A.id "burn" 350 + , A.id "kept" 351 351 , A.type_ "checkbox" 352 352 , A.class "mt-1 h-4 w-4 text-black border-gray-300 rounded focus:ring-black focus:ring-2" 353 353 , A.disabled isDisabled 354 354 ] 355 355 [] 356 356 , H.div [ A.class "flex-1" ] 357 - [ H.label [ A.for "burn", A.class "block text-sm font-medium text-gray-700 cursor-pointer" ] 357 + [ H.label [ A.for "kept", A.class "block text-sm font-medium text-gray-700 cursor-pointer" ] 358 358 [ H.text "Keep the note until its expiration time, even if it has already been read." ] 359 359 , H.span [ A.class "block text-sm font-medium text-gray-500 cursor-pointer" ] 360 360 [ H.text "Can only be used if expiration time is set" ]
M
web/src/Pages/Secret/Slug_.elm
··· 193 193 viewShowNoteHeader : Zone -> String -> Note -> Html Msg 194 194 viewShowNoteHeader zone slug note = 195 195 H.div [] 196 - [ Components.Utils.viewIf note.burnBeforeExpiration 196 + [ Components.Utils.viewIf note.keepBeforeExpiration 197 197 (H.div [ A.class "bg-orange-50 border-b border-orange-200 p-4" ] 198 198 [ H.div [ A.class "flex items-center gap-3" ] 199 199 [ H.div [ A.class "w-6 h-6 bg-orange-100 rounded-full flex items-center justify-center flex-shrink-0" ]