-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Felines is a full-stack web application built on Next.js 14 with Supabase as the complete backend. The deploy runs on Netlify with continuous deployment via GitHub.
Browser / Mobile Web ↓ Next.js 14 (Netlify) App Router · SSR · TypeScript ↓ Supabase (PostgreSQL) Auth · Storage · RLS · RPCs ↓ External APIs OpenWeatherMap · Nominatim (OSM)Map Layer Leaflet.js · OpenStreetMap tiles
| Layer | Technology | Why |
|---|---|---|
| Frontend | Next.js 14 + TypeScript | App Router, SSR, performance |
| Styling | Tailwind CSS | Speed, consistency, no custom CSS |
| Map | Leaflet.js + OpenStreetMap | Open source, no API key, free |
| Database | Supabase (PostgreSQL) | SQL, native RLS, integrated auth |
| Auth | Supabase Auth | Built-in, robust, secure |
| Storage | Supabase Storage | Per-user RLS, public read for photos |
| Deploy | Netlify | Auto CD via GitHub, free HTTPS |
| Weather | OpenWeatherMap API | Free tier sufficient |
| Geocoding | Nominatim (OSM) | Free, no API key required |
| i18n | Custom LanguageContext | No routing complexity, localStorage |
felines/ ├── app/ # Next.js App Router (19 routes) │ ├── / # Educational home │ ├── map/ # Interactive colony map │ ├── colony/[id]/ # Colony detail (6 tabs) │ ├── colony/new/ # Register a colony │ ├── learn/ # Educational guide index │ ├── learn/[slug]/ # Individual article (18 articles) │ ├── help/ # Emergency help flow │ ├── reports/ # Community reports + resources │ ├── impact/ # Public impact statistics │ ├── stories/ # Community stories wall │ ├── glossary/ # Interactive glossary (36 terms) │ ├── plants/ # Toxic plants guide │ ├── contacts/ # Contact directory │ ├── curso/ # Caretaker course │ ├── profile/ # User profile │ ├── u/[id]/ # Public caretaker profile │ ├── login/ # Login │ └── signup/ # Sign up ├── components/ # Reusable React components ├── hooks/ │ ├── useFelinesAssistant.ts # Cat assistant triggers │ ├── useCaretakerAccess.ts # Caretaker validation │ └── useLanguage.ts # PT/EN context ├── lib/ │ ├── articles.ts # 18 educational articles │ ├── catCuriosities.ts # 25 curiosities for assistant │ ├── storage.ts # Safe upload (anti path traversal) │ ├── geocode.ts # Validated geocoding (anti SSRF) │ ├── validateCoordinates.ts # Geographic range validation │ └── i18n/ │ ├── pt.ts # Portuguese translations │ └── en.ts # English translations ├── supabase/ │ └── migrations/ # 75 SQL migrations ├── public/ │ └── videos/ # Cat assistant animation ├── AUDIT_REPORT.md # Aikido Security audit results ├── SECURITY.md # Security policy └── CONTRIBUTING.md # Contribution guide
User opens /map → Leaflet renders map centered on user location → Supabase query returns colonies with BLURRED coordinates (latitude_blurred, longitude_blurred — RLS allows public read) → User clicks a pin → /colony/[id] loads colony data (public fields only) → Exact coordinates never returned by any public query
User is linked as caretaker → Frontend calls RPC get_colony_exact_location(colony_id) → RPC validates: is the caller a caretaker of this colony? → If yes: returns exact lat/lng → If no: returns permission denied → Validation happens server-side — cannot be bypassed by client
User selects photo → Client validates: type (jpeg/png/webp/gif) + size (max 5MB) → buildSafeStoragePath() generates safe path: folder sanitized + UUID filename (no user input in path) → Upload to Supabase Storage → RLS: only authenticated users can upload → Public read: photos are intentionally public
Why Supabase over Firebase? PostgreSQL with native Row Level Security that can be audited. SQL is standard and portable. Storage is integrated. Visual table editor makes field research data entry easy.
Why Leaflet.js over Mapbox or Google Maps? Open source, no API key, no cost, no request limits. OpenStreetMap tiles are reliable and community-maintained.
Why custom i18n over next-intl? No URL-based language routing (/pt/ vs /en/). Preference stored in localStorage. Simple LanguageContext with two translation files. Less dependency, same result.
Why progressive blur instead of hiding coordinates entirely? Users need geographic context for the product to make sense. Hiding coordinates completely would break the map experience. Progressive blur balances utility and animal safety at each trust level.