Catalog and offer API for Slovenian operator phone data. The database is populated in this order:
- Reborn canonical catalog (
PhoneModel,RebornProduct) - Operator catalog discovery (
OperatorCatalogItem) - Operator offer sync (
OperatorOffer)
Create .env with:
DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
REBORN_WAVE_API_URL=https://api.wave.si
PORT=8787Optional runtime settings:
CRON_SECRET=shared-secret-for-cron-routes
HEADFUL=1
SYNC_PDP_CONCURRENCY=3
SYNC_DB_CONCURRENCY=6
REBORN_SYNC_CONCURRENCY=5Install Chromium once, then run the full resumable pipeline:
npm install
npm run playwright:install
npm run bootstrap:dataThe bootstrap command validates DATABASE_URL, REBORN_WAVE_API_URL, database connectivity, then runs:
npm run db:generate
npm run sync:reborn -- --rematch
npm run discover
npm run syncAll steps use upserts, so rerunning after a failed later step is expected.
The same operations are exposed for cron:
GET/POST /api/jobs/reborn
GET/POST /api/jobs/discover
GET/POST /api/jobs/sync
If CRON_SECRET is set, call them with:
Authorization: Bearer <CRON_SECRET>
vercel.json schedules Reborn twice daily, discovery daily, and offer sync every four hours.
After bootstrap, check:
GET /health
GET /api/status/data
GET /api/phones?q=iPhone
GET /api/public/operator-offers?model=iPhone%2015%20256GB
Expected /api/status/data counts after a successful run:
PhoneModel > 0
RebornProduct > 0
OperatorCatalogItem > 0
OperatorOffer > 0
matchedCatalogItems > 0
Inspect matching gaps with:
npm run review:unmatchedDo matcher/parser work only from real unmatched output.
GET /api/public/operator-offers?model=<display name> returns a stable DTO for frontends such as zamenjaj-mobi:
{
"data": {
"phone": {
"slug": "apple-iphone-15",
"brand": "Apple",
"series": "iPhone 15"
},
"offers": [
{
"operatorId": "a1",
"operatorName": "A1",
"retailPriceEur": 999,
"monthlyEur": 41.62,
"initialDepositEur": null,
"contractMonths": 24,
"planLabel": "Paket",
"productUrl": "https://...",
"availability": "IN_STOCK"
}
]
},
"count": 1
}