SB
projects
Live · 2026 Personal

Klabi

Invite-only PWA replacing a 60-people WhatsApp group of dog owners in Vaulx-en-Velin. Centralises evening dog walks (3 fixed spots + ad-hoc), live GPS recording, and route sharing. JWT auth with magic links, photos on Cloudflare R2, fully free-tier deployment.

The problem

Why I built it

The neighborhood WhatsApp group grew to 60 dog owners, but the useful signal (who is going out tonight? which spot? who is joining?) keeps getting buried under photos and chatter. People hesitate to ping the group « just to ask », and end up walking solo when they could have met up. Klabi targets exactly these two usages: meeting up in the evening and sharing routes. Three fixed spots show in real time who is heading there, any member can launch an ad-hoc spot somewhere else in two taps, and anyone can record their GPS walk live then share it as inspiration for the rest of the pack.

Highlights

What is technically interesting

01

Installable PWA with background GPS recording

Geolocation API with watchPosition, Wake Lock API to keep the screen alive while walking, localStorage persistence with a 6h TTL so an interrupted session (phone locked, browser killed) can be resumed. Manifest + Service Worker for « Add to Home Screen » install on iOS and Android, the trace stays smooth even offline.

02

Storage abstraction R2 ↔ FS via SpEL conditional beans

PhotoStorage interface with two implementations picked at boot via `@ConditionalOnExpression("'${klabi.r2.bucket:}'.isEmpty()")` — LocalFs in dev, Cloudflare R2 (S3-compatible, AWS SDK v2 + UrlConnectionHttpClient) in prod. Flyway V25 backfills the existing avatars for the swap, V26 redirects seeded photos to their R2 public URLs.

03

Spring Boot 3.5 under the Render free-tier health-check window

Render free-tier kills the deploy if the app does not answer in 60s. Spring Boot 3.5 + Hibernate + Flyway cold-boots in ~160s — flipping `spring.main.lazy-initialization=true` defers non-essential bean creation to first use. Combined with an UptimeRobot ping every 5 min on `/actuator/health`, the 50s cold-start disappears for end users.

04

JWT auth + magic-link via Brevo, no Keycloak needed

Full passwordless flow: random token (concatenated UUIDs), SHA-256 hash stored in DB with a 30 min TTL, HTML email shipped via Brevo (free 300/day), verify + issue an HS256-signed JWT. Spring Security in stateless mode with a custom JWT filter, strict CORS, public endpoints whitelisted (auth, invite preview, health).

05

Activity feed + notifications via Postgres jsonb

`notifications` table with a jsonb column for event-specific payloads, mapped by Hibernate 6.6 through `@JdbcTypeCode(SqlTypes.JSON)`. The notification service factors out the event types (callout launched, walk shared, etc.) and the neighborhood activity feed aggregates them in reverse chronological order with deep links to member profiles and walk pages.

06

26 Flyway migrations with zero rewrites

From V1 (initial schema) to V26 (R2 photo URLs), every schema change is a versioned migration with an explicit backfill when needed (V25 strips `/uploads/` from legacy avatar URLs, V23 adds `shared_at` without breaking older walks). Each file leads with a header comment explaining the *why*, not just the *what*.

Stack

Under the hood

Backend
Java 17 Spring Boot 3.5 Spring Security JWT (JJWT) Spring Data JPA lazy-init
Frontend
React 18 TypeScript Vite Tailwind v4 React Router v7 Leaflet
PWA
Service Worker Web Manifest Geolocation API Wake Lock API localStorage TTL
Persistence
PostgreSQL 17 (Neon) Hibernate 6.6 Flyway (26 migrations) jsonb + @JdbcTypeCode
Storage
Cloudflare R2 (S3 SDK v2) PhotoStorage abstraction @ConditionalOnExpression
Email
Brevo (free 300/day) Magic-link flow HTML email templates
Ops
Render (free tier) Cloudflare Pages UptimeRobot keep-warm Sentry (errors)

Current status

Rolling out to the 60 neighborhood dog owners from the original WhatsApp group. Private beta first, broader opening right after. Next features will be co-designed with the community: dog-sitting requests, neighbor-to-neighbor services, vet recommendations — anything worth centralizing instead of drowning in the WhatsApp scroll.