Commit Graph

21 Commits

Author SHA1 Message Date
Claude (audit §9.4)
bdbdd07566 feat(notifications): operability tier — send_limit + open-pixel + signed-URL exp + retention + sent-flag (audit §9.4)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Lands the §9.4 operability tier of postal/mailAudit.md as one PR on top of
the §9.3 stack (PR #101). Seven items, all independent of each other but
sharing the same notifications surface.

#15 Postal send_limit fuse — set per-hour cap (already applied LIVE in DB:
  servers.send_limit = 500). A runaway loop now hits Postal's own throttle
  long before flooding recipient ISPs into a rate-limit penalty.

#16 noreply@sase.tr decommission — change default fromAddress in both
  email.service.ts and config/configuration.ts to destek@sase.tr. `noreply@`
  had no inbound route so user replies bounced; `destek@` lands in the
  SnappyMail destek inbox. Overridable via POSTAL_FROM_ADDRESS env per workflow
  that genuinely shouldn't accept replies.

#21 Welcome CTA fallback — flip the {{else}} branch in novu-welcome.html
  from https://sase.tr to https://sase.tr/dashboard (the actual onboarding
  entry, not the marketing page). Already pushed live to Novu Mongo too.

#17 Open-pixel embed — new buildTrackPixelUrl() in novu.ts; injected
  trackPixel payload into welcome/trial-ending/win-back/referral×3/
  payment×2 NovuService methods + lifecycle-email.processor. Auth flows
  (email-verification, password-reset) deliberately skip the pixel.
  Templates updated with {{#if trackPixel}}<img ...>{{/if}} just before the
  footer; 8 templates touched, 2 (auth) skipped. Novu Mongo updated.

#18 Signed-URL exp / replay-resistance — track.sase.tr Worker /c endpoint
  now expects `e=<unix-ms>` + `s=HMAC(MID|TARGET|EXP)`. Expired signatures
  return 410. Legacy signatures (no `e=`) still accepted while in-flight
  mail with old links drains; remove that branch ~30 days post-deploy.
  buildTrackedUrl() now mints exp=now+30d.

#19 D1 retention cron — Cloudflare Cron Trigger added to mailtrack worker
  (`17 4 * * *` UTC, after Europe/Istanbul cron settles). `scheduled` handler
  DELETEs events older than RETENTION_DAYS (default 90). Both code and the
  cron schedule are LIVE on the production worker.

#20 Lifecycle sent-flag idempotency — new `lifecycle_email_sent` table
  (migration 0012) keyed (user_id, workflow). Replaces the 1-day endDate
  window's at-most-once trick that lost cohorts on skipped days. Cron now
  LEFT JOINs and writes the row immediately after each successful trigger.
  Historical seed in scripts/backfill-lifecycle-sent.sql (19 trial-ending +
  5 win-back users — generated from postal-server-1.messages) so the first
  post-deploy cron doesn't re-send to users we already mailed.

## Live infrastructure (deploy-independent)

- Postal MariaDB: `UPDATE servers SET send_limit = 500`.
- Cloudflare Worker mailtrack redeployed with new /c logic + scheduled handler.
- Cloudflare Worker: cron `17 4 * * *` registered on production env.
- Cloudflare Worker: RETENTION_DAYS=90 plain_text binding.
- Novu Mongo: 16 messagetemplates updated with pixel + Welcome /dashboard.

## Companion deploy steps post-merge

1. `pnpm db:generate` to refresh drizzle snapshots for 0011 + 0012.
2. Run `scripts/backfill-lifecycle-sent.sql` against prod + dev BEFORE the
   first cron tick post-deploy.
3. Apply host-side novu-patches/apply-headers-patch.sh again if Novu
   container rolled (idempotent).

## Verification

  curl /c?…e=<future>… ⇒ 302    new-style signature accepted
  curl /c?…(no e)…    ⇒ 302    legacy signature still accepted (drain)
  curl /c?…e=<past>…  ⇒ 410    expired signature rejected
  curl /c?…s=bad…     ⇒ 403    bad signature rejected
  CF API schedules    ⇒ `17 4 * * *` live on mailtrack worker.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 17:04:17 +03:00
Claude (audit §9.3)
327d698945 feat(notifications): TR-only templates + name canonicalisation + MTA-STS + 2048-bit DKIM + unsubscribe (audit §9.3)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Lands the §9.3 "compliance + brand" tier of mailAudit.md as one PR. Six
changes share enough surface (notifications, shared utils, infrastructure)
that splitting them would require multiple stacked PRs.

#9 — Turkish-locale title-case for names at signup
   • New `normalizeName()` in @sase/shared, locale-aware (İ/ı pairs handled
     via toLocaleLowerCase('tr-TR') + matching toLocaleUpperCase). Hyphen-
     aware, collapses whitespace, idempotent.
   • Wired into better-auth's `user.create.before` hook so every new signup
     gets canonicalised before the row lands in Postgres.
   • 28 unit tests in packages/shared/src/index.spec.ts.
   • Backfill script at `scripts/backfill-user-names.ts` (already run
     against prod + dev — 210/402 prod users and 72/153 dev users
     canonicalised, plus 71 Novu subscribers).

#10 — Email typo correction at signup
   • New `suggestEmailFix()` in @sase/shared: exact-match typo dictionary
     for the addresses we've actually suppressed (icould.com, gmial.com,
     xn--gmail-bgd.com, …) plus Levenshtein ≤ 2 fallback against popular
     providers.
   • Inline UI hint on the register form — "Bunu mu demek istedin? <link>"
     that swaps the email on click; PostHog event tracks acceptance.

#11 — Strip EN branches (decision: TR-only)
   • 0/205 prod subscribers have locale='en' and there's no marketing in
     English — the {{#equals subscriber.locale "en"}}…{{else}}…{{/equals}}
     framework was dead code in all 10 templates.
   • Templates updated in-place (avg ~30 % smaller). Renamed
     `novu-welcome-tr.html` → `novu-welcome.html` for consistency with the
     other 9 files.
   • Novu workflow definitions in both Dev + Prod envs updated via Mongo:
     subjects collapsed to TR-only, content replaced with new HTML
     (mongodump/restore-safe).
   • App code: `NovuRecipient.locale` and `NovuUser.locale` removed; the
     `...(user.locale === "en" ? { locale: "en" } : {})` spread in NovuService
     is gone.

#12 — DKIM rotated to 2048-bit RSA
   • Postal default was 1024-bit (selector `postal-YeIm3w`). Generated new
     2048-bit key, added DNS TXT `postal-2k260604._domainkey.sase.tr`,
     atomically swapped `domains.dkim_identifier_string` +
     `dkim_private_key` in Postal MariaDB, restarted Postal SMTP.
   • Verified: outgoing welcome mail now signs with `s=postal-2k260604`
     and a 256-byte signature body (vs the previous 128-byte 1024-bit
     signature). Pubkey on DNS matches the private key.
   • OLD TXT record (`postal-YeIm3w._domainkey`) stays in DNS for ~7 days
     as a grace window for in-flight mail.

#13 — MTA-STS + TLS-RPT
   • Extended the existing mailtrack Cloudflare Worker to also serve
     `mta-sts.sase.tr/.well-known/mta-sts.txt` (`mode: enforce, mx:
     mx.postal.sase.tr, max_age: 604800`). Workers Domain bound to the
     mailtrack service via Cloudflare API.
   • DNS:
       `_mta-sts.sase.tr`        TXT  "v=STSv1; id=20260604111347"
       `_smtp._tls.sase.tr`      TXT  "v=TLSRPTv1; rua=mailto:dmarc@sase.tr"
   • Verified policy fetch returns 200 with the expected body; cert valid
     (sase.tr SAN issued by GTS).

#14 — Unsubscribe preferences + RFC 8058 one-click endpoint
   • New `email_preferences` table (migration 0011) keyed
     (user_id, workflow), captures source for audit
     (one_click / manual_link / settings_page).
   • New `UnsubscribeController` at `/api/email/unsubscribe`:
       - POST: Gmail/Yahoo one-click bot path (200 fast)
       - GET:  human-visit, renders a Turkish confirmation page
     Both validate an HMAC-SHA256(`userId|workflow`) token under
     `UNSUBSCRIBE_SECRET` — stateless, no DB lookup to validate, secret
     rotation invalidates all outstanding tokens.
   • `triggerNovu()` now mints the per-call `overrides.email.headers`:
       `List-Unsubscribe: <https://…?u=&w=&t=>, <mailto:unsubscribe@…>`
       `List-Unsubscribe-Post: List-Unsubscribe=One-Click`
     Auth + payment workflows opt out via NO_UNSUBSCRIBE_WORKFLOWS so the
     unsubscribe URL never appears on transactional mail.
   • `NovuService.trigger()` pre-flight-checks `isOptedOut()` and skips the
     trigger entirely if the user opted out. Fail-open on DB error so a
     transient blip can't swallow auth mail.
   • `lifecycle-email.processor.ts` (standalone BullMQ worker — no NestJS
     DI) does the same check inline via a LEFT JOIN on
     `email_preferences WHERE opted_out IS NULL`.
   • Coolify env wired in both Prod and Dev apps:
       `UNSUBSCRIBE_SECRET` (32-byte hex, distinct per env)
       `UNSUBSCRIBE_URL_BASE` = `https://(dev.)sase.tr/api/email/unsubscribe`

## Companion sibling changes (already applied, NOT in this PR)

- Cloudflare worker `mailtrack` redeployed with mta-sts.sase.tr custom domain.
- Postal MariaDB `domains.dkim_identifier_string` + `dkim_private_key`
  updated to the new 2k260604 selector (live since 2026-06-04 11:18).
- `postal-2k260604._domainkey.sase.tr` TXT record live at Cloudflare.
- `_mta-sts.sase.tr` + `_smtp._tls.sase.tr` TXT records live at Cloudflare.
- Novu Mongo notification + message templates updated to TR-only.
- 282 user names canonicalised across prod + dev + Novu subscribers.

## Verification snapshot

- Postal raw_headers (ID 157, post-rotation): `s=postal-2k260604` + 256-byte b=
- `dig +short TXT _mta-sts.sase.tr @1.1.1.1` ⇒ live id=20260604111347
- `curl https://mta-sts.sase.tr/.well-known/mta-sts.txt` ⇒ 200 with policy
- 28 unit tests (normalizeName + suggestEmailFix) all green via Node sanity.

## Deploy notes

- Re-run `pnpm db:generate` to regenerate the drizzle snapshot for 0011
  (added the journal entry manually because no drizzle-kit on this box).
- Run `pnpm tsx scripts/backfill-user-names.ts --apply` against any DB not
  yet canonicalised (already done for prod + dev today).
- The host-side Novu nodemailer-headers patch at
  `postal/novu-patches/apply-headers-patch.sh` must be re-run after every
  Novu container redeploy or the List-Unsubscribe header is silently dropped
  before reaching Postal (see audit §9.1 #3 for the upstream cause).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 14:28:39 +03:00
3514272936 perf(emex): replace networkidle + 2s sleeps with targeted selector waits
EMEX leaf parts fetch was averaging 12.5s cold per category (Honda Civic
prod traces). Almost all of that lived in the scraper's two waterfall page
loads, each of which (a) waited for `networkidle` — which only fires after
analytics/ads stop chattering — then (b) slept an unconditional 2 seconds.
The actual extraction work runs in <100ms once the DOM is parsed.

Three focused changes in scripts/emex-vin-scraper.js:

1) getParts() resolves the Unit.aspx URL via a plain `fetch()` of
   QuickDetails.aspx instead of opening it in the browser. Probe showed
   the page is fully server-rendered and the anchor is present in the
   initial HTML, so the browser stage is dead weight (~3s saved). Falls
   back to a browser load if the fetch fails or the anchor isn't there,
   so catalogs that gate the link behind JS still work.

2) Both remaining `page.goto()` calls in getParts/getCategories/
   getCategoryTree drop `waitUntil:'networkidle'` for `'domcontentloaded'`
   plus a targeted `waitForSelector('img.dragger' | 'a[href*=…]', {timeout:5000-8000})`.
   `.catch(()=>null)` makes the wait advisory — the evaluate() below has
   its own null-safe fallbacks — but in practice the selector is present
   well before networkidle would have fired.

3) Removes the two unconditional `setTimeout(r,2000)` sleeps in getParts.
   They predate the selector-wait pattern and were belt-and-braces.

Expected: ~12.5s → ~3-5s cold leaf fetch. Warm path (DB-cached) is
unchanged at ~45ms. Dev-only push for verification before main merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:24:38 +03:00
Fusion
584cda2b6c feat(FN-399): cross-session funnel attribution audit + regression check
Commits merged:
- feat(FN-399): cross-session funnel attribution audit + regression check

Files changed:
docs/product/funnel-audit-p0-cro-2026-05.md |  52 +++++++
 scripts/check-posthog-identified-only.mjs   | 220 ++++++++++++++++++++++++++++
 2 files changed, 272 insertions(+)

Fusion-Task-Id: FN-399
2026-05-16 11:07:29 +03:00
Fusion
561b02b3d2 feat(FN-343): remove lingering iyzico references after Stripe migration (+1 more)
Commits merged:
- chore(FN-343): remove lingering iyzico references from docs, config, and scripts
- feat(FN-343): remove lingering iyzico references after Stripe migration

Files changed:
CLAUDE.md                                    | 10 +++++-----
 README.md                                    |  2 +-
 apps/api/src/database/schema/core.ts         |  1 +
 apps/web/src/messages/en.json                |  1 -
 apps/web/src/messages/tr.json                |  1 -
 apps/web/src/routes/dashboard/billing.tsx    |  6 +++---
 docker-compose.coolify.yml                   |  5 ++---
 docs/INDEX.md                                | 27 ++++++++++++-------------
 knowledge.md                                 | 30 ++++++++++++++--------------
 packages/shared/src/constants/error-codes.ts |  1 -
 packages/shared/src/index.ts                 |  1 -
 packages/shared/src/types/payment.ts         | 12 +----------
 scripts/fn342-pw-verify.mjs                  |  2 +-
 scripts/validate-env.sh                      |  3 +--
 14 files changed, 43 insertions(+), 59 deletions(-)

Fusion-Task-Id: FN-343
2026-05-14 02:31:42 +00:00
Fusion
c1085d0bbf feat(FN-342): add Playwright visual verification script for P0 subscription CRO fixes (+1 more)
Commits merged:
- feat(FN-342): finalize production verification — all P0 CRO fixes confirmed live
- feat(FN-342): add Playwright visual verification script for P0 subscription CRO fixes

Files changed:
scripts/fn342-pw-verify.mjs | 416 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 416 insertions(+)

Fusion-Task-Id: FN-342
2026-05-14 01:29:02 +00:00
Fusion
c9c168b89c feat(FN-191): replace manual migration comment with working db:migrate step (+4 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- fix(FN-191): fix typecheck and lint issues for bootstrap changes
- test(FN-191): complete Step 4 — update tests for new multi-table bootstrap logic
- fix(FN-191): complete Step 3 — strengthen bootstrap canary to detect partial-baseline state
- fix(FN-191): complete Step 2 — add dotenv bootstrap loader to migrate.ts
- fix(FN-191): complete Step 1 — replace manual migration comment with working db:migrate step

Files changed:
apps/api/src/database/__tests__/migrate.spec.ts | 146 ++++++++++++++++++------
 apps/api/src/database/migrate.ts                | 124 ++++++++++++++------
 scripts/deploy.sh                               |  12 +-
 3 files changed, 203 insertions(+), 79 deletions(-)

Fusion-Task-Id: FN-191
2026-05-12 05:25:37 +00:00
Fusion
f4fea1e429 feat(FN-094): add comment line for deployment verification
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
- Added a comment line to main.ts for deployment verification purposes
2026-05-11 02:07:03 +00:00
1e8a049219 feat: EMEX catalog integration — schema, data migration, backend & frontend
Some checks failed
CI / Lint, Typecheck, Test & Build (push) Has been cancelled
Redesign emex_* tables from vehicle-centric to catalog-centric structure with
junction tables (emex_vehicle_group_links, emex_vehicle_part_links). Migrate
92M+ rows from emex DB via postgres_fdw. Add EmexCatalogService for browse
and VIN pre-scraped matching, with Redis caching. Frontend catalog page now
has PL24/Emex tabs with full drill-down routes (brand → vehicle → group → parts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:06:29 +00:00
Sase Dev
ce8784014e feat: EMEX HTTP decode + multi-candidate selection UI
Some checks failed
CI / Lint, Typecheck, Test & Build (push) Has been cancelled
Deploy / Deploy to Production (push) Has been cancelled
Replace Playwright browser with pure HTTP for EMEX VIN decode:
- Vehicles.aspx, QuickGroups.aspx, QuickDetails.aspx all work
  without authentication — no session/browser needed
- VIN decode drops from ~10-20s (Playwright) to ~1-5s (HTTP)
- Playwright browser kept as fallback if HTTP fails

Add multi-candidate selection for EMEX (mirrors PartsCatalogs flow):
- EmexCandidate interface matches PcatCar shape so VehicleSelectModal
  renders without changes
- decodeVinOrCandidates(): single HTTP fetch returns vehicle OR
  candidates (avoids double Vehicles.aspx request)
- decodeVinByIndex(): resolves user-selected candidate by index
- vehicles.service: emexCandidates in VinResolveResult, emexCarIndex
  param in decodeVin/resolveVin, resolveEmexCarByIndex() method
- vehicles.controller: @Body("emexCarIndex") accepted
- search.tsx: candidateSource state, handleCandidateSelect sends
  emexCarIndex or pcatCarId based on source

Test VIN NM417800006410193 (Fiat SIENA) now shows 2-option modal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 09:46:25 +00:00
Sase Dev
f29a923e6e fix: deploy.sh git pull origin master → main
Some checks failed
CI / Lint, Typecheck, Test & Build (push) Has been cancelled
2026-03-02 00:32:31 +00:00
Sase Dev
72c0de65de docs: update INDEX.md + add catalog module, Ford/PSA legacy catalog, shared vehicles
- CatalogModule: VIN-less PL24 catalog browser (brands, models, categories, parts)
  - Supports P5 Modern (REST) and P4 Legacy (Ford, PSA) catalog architectures
  - Ford variant selector (model-year/engine/gearbox), PSA variant selector (body/engine/gearbox)
  - New API endpoints: ford-config, psa-bodies, psa-engines, psa-gearboxes, brands/:name/catalogs
- Shared vehicles: vehicles table decoupled from users via userVehicles junction table
- PL24 Ford Legacy service: comprehensive HTML-scraping for Ford/PSA/Hyundai/Kia/Nissan/Opel/Volvo
- PL24 types and service updated for P4 Legacy brand support
- Categories/parts service updated for dual FK (vehicleId + catalogVehicleId) pattern
- Catalog browser frontend routes and components
- docs/INDEX.md: updated with all new endpoints, components, hooks, routes (2026-03-02)
- docs/pl24-catalog/: per-brand catalog exploration docs
- scripts/migration-shared-vehicles.sql, pl24-catalog-explorer.js, posthog-dashboards.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 00:31:15 +00:00
Sase Dev
699d140f14 fix: deploy.sh git pull branch master yerine main yazılmıştı
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 14:23:31 +00:00
Sase Dev
7194393e6d feat: SEO altyapısı — pre-render, meta tags, blog posts, robots/sitemap
- vite-plugin-prerender yerine Playwright tabanlı post-build pre-render scripti
  (apps/web/scripts/prerender.mjs): 10 public route statik HTML'e dönüştürülüyor
- usePageMeta hook: her public route'da özgün title/description/canonical/OG tags
- index.html: favicon.svg, canonical, OG/Twitter Card, geliştirilmiş title+desc
- apps/web/public/: robots.txt, sitemap.xml (10 URL), favicon.svg
- nginx HSTS header eklendi (Strict-Transport-Security)
- Blog post sayfaları: blog_/$slug.tsx (4 tam yazı, 600-800 kelime)
- blog.tsx: post kartlarına Link eklendi
- Homepage: Schema.org JSON-LD (Organization + SoftwareApplication)
- Footer: placeholder href="#" sosyal linkler kaldırıldı
- deploy.sh + deploy.yml: playwright install chromium + prerender adımı

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 14:15:35 +00:00
Sase Dev
f3f3f57756 feat: propagate PL24 part metadata, improve auth UX with Google sign-in prominence
- Add unavailable/remark/modelCodes/presel fields to categories and parts DB schema
- Pass PL24 unavailable flag through pipeline instead of filtering out records
- Show unavailable categories/parts at reduced opacity in grid, tree, and parts panel
- Display part remark and model codes as secondary info in parts table
- Move Google sign-in button above email form on login/register pages with branded icon
- Add public email existence check endpoint for better login error messages
- Update INDEX.md documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:11:28 +00:00
Sase Dev
f2126754a6 feat: add OEM code copy tracking, PostHog analytics, Postal email integration
- Add oem_code_copies table and analytics module for tracking part code copies
- Integrate PostHog for frontend product analytics (VIN decode, OEM copy events)
- Switch email service from stub to Postal API with proper error handling
- Add copy button to parts panel with clipboard + server-side logging
- Add admin copy-logs page with filtering and top-copied-codes view
- Add VIN report endpoint for users to flag unrecognized chassis numbers
- Add Postal email env vars to config schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 18:09:07 +00:00
Sase Dev
dcbeb83ccc feat: add OpenTelemetry observability, Faro frontend monitoring, remove legacy Next.js app
- Add OpenTelemetry SDK with tracing, metrics, and OTLP export for API and worker
- Integrate Grafana Faro for frontend real-user monitoring
- Instrument health checks, database, Bull queues, and HTTP exception filter
- Add Grafana dashboard JSON for service overview
- Remove deprecated apps/web-nj (Next.js) — fully replaced by Vite+React frontend
- Update nginx config with OTEL collector proxy
- Minor UI fixes in schema viewer, category components, and subscription flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 17:20:49 +00:00
Sase Dev
6d7d1873ee feat: redesign auth layout, dashboard sidebar, landing page social proof & new static pages
Split-panel auth layout with dark promo panel, sectioned dashboard nav with theme toggle,
testimonials/social proof on landing page, e-commerce Remotion demo, and static pages
(about, blog, contact, terms, privacy, KVKK).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 11:34:09 +00:00
Sase Dev
4a06ba5fdb feat: Ford legacy support, EMEX browser pooling, collapsible sidebar
- Add PL24 Ford legacy service for fordt_parts architecture
- Refactor EMEX to use persistent browser pool instead of per-call instances
- Make vehicle decode resilient: fallback to PL24 when Corgi doesn't recognize VIN
- Add collapsible sidebar with persistent user preference
- Improve brand access guard and categories service
- Add debug/test scripts for VIN e2e testing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:25:06 +00:00
Sase Dev
996d614d50 feat: admin user creation, Vite migration, dialog fix, pl24 integration
- Add POST /admin/users endpoint with password hashing and role support
- Add user creation dialog to admin users page
- Migrate web from Next.js to Vite + TanStack Router
- Fix Dialog component positioning for Tailwind CSS v4
- Add @source directive for @sase/ui package scanning
- Add pl24 integration parsers and vehicle decode flow
- Backup old Next.js app to apps/web-nj

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:24:58 +00:00
Sase Dev
56a3c8bfaa feat: sase.tr v2 full application implementation
Complete rewrite of sase.tr VIN lookup platform with modern stack:

Backend (NestJS 10 + Drizzle ORM + PostgreSQL + Redis + BullMQ):
- 34 DB models (core + PL24 + EMEX schemas)
- Auth via Better Auth (email/password + social)
- Brands, Plans, Subscriptions, Payments (iyzico + EFT)
- VIN decode orchestration (Corgi + PL24 + EMEX + NHTSA)
- Interactive schema viewer backend (MinIO storage)
- EMEX scraping integration (Puppeteer + BullMQ workers)
- Translation module (EN→TR automotive dictionary)
- Admin dashboard API (stats, user mgmt, payment approval)
- Rate limiting, Helmet security, file upload validation

Frontend (Next.js 15 + Tailwind v4 + shadcn/ui + TanStack Query + Zustand):
- 20 routes: auth, dashboard, VIN search, schema viewer, admin
- Interactive schema viewer with zoom/pan/hotspot highlighting
- Subscription management with brand selector
- Payment flow (iyzico 3D Secure + EFT with receipt upload)
- i18n support (TR/EN)
- Error boundaries, loading skeletons, 404 page

Infrastructure:
- 85 tests (52 backend + 33 frontend, Vitest)
- CI/CD (GitHub Actions: lint, typecheck, test, build, deploy)
- Zero-downtime deploy script (PM2)
- Env validation script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 02:03:56 +00:00