Temă
Roadmap and decisions
Working plan for the rewrite of the Taraba prototype (static HTML) into a React + MySQL platform. The API was first built in NestJS and then rewritten in Laravel (PHP), so that it runs on ordinary cPanel hosting; see the next section.
API rewritten in Laravel (October 2026)
The hosting (cPanel, Romarg) offers PHP but no Node.js, Redis or object storage. The API was rewritten in Laravel 13 behind the same GraphQL schema, so the three apps did not change. These decisions replace the API rows of the decision table below (TypeScript, NestJS, MikroORM, SeaweedFS, ESM):
| Area | Decision | Why |
|---|---|---|
| API | Laravel 13, PHP 8.3+, Lighthouse 6 (schema-first GraphQL) | Runs natively on cPanel; the existing schema stays the contract |
| Data access | Laravel query builder, typed readonly objects, no Eloquent | The SQL stays explicit and close to the previous queries |
| Sessions, cache, locks | in MySQL (Laravel database drivers), no Redis | Nothing extra to host |
| Files | local disk (storage/app), served by the API | Nothing extra to host; part of the backups |
| Periodic jobs | Laravel scheduler (schedule:work in Docker, one cron line on cPanel), locks in the database | A job never runs twice at once |
| Passwords | bcrypt (cost 12) over SHA-256 of the password | bcrypt alone reads only 72 bytes |
| Verification | the 14 end-to-end suites of the previous API, unchanged, plus PHPUnit and Larastan level 8 | 439/439 checks on MySQL 8.0 and MariaDB 10.11 |
Stages: foundation (migrations, seeders, health) → accounts and auth → catalog → messaging → commerce → Backoffice, administration, executive, status → CRM → removal of the NestJS API. All 185 operations are ported.
Not ported yet:
- the
volumedata profile anddb:generate(thousands of generated rows) and the performance script with its budgets (docs/tehnic/02-specificatii-tehnice.mdkeeps the budgets and the values measured on the NestJS API); - real SMS and push providers (still simulated, as before). Update the status column whenever a stage changes.
Stages
| # | Stage | Status |
|---|---|---|
| 1 | Docker skeleton: 8 containers, monorepo, shared TypeScript, status page per app, API health + mail | done (needs a run on the developer's machine) |
| 2 | Database schema, migrations and seed data (same users, listings, orders as the prototype) | done — see docs/DATABASE.md |
| 3 | API + pages, in vertical slices (agreed 2026-10-29): each module's API, then the React pages that use it | in progress — slices 1–3 done (auth, catalogue, selling, messaging, notifications, checkout, orders, reviews, promotions; docs/SITE.md, docs/NOTIFICATIONS.md, docs/ORDERS.md); slices 4–6 done: account settings + GDPR, help/legal/contact pages, reports and disputes, Backoffice (docs/WORKSPACE.md), CRM (docs/CRM.md), Backoffice administration (listings, payouts, reports, categories, settings, team and audit) and CRM templates/settings/analytics: every section of the prototype is implemented; next: final tests, performance and documentation |
| 4 | @taraba/ui: components, i18n (6 languages), data access with generated GraphQL types | planned |
| 5 | Public site in React: the 20 pages of the prototype | planned |
| 6 | Backoffice and CRM in React, one login (Workspace), role-based access | planned |
| 7 | Automated tests (unit, API, end-to-end) and final documentation | planned |
Decisions (agreed with the owner on 2026-10-29)
| Topic | Decision | Reason |
|---|---|---|
| Node.js | 26 (Active LTS since 2026-10-28) | Owner wants the latest LTS |
| React | 19.x latest stable | React has no LTS line |
| MySQL | 8.4 LTS | Production-grade; 9.x is the innovation track |
| TypeScript | 6.x | NestJS GraphQL supports 5.x/6.x only; TS 7 (Go port) is too new for the ecosystem |
| GraphQL | 16.x | Apollo Server 5 does not support graphql 17 yet |
| API architecture | NestJS modular monolith, GraphQL code-first + a few REST endpoints (health, uploads) | KISS now; modules can be split into services later |
| ORM | MikroORM 7 (legacy decorators + reflect-metadata) | Unit of work, strict typing, pure JS migrations (no binary downloads) |
| Object storage | SeaweedFS (S3 API) | MinIO community edition was abandoned (no images since 2025-10, archived 2026-04) |
| Mailpit locally; allowlist of 3 addresses enforced in code | No SMTP account yet; nothing may leak to real users | |
| SMS / push | not implemented | Owner's request |
| Payments / couriers / Google login | adapters with simulated implementations | No real integration requested |
| Language | code, comments, DB, API in English; UI in Romanian + 5 translations | Owner's request |
| Package policy | exact versions, published ≥ 14 days before install (minimumReleaseAge) | Supply-chain safety |
| Module system | ESM everywhere (Nest 12 and MikroORM 7 are native ESM) | Current defaults of the ecosystem |
| Front-end ↔ API | same origin: nginx in each SPA image proxies /api/* to the API | Cookies without CORS, one image for every environment (/env.js written at start) |
| Dev workflow on Windows | infrastructure in Docker, apps on the host with pnpm dev | Bind mounts on Windows are slow and lose file events |
Stage 1 — what exists
apps/api: validated configuration (AppConfig), modulesdatabase,cache,storage,mail, each with aHealthCheck;HealthModule(REST/health,/health/live, GraphQLhealth);DiagnosticsModule(sendTestEmail, development only). Tested against real MySQL, Redis, SeaweedFS, Mailpit.packages/shared:HealthReport,AppId,RuntimeEnvtypes.packages/ui: design tokens (light/dark),ThemeProvider,ThemeToggle,GraphQLClient,SystemStatusPage.packages/vite-config: shared Vite config (dev proxy/api,/env.jsplugin, polling option).docker/:api.Dockerfile,spa.Dockerfile(one image definition for the three apps), nginx template.docker-compose.yml: validated with Docker Compose 5.6; not yet executed (no Docker in the build sandbox).
Stage 2 — what exists
- 36 entities in 9 modules (
apps/api/src/modules/*/entities), registered insrc/database/entities.ts. src/database/orm-options.ts: one ORM configuration for the Nest module and the command line.src/database/cli.ts:setup,migrate,seed,fresh,status,migration:create.- One initial migration;
migration:createreports "No schema changes detected" against the entities. - Seed: 10 seeders (
src/database/seed/seeders), deterministic, one transaction, idempotent; fixed screens of the prototype come fromseed/fixtures/prototype-site.json. - Reusable domain code already used by the seed:
commerce/domain/order-pricing.ts(fees and totals),platform/domain/platform-settings.ts(default fees, packages, rules),common/security/password-hasher.ts(scrypt, no native dependency),common/text/slugify.ts. - Docker:
db-setupone-shot container runs migrations (and the demo data) before the API. - Verified: 10 integrity rules (payouts, disputes, refunds, totals, timestamps, consents) return 0 problems; no redundant indexes; 66 foreign keys.
Stage 3 — progress
Done:
modules/auth(sessions in Redis, customer and staff login, TOTP, email links, throttling, guards, permissions),common/errors(codes, validation pipe, production masking),common/events(domain events),modules/platform/services/audit.service.ts,modules/crm/listeners/user-registered.listener.ts.Verified: RFC 6238/4226 test vectors; 40/40 end-to-end checks (
apps/api/test/e2e/auth.e2e.mjs).Done since: catalogue API (search with FULLTEXT, product page, favourites, saved searches, media), 41 e2e checks.
Fixed along the way: optional GraphQL arguments typed as
X | nullwere not validated (metadata = Object); nested inputs need@ValidateNested+@Typeor the whitelist drops them.Next modules, in this order: catalog (listings, search, favourites, saved searches, photos upload), messaging (conversations, offers), commerce (checkout with simulated payment, orders, AWB, reviews, promotions), notifications, Backoffice (moderation, users, orders, disputes, reports, support, payouts, reports, categories, settings, team), CRM (contacts, segments engine, campaigns, automations, coupons, consent).
Slice 1 (site): React site with listings, product, favourites, saved searches, seller profile, login, registration, password reset, email confirmation; 6 languages; light/dark theme. Verified in a browser through the production nginx config (deep links, CSP, cookies). Fixed: Vite
watch: nulldisabled hot reload; TanStackmutate()callbacks are skipped when the form unmounts (usemutateAsync).Slice 2: photo upload (sharp: upright, ≤1600 px, WebP, EXIF/GPS removed), listing create/edit/drafts with risk signals, my listings (reprice → price-drop notifications, sold, reactivate, delete), messaging with offers and counter-offers, notifications + header badges, SMS/push captured in Mailpit. 130 API e2e checks.
Decisions (owner, 2026-10-29): every URL, route and query parameter in English, REST style, from one shared definition (
packages/shared/src/routes.ts); SMS and push captured locally like email, no paid services.Fixed: SeaweedFS
volume.maxtoo low (writes would stop after ~2 GB); nested/ID inputs without validation rules were stripped (nowcheck:inputsfails the build); conversation id missing in the first notification link.Slice 3: checkout with simulated card / cash on delivery, row lock against double purchase, order lifecycle with simulated AWB and delivery, 48 h auto-confirmation job, refunds on cancellation, reviews, paid promotions, order SMS. Status pages got SMS/push test buttons. 171 API e2e checks.
Fixed: recomputing seller ratings from the reviews table wiped totals that predate it (now incremental, atomic).
Slices 4–6: account settings (IBAN with ISO 13616 check, consent evidence, GDPR export and anonymising deletion), help/legal/contact pages, listing reports, order disputes (payment held), shared workspace (two-step login with QR setup, SSO between Backoffice and CRM), Backoffice: dashboard, moderation, reports and disputes, support tickets, orders (cancel + refund), users (suspend/reactivate), system status. 223 API e2e checks.
Test robustness: suites no longer depend on each other's leftovers (dynamic sellers; TOTP replay protection waits for the next 30-second window, as a person would).
CRM slice: contacts, segments (rules → parameterised SQL), campaigns on email/push/SMS with consent, suppression, frequency caps, quiet hours, signed unsubscribe links; automations with enrolments (new table); coupons at checkout and for promotions; consent log. 258 API e2e checks.
Fixed: demo automation steps lost their template ids (read before the templates were saved).
Seeding: profiles (reference, demo, volume), factories,
db:generateanddb:check(12 integrity rules); fixed the demo data (orders on listings still for sale; automation templates).Unit tests with Vitest across all packages (274), coverage thresholds as a ratchet (
docs/TESTING.md). From now on every implementation includes its unit tests.Administration slice: all listings, categories, banned words, platform settings (validated, audited, effective at once), payouts to sellers (hourly job, IBAN rule, retry), reports, team (invitations with one-time links, roles, disabling, two-step reset, last-admin rule) and the audit log; CRM templates, settings and analytics. 296 API e2e checks, 306 unit tests.
Fixed:
findOnewithout a condition (category creation); raw SQL dates returned as text (CRM analytics); coupon uses in analytics; error messages duplicated inside closed dialogs.Trust and discovery slice: phone verification by SMS code (HMAC-stored, 3 codes/hour, 5 attempts), identity verification (private document, reviewed in the Backoffice, deleted after the decision, every view audited), profile photos (moderators can remove them), user reports, "Disputele mele", category pages at /categories/:slug with meta descriptions, robots.txt and sitemaps (index + 10 000 URLs per file), About, Careers and Press pages; CRM segments by verified phone/identity. 328 e2e checks, 349 unit tests, browser journey + WCAG 2.1 AA checks on 54 pages.
Fixed: seeders kept counters between runs (now new instances per run).
Notes for later stages
- Listing photos in the demo data point to
demo/listings/<slug>/<n>.webp; stage 4/5 will generate placeholder images into the bucket (or serve generated placeholders for missing keys). - Template variables are
,,,(English, unlike the prototype). - Rating caches on
userskeep the prototype's historic numbers; new reviews will update them.