Files
pamietnik/backend/internal/db/journal.go
Christoph K. d0b0b4f8bd
Some checks failed
Deploy to NAS / deploy (push) Failing after 4s
Convert backend from submodule to regular directory
Remove submodule tracking; backend is now a plain directory in the repo.
Also update deploy workflow: remove --recurse-submodules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 16:59:50 +02:00

110 lines
3.0 KiB
Go

package db
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/jacek/pamietnik/backend/internal/domain"
)
type JournalStore struct {
pool *pgxpool.Pool
}
func NewJournalStore(pool *pgxpool.Pool) *JournalStore {
return &JournalStore{pool: pool}
}
// InsertEntry creates a new journal entry and returns it with the generated entry_id.
func (s *JournalStore) InsertEntry(ctx context.Context, e domain.JournalEntry) (domain.JournalEntry, error) {
err := s.pool.QueryRow(ctx,
`INSERT INTO journal_entries (user_id, entry_date, entry_time, title, description, lat, lon)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING entry_id, created_at`,
e.UserID, e.EntryDate, e.EntryTime, e.Title, e.Description, e.Lat, e.Lon,
).Scan(&e.EntryID, &e.CreatedAt)
return e, err
}
// InsertImage attaches an image record to an entry.
func (s *JournalStore) InsertImage(ctx context.Context, img domain.JournalImage) (domain.JournalImage, error) {
err := s.pool.QueryRow(ctx,
`INSERT INTO journal_images (entry_id, filename, original_name, mime_type, size_bytes)
VALUES ($1, $2, $3, $4, $5)
RETURNING image_id, created_at`,
img.EntryID, img.Filename, img.OriginalName, img.MimeType, img.SizeBytes,
).Scan(&img.ImageID, &img.CreatedAt)
return img, err
}
// ListByDate returns all journal entries for a given date (YYYY-MM-DD), including their images.
func (s *JournalStore) ListByDate(ctx context.Context, userID, date string) ([]domain.JournalEntry, error) {
rows, err := s.pool.Query(ctx,
`SELECT entry_id, user_id, entry_date::text, entry_time::text, title, description, lat, lon, created_at
FROM journal_entries
WHERE user_id = $1 AND entry_date = $2
ORDER BY entry_time`,
userID, date,
)
if err != nil {
return nil, err
}
defer rows.Close()
var entries []domain.JournalEntry
for rows.Next() {
var e domain.JournalEntry
if err := rows.Scan(
&e.EntryID, &e.UserID, &e.EntryDate, &e.EntryTime,
&e.Title, &e.Description, &e.Lat, &e.Lon, &e.CreatedAt,
); err != nil {
return nil, err
}
entries = append(entries, e)
}
if err := rows.Err(); err != nil {
return nil, err
}
if len(entries) == 0 {
return entries, nil
}
// Load all images in a single query to avoid N+1
entryIDs := make([]string, len(entries))
for i, e := range entries {
entryIDs[i] = e.EntryID
}
imgRows, err := s.pool.Query(ctx,
`SELECT image_id, entry_id, filename, original_name, mime_type, size_bytes, created_at
FROM journal_images WHERE entry_id = ANY($1) ORDER BY created_at`,
entryIDs,
)
if err != nil {
return nil, err
}
defer imgRows.Close()
imgMap := make(map[string][]domain.JournalImage)
for imgRows.Next() {
var img domain.JournalImage
if err := imgRows.Scan(
&img.ImageID, &img.EntryID, &img.Filename, &img.OriginalName,
&img.MimeType, &img.SizeBytes, &img.CreatedAt,
); err != nil {
return nil, err
}
imgMap[img.EntryID] = append(imgMap[img.EntryID], img)
}
if err := imgRows.Err(); err != nil {
return nil, err
}
for i, e := range entries {
entries[i].Images = imgMap[e.EntryID]
}
return entries, nil
}