MDMaurice Däppen.
04

Lobsicar.

A production website for a Swiss garage and carrosserie, plus a separate admin app to manage the used-car inventory. Two Next.js apps share one Postgres, with unfinished drafts physically isolated from the public site at the database level, full SEO, and a background contact-mail pipeline.

lobsicar.ch
Lobsicar screenshot
Lines of code
~11.3k
Auth
Argon2id + TOTP
Image EXIF
Stripped
Live

Lobsicar is a production website and content system for a family-run garage and carrosserie in Schüpfen. It is two separate Next.js apps sharing a single Postgres: a fast, SEO-heavy public site with a used-car catalogue and a background contact-mail pipeline, and a private admin app where the owner manages the inventory behind an Argon2id and TOTP login. The hard requirement, that an unfinished draft can never leak to the public site, is enforced at the database level rather than by an application flag.

The core design decision is database-level isolation. The admin app owns the schema and instantiates the same car table shape in two Postgres schemas, drafts and published, through a Drizzle pgSchema factory. Two least-privilege roles back it: the admin role has full read and write on both schemas, while the customer role the public site connects as holds only USAGE and SELECT on published, with no grant on drafts at all. A draft is therefore physically unreadable by the customer app, so an unfinished listing cannot leak even if application code is wrong. Publishing is a transactional cross-schema move of the row, and because image files never move, their paths stay stable across the transition.

Authentication is deliberately small and correct. Passwords are hashed with Argon2id via @node-rs/argon2, and login requires a TOTP second factor whose secret is stored AES-256-GCM encrypted at rest. Sessions are opaque, database-backed tokens rather than a JWT library, so they can be revoked and rotated server-side, carried in HttpOnly, Secure, SameSite=Strict cookies. Every server action and route calls a requireAdmin gate first, deny by default, backed by per-IP and per-account rate limiting with lockout and an audit log that never records secrets. A nonce-based CSP and the full set of security headers are owned by the app's own middleware rather than trusted from the proxy.

Image uploads are treated as an untrusted surface. Each file is checked by magic bytes with file-type, allow-listed to jpeg, png and webp, size and count capped, then re-encoded through sharp to strip EXIF and cap its dimensions before it is written under a non-guessable name to a mounted volume. Serving is auth-gated and path-traversal safe, so the volume is never browsable. Deleting a car cascades the database rows and then removes the backing files, and an orphan-sweep script reconciles the volume against both schemas so no file is ever left behind.

The public site is a Next.js 15 and React 19 app in a bespoke 'Heritage Workshop' design system, reading only from published as the restricted role. It ships heavy SEO: dynamic sitemap and robots routes, a rendered Open Graph image, and JSON-LD @graph structured data (LocalBusiness and AutoRepair, per-vehicle Car and Offer, breadcrumbs) so every listing is machine-readable. The contact form validates on both client and server, escapes control characters to block header injection, and fires a Proton SMTP mail through Next's after() hook so the response is instant and nothing is persisted. Everything ships from a single git tag: the app repos push images to GHCR, and a tag on the infra repo SSHes to the server to pull, migrate, and restart the stack behind Caddy.

PrevmdslidesNext ThreeJS Portfolio
All projects