Documentation
¶
Index ¶
- type APIKeyHandler
- type APIKeyListItem
- type APIKeyService
- type AliasRedirectHandler
- type ContentHandler
- func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) DeleteContent(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) GenerateSlug(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) GetContent(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) GetPublishedAuthor(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) GetPublishedContent(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) GetPublishedContentByAuthor(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) GetSEO(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) ListContents(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) ListPublishedArchive(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) ListPublishedAuthors(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) ListPublishedContents(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) SearchPublished(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) SetSystemFields(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) UpdateContent(w http.ResponseWriter, r *http.Request)
- func (h *ContentHandler) WithIFrameHosts(hosts ...string) *ContentHandler
- func (h *ContentHandler) WithPublicFieldRegistry(registry PublicFieldLookup) *ContentHandler
- type ContentServiceInterface
- type CreateAPIKeyRequest
- type CreateAPIKeyResponse
- type CreateContentData
- type CreateContentRequest
- type CreateContentResponse
- type DashboardHandler
- type DashboardServiceInterface
- type ExportHandler
- type GenerateSlugData
- type GenerateSlugRequest
- type GenerateSlugResponse
- type NotificationHandler
- type NotificationRef
- type NotificationsResponse
- type PostTypeLister
- type PublicArchiveMonthResponse
- type PublicAuthorResponse
- type PublicFieldLookup
- type ResponseMeta
- type RevokeAPIKeyResponse
- type SSGHandler
- type SearchResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIKeyHandler ¶
type APIKeyHandler struct {
// contains filtered or unexported fields
}
APIKeyHandler handles browser-realm API key lifecycle endpoints.
func NewAPIKeyHandler ¶
func NewAPIKeyHandler( apiKeyService APIKeyService, logger *util.Logger, ) *APIKeyHandler
NewAPIKeyHandler constructs a new APIKeyHandler.
func (*APIKeyHandler) CreateAPIKey ¶
func (h *APIKeyHandler) CreateAPIKey(w http.ResponseWriter, r *http.Request)
CreateAPIKey handles POST /api/admin/api-keys.
func (*APIKeyHandler) ListAPIKeys ¶
func (h *APIKeyHandler) ListAPIKeys(w http.ResponseWriter, r *http.Request)
ListAPIKeys handles GET /api/admin/api-keys. Returns the caller's keys as masked list entries (prefix + metadata only). The secret, key_hash, and last_used_ip are NEVER included in the response.
func (*APIKeyHandler) RevokeAPIKey ¶
func (h *APIKeyHandler) RevokeAPIKey(w http.ResponseWriter, r *http.Request)
RevokeAPIKey handles DELETE /api/admin/api-keys/:id. Soft-deletes the key (sets revoked_at). Idempotent: revoking an already-revoked key returns 200. Cross-user or nonexistent ids return 404 NOT_FOUND (identical response — no cross-user disclosure).
type APIKeyListItem ¶
type APIKeyListItem struct {
ID int `json:"id"`
Name string `json:"name"`
Prefix string `json:"prefix"`
CreatedAt time.Time `json:"createdAt"`
LastUsedAt *time.Time `json:"lastUsedAt"`
ExpiresAt *time.Time `json:"expiresAt"`
RevokedAt *time.Time `json:"revokedAt"`
}
APIKeyListItem is a safe (masked) list entry. It MUST NOT contain keyHash, the secret, or lastUsedIp (privacy-sensitive). Prefix is the masked display form "lesstruct_<keyID>••••" produced via apikey.DisplayPrefix.
type APIKeyService ¶
type APIKeyService interface {
// Existing (Story 1.1):
Create(ctx context.Context, principal kernel.Principal, name string) (string, *apikey.APIKey, error)
// New (Story 1.2):
List(ctx context.Context, principal kernel.Principal) ([]*apikey.APIKey, error)
Revoke(ctx context.Context, id int, principal kernel.Principal) (*apikey.APIKey, error)
}
APIKeyService is the narrow interface the handler depends on (avoids hard-coupling to *apikey.Service; mirrors the dashboard ServiceInterface pattern).
type AliasRedirectHandler ¶ added in v0.8.0
type AliasRedirectHandler struct {
// contains filtered or unexported fields
}
func NewAliasRedirectHandler ¶ added in v0.8.0
func NewAliasRedirectHandler( aliasService *aliasdomain.Service, contentSvc contentGetter, next http.Handler, ) *AliasRedirectHandler
func (*AliasRedirectHandler) ServeHTTP ¶ added in v0.8.0
func (h *AliasRedirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
type ContentHandler ¶
type ContentHandler struct {
// contains filtered or unexported fields
}
func NewContentHandler ¶
func NewContentHandler( contentService ContentServiceInterface, logger *util.Logger, baseURL string, profilePictureURLResolver func(string) string, featuredImageResolver func(string) string, ) *ContentHandler
func (*ContentHandler) CreateContent ¶
func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) DeleteContent ¶
func (h *ContentHandler) DeleteContent(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) GenerateSlug ¶
func (h *ContentHandler) GenerateSlug(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) GetContent ¶
func (h *ContentHandler) GetContent(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) GetPublishedAuthor ¶ added in v0.7.0
func (h *ContentHandler) GetPublishedAuthor(w http.ResponseWriter, r *http.Request)
GetPublishedAuthor returns a single published author's public profile. It follows the same response shape as the ListPublishedAuthors endpoint (PublicAuthorResponse) but for one author. Returns 404 when the author has no published content or does not exist.
func (*ContentHandler) GetPublishedContent ¶
func (h *ContentHandler) GetPublishedContent(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) GetPublishedContentByAuthor ¶
func (h *ContentHandler) GetPublishedContentByAuthor(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) GetSEO ¶
func (h *ContentHandler) GetSEO(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) ListContents ¶
func (h *ContentHandler) ListContents(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) ListPublishedArchive ¶ added in v0.6.0
func (h *ContentHandler) ListPublishedArchive(w http.ResponseWriter, r *http.Request)
ListPublishedArchive returns published-content counts grouped by year and month, newest first. Optional ?post_type filters to a single post type; optional ?language filters to a single language. Each entry includes a URL pointing to the matching listing page with ?year= and ?month= params.
func (*ContentHandler) ListPublishedAuthors ¶ added in v0.5.0
func (h *ContentHandler) ListPublishedAuthors(w http.ResponseWriter, r *http.Request)
ListPublishedAuthors returns the users who have published at least one content item, with only safe public fields (username, display name, avatar, profile URL, published-content count, and the distinct post types they publish under). No email, role, or custom fields are exposed. Ordered by published-content count (desc) then username (asc) — useful for "most active contributors" widgets and author directories.
func (*ContentHandler) ListPublishedContents ¶
func (h *ContentHandler) ListPublishedContents(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) SearchPublished ¶
func (h *ContentHandler) SearchPublished(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) SetSystemFields ¶
func (h *ContentHandler) SetSystemFields(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) UpdateContent ¶
func (h *ContentHandler) UpdateContent(w http.ResponseWriter, r *http.Request)
func (*ContentHandler) WithIFrameHosts ¶ added in v0.10.0
func (h *ContentHandler) WithIFrameHosts(hosts ...string) *ContentHandler
WithIFrameHosts attaches the sanitizer's iframe host allowlist (derived from the CSP frame-src directive) so HTML-format content keeps allowed embeds on the write path. When not called, iframes are stripped on save. Returns the receiver for chaining at construction time.
func (*ContentHandler) WithPublicFieldRegistry ¶ added in v0.7.0
func (h *ContentHandler) WithPublicFieldRegistry(registry PublicFieldLookup) *ContentHandler
WithPublicFieldRegistry attaches a [[public_field]] registry to the handler so /api/v1/public/* endpoints can enforce the public query allowlist. When not called (or called with nil), the handler rejects every cf_*/sort_by=cf:* parameter — fail-closed is the safe default. Returns the receiver for chaining at construction time.
type ContentServiceInterface ¶
type ContentServiceInterface interface {
Create(ctx context.Context, principal kernel.Principal, req contentdomain.CreateContentRequest) (*contentdomain.Content, error)
GetByUser(ctx context.Context, userID int, limit int, offset int) ([]*contentdomain.Content, error)
GetAll(ctx context.Context, limit int, offset int) ([]*contentdomain.Content, error)
GetByID(ctx context.Context, id int) (*contentdomain.Content, error)
GenerateSlugFromTitle(ctx context.Context, title string) (string, error)
Update(ctx context.Context, id int, principal kernel.Principal, req contentservice.UpdateContentRequest) (*contentdomain.Content, error)
DeleteContent(ctx context.Context, id int, principal kernel.Principal) error
GetPublished(ctx context.Context, limit int, offset int) ([]*contentdomain.Content, error)
GetPublishedBySlug(ctx context.Context, slug string, language string) (*contentdomain.Content, error)
GetTranslations(ctx context.Context, translationGroupID int, excludeID int) ([]*contentdomain.Content, error)
GetPublishedByAuthorUsername(ctx context.Context, username string, languages []string, limit int, offset int) ([]*contentdomain.Content, error)
AuthorExists(ctx context.Context, username string) (bool, error)
ListByFilters(ctx context.Context, userID int, filters contentdomain.ContentFilters) ([]*contentdomain.Content, error)
Count(ctx context.Context, userID int, filters contentdomain.ContentFilters) (int, error)
SetSystemFields(ctx context.Context, contentID int, principal kernel.Principal, systemFields map[string]any) (*contentdomain.Content, error)
SearchPublished(ctx context.Context, query string, limit int) ([]*contentdomain.Content, error)
GetPublishedAuthors(ctx context.Context, filters contentdomain.PublishedAuthorFilters) ([]*contentdomain.PublishedAuthor, error)
GetPublishedAuthor(ctx context.Context, username string) (*contentdomain.PublishedAuthor, error)
GetPublishedArchive(ctx context.Context, postType string, languages []string) ([]*contentdomain.ArchiveMonth, error)
GetPublishedByPostType(ctx context.Context, postType string, languages []string, year int, month int, limit int, offset int) ([]*contentdomain.Content, error)
}
type CreateAPIKeyRequest ¶
type CreateAPIKeyRequest struct {
Name string `json:"name"`
Scope string `json:"scope,omitempty"` // TODO(story-future): per-key scopes deferred — accepted but not persisted
}
CreateAPIKeyRequest is the payload for POST /api/admin/api-keys.
type CreateAPIKeyResponse ¶
type CreateAPIKeyResponse struct {
Key string `json:"key"`
ID int `json:"id"`
Name string `json:"name"`
KeyPrefix string `json:"keyPrefix"`
CreatedAt time.Time `json:"createdAt"`
}
CreateAPIKeyResponse is returned on successful key creation. The full key string is included exactly once; the client must store it.
type CreateContentData ¶
type CreateContentData struct {
Content *contentdomain.Content `json:"content"`
}
type CreateContentRequest ¶
type CreateContentRequest struct {
Title string `json:"title"`
Slug string `json:"slug,omitempty"`
Content string `json:"content"`
Format string `json:"format,omitempty"`
Tags []string `json:"tags"`
Status string `json:"status"`
PostType string `json:"postType"`
MetaDescription string `json:"metaDescription,omitempty"`
OGTitle string `json:"ogTitle,omitempty"`
OGDescription string `json:"ogDescription,omitempty"`
AllowComments *bool `json:"allowComments,omitempty"`
CustomFields map[string]any `json:"customFields,omitempty"`
Language string `json:"language,omitempty"`
TranslationGroupID *int `json:"translationGroupId,omitempty"`
}
type CreateContentResponse ¶
type CreateContentResponse struct {
Data *CreateContentData `json:"data"`
Meta *ResponseMeta `json:"meta"`
}
type DashboardHandler ¶
type DashboardHandler struct {
// contains filtered or unexported fields
}
func NewDashboardHandler ¶
func NewDashboardHandler(dashboardService DashboardServiceInterface, postTypeLister PostTypeLister, logger *util.Logger) *DashboardHandler
func (*DashboardHandler) GetStats ¶
func (h *DashboardHandler) GetStats(w http.ResponseWriter, r *http.Request)
type ExportHandler ¶ added in v0.8.0
type ExportHandler struct {
// contains filtered or unexported fields
}
func NewExportHandler ¶ added in v0.8.0
func NewExportHandler(exporter *export.Exporter, logger *util.Logger) *ExportHandler
func (*ExportHandler) Export ¶ added in v0.8.0
func (h *ExportHandler) Export(w http.ResponseWriter, r *http.Request)
type GenerateSlugData ¶
type GenerateSlugData struct {
Slug string `json:"slug"`
}
type GenerateSlugRequest ¶
type GenerateSlugRequest struct {
Title string `json:"title"`
}
type GenerateSlugResponse ¶
type GenerateSlugResponse struct {
Data *GenerateSlugData `json:"data"`
Meta *ResponseMeta `json:"meta"`
}
type NotificationHandler ¶
type NotificationHandler struct {
// contains filtered or unexported fields
}
NotificationHandler handles notification HTTP requests
func NewNotificationHandler ¶
func NewNotificationHandler( logger *util.Logger, notificationRepo repository.NotificationRepo, ) *NotificationHandler
NewNotificationHandler creates a new notification handler
func (*NotificationHandler) GetNotifications ¶
func (h *NotificationHandler) GetNotifications(w http.ResponseWriter, r *http.Request)
GetNotifications handles GET /api/notifications
type NotificationRef ¶
type NotificationRef struct {
ID int `json:"id"`
Type repository.NotificationType `json:"type"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
}
NotificationRef represents a notification in the response
type NotificationsResponse ¶
type NotificationsResponse struct {
Notifications []NotificationRef `json:"notifications"`
}
NotificationsResponse represents the notifications response
type PostTypeLister ¶ added in v0.9.0
PostTypeLister lists registered post types. It is satisfied by the post type service, and lets the dashboard drop hidden post types from its per-type breakdown cards.
type PublicArchiveMonthResponse ¶ added in v0.6.0
type PublicArchiveMonthResponse struct {
Year int `json:"year"`
Month int `json:"month"`
Count int `json:"count"`
URL string `json:"url"`
}
PublicArchiveMonthResponse is the public-facing shape for one month in a content archive. URL points to the listing page filtered by year/month.
type PublicAuthorResponse ¶ added in v0.5.0
type PublicAuthorResponse struct {
Username string `json:"username"`
DisplayName string `json:"displayName"`
AvatarURL string `json:"avatarURL"`
ProfileURL string `json:"profileURL"`
ContentCount int `json:"contentCount"`
PostTypes []string `json:"postTypes"`
PublicFields map[string]any `json:"publicFields,omitempty"`
}
PublicAuthorResponse is the safe, public-facing shape for a published author. It deliberately omits email, role, and status — those stay behind authentication (per Lesstruct's no-enumeration model). PublicFields carries the raw values of custom/system fields that are allowlisted with the "expose" operation in the [[public_field]] config. When no fields are allowlisted, the key is omitted from the JSON response (omitempty).
type PublicFieldLookup ¶ added in v0.7.0
type PublicFieldLookup interface {
IsQueryable(resource, postType, field, operation string) bool
ExposedFields(resource, postType string) []string
}
PublicFieldLookup is the minimal subset of *config.PublicFieldRegistry that ContentHandler consults to enforce the [[public_field]] allowlist. Defining it locally keeps the handler package decoupled from the config package and makes the handler trivially testable with a stub.
type ResponseMeta ¶
ResponseMeta represents metadata in API responses
type RevokeAPIKeyResponse ¶
RevokeAPIKeyResponse is returned on successful (or idempotent) revoke.
type SSGHandler ¶ added in v0.8.0
type SSGHandler struct {
// contains filtered or unexported fields
}
func NewSSGHandler ¶ added in v0.8.0
func NewSSGHandler(generator *ssg.Generator, logger *util.Logger) *SSGHandler
func (*SSGHandler) Generate ¶ added in v0.8.0
func (h *SSGHandler) Generate(w http.ResponseWriter, r *http.Request)