From 30b4292c4a3bcb1c4839bec78a063a80a1eddbbc Mon Sep 17 00:00:00 2001 From: Ethan Wellenreiter Date: Wed, 7 May 2025 10:24:13 -0400 Subject: [PATCH] Updating sql Image structure to allow expiration of an image Signed-off-by: Ethan Wellenreiter --- backend/internal/db/temp.txt | 1 + backend/internal/storage/sql-images.go | 38 ++++++++++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/backend/internal/db/temp.txt b/backend/internal/db/temp.txt index fd8b199..0447563 100644 --- a/backend/internal/db/temp.txt +++ b/backend/internal/db/temp.txt @@ -52,6 +52,7 @@ Table images { receiptid integer created_at timestamp path varchar + expiration timestamp added bool [default: false] } diff --git a/backend/internal/storage/sql-images.go b/backend/internal/storage/sql-images.go index 0a02ba6..57e69a7 100644 --- a/backend/internal/storage/sql-images.go +++ b/backend/internal/storage/sql-images.go @@ -3,17 +3,26 @@ package storage import ( "context" "database/sql" + "time" ) type SQLImagesStore struct { db *sql.DB } -func (s SQLImagesStore) GetByID(ctx context.Context, id int64) (*Image, error) { - query := `SELECT id, receiptid, created_at, path FROM images WHERE id = $1` +const imgStoreCleanupTime = time.Hour * 12 + +func NewSQLImageStore(db *sql.DB) *SQLImagesStore { + imgstore := &SQLImagesStore{db} + go imgstore.cleanupDeadImages() + return imgstore +} + +func (s *SQLImagesStore) GetByID(ctx context.Context, id int64) (*Image, error) { + query := `SELECT id, receiptid, created_at, path FROM images WHERE id = $1 AND added = $2` image := &Image{} - err := s.db.QueryRowContext(ctx, query, id).Scan(&image.ID, &image.ReceiptID, &image.CreatedAt, &image.Path) + err := s.db.QueryRowContext(ctx, query, id, true).Scan(&image.ID, &image.ReceiptID, &image.CreatedAt, &image.Path) if err != nil { return nil, err } @@ -21,10 +30,10 @@ func (s SQLImagesStore) GetByID(ctx context.Context, id int64) (*Image, error) { return image, nil } -func (s SQLImagesStore) Create(ctx context.Context, img *Image) error { +func (s *SQLImagesStore) Create(ctx context.Context, img *Image, exp time.Duration) error { query := ` - INSERT INTO images (receiptid, path) - VALUES ($1, $2) RETURNING id, created_at` + INSERT INTO images (receiptid, path, expiration) + VALUES ($1, $2, $3) RETURNING id, created_at` ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration) defer cancel() @@ -34,6 +43,7 @@ func (s SQLImagesStore) Create(ctx context.Context, img *Image) error { query, img.ReceiptID, img.Path, // might need to marshal or serialize this + time.Now().Add(exp), ).Scan( &img.ID, &img.CreatedAt, @@ -41,7 +51,7 @@ func (s SQLImagesStore) Create(ctx context.Context, img *Image) error { return err } -func (s SQLImagesStore) Delete(ctx context.Context, id int64) error { +func (s *SQLImagesStore) Delete(ctx context.Context, id int64) error { query := `DELETE FROM images WHERE id = $1` ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration) @@ -64,7 +74,7 @@ func (s SQLImagesStore) Delete(ctx context.Context, id int64) error { return nil } -func (s SQLImagesStore) ActivateImage(ctx context.Context, id int64) error { +func (s *SQLImagesStore) ActivateImage(ctx context.Context, id int64) error { query := `UPDATE images SET added = $1` ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration) @@ -77,3 +87,15 @@ func (s SQLImagesStore) ActivateImage(ctx context.Context, id int64) error { return nil } + +func (s *SQLImagesStore) cleanupDeadImages(ctx context.Context) { + for range time.Tick(imgStoreCleanupTime) { + query := `DELETE FROM images WHERE added = false AND expiration < $1` + + ctx, cancel := context.WithTimeout(ctx, QueryTimeoutDuration) + defer cancel() + + s.db.ExecContext(ctx, query, time.Now()) + } + // ignore any errors, just keep looping. Not much lost in a missed cleanup. A little extra temporary storage. Big woop. If it's an issue, decrease the cleanup period +}