@@ -0,0 +1,90 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"gardomatic.kleiax.de/internal/storage"
|
||||
)
|
||||
|
||||
// ImageModel implements storage.ImageModelInterface for PostgreSQL. Image
|
||||
// retrieval is always constrained by the owning garden.
|
||||
type ImageModel struct{ DB *sql.DB }
|
||||
|
||||
// Insert stores an image in its garden's library.
|
||||
func (m ImageModel) Insert(image storage.Image) (storage.Image, error) {
|
||||
ctx, cancel := contextWithTimeout()
|
||||
defer cancel()
|
||||
sum := sha256.Sum256(image.Data)
|
||||
err := m.DB.QueryRowContext(ctx, `INSERT INTO images(garden_id,file_name,media_type,data,size,checksum,source,created_by)
|
||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id,size,created_at`, image.GardenID, image.FileName,
|
||||
image.MediaType, image.Data, len(image.Data), hex.EncodeToString(sum[:]), image.Source, nullableUserID(image.CreatedBy)).Scan(&image.ID, &image.Size, &image.CreatedAt)
|
||||
if err != nil {
|
||||
return storage.Image{}, recordError(err)
|
||||
}
|
||||
image.Data = nil
|
||||
return image, nil
|
||||
}
|
||||
|
||||
// Get returns an image within its garden.
|
||||
func (m ImageModel) Get(gardenID, id int) (storage.Image, error) {
|
||||
ctx, cancel := contextWithTimeout()
|
||||
defer cancel()
|
||||
var image storage.Image
|
||||
err := m.DB.QueryRowContext(ctx, `SELECT id,garden_id,file_name,media_type,size,source,COALESCE(created_by,0),created_at,data FROM images WHERE garden_id=$1 AND id=$2`, gardenID, id).Scan(
|
||||
&image.ID, &image.GardenID, &image.FileName, &image.MediaType, &image.Size, &image.Source, &image.CreatedBy, &image.CreatedAt, &image.Data)
|
||||
if err != nil {
|
||||
return storage.Image{}, recordError(err)
|
||||
}
|
||||
return image, nil
|
||||
}
|
||||
|
||||
// GetAllForGarden lists image metadata matching filter.
|
||||
func (m ImageModel) GetAllForGarden(gardenID int, filter storage.ImageFilter) ([]storage.Image, error) {
|
||||
ctx, cancel := contextWithTimeout()
|
||||
defer cancel()
|
||||
rows, err := m.DB.QueryContext(ctx, `SELECT id,garden_id,file_name,media_type,size,source,COALESCE(created_by,0),created_at
|
||||
FROM images WHERE garden_id=$1 AND ($2='' OR source=$2) AND ($3='' OR LOWER(file_name) LIKE '%'||LOWER($3)||'%') ORDER BY created_at DESC,id DESC`, gardenID, filter.Source, strings.TrimSpace(filter.Query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
result := []storage.Image{}
|
||||
for rows.Next() {
|
||||
var image storage.Image
|
||||
if err = rows.Scan(&image.ID, &image.GardenID, &image.FileName, &image.MediaType, &image.Size, &image.Source, &image.CreatedBy, &image.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, image)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
// CountForGarden returns the number of images in a garden library.
|
||||
func (m ImageModel) CountForGarden(gardenID int) (int, error) {
|
||||
ctx, cancel := contextWithTimeout()
|
||||
defer cancel()
|
||||
var count int
|
||||
err := m.DB.QueryRowContext(ctx, `SELECT COUNT(*) FROM images WHERE garden_id=$1`, gardenID).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
// RecordAssignment appends an entity image-change history record.
|
||||
func (m ImageModel) RecordAssignment(c storage.ImageAssignment) error {
|
||||
if sameImage(c.PreviousImageID, c.ImageID) {
|
||||
return nil
|
||||
}
|
||||
ctx, cancel := contextWithTimeout()
|
||||
defer cancel()
|
||||
_, err := m.DB.ExecContext(ctx, `INSERT INTO entity_image_history(garden_id,entity_type,entity_id,previous_image_id,image_id,changed_by) VALUES($1,$2,$3,$4,$5,$6)`, c.GardenID, c.EntityType, c.EntityID, c.PreviousImageID, c.ImageID, nullableUserID(c.ChangedBy))
|
||||
return err
|
||||
}
|
||||
|
||||
func sameImage(a, b *int) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == nil && b == nil
|
||||
}
|
||||
return *a == *b
|
||||
}
|
||||
Reference in New Issue
Block a user