diff --git a/plugin/deps.go b/plugin/deps.go index 6a5478c..5d1d517 100644 --- a/plugin/deps.go +++ b/plugin/deps.go @@ -38,6 +38,9 @@ type CoreServices struct { MediaPath string AppURL string + // Media library — deposit images through the full upload pipeline + Media Media + // AI ToolRegistry ai.ToolRegistry AITextCall func(ctx context.Context, taskKey, systemPrompt, userMessage string) (string, error) @@ -60,6 +63,45 @@ type CoreServices struct { RAGService RAGService } +// MediaDeposit describes an image a plugin deposits into the CMS media library. +// The bytes flow through the same pipeline as an admin upload (optimize, WebP, +// thumbnails, LQIP, dimensions); responsive variants are generated on demand. +type MediaDeposit struct { + // ID is the media row's primary key. REQUIRED for Provisioner.EnsureMedia — + // it is the deterministic, template-referable key a seeded {% img %} tag + // resolves by. Optional for Media.Deposit, where a zero value is generated. + ID uuid.UUID + // Filename is the original filename, e.g. "hero.jpg". + Filename string + // Data is the raw image bytes, typically from go:embed. + Data []byte + // AltText is optional accessibility text. + AltText string + // Folder optionally groups the media under a named library folder, + // created if absent. + Folder string + // Source is an optional provenance label, e.g. the plugin name. + Source string +} + +// MediaResult reports the outcome of a deposit. +type MediaResult struct { + // ID is the media row's primary key (the supplied ID, or a generated one). + ID uuid.UUID + // Ref is a ready-to-use reference ("media:") for a block src or a + // {% img %} tag. + Ref string + // Created is false when an existing row with the supplied ID was reused. + Created bool +} + +// Media lets a plugin deposit images into the CMS media library at runtime. +// Seed-time deposits use Provisioner.EnsureMedia instead — it is idempotent +// (skip if the ID exists; on changed bytes it warns rather than overwriting). +type Media interface { + Deposit(ctx context.Context, deposit MediaDeposit) (MediaResult, error) +} + // JobRunner submits background jobs for async processing. type JobRunner interface { Submit(ctx context.Context, jobType string, config []byte) error diff --git a/plugin/provisioner.go b/plugin/provisioner.go index d343e9b..91f5eb0 100644 --- a/plugin/provisioner.go +++ b/plugin/provisioner.go @@ -23,6 +23,11 @@ type Provisioner interface { DisableOrphanedJobSchedules(registeredTypes []string) error EnsurePlugin(name string) error EnsureCustomColor(config CustomColorConfig) error + // EnsureMedia deposits an image under the plugin-chosen MediaDeposit.ID if a + // row with that ID does not already exist. Idempotent: an existing ID is a + // no-op; if the bytes differ from what was first deposited it logs a warning + // rather than overwriting (seeded media is immutable once placed). + EnsureMedia(deposit MediaDeposit) error } // DataTableConfig defines a data table to provision.