Files
Gardomatic/internal/storage/journal.go
T
kleiax 904d14b64c
CI / test (push) Canceled after 0s
Initial commit
2026-09-12 22:22:17 +02:00

78 lines
3.0 KiB
Go

package storage
import (
"strings"
"time"
"gardomatic.kleiax.de/internal/platform/validate"
)
// MaxJournalAttachmentSize is the largest journal attachment accepted, in bytes.
const MaxJournalAttachmentSize = 25 << 20
// JournalModelInterface persists journal entries and their attachments.
type JournalModelInterface interface {
Insert(entry JournalEntry) (JournalEntry, error)
Get(gardenID, id int) (JournalEntry, error)
GetAllForGarden(gardenID int, entryType JournalEntryType) ([]JournalEntry, error)
Update(gardenID int, entry JournalEntry) (JournalEntry, error)
Delete(gardenID, id int) error
InsertAttachment(gardenID, entryID int, attachment JournalAttachment) (JournalAttachment, error)
GetAttachment(gardenID, entryID, attachmentID int) (JournalAttachment, error)
DeleteAttachment(gardenID, entryID, attachmentID int) error
}
// JournalEntryType distinguishes chronological journal entries from pinboard
// notes while sharing the same persistence model.
type JournalEntryType string
// Supported journal entry types.
const (
JournalEntryTypeJournal JournalEntryType = "journal"
JournalEntryTypePinboard JournalEntryType = "pinboard"
)
// JournalEntry is a garden note with optional tags and attachments.
type JournalEntry struct {
ID int `json:"id"`
GardenID int `json:"garden_id"`
AuthorID int `json:"author_id"`
AuthorName string `json:"author_name"`
AuthorColor string `json:"author_color"`
EntryType JournalEntryType `json:"entry_type"`
Title string `json:"title"`
Body string `json:"body"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Version int `json:"version"`
Tags []string `json:"tags,omitempty"`
Attachments []JournalAttachment `json:"attachments,omitempty"`
}
// JournalAttachment contains either inline binary data or a reference to a
// reusable image-library item.
type JournalAttachment struct {
ID int `json:"id"`
EntryID int `json:"entry_id"`
FileName string `json:"file_name"`
MediaType string `json:"media_type"`
Size int64 `json:"size"`
CreatedAt time.Time `json:"created_at"`
Data []byte `json:"-"`
ImageID *int `json:"image_id,omitempty"`
}
// ValidateJournalEntry applies the journal-entry input rules.
func ValidateJournalEntry(v *validate.Validator, entry JournalEntry) {
entryType := entry.EntryType
if entryType == "" {
entryType = JournalEntryTypeJournal
}
v.Check(validate.PermittedValue(entryType, JournalEntryTypeJournal, JournalEntryTypePinboard), "entry_type", "must be journal or pinboard")
if entryType == JournalEntryTypeJournal {
v.Check(strings.TrimSpace(entry.Title) != "", "title", "must be provided")
}
v.Check(len(entry.Title) <= 500, "title", "must not be more than 500 bytes long")
v.Check(len(entry.Body) <= 100_000, "body", "must not be more than 100000 bytes long")
}