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.
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.
What is technically interesting
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.
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.
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.
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).
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.
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*.
Under the hood
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.