Documentation
¶
Overview ¶
Package internal is every implementation of the content module. Nothing outside modules/content can import it, which is the compiler enforcing idea 3: a consumer takes contracts.Service, and taking anything else does not build.
Index ¶
- Constants
- func RegisterRoutes(api *httpx.API, spec rest.Spec[*contracts.Content], svc contracts.Service)
- func Render(body string) (string, error)
- type Page
- type Service
- func (s *Service) Archive(ctx context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Content, error)
- func (s *Service) Public(_ context.Context, tx db.Tx[db.Tenant], slug string) (*contracts.Content, error)
- func (s *Service) Publish(ctx context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Content, error)
- func (s *Service) Unpublish(ctx context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Content, error)
Constants ¶
const PublicPath = "/api/v1/content/public/{slug}"
PublicPath is where the site reads a page by name. It is a sibling of the Spec's collection rather than a route under it, so that "everything under /api/v1/content/contents needs content:read" stays true by looking.
Variables ¶
This section is empty.
Functions ¶
func RegisterRoutes ¶
RegisterRoutes mounts the three lifecycle commands and the one public read.
The commands are routes rather than fields of a PATCH because each is a rule about the state the content is in and each publishes an event: a caller who could write status="published" through the generic update would serve a page with no publication time and tell nobody. spec.Immutable is the other half of that argument.
Types ¶
type Page ¶
type Page struct {
Slug string `json:"slug"`
Title string `json:"title"`
Kind string `json:"kind"`
HTML string `json:"html" doc:"The body, rendered from Markdown and sanitized"`
PublishedAt time.Time `json:"publishedAt"`
}
Page is what the public route answers with: the content as a reader needs it, and nothing else. The body is HTML and not Markdown because a reader has no renderer, and the author, the timestamps and the status are not a reader's business — a public response that carried the whole entity would be an admin screen anybody could read.
type Service ¶
type Service struct{}
Service is the content lifecycle. It has no fields: everything a command needs arrives with the transaction it is given, which is what lets one instance serve a request and an event handler at once.
func NewService ¶
func NewService() *Service
NewService returns the lifecycle commands. module.go constructs it.
func (*Service) Archive ¶
func (s *Service) Archive(ctx context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Content, error)
Archive keeps it and serves it to nobody. See contracts.Service.
func (*Service) Public ¶
func (s *Service) Public(_ context.Context, tx db.Tx[db.Tenant], slug string) (*contracts.Content, error)
Public is the published content at this slug. The status is in the query rather than checked afterwards, so a draft and a slug nobody has used are one answer and the caller cannot tell which it was.
The slug is normalised the same way a write normalises it, because a name stored one way and looked up another is a page nobody can reach.