feat: add menus, subscriptions, public users, and plugin bridge to SDK

New capability interfaces:
- menus.Menus: menu/nav access (GetMenuByName, GetMenuItems)
- subscriptions.Subscriptions: tier/plan access (GetUserTierLevel, GetTierBySlug, ListActivePlans)
- auth.PublicUsers: public user profiles (GetByUsername, GetByID)
- plugin.PluginBridge: inter-plugin service registry with typed GetServiceAs[T] helper

All added to ServiceDeps for plugin consumption.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alex Dunmow 2026-05-01 12:36:13 +08:00
parent 43deff21f7
commit 917eee13a2
5 changed files with 138 additions and 4 deletions

25
auth/public_users.go Normal file
View File

@ -0,0 +1,25 @@
package auth
import (
"context"
"github.com/google/uuid"
)
// PublicUsers provides public/community user profile access for plugins.
type PublicUsers interface {
GetByUsername(ctx context.Context, username string) (*PublicUserProfile, error)
GetByID(ctx context.Context, id uuid.UUID) (*PublicUserProfile, error)
}
// PublicUserProfile is a plugin-facing view of a public user.
type PublicUserProfile struct {
ID uuid.UUID
Email string
Username string
DisplayName string
AvatarURL string
Bio string
EmailVerified bool
Role string
}

34
menus/menus.go Normal file
View File

@ -0,0 +1,34 @@
package menus
import (
"context"
"github.com/google/uuid"
)
// Menus provides menu and navigation access for plugins.
type Menus interface {
GetMenuByName(ctx context.Context, name string) (*Menu, error)
GetMenuItems(ctx context.Context, menuID uuid.UUID) ([]MenuItem, error)
}
// Menu represents a named navigation menu.
type Menu struct {
ID uuid.UUID
Name string
}
// MenuItem represents a single entry in a navigation menu.
type MenuItem struct {
ID uuid.UUID
MenuID uuid.UUID
Label string
URL string
PageSlug string
ParentID *uuid.UUID
SortOrder int32
OpenInNewTab bool
CssClass string
ItemType string
Icon string
}

22
plugin/bridge.go Normal file
View File

@ -0,0 +1,22 @@
package plugin
// PluginBridge allows plugins to share services with each other.
// Plugins register named services during startup; other plugins look them up at runtime.
type PluginBridge interface {
RegisterService(pluginName, serviceName string, service any)
GetService(pluginName, serviceName string) any
}
// GetServiceAs retrieves a typed service from the bridge.
func GetServiceAs[T any](bridge PluginBridge, pluginName, serviceName string) (T, bool) {
var zero T
if bridge == nil {
return zero, false
}
svc := bridge.GetService(pluginName, serviceName)
if svc == nil {
return zero, false
}
typed, ok := svc.(T)
return typed, ok
}

View File

@ -5,10 +5,13 @@ import (
"connectrpc.com/connect"
"git.dev.alexdunmow.com/block/core/ai"
"git.dev.alexdunmow.com/block/core/auth"
"git.dev.alexdunmow.com/block/core/content"
"git.dev.alexdunmow.com/block/core/crypto"
"git.dev.alexdunmow.com/block/core/gating"
"git.dev.alexdunmow.com/block/core/menus"
"git.dev.alexdunmow.com/block/core/settings"
"git.dev.alexdunmow.com/block/core/subscriptions"
)
// ServiceDeps provides dependencies that plugins need for RPC service handlers.
@ -18,6 +21,9 @@ type ServiceDeps struct {
Settings settings.Settings
Gating gating.Gating
Crypto crypto.Crypto
Menus menus.Menus
PublicUsers auth.PublicUsers
Subscriptions subscriptions.Subscriptions
// Database — for plugin's own sqlc queries
Pool Pool
@ -36,6 +42,9 @@ type ServiceDeps struct {
// Email
EmailSender EmailSender
// Plugin interop
Bridge PluginBridge
// Extension points — typed as narrow interfaces where possible
JobRunner JobRunner
EmbeddingService EmbeddingService

View File

@ -0,0 +1,44 @@
package subscriptions
import (
"context"
"time"
"github.com/google/uuid"
)
// Subscriptions provides subscription tier and billing plan access for plugins.
type Subscriptions interface {
GetUserTierLevel(ctx context.Context, userID uuid.UUID) (*TierLevel, error)
GetTierBySlug(ctx context.Context, slug string) (*Tier, error)
ListActivePlans(ctx context.Context, tierID uuid.UUID) ([]Plan, error)
}
// TierLevel is the resolved tier for a user.
type TierLevel struct {
Level int
Features []byte
}
// Tier is a subscription tier definition.
type Tier struct {
ID uuid.UUID
Name string
Slug string
Level int
Description string
Features []byte
IsDefault bool
Position int
}
// Plan is an active billing plan within a tier.
type Plan struct {
ID uuid.UUID
TierID uuid.UUID
BillingInterval string
Amount int32
Currency string
IsActive bool
CreatedAt time.Time
}