internal

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 3, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

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

View Source
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

func RegisterRoutes(api *httpx.API, spec rest.Spec[*contracts.Content], svc contracts.Service)

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.

func Render

func Render(body string) (string, error)

Render turns a body into the HTML the public route serves. A body that goldmark cannot render at all is an invalid entity rather than an empty page: serving nothing where a document should be is the failure nobody notices.

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.

func (*Service) Publish

func (s *Service) Publish(ctx context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Content, error)

Publish serves it to anybody, and records when. See contracts.Service.

func (*Service) Unpublish

func (s *Service) Unpublish(ctx context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Content, error)

Unpublish takes it back to a draft. See contracts.Service.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL