Studiu de caz01 / 06
Dinastia
Marketplace de animale cu pedigree în Moldova · RO/RU
Produsul meu. Am făcut totul, de la nume și logo până la baza de date. Este un catalog bilingv în care crescătorii și proprietarii publică câini și pisici cu pedigree, iar cumpărătorii trimit o cerere direct de pe pagina animalului.
- Rol
- Fondator · design și full-stack
- Perioadă
- aug. 2026 – prezent
- Context
- Produs propriu
- Format
- Singur · Chișinău
- Next.js App Router
- React 19
- Supabase (Postgres, Auth, Storage)
- Row Level Security
- next/image
- Resend
- Telegram Bot API
- Vercel
- node:test


Sarcina
Cățeii și pisoii cu pedigree din Moldova se vând mai ales prin anunțuri împrăștiate și rețele sociale, unde actele și vânzătorii sunt greu de verificat. Voiam un catalog în care oamenii să aibă încredere. Trebuia să apară în Google la căutări după rasă și oraș și să meargă în română și rusă. Crescătorii trebuiau să-și gestioneze singuri anunțurile, dar fără să poată ocoli moderarea.
- Pagini rapide și indexabile pentru fiecare rasă și oraș
- Conturi de crescători în regim self-service, cu moderare înainte ca ceva să devină public
- Cereri care ajung imediat la un om, pentru că piața funcționează prin apeluri telefonice
- Datele personale, precum actele scanate și numerele de telefon, nu apar în HTML-ul public
Ce am construit
Catalog și SEO
- Next.js App Router cu prefixele /ro și /ru. Un singur segment dinamic servește atât paginile de rasă, cât și cele de oraș (11 rase de câini, 7 rase de pisici, pagini pe orașe).
- Generare statică cu generateStaticParams și ISR la fiecare oră, circa 90 de pagini statice la fiecare build.
- Toată marcarea schema.org vine dintr-un singur helper. Un animal primește Product + Offer, iar o pagină de rasă primește AggregateOffer. Mai sunt BreadcrumbList, ItemList, FAQPage și BlogPosting, plus Organization sau Person pentru fiecare vânzător.
- Sitemap cu date lastmod reale și hreflang RU/RO. Anunțurile slabe, fără poze sau cu o descriere scurtă, sunt excluse.
- Un middleware care face o singură redirecționare 308 spre URL-ul final, fără lanțuri de redirecționări.
Cabinetul crescătorului și admin
- Editor de anunțuri cu fluxul draft → în așteptare → publicat / respins / arhivat. Motivele respingerii se văd în cabinet.
- Pozele se comprimă în browser înainte de încărcare. Sunt redimensionate pe un canvas și salvate ca WebP, cu JPEG ca rezervă, de maximum 200 KB.
- Consolă de moderare pentru statusuri, verificarea actelor prin linkuri semnate de scurtă durată, vânzători, setările site-ului și statistici.
- Revalidarea cache-ului pe taguri după moderare și un cron zilnic care șterge conturile după perioada de grație.
Cereri și contacte
- /api/lead validează datele, are un câmp honeypot și permite 5 cereri pe oră de pe un IP. Caută animalul și vânzătorul pe server, apoi trimite cererea în Telegram, o scrie în jurnalul de cereri și îl anunță pe crescător.
- Numărul de telefon al vânzătorului nu apare în HTML. Se încarcă la clic, prin /api/contact, cu o limită de 20 de cereri la 10 minute de pe un IP.
- Limitele se numără în Postgres printr-un singur upsert atomic, așa că rezistă pe toate instanțele serverless.
Rezultat
- de clicuri organice din Google în primele 2 luni (Search Console)
- ~500
- în Google pentru „cumpăr pisică / animal cu pedigree” în Moldova (oct. 2026)
- Top 3–4
- de pagini generate static
- ~90
- limbi, legate prin hreflang
- 2
Tot eu am creat brandul și logoul. Cele mai riscante părți sunt acoperite de teste node:test pentru escaparea JSON-LD, redirecționări sigure, încărcarea actelor, contactele vânzătorilor și autorizarea cronului.
Modelul de securitate
Browserul are doar cheia publică Supabase, așa că baza de date decide ce poate atinge fiecare utilizator.
- Row Level Security pe fiecare tabel. Mai întâi sunt revocate drepturile implicite ale rolurilor anon și authenticated.
- Drepturile pe coloane îi permit vânzătorului să-și editeze numele, orașul, descrierea și contactele, dar nu și marcajul de verificare, statusul, marcajul de recomandat sau slug-ul.
- Un trigger guard_publish respinge orice schimbare a statusului „publicat” care nu vine de la rolul de server. O politică vede doar rândul nou. Triggerul îl vede și pe cel vechi.
- Nu există politică de ștergere. Anunțurile se arhivează, pentru că o pagină indexată care dă 404 e mai rea decât una marcată „vândut”.
- Scanările pedigree-ului și ale pașaportului veterinar stau într-un bucket privat, fără politici de citire. Moderatorul le deschide prin linkuri semnate de scurtă durată.
- Contoarele și limitele rulează prin funcții SECURITY DEFINER pe care le poate apela doar serverul.