Skip to content

← Back to the blog

Private routes, CSRF, and a server-rendered admin in JSkelet

· 2 min read

Abstract admin panel blocks for private routes in JSkelet

Content sites still need an admin: drafts, publish, media, moderation. JSkelet does not force a React SPA for that job. You can ship a private, server-rendered panel on the same Node process that serves cacheable public HTML — if you respect the caching boundary.

Public vs private in one process

Public brochure and blog routes use route() with a revalidate window and land in the HTML cache. Admin routes set private: true so signed-in documents never enter that cache. Mixing both in one app is normal. Mixing both in one cache key is a vulnerability and a support nightmare.

Sessions, cookies, and role checks live in middleware and controllers. The panel layout can be a separate shell (sidebar + top bar) selected from layoutContext based on pathname — still complete HTML, still progressive forms.

Mutations stay boring on purpose

Prefer:

  • POST + CSRF token (CsrfField)
  • Validate on the server
  • Persist
  • invalidateHtmlCache for affected public paths
  • 303 redirect back to a list or edit screen

That loop deletes entire classes of client state bugs. Optional islands can polish pickers later; they should not be the only way to save.

CSRF and Origin checks

JSkelet’s CSRF story is designed for form posts. Keep admin POSTs inside the protection path. If you exclude a webhook or upload endpoint, document why. Public comment forms need the same discipline as login.

Storage is your choice

The framework does not mandate Postgres. Examples and apps often use a small store (SQL, JSON file, or external CMS). What matters for marketing the architecture is the boundary: public read models that are cacheable, private write UIs that are not.

Media uploads can land on disk or object storage. Keep public URLs stable; reference them from content records; invalidate HTML when featured images change.

Roles and “good enough” for v1

Many content teams need one administrator role on day one. Ship that before a permission matrix. Expand when a second editor appears — not when a template blog starter invents ten roles.

Checklist for a trustworthy panel

  • Every admin GET is private: true
  • Every state-changing POST has CSRF
  • Publish invalidates list + detail (+ locale variants if any)
  • Errors re-render with messages; successes redirect
  • No secrets in clientEnv

JSkelet’s bet is that a WordPress-shaped editorial loop does not require PHP or a React admin SPA — it requires HTML forms, sessions, and a hard line between private and cached public documents.

Comments

No comments yet.

Leave a comment

Comments are held for moderation before they appear.