bdbdd0756667df7ac2b16b339435b0f055c16c07
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>
Sase
VIN/şase numarası sorgulama ve otomotiv yedek parça katalog platformu (Türkiye). Site: https://sase.tr · Staging: https://dev.sase.tr
Stack
| Katman | Teknoloji |
|---|---|
| Monorepo | pnpm 10 + Turborepo |
| Backend | NestJS 10, TypeScript 5.7, Node 22 |
| DB | PostgreSQL 17 + Drizzle ORM |
| Cache / Queue | Redis 7 + BullMQ |
| Auth | Better Auth (cookie session) |
| Frontend | Vite 6, React 19, TanStack Router + Query |
| UI | Tailwind v4, shadcn/ui (Radix) |
| Ödeme | Stripe (kart) |
| Postal | |
| Storage | MinIO (S3) |
| Analitik | PostHog |
| Test | Vitest, Playwright |
| Deploy | GitHub Actions → SSH → PM2 |
Yapı
ss/
├── apps/
│ ├── api/ NestJS (port 4000, /api)
│ └── web/ Vite + React (port 3000)
├── packages/
│ ├── shared/ Tipler, Zod şemaları
│ ├── config/ Env şeması (Zod)
│ └── ui/ Paylaşılan shadcn bileşenleri
├── docker/ docker-compose + nginx
├── scripts/ Deploy + bakım scriptleri
└── docs/ Detay dökümantasyon (INDEX.md)
Komutlar
pnpm dev # tüm app'ler (Turbo)
pnpm build
pnpm test # Vitest
pnpm lint # Biome
pnpm typecheck
# DB (apps/api/)
pnpm db:push # şemayı push
pnpm db:studio # Drizzle Studio
pnpm db:generate # migration üret
# Tek app
pnpm dev --filter=api
pnpm dev --filter=web
VIN Decode Akışı
Corgi (offline WMI) → PartsCatalogs API → PL24 API → EMEX scraper → NHTSA
Birden fazla araç eşleşmesi gelirse frontend seçim modalı gösterir.
Katalog Mimarisi
- PL24 P5 Modern (REST/JSON) — VW Group, BMW, Mercedes, Renault, Toyota...
- PL24 P4 Legacy (HTML scrape) — Ford, PSA, Hyundai/Kia, Nissan, Opel, Volvo
- PartsCatalogs (REST + Playwright JWT) — geniş VIN kapsamı
- EMEX (Playwright scrape,
emexdwc.ae) — async BullMQ worker
EMEX & PartsCatalogs Türkçeleştirme
EMEX ve PartsCatalogs kaynakları kategori/parça isimlerini İngilizce (zaman zaman Rusça) döner. Çeviri akışı:
TranslationsService(apps/api/src/translations/) — Redis cache → DB lookup (emex_category_translations) → orijinal döndür. Dictionary tabanlı word-by-word replace devre dışı (yarı-İngilizce çıktı yaratıyordu).- Hot path (
categories.service.ts) — EMEX/PCAT insert noktalarındatranslateMany()ile bulk lookup yapılır; cache miss varsa orijinal yazılır, sonraki bootstrap pass'inde LLM ile çevrilir. - Bootstrap script (
scripts/emex-translate-bootstrap.ts) — DB'deki tüm uniquename_originaldeğerlerini OpenRouter üzerinden DeepSeek V3 ile çevirir,emex_category_translationstablosuna yazar. - Backfill script (
scripts/emex-backfill-tr-names.ts) — translation tablosuna göre mevcutcategories.nameveparts.namedeğerlerini günceller, Redis cache flush eder.
# 1) Tüm yeni terimleri çevir (~$0.50, ~30 dk)
pnpm exec tsx scripts/emex-translate-bootstrap.ts --dry-run
pnpm exec tsx scripts/emex-translate-bootstrap.ts
# 2) DB kayıtlarını güncelle
pnpm exec tsx scripts/emex-backfill-tr-names.ts --dry-run
pnpm exec tsx scripts/emex-backfill-tr-names.ts
# 3) API restart (yeni hot-path translateMany için)
pm2 restart sase-api
Env: OPENROUTER_API_KEY (apps/api/.env).
Test Bilgileri
- Admin:
admin@sase.tr/Sase2026 - Test VIN (VW):
WVWZZZ1JZ3W597935 - Login:
POST /api/auth/sign-in/email→ cookiebetter-auth.session_token
Daha Fazla
docs/INDEX.md— kapsamlı proje rehberi (rotalar, API, DB şeması, bileşenler)CLAUDE.md— Claude Code için proje rehberi
Description
Languages
TypeScript
74.8%
HTML
20.2%
JavaScript
4%
Shell
0.5%
PLpgSQL
0.2%
Other
0.2%