Commit Graph

6 Commits

Author SHA1 Message Date
Sase Dev
9c05a05a7a fix(drizzle): register 0004_hot_quicksilver in migration journal
The 0004 migration file shipped in 6a0b36a but _journal.json did not
include its entry, so the hash-based migrate runner saw "0 applied,
4 already applied" on the previous deploy. Append idx 4 so the next
deploy picks it up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 22:46:54 +00:00
Sase Dev
6a0b36a7a0 feat: replace Iyzico with Stripe + single-page subscription stepper
Subscription/checkout flow rewritten end-to-end. The plan-card "Devam Et"
button silently wiped brand selection on re-click and the actual proceed
button lived offscreen — fixed by collapsing plan/brands/payment/confirm
into a single vertical stepper with one sticky CTA.

Backend
- Stripe Hosted Checkout (`/payments/stripe/checkout`) + webhook
  (`/payments/stripe/webhook`, raw body) replacing the stubbed Iyzico
  module. Webhook activates subscription on `checkout.session.completed`,
  expires the pending subscription on cancel/expire so users can retry.
- New `bank_accounts` table — multiple rows, single `is_active` enforced
  by a partial unique index. Admin CRUD under `/admin/bank-accounts`
  with multipart QR upload to MinIO; atomic `/activate` swap in a
  transaction; `GET /payments/bank-info` returns the active row.
- `payments` gains `stripe_session_id`, `stripe_payment_intent_id`,
  `bank_account_id`. EFT flow now reads the active bank account at
  payment time and stores the FK for reconciliation.
- Env: `IYZICO_*` removed, `STRIPE_*` added (validated by zod schema).
- `main.ts` `rawBody: true` for Stripe signature verification.
- Drizzle 0003 snapshot id collision fixed (VIEW-only migration shared
  prevId with 0002, blocking new generates).

Frontend
- `/dashboard/subscription` rewritten as a 4-step vertical stepper with
  step-aware sticky bottom CTA; plan re-selection is idempotent and
  preserves brand state. `/dashboard/subscription/pay` deleted; Stripe
  returns to the same page via `?stripe=success|cancelled` and the UI
  polls `/subscriptions/me` until the webhook activates the row.
- New components: `bank-transfer-card.tsx` (DB-driven IBAN + Kolay Adres
  + uploaded QR image + receipt upload) and `stripe-checkout-button.tsx`.
- Active subscription view, trial onboarding/urgency banner, downgrade
  and cancel dialogs preserved.
- TR/EN i18n: new `subscription.steps.*`, `subscription.stickyCta.*`,
  `payment.stripe.*`, `payment.bank.*`; provider label updated.

PostHog: `method: "iyzico"` → `"stripe"`; new events `iban_copied`,
`kolay_adres_copied`, `qr_viewed`, `eft_initiated`,
`stripe_redirect_returned`.

Deploy runs `db:migrate` which applies 0004_hot_quicksilver
(additive: new table + nullable columns; safe to apply on prod).
Operator must add `STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`,
`STRIPE_WEBHOOK_SECRET` to env and create the first
`bank_accounts` row via the admin endpoint before the bank tab works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 22:42:28 +00:00
Sase Dev
e4b430f69c feat(analytics): enrich query_logs metadata + query_log_insights view
For continuous optimization we need to attribute slow/failed decodes to
the right cause. query_logs.timings jsonb is now a structured decode-meta
blob, not just stage timings:

- wmi: first 3 chars of VIN (per-brand aggregation)
- result_kind: vehicle / pcat_candidates / emex_candidates / unknown / aborted
- cache_source: db_hit / redis_positive / redis_negative / lock_wait / miss
- candidate_pick: pcat / emex / none (when user picks from candidate modal)
- pcat_car_count, emex_candidate_count (cardinality, drives candidate-modal rate)
- pl24_circuit_open, pl24_skipped (CB state at request time)
- vin_api_used, vin_api timing (NHTSA fallback frequency)

Migration 0003 adds a query_log_insights VIEW that flattens these keys
into typed columns, so ad-hoc SQL doesn't need json operators. New meta
keys appear automatically as NULL; the VIEW stays stable.

docs/analytics-queries.sql has 8 starter queries: cache hit ratio,
per-source latency, slowest WMIs, stage breakdowns, CB/abort frequency,
candidate-modal rate, top failing VINs, dedup effectiveness.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:04:23 +00:00
Sase Dev
c4f6cc1caf perf(vehicles): dedup, abort budget, circuit breaker for VIN decode
Production p99 was 18min for failed decodes and 15min for PL24 successes;
same VIN could trigger N parallel 10+ min decodes. Three layered fixes:

- In-flight dedup via Redis SETNX (vin🔒*); concurrent same-VIN requests
  poll the resolve cache instead of re-firing the upstream chain.
