Motor de căutare a locurilor de muncă din România
Platforma agregează joburi din peste 100 de surse (site-uri de carieră, ANAFM, companii direct) și le pune la dispoziție printr-un search engine full-text alimentat de Apache SOLR, cu un API BFF în PHP și o interfață web în React.
▼️ Prezentare Generală
Scop
Acest document descrie arhitectura software a platformei peviitor.ro — un motor de căutare a locurilor de muncă din România.
Domeniu
Sistemul acoperă: colectarea automată a datelor (scraping), validarea, indexarea full-text în Apache SOLR, expunerea printr-un API BFF și interfața web React.
Componente principale
| Componentă | URL |
|---|---|
| Platforma live | peviitor.ro |
| API (BFF) | api.peviitor.ro |
| SOLR (search) | solr.peviitor.ro |
| Admin/Validator | admin.peviitor.ro |
| Test | test.peviitor.ro |
🏗️ Context View (C4 Level 1)
Diagrama de context arată interacțiunile dintre utilizatori, frontend, API, SOLR și scrapere:
+-------------------+
| Cautator de |
| munca (Browser) |
+--------+----------+
|
HTTPS (peviitor.ro)
|
+--------v----------+
| search-engine |
| (React + Vite) |
+--------+----------+
|
HTTPS (api.peviitor.ro)
|
+--------v----------+
| API (BFF) v1 |
| PHP 8.x, RPi 5 |
| (4GB RAM) |
+--------+----------+
|
HTTP (192.168.1.x:8983)
Basic Auth (LAN)
|
+--------v----------+
| Apache SOLR 10.x |
| RPi 5 (16GB RAM) |
+--------+----------+
^
|
+--------------+--------------+
| | |
+--------v------+ +----v------+ +-----v--------+ +------------------+
| Scrapers (Py) | | JMeter | | ANOFM | | Scrapers (Node) |
| GitHub Action | | Scraper | | Scraper | | JS / TypeScript |
+---------------+ +-----------+ +---------------+ +------------------+
Stakeholders
Căutători de locuri de muncă
Interfață rapidă, căutare relevantă, date corecte
Contributori open-source
Scrapers, bug fixes, features
QA Team
Validare date, testare automată
Administrator platformă
Monitorizare, mentenanță, securitate
🖥️ Deployment View
Infrastructură hardware
+-------------------------------------------------------------+
| Acasa / RCS&RDS |
| |
| +------------------+ +-----------------------+ |
| | Router TP-Link | | RPi 5 (4GB) — API | |
| | 192.168.1.1 | | 192.168.1.135 | |
| | Ethernet 1 Gbps +----+ Nginx Proxy :80/443 | |
| | DDNS: | | Nginx UI :81 | |
| | zimbor.go.ro | | PHP BFF :8080 | |
| | | +-----------------------+ |
| | | +------------------------+ |
| | +----+ RPi 5 (16GB) — SOLR | |
| | | 192.168.1.134 | |
| | | Docker solr:10-slim | |
| | | SOLR :8983 | |
| +--------+---------+ +------------------------+ |
| | |
+-----------|---------------------------------------------------+
| Internet (NAT)
|
+-------v--------+
| IP Public |
| 86.122.35.88 |
+-----------------+
|
+-------v--------+
| Frontend |
| (GitHub Pages) |
+-----------------+
API Server — RPi 5 (4GB)
| Model | Raspberry Pi 5 Model B Rev 1.0 |
| SoC | Broadcom BCM2712 (Cortex-A76, 4 cores) |
| RAM | 4 GB LPDDR4X |
| Stocare | microSD 59.4 GB (31 GB liberi) |
| OS | Debian 12 (Bookworm), kernel 6.12.87 |
| Rețea | eth0: 192.168.1.135/24 |
| Docker | php:8.3-apache (API), orase-api, nginx-proxy-manager |
| Monitoring | Netdata v2.10.0, solr-monitor (Discord) |
SOLR Server — RPi 5 (16GB)
| Model | Raspberry Pi 5 Model B Rev 1.1 |
| CPU | ARM Cortex-A76, 4 cores @ 2.4 GHz |
| RAM | 16 GB LPDDR4X + 16 GB swap + 2 GB zram |
| Stocare | microSD 64 GB (33 GB folosiți) |
| OS | Debian 13 (Trixie), kernel 6.18.29 |
| Rețea | eth0: 192.168.1.134/24 |
| Docker | solr:10-slim (port 8983) |
| Runtime | Python 3.13.5, Node.js v24.16.0, GCC 14.2.0 |
🚀 API BFF — api.peviitor.ro
API-ul BFF (Backend for Frontend) este scris în PHP 8.3 și rulează într-un container Docker php:8.3-apache pe portul 8080, expus public prin Nginx Proxy Manager.
Endpoint-uri v1
| Endpoint | Metodă | Descriere |
|---|---|---|
/v1/search | GET | Căutare full-text |
/v1/jobs | GET | Listare joburi |
/v1/companies | GET | Listare companii |
/v1/company | GET | Detalii companie |
/v1/total | GET | Număr total joburi |
/v1/suggest | GET | Autocomplete sugestii |
/v1/random | GET | Joburi aleatoare |
/v1/health | GET | Status sănătate |
| Endpoint | Metodă | Descriere |
|---|---|---|
/v1/firme | GET | Firme |
/v1/add | POST | Adăugare documente |
/v1/update | PUT | Actualizare documente |
/v1/cleanjobs | DELETE | Curățare joburi expirate |
/v1/empty | DELETE | Golire core |
Flux căutare
- Normalizare query (diacritice → ASCII)
- Construire SOLR query (edismax, fq, bq, sort)
- Fetch de la
http://192.168.1.134:8983/solr/job/select - Mapare câmpuri SOLR → API response
- Return JSON
Monitorizare
Conexiunea la SOLR se face prin Basic Auth pe LAN. CORS este restricționat la domenii cunoscute. API-ul are integrare Sentry pentru error monitoring.
Linkuri utile:
🔍 Apache SOLR Index — solr.peviitor.ro
Apache SOLR 10.x rulează în container Docker solr:10-slim pe RPi 5 (16GB), cu autentificare Basic Auth configurată prin security.json.
Core: job (locuri de muncă)
| Field | Tip | Stocat | Indexat | MultiValued | Required |
|---|---|---|---|---|---|
url | string | ✓ | ✓ | DA (uniqueKey) | |
title | text_general | ✓ | ✓ | ||
company | string | ✓ | ✓ | ||
cif | string | ✓ | ✓ | ||
location | text_general | ✓ | ✓ | ✓ | |
workmode | string | ✓ | ✓ | ||
status | string | ✓ | ✓ | ||
salary | text_general | ✓ | ✓ | ||
date | pdate | ✓ | ✓ | ||
vdate | pdate | ✓ | ✓ | ||
expirationdate | pdate | ✓ | ✓ | ||
tags | text_general | ✓ | ✓ | ✓ |
Status Flow (Job)
scraped -> tested -> published scraped -> verified -> published
- scraped — abia colectat, nevalidat
- tested — URL funcțional, dar date incomplete
- verified — complet, toate câmpurile extrase
- published — importat în indexul principal
Validare URL & Expirare
Joburile cu expirationdate < NOW() sunt șterse automat (cron zilnic 02:00). Validarea URL se face prin HEAD requests paralele (max 1000 concurrent) — 404 → DELETE, conținut invalid → tested.
🎨 Frontend — search-engine (React)
Tech Stack
| Framework | React 18 |
| State Management | Redux Toolkit + redux-thunk |
| Routing | react-router-dom v6 |
| Styling | Tailwind CSS 3 + shadcn/ui (Radix) |
| Build | Vite 5 |
| Monitoring | Sentry |
| Analytics | Microsoft Clarity |
| Deploy | GitHub Pages |
Structură src/
src/ components/ -> UI (shadcn/ui + custom) context/ -> React context providers pages/ -> Search, JobDetail, etc. reducers/ -> Redux slices lib/ -> Utility library utils/ -> Helpers assets/ -> Statics (SVG, images) store.js -> Redux store config App.jsx -> Root component + routes index.jsx -> Entry point
📈 Scrapers & Data Pipeline
Sursa (site companie)
| (Python/Scrapy/JMeter)
v
GitHub Actions (cron zilnic)
|
v
Script de upload (upload_to_solr.sh)
|
v
SOLR index (job core)
|
v (cron daily 06:00)
Validare URL-uri (HEAD requests)
|
v (cron daily 02:00)
Stergere joburi expirate (expirationdate < NOW())
Scrapers
Scriși în Python/Scrapy, NodeJS/JS, sau Apache JMeter. Rulează prin GitHub Actions în repo-uri separate (inclusiv pe conturi externe) pentru paralelism maxim.
scraper.peviitor.roValidare Automată
HEAD requests paralele verifică zilnic URL-urile. 404 → DELETE, conținut invalid → retestat.
Cron: 06:00Curățare Expirate
Joburile cu expirationdate < NOW() sunt șterse automat. Expirare: vdate + max 30 zile.
Cron: 02:00📝 Architectural Decision Records (ADR)
| ID | Decizie | Rațional |
|---|---|---|
| ADR-001 | SOLR pe RPi 5 (16GB) | Cost zero, consum mic, suficient pentru ~50k joburi |
| ADR-002 | PHP BFF pe RPi 5 separat (4GB) | Separarea API de SOLR îmbunătățește securitatea și izolarea |
| ADR-003 | PHP BFF (nu API direct SOLR) | Izolare, normalizare query/response, Basic Auth ascuns |
| ADR-004 | React + Redux + Vite | Performanță, ecosistem matur, tooling modern |
| ADR-005 | DDNS zimbor.go.ro | IP dinamic RCS&RDS; DDNS asigură acces extern |
| ADR-006 | Scrapers în repo-uri separate | Paralelism, mentenabilitate, contribuții distribuite |
| ADR-007 | edismax + copyField _text_ | Căutare full-text pe multiple câmpuri simultan |
| ADR-008 | Status flow scraped→verified→published | Pipeline de calitate înainte de publicare |
🛢 Testare
Strategia de testare este definită integral în documentul separat: Test Strategy — peviitor.ro.
Arii de testare
Frontend
Testare manuală exploratorie + Playwright E2E + testare vizuală
API
Validare endpoint-uri în Swagger UI + Postman/Bruno + PHPUnit
SOLR Index
Conformitate schemă, căutare diacritice, CRUD, facet search
Validare date
Status flow, câmpuri obligatorii, matching companii
Cross-component
Integrare Frontend ↔ API ↔ SOLR ↔ Scrapers
Non-functional
Performanță (P95 < 2s), Securitate (OWASP Top 10), Accesibilitate (WCAG 2.2 AA)
Medii de testare
| Mediu | URL | Scop |
|---|---|---|
| Local | http://localhost:3000 | Dezvoltare, unit testing (Docker) |
| Test | test.peviitor.ro | Integrare, UAT, regresie |
| Producție | peviitor.ro | Live |
KPI
Bug Lifecycle
[New] → [Assigned] → [In Progress] → [Fixed] → [Verified] → [Closed]
Bug severity: S1 (Blocker) → S4 (Trivial). Triaj zilnic; S1/S2 rezolvate în < 4h.
Tool-uri
Playwright, Postman/Bruno, PHPUnit/Jest/Vitest, Apache JMeter, k6, OWASP ZAP, axe DevTools/WAVE, TestLink, GitHub Issues.
⚠️ Riscuri și Compromisuri
| Risc | Impact | Mitigare |
|---|---|---|
| Single point of failure (2 RPi-uri) | API și/sau SOLR indisponibile | Backup config; scripturi de restore rapide |
| Stocare microSD | Fiabilitate redusă, uzură | Monitoring health; backup zilnic |
| IP dinamic RCS&RDS | Pierdere conexiune externă | DDNS cu TTL scăzut; fallback manual |
| Fără replică SOLR | Pierdere date la defecțiune | Backup configurabil; rebuild din scrapers |
| Dependență GitHub Actions | Scrapers nu rulează fără GitHub | Self-hosted runner ca alternativă |
💻 Tehnologii Utilizate
| Tehnologie | Rol | Versiune |
|---|---|---|
| Apache SOLR | Search & indexare | 10-slim (Docker) |
| PHP | API BFF | 8.x |
| React | Frontend UI | 18.x |
| Redux Toolkit | State management | 2.x |
| Vite | Build tool | 5.x |
| Tailwind CSS | Styling | 3.x |
| shadcn/ui (Radix) | Componente UI | - |
| Python / Scrapy | Web scraping | 3.x |
| Apache JMeter | Web scraping | 5.x |
| Sentry | Error monitoring | - |
| Netdata | Monitoring infrastructură | 2.10.0 |
| Microsoft Clarity | User analytics | - |
| Docker | Containerizare | - |
| Debian Linux | OS (RPi) | 12 / 13 |
| Nginx / Apache | Reverse proxy | - |
| Playwright | Testare E2E | - |
| PHPUnit / Jest / Vitest | Testare unitară | - |
| k6 | Testare de performanță | - |
| OWASP ZAP | Testare de securitate | - |
| GitHub Actions | CI/CD, scrapers | - |
📖 Glosar
| Termen | Definiție |
|---|---|
| BFF | Backend for Frontend — API care servește specific unui client (frontend) |
| SOLR | Platformă de căutare full-text open-source (Apache Lucene) |
| Core | Index SOLR (echivalentul unui tabel în DB) |
| edismax | Query parser SOLR care permite boost și câmpuri multiple |
| BFF API | API-ul PHP (v1) care face proxy între frontend și SOLR |
| Scraper | Script care extrage automat date de pe site-uri externe |
| DDNS | Dynamic DNS — serviciu care actualizează DNS-ul pentru IP-uri dinamice |
| CIF/CUI | Codul de Identificare Fiscală al companiilor din România |
| vdate | Verified date — data la care un job a fost verificat |
| ANAF | Agenția Națională de Administrare Fiscală |