feat(plugin): media deposit SDK surface — Media, MediaDeposit, EnsureMedia

Add the SDK surface for plugins to deposit images into the CMS media library.
MediaDeposit / MediaResult types and a Media interface on CoreServices for
runtime deposits; EnsureMedia on the Provisioner interface for idempotent
seed-time deposits under a plugin-chosen, template-referable UUID. The CMS
implements both against one internal depositor; seeded templates reference
the chosen UUID directly via {% img "<id>" %}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Dunmow 2026-06-13 13:14:31 +08:00
parent e309e3d80b
commit d9788abe4c
2 changed files with 47 additions and 0 deletions

View File

@ -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:<uuid>") 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

View File

@ -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.