- 24h positive cache (vin:resolve:*) and 6h negative cache
  (vin:resolve:neg:*); previously 5min positive / no negative.
- 25s hard abort budget via AbortController; PCAT gets the signal natively
  (AbortSignal.any), PL24/EMEX wrapped with raceWithSignal at the boundary.
  Aborted decodes don't poison the negative cache.
- PCAT/EMEX real race: first definitive single-result wins; the slower
  source is skipped (previously PCAT was always awaited first).
- PL24 circuit breaker: 3 consecutive failures opens a 30s cooldown
  (pl24:cb:cooldown_until); successes reset the counter.
- Stage-level timings in query_logs.timings (jsonb): pcat/emex/pl24/
  lock_wait/cache_hit/aborted. Failed source now logged as "none" or
  "aborted" instead of misleading "corgi".

Verified locally with 3 parallel decodes of a fresh VIN: 1 real decode
(3.59s), 2 lock-waits (3.53s) sharing the result, 4th request 23ms cache
hit. Previously this would have been 3 separate 10+ min PL24 decodes.

Migration 0002 adds query_logs.timings jsonb (NULL default). Must be
applied manually before deploy (deploy.sh does not run db:push).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:31:25 +00:00
Fusion
0f80f5292b feat(FN-192): DB schema + Migration (+7 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-192): complete Step 8 — Documentation & Delivery
- feat(FN-192): complete Step 7 — Testing & Verification
- feat(FN-192): complete Step 6 — i18n keys replace stage with changeType
- feat(FN-192): complete Step 5 — ChangelogTab rename stage to changeType
- feat(FN-192): complete Step 4 — Badge component rename stage to changeType
- feat(FN-192): complete Step 3 — API Service + Controller Spec
- feat(FN-192): complete Step 2 — Shared Zod schemas + type exports + DTO
- feat(FN-192): complete Step 1 — DB schema + Migration

Files changed:
apps/api/drizzle/0001_charming_quicksand.sql       |    2 +
 apps/api/drizzle/meta/0001_snapshot.json           | 5167 ++++++++++++++++++++
 apps/api/drizzle/meta/_journal.json                |    9 +-
 .../api/src/changelog/changelog.controller.spec.ts |    2 +-
 apps/api/src/changelog/changelog.dto.ts            |    2 +-
 apps/api/src/changelog/changelog.service.spec.ts   |    4 +-
 apps/api/src/changelog/changelog.service.ts        |    4 +-
 apps/api/src/database/schema/core.ts               |    2 +-
 apps/web/src/components/settings/changelog-tab.tsx |   11 +-
 apps/web/src/messages/en.json                      |    8 +-
 apps/web/src/messages/tr.json                      |    8 +-
 docs/INDEX.md                                      |    4 +-
 packages/shared/src/index.ts                       |    4 +-
 packages/shared/src/schemas/changelog.ts           |    8 +-
 packages/shared/src/types/changelog.ts             |    2 +-
 packages/ui/src/badge.tsx                          |   12 +-
 16 files changed, 5215 insertions(+), 34 deletions(-)

Fusion-Task-Id: FN-192
2026-05-12 06:13:34 +00:00
Fusion
e87aba8829 feat(FN-190): move drizzle-kit to production dependencies (+8 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged:
- feat(FN-190): complete Step 9 — add Migration Workflow section to docs/INDEX.md
- fix(FN-190): apply biome import sorting and formatting
- feat(FN-190): complete Step 7 — write unit tests for migrate runner (5 tests)
- feat(FN-190): complete Step 6 — replace db:push with db:migrate in deploy workflow
- feat(FN-190): complete Step 5 — update Dockerfile with drizzle/ copy and start.sh CMD
- feat(FN-190): complete Step 4 — add db:migrate and db:migrate:dist scripts
- feat(FN-190): complete Step 3 — write migrate runner with bootstrap logic
- feat(FN-190): complete Step 2 — generate baseline Drizzle migration (46 tables)
- feat(FN-190): complete Step 1 — move drizzle-kit to production dependencies

Files changed:
.github/workflows/deploy.yml                    |    2 +-
 Dockerfile                                      |    6 +-
 apps/api/drizzle/0000_brief_guardian.sql        |  658 +++
 apps/api/drizzle/meta/0000_snapshot.json        | 5167 +++++++++++++++++++++++
 apps/api/drizzle/meta/_journal.json             |   13 +
 apps/api/package.json                           |    4 +-
 apps/api/src/database/__tests__/migrate.spec.ts |  179 +
 apps/api/src/database/migrate.ts                |  160 +
 apps/api/start.sh                               |    8 +
 docs/INDEX.md                                   |   24 +-
 pnpm-lock.yaml                                  |    6 +-
 11 files changed, 6220 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-190
2026-05-11 22:03:33 +00:00