fix: replace any with typed context accessors in SDK
- Define PageContext, PostContext, AuthorContext, CategoryContext, MasterPageContext structs for typed context passing - Define EmbedResolver interface - Make GetQueries generic: GetQueries[T](ctx) (T, bool) - Fix Content.BlockNoteToHTML to take map[string]any, not any Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
99fc63ddfd
commit
79c558a968
@ -8,6 +8,61 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// --- Typed context structs ---
|
||||
|
||||
// PageContext provides page information to blocks via context.
|
||||
// The CMS populates this from its internal db.Page type.
|
||||
type PageContext struct {
|
||||
ID uuid.UUID
|
||||
Slug string
|
||||
Title string
|
||||
PostType string // "page", "post", "master", "system"
|
||||
Status string // "published", "draft", "scheduled"
|
||||
}
|
||||
|
||||
// PostContext provides blog post information to blocks via context.
|
||||
type PostContext struct {
|
||||
ID uuid.UUID
|
||||
Slug string
|
||||
Title string
|
||||
Excerpt string
|
||||
FeaturedImageURL string
|
||||
AuthorID uuid.UUID
|
||||
PublishedAt time.Time
|
||||
ReadingTime int
|
||||
IsFeatured bool
|
||||
}
|
||||
|
||||
// AuthorContext provides author information to blocks via context.
|
||||
type AuthorContext struct {
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
Slug string
|
||||
Bio string
|
||||
AvatarURL string
|
||||
}
|
||||
|
||||
// CategoryContext provides category information to blocks via context.
|
||||
type CategoryContext struct {
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
Slug string
|
||||
}
|
||||
|
||||
// MasterPageContext provides master page information during rendering.
|
||||
type MasterPageContext struct {
|
||||
ID uuid.UUID
|
||||
Slug string
|
||||
Title string
|
||||
}
|
||||
|
||||
// EmbedResolver resolves embedded component blocks within blog content.
|
||||
type EmbedResolver interface {
|
||||
RenderEmbed(ctx context.Context, blockID uuid.UUID, dataSource, layout string) string
|
||||
}
|
||||
|
||||
// --- Template key ---
|
||||
|
||||
type templateKeyContextKey struct{}
|
||||
|
||||
func WithTemplateKey(ctx context.Context, templateKey string) context.Context {
|
||||
@ -21,56 +76,84 @@ func GetTemplateKey(ctx context.Context) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// --- Queries (generic) ---
|
||||
|
||||
type queriesContextKey struct{}
|
||||
|
||||
func WithQueries(ctx context.Context, queries any) context.Context {
|
||||
// WithQueries stores a queries value in context. Use with GetQueries[T].
|
||||
func WithQueries[T any](ctx context.Context, queries T) context.Context {
|
||||
return context.WithValue(ctx, queriesContextKey{}, queries)
|
||||
}
|
||||
|
||||
func GetQueries(ctx context.Context) any {
|
||||
return ctx.Value(queriesContextKey{})
|
||||
// GetQueries retrieves a typed queries value from context.
|
||||
// Usage: queries, ok := blocks.GetQueries[*db.Queries](ctx)
|
||||
func GetQueries[T any](ctx context.Context) (T, bool) {
|
||||
val, ok := ctx.Value(queriesContextKey{}).(T)
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// --- Current page ---
|
||||
|
||||
type currentPageContextKey struct{}
|
||||
|
||||
func WithCurrentPage(ctx context.Context, page any) context.Context {
|
||||
func WithCurrentPage(ctx context.Context, page *PageContext) context.Context {
|
||||
return context.WithValue(ctx, currentPageContextKey{}, page)
|
||||
}
|
||||
|
||||
func GetCurrentPage(ctx context.Context) any {
|
||||
return ctx.Value(currentPageContextKey{})
|
||||
func GetCurrentPage(ctx context.Context) *PageContext {
|
||||
if v, ok := ctx.Value(currentPageContextKey{}).(*PageContext); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Current blog post ---
|
||||
|
||||
type currentBlogPostContextKey struct{}
|
||||
|
||||
func WithCurrentBlogPost(ctx context.Context, post any) context.Context {
|
||||
func WithCurrentBlogPost(ctx context.Context, post *PostContext) context.Context {
|
||||
return context.WithValue(ctx, currentBlogPostContextKey{}, post)
|
||||
}
|
||||
|
||||
func GetCurrentBlogPost(ctx context.Context) any {
|
||||
return ctx.Value(currentBlogPostContextKey{})
|
||||
func GetCurrentBlogPost(ctx context.Context) *PostContext {
|
||||
if v, ok := ctx.Value(currentBlogPostContextKey{}).(*PostContext); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Current author ---
|
||||
|
||||
type currentAuthorContextKey struct{}
|
||||
|
||||
func WithCurrentAuthor(ctx context.Context, author any) context.Context {
|
||||
func WithCurrentAuthor(ctx context.Context, author *AuthorContext) context.Context {
|
||||
return context.WithValue(ctx, currentAuthorContextKey{}, author)
|
||||
}
|
||||
|
||||
func GetCurrentAuthor(ctx context.Context) any {
|
||||
return ctx.Value(currentAuthorContextKey{})
|
||||
func GetCurrentAuthor(ctx context.Context) *AuthorContext {
|
||||
if v, ok := ctx.Value(currentAuthorContextKey{}).(*AuthorContext); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Current category ---
|
||||
|
||||
type currentCategoryContextKey struct{}
|
||||
|
||||
func WithCurrentCategory(ctx context.Context, category any) context.Context {
|
||||
func WithCurrentCategory(ctx context.Context, category *CategoryContext) context.Context {
|
||||
return context.WithValue(ctx, currentCategoryContextKey{}, category)
|
||||
}
|
||||
|
||||
func GetCurrentCategory(ctx context.Context) any {
|
||||
return ctx.Value(currentCategoryContextKey{})
|
||||
func GetCurrentCategory(ctx context.Context) *CategoryContext {
|
||||
if v, ok := ctx.Value(currentCategoryContextKey{}).(*CategoryContext); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Requested path (for 404 pages) ---
|
||||
|
||||
type requestedPathContextKey struct{}
|
||||
|
||||
func WithRequestedPath(ctx context.Context, path string) context.Context {
|
||||
@ -84,6 +167,8 @@ func GetRequestedPath(ctx context.Context) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// --- Injected slots (master page rendering) ---
|
||||
|
||||
type injectedSlotsContextKey struct{}
|
||||
|
||||
func WithInjectedSlots(ctx context.Context, slots map[string]string) context.Context {
|
||||
@ -104,20 +189,27 @@ func GetInjectedSlots(ctx context.Context) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Master page ---
|
||||
|
||||
type masterPageContextKey struct{}
|
||||
|
||||
func WithMasterPage(ctx context.Context, masterPage any) context.Context {
|
||||
func WithMasterPage(ctx context.Context, masterPage *MasterPageContext) context.Context {
|
||||
return context.WithValue(ctx, masterPageContextKey{}, masterPage)
|
||||
}
|
||||
|
||||
func GetMasterPage(ctx context.Context) any {
|
||||
return ctx.Value(masterPageContextKey{})
|
||||
func GetMasterPage(ctx context.Context) *MasterPageContext {
|
||||
if v, ok := ctx.Value(masterPageContextKey{}).(*MasterPageContext); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsMasterPageContext(ctx context.Context) bool {
|
||||
return ctx.Value(masterPageContextKey{}) != nil
|
||||
}
|
||||
|
||||
// --- HTTP request ---
|
||||
|
||||
type requestContextKey struct{}
|
||||
|
||||
func WithRequest(ctx context.Context, r *http.Request) context.Context {
|
||||
@ -131,6 +223,8 @@ func GetRequest(ctx context.Context) *http.Request {
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Editor mode ---
|
||||
|
||||
type isEditorContextKey struct{}
|
||||
|
||||
func WithIsEditor(ctx context.Context, isEditor bool) context.Context {
|
||||
@ -144,6 +238,8 @@ func IsEditor(ctx context.Context) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Expected slots ---
|
||||
|
||||
type expectedSlotsContextKey struct{}
|
||||
|
||||
func WithExpectedSlots(ctx context.Context, slots []string) context.Context {
|
||||
@ -157,6 +253,8 @@ func GetExpectedSlots(ctx context.Context) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Block ID ---
|
||||
|
||||
type blockIDContextKey struct{}
|
||||
|
||||
func WithBlockID(ctx context.Context, id uuid.UUID) context.Context {
|
||||
@ -170,6 +268,8 @@ func GetBlockID(ctx context.Context) uuid.UUID {
|
||||
return uuid.Nil
|
||||
}
|
||||
|
||||
// --- Current page ID ---
|
||||
|
||||
type currentPageIDContextKey struct{}
|
||||
|
||||
func WithCurrentPageID(ctx context.Context, id uuid.UUID) context.Context {
|
||||
@ -183,6 +283,8 @@ func GetCurrentPageID(ctx context.Context) uuid.UUID {
|
||||
return uuid.Nil
|
||||
}
|
||||
|
||||
// --- Slot renderer ---
|
||||
|
||||
type slotRendererContextKey struct{}
|
||||
|
||||
func WithSlotRenderer(ctx context.Context, r SlotRenderer) context.Context {
|
||||
@ -196,6 +298,8 @@ func GetSlotRenderer(ctx context.Context) SlotRenderer {
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Human proof banner ---
|
||||
|
||||
type humanProofBannerContextKey struct{}
|
||||
|
||||
func WithHumanProofBanner(ctx context.Context, data *HumanProofBannerData) context.Context {
|
||||
@ -209,16 +313,23 @@ func GetHumanProofBanner(ctx context.Context) *HumanProofBannerData {
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Embed resolver ---
|
||||
|
||||
type embedResolverContextKey struct{}
|
||||
|
||||
func WithEmbedResolver(ctx context.Context, resolver any) context.Context {
|
||||
func WithEmbedResolver(ctx context.Context, resolver EmbedResolver) context.Context {
|
||||
return context.WithValue(ctx, embedResolverContextKey{}, resolver)
|
||||
}
|
||||
|
||||
func GetEmbedResolver(ctx context.Context) any {
|
||||
return ctx.Value(embedResolverContextKey{})
|
||||
func GetEmbedResolver(ctx context.Context) EmbedResolver {
|
||||
if r, ok := ctx.Value(embedResolverContextKey{}).(EmbedResolver); ok {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- RenderSlot ---
|
||||
|
||||
// RenderSlot renders children for the current container block.
|
||||
func RenderSlot(ctx context.Context) string {
|
||||
blockID := GetBlockID(ctx)
|
||||
@ -232,6 +343,8 @@ func RenderSlot(ctx context.Context) string {
|
||||
return renderer.RenderContainerSlot(ctx, blockID)
|
||||
}
|
||||
|
||||
// --- BlockContext (pongo2 template data) ---
|
||||
|
||||
// BlockContext provides contextual information available to all block templates.
|
||||
type BlockContext struct {
|
||||
URL string `pongo2:"url"`
|
||||
@ -312,6 +425,8 @@ func (bc *BlockContext) ToMap() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Detail row ---
|
||||
|
||||
// DetailRowInfo holds the resolved data table row for detail pages.
|
||||
type DetailRowInfo struct {
|
||||
TableID string
|
||||
|
||||
@ -41,7 +41,7 @@ type Content interface {
|
||||
GetPage(ctx context.Context, slug string) (*PageInfo, error)
|
||||
GetPost(ctx context.Context, slug string) (*PostInfo, error)
|
||||
Slugify(text string) string
|
||||
BlockNoteToHTML(doc any) string
|
||||
BlockNoteToHTML(doc map[string]any) string
|
||||
GenerateExcerpt(html string, maxLen int) string
|
||||
StripHTML(s string) string
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user