|
|
|
|
@ -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
|
|
|
|
|
}
|
|
|
|
|
|