Ga naar hoofdinhoud

The public and community surface

The parts of the app a reader meets without an account: a status page, a notice board, a feed, and an endpoint that renders this app's markup the way this app renders it.

The status page​

GET /apps/opencatalogi/api/status lists named components with the state somebody set on them.

It probes nothing. A status page that infers a component's health from a health check tells the reader that the monitoring is up, which is the failure mode every incident starts with. The response carries probed: false so a caller cannot mistake a green for a measurement.

A component whose state has not been touched within the staleness period (status_staleness_hours, 24 by default) is marked stale: true and shows the date it was last set. A page that is out of date is worse than no page, and the one thing it must not do is render a confident green over a fact nobody has checked since Tuesday.

A status page this app could not read answers 503 with status-unreadable. It never answers 200 with an empty list, because an empty status page reads as "nothing is wrong".

The banner​

An instance banner has a body, a period, a severity and a dismissable flag. It shows to every signed-in user between its dates and to nobody outside them.

A banner belongs to the instance, not to a catalogue, because planned maintenance is not per catalogue. A dismissable banner a user dismissed is not shown to that user again, and is still shown to everyone else: a dismissal is one person's. A banner marked not dismissable keeps showing, because a critical maintenance notice is not something a click makes go away.

A banner whose period cannot be read is refused at the save and is not shown, so no banner can carry on for ever.

Notice boards​

A publication is a document the Woo obliges us to hold. A notice is something a municipality wants to say this week. Different lifetimes, different obligations, different readers.

A notice is therefore its own schema and never enters the sitemap or the DiWoo feed. Reusing publication for a storingsmelding would put it in both.

Comments on a notice are a moderation duty, so they are off by default, and a board that enables them names a moderator or the save is refused with the reason.

The feed​

GET /api/feeds/{catalogSlug} is a catalogue's published and updated records plus its current notices, as Atom, readable without an account. A ketenpartner who wants to watch without a webhook reads this.

The access check is the publication's, run per entry, not a rule of the feed's own. A draft is absent because it is not published. Two access decisions disagree eventually, so there is only one.

The markup endpoint​

POST /api/markup/render takes this app's markup and returns the HTML this app renders for it. No side effect, nothing stored. A mobile client that renders its own markdown shows something different from the website, and the difference is a defect report nobody can reproduce.

The dialect​

ConstructWritten as
Heading# Kop through ###### Kop
ParagraphA block of text
Bold**vet**
Italic*schuin*
Inline code`code`
Code blockThree backticks, the code, three backticks
Unordered listLines starting - or *
Ordered listLines starting 1.
BlockquoteLines starting >
Link[tekst](https://example.org)

Everything else is text. Every character of input is escaped before any markup rule is applied, so a <script> in the input cannot become a tag in the output. A link whose scheme is not http, https or mailto renders as text and never as a link: a renderer that emits whatever scheme it was handed turns every client of this endpoint into a way to run somebody else's code.

Bodies over 64000 characters are truncated, and the response says so.

What is not claimed​

ViewObject.vue still renders markdown in the browser with marked, so byte-parity between that view and this endpoint is not claimed. Migrating it to call this endpoint is follow-up work; until then the endpoint is the contract and that view is the exception.