aboutsummaryrefslogtreecommitdiffstats
path: root/internal/store/images.go
diff options
context:
space:
mode:
authorgramanas <grm@eyesin.space>2026-09-12 11:24:17 +0300
committergramanas <grm@eyesin.space>2026-09-12 11:24:17 +0300
commit3eb04b1a2bdf9e53231fe862cfd76327371a9741 (patch)
treeb38b2d82a47233fd8e0bb18c59e4a8f3dd2412d7 /internal/store/images.go
downloadblogspace-3eb04b1a2bdf9e53231fe862cfd76327371a9741.tar.gz
blogspace-3eb04b1a2bdf9e53231fe862cfd76327371a9741.tar.bz2
blogspace-3eb04b1a2bdf9e53231fe862cfd76327371a9741.zip
Initial multi-tenant blog host
Go + Postgres application serving a management dashboard on the base domain and one public blog per subdomain. Markdown posts organised in pages, form-based theme customisation, image uploads stored in Postgres, JWT cookie sessions with CSRF, superadmin user management, RSS feeds. Docker/compose deployment and a Makefile-driven dev environment with seed data. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/store/images.go')
-rw-r--r--internal/store/images.go58
1 files changed, 58 insertions, 0 deletions
diff --git a/internal/store/images.go b/internal/store/images.go
new file mode 100644
index 0000000..3699e75
--- /dev/null
+++ b/internal/store/images.go
@@ -0,0 +1,58 @@
+package store
+
+import (
+ "context"
+ "time"
+
+ "github.com/google/uuid"
+)
+
+type Image struct {
+ ID uuid.UUID
+ BlogID int64
+ Filename string
+ ContentType string
+ Size int
+ Data []byte // only populated by ImageData
+ CreatedAt time.Time
+}
+
+func (s *Store) CreateImage(ctx context.Context, blogID int64, filename, contentType string, data []byte) (*Image, error) {
+ img := &Image{ID: uuid.New(), BlogID: blogID, Filename: filename, ContentType: contentType, Size: len(data)}
+ err := s.db.QueryRow(ctx, `INSERT INTO images (id, blog_id, filename, content_type, size, data) VALUES ($1,$2,$3,$4,$5,$6) RETURNING created_at`,
+ img.ID, blogID, filename, contentType, len(data), data).Scan(&img.CreatedAt)
+ return img, err
+}
+
+func (s *Store) ListImages(ctx context.Context, blogID int64) ([]Image, error) {
+ rows, err := s.db.Query(ctx, `SELECT id, blog_id, filename, content_type, size, created_at FROM images WHERE blog_id=$1 ORDER BY created_at DESC`, blogID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var out []Image
+ for rows.Next() {
+ var i Image
+ if err := rows.Scan(&i.ID, &i.BlogID, &i.Filename, &i.ContentType, &i.Size, &i.CreatedAt); err != nil {
+ return nil, err
+ }
+ out = append(out, i)
+ }
+ return out, rows.Err()
+}
+
+// ImageData loads an image including its bytes.
+func (s *Store) ImageData(ctx context.Context, id uuid.UUID) (*Image, error) {
+ var i Image
+ err := s.db.QueryRow(ctx, `SELECT id, blog_id, filename, content_type, size, data, created_at FROM images WHERE id=$1`, id).
+ Scan(&i.ID, &i.BlogID, &i.Filename, &i.ContentType, &i.Size, &i.Data, &i.CreatedAt)
+ if err != nil {
+ return nil, wrap(err)
+ }
+ return &i, nil
+}
+
+func (s *Store) DeleteImage(ctx context.Context, blogID int64, id uuid.UUID) error {
+ _, err := s.db.Exec(ctx, `DELETE FROM images WHERE blog_id=$1 AND id=$2`, blogID, id)
+ return err
+}