docs: add phased self-hosted redesign plan and checklist

This commit is contained in:
Poyraz
2026-07-10 22:07:50 +03:00
parent 57aab2932e
commit ae8fa1425c
9 changed files with 3786 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+195
View File
@@ -0,0 +1,195 @@
---
title: Phase 0 ADR Set
description: Self-hosted redesign için Faz 0'da kilitlenen mimari kararlar.
status: active
last_updated: 2026-07-10
---
# Phase 0 ADR Set
Bu dosya Faz 0 mimari karar kayıtlarını tutar. Kararlar, implementation fazlarında tersine kanıt çıkmadığı sürece geçerlidir. Faz 1 spike'ı bu kararların teknik olarak çalıştığını kanıtlayacak ilk uygulama işidir.
## ADR-0001: SQLite + better-sqlite3
Status: Accepted for redesign baseline.
Karar:
- Hedef self-hosted runtime tek instance olacak.
- Veritabanı SQLite olacak.
- Node tarafında synchronous native driver olarak `better-sqlite3` kullanılacak.
Gerekçe:
- Freelancer ölçeğinde ayrı Postgres servisi, connection pool ve network latency self-host maliyetini artırıyor.
- Tek persistent volume ile backup/restore daha anlaşılır.
- SQLite WAL ve kısa transaction disiplini bu ürünün beklenen write concurrency ihtiyacına yeterli.
Sınır:
- Horizontal replica yok.
- NFS/shared volume üzerinde aynı DB dosyasını birden fazla app instance yazmayacak.
- Bu sınır aşılırsa PostgreSQL migration ayrı ADR ve faz gerektirir.
## ADR-0002: Drizzle ORM ve kaynak kontrollü migration
Status: Accepted for redesign baseline.
Karar:
- Schema ve query katmanı Drizzle ORM ile yazılacak.
- Production'da `drizzle-kit push` kullanılmayacak.
- Migration dosyaları kaynak kontrolünde, image içinde ve startup migration runner üzerinden uygulanacak.
Gerekçe:
- Type-safe query ihtiyacı var.
- Runtime overhead düşük kalmalı.
- SQLite schema değişimleri deterministic ve review edilebilir olmalı.
## ADR-0003: Better Auth
Status: Accepted for redesign baseline.
Karar:
- Supabase Auth yerine Better Auth kullanılacak.
- Email/password ve DB session ilk release için yeterli kapsam.
- İlk freelancer setup akışı public registration yerine kurulum kilidiyle yönetilecek.
Gerekçe:
- Auth protokolünü sıfırdan yazmak gereksiz risk.
- External auth service self-host hedefiyle çelişir.
- Portal client invitation flow Better Auth user/session modeliyle kurulabilir.
## ADR-0004: Server-side authorization
Status: Accepted for redesign baseline.
Karar:
- Browser hiçbir zaman DB/auth secret/filesystem import etmeyecek.
- RLS yerine `requireSession`, role checks ve owner-filtered repository/service functions kullanılacak.
- Authorization testleri her resource için negatif test içerecek.
Gerekçe:
- SQLite RLS sağlamaz.
- Güvenlik sınırı her query/mutation'da server service katmanında kurulmalı.
## ADR-0005: Yerel filesystem storage
Status: Accepted for redesign baseline.
Karar:
- Avatar ve project asset dosyaları `/app/data/uploads` altında saklanacak.
- DB içinde dosya metadata tablosu olacak.
- Upload/download Route Handler'ları auth, owner, size, MIME ve path traversal kontrolü yapacak.
Gerekçe:
- S3 veya Supabase Storage self-host kurulumunu ağırlaştırır.
- Tek volume backup modeli basit kalır.
## ADR-0006: Internal Neta UI
Status: Accepted for redesign baseline.
Karar:
- `poyraz-ui` runtime bağımlılığı kaldırılacak.
- Neta'nın kendi `components/ui` primitive'leri ve semantic CSS token'ları kullanılacak.
- Dialog, select, menu, tabs gibi davranışlı bileşenlerde tek bir headless primitive katmanı izole edilebilir.
Gerekçe:
- UI bağımlılığı self-host dağıtım ve uzun vadeli bakım riskini artırıyor.
- Ürün UX'i freelancer workflow'una göre yeniden tasarlanacak.
## ADR-0007: PWA/offline sync ilk release kapsam dışı
Status: Accepted for redesign baseline.
Karar:
- İlk self-hosted release'te PWA/offline sync hedeflenmez.
- `next-pwa` kaldırılacak veya devre dışı bırakılacak.
Gerekçe:
- Offline cache, auth/session ve local DB geçişinde ek tutarlılık riski getiriyor.
- Öncelik deploy basitliği ve server-side veri doğruluğu.
## ADR-0008: Embeddings operasyonel hedefe taşınmayacak
Status: Accepted for redesign baseline.
Karar:
- `document_embeddings` pgvector runtime kabiliyeti ilk release'e taşınmayacak.
- Mevcut embeddings archive/import source olarak değerlendirilecek.
- Gerekirse sonraki fazda SQLite FTS5 veya harici vector store için ayrı karar alınacak.
Gerekçe:
- pgvector self-host hedefinde Postgres bağımlılığını geri getirir.
- Mevcut RAG yüzeyi core freelancer workflow'u için kritik değil.
## ADR-0009: Para integer minor unit
Status: Accepted for redesign baseline.
Karar:
- Para alanları hedef DB'de integer minor unit olarak saklanacak.
- Formatlama UI/shared formatting katmanında yapılacak.
Gerekçe:
- Decimal farkları, locale parse hataları ve floating point riski azaltılır.
- Aggregate SQL daha net olur.
## ADR-0010: journals + daily_logs birleşimi
Status: Accepted for redesign baseline.
Karar:
- Aktif ürün davranışı `daily_logs` üzerinden devam eder.
- Legacy `journals` kaynak verisi `journal_entries` içine merge veya archive edilir.
Gerekçe:
- İki ayrı günlük modeli aynı üründe gereksiz karmaşa yaratıyor.
- Faz 5 journal redesign tek kanonik modeli hedefler.
## ADR-0011: Production dual-write yok
Status: Accepted for redesign baseline.
Karar:
- Eski Supabase sürümü redesign tamamlanana kadar production olarak kalacak.
- Yeni sistem ayrı import rehearsal ve maintenance cutover ile devreye alınacak.
- Supabase ve SQLite'a aynı anda production dual-write yapılmayacak.
Gerekçe:
- Dual-write tutarlılık ve rollback riskini artırır.
- Küçük ürün ölçeğinde kontrollü cutover daha güvenli.
## ADR-0012: Vercel hedef deploy değil
Status: Accepted for redesign baseline.
Karar:
- Yerel SQLite dosya sistemi gerektiren hedef runtime Vercel serverless/deploy modeline göre tasarlanmaz.
- Hedef Coolify, Dokploy veya standart Docker host'tur.
Gerekçe:
- Persistent local volume ve single long-running Node process gereksinimi var.
@@ -0,0 +1,309 @@
---
title: Phase 0 Baseline
description: Supabase ve Poyraz UI ayrımı başlamadan önce mevcut Neta uygulamasının repo, davranış, bağımlılık ve risk baseline'ı.
status: active
last_updated: 2026-07-10
---
# Phase 0 Baseline
Bu dosya, self-hosted redesign başlamadan önce repo üzerinden doğrulanabilen mevcut durumu sabitler. Production Supabase verisine erişim gerektiren satır sayıları, storage boyutları, gerçek kullanıcı akışı ekran kayıtları ve backup doğrulaması bu dokümanda tamamlanmış sayılmaz; bunlar `phase-0-data-mapping.md` içindeki production audit sorguları ve checklist istisnalarıyla takip edilir.
## Kanıt komutları
Bu baseline aşağıdaki local komutlarla oluşturuldu:
```powershell
git status --short
rg --files
rg --files .\app
rg -n 'supabase|poyraz-ui|createClient|service_role|serviceRole|NEXT_PUBLIC_SUPABASE|from\(' app components lib config hooks --glob '*.ts' --glob '*.tsx'
rg -n 'poyraz-ui' app components --glob '*.ts' --glob '*.tsx'
rg -n 'create table if not exists public\.|create table if not exists neta_internal\.|alter table public\..*enable row level security|create policy|create or replace function|create index if not exists|create trigger|insert into storage\.buckets' supabase\migrations supabase\schema.sql
rg -n 'select\("\*"\)|select\(''\*''\)|order_index|sort_order|completed|done|\.limit\(|\.range\(|useTransition|useOptimistic|startTransition|isPending|pending' app lib components --glob '*.ts' --glob '*.tsx'
```
## Repo durumu
- Next.js App Router kullanılıyor.
- Ayrı backend uygulaması yok; server davranışı `app` altındaki Server Components, Server Actions ve Route Handlers içinde.
- Supabase şu anda auth, session refresh, RLS ile authorization, Postgres schema, RPC aggregate, storage ve service-role tabanlı kullanıcı/dosya işlemlerini taşıyor.
- Poyraz UI uygulama shell, auth, dashboard, portal ve loading ekranlarına yayılmış durumda.
- Dockerfile, Compose, migration runner, backup/restore script'i ve test harness yok.
- `package.json` içinde `typecheck` veya test script'i yok; typecheck local TypeScript binary ile çalıştırılmalı.
- Doküman olarak Faz 0 başlangıcında eklenmiş dosyalar: `docs/18-self-hosted-redesign-plani.md`, `docs/19-self-hosted-redesign-checklist.md`.
## Route envanteri
Route group'lar:
- `(dashboard)`: freelancer dashboard, core OS ekranları ve business ekranları.
- `portal`: client portal.
- `api`: AI, health ve internal route handler'ları.
- Top-level auth: `login`, `register`.
Sayfa/layout/loading/action dosyaları:
| Dosya | Rol |
| --- | --- |
| `app/layout.tsx` | Root layout, manifest, global toaster |
| `app/login/page.tsx` | Login ekranı |
| `app/login/actions.ts` | Login, signup, sign out Server Actions |
| `app/register/page.tsx` | İlk admin/freelancer setup ekranı |
| `app/(dashboard)/layout.tsx` | Dashboard auth guard ve shell |
| `app/(dashboard)/page.tsx` | Dashboard Server Component |
| `app/(dashboard)/dashboard-client.tsx` | Dashboard client UI |
| `app/(dashboard)/loading.tsx` | Dashboard loading skeleton |
| `app/(dashboard)/analytics/page.tsx` | Analytics Server Component |
| `app/(dashboard)/analytics/analytics-client.tsx` | Analytics client UI |
| `app/(dashboard)/analytics/loading.tsx` | Analytics loading skeleton |
| `app/(dashboard)/business/invoices/page.tsx` | Invoices Server Component |
| `app/(dashboard)/business/invoices/invoices-client.tsx` | Invoices client UI |
| `app/(dashboard)/business/proposals/page.tsx` | Proposals Server Component |
| `app/(dashboard)/business/proposals/proposals-client.tsx` | Proposals client UI |
| `app/(dashboard)/business/subscriptions/page.tsx` | Subscriptions Server Component |
| `app/(dashboard)/business/subscriptions/subscriptions-client.tsx` | Subscriptions client UI |
| `app/(dashboard)/calendar/page.tsx` | Calendar Server Component |
| `app/(dashboard)/calendar/calendar-client.tsx` | Calendar client UI |
| `app/(dashboard)/calendar/actions.ts` | Calendar mutation Server Actions |
| `app/(dashboard)/chat/page.tsx` | AI chat client-heavy screen |
| `app/(dashboard)/clients/page.tsx` | Clients Server Component |
| `app/(dashboard)/clients/clients-client.tsx` | Clients client UI |
| `app/(dashboard)/clients/actions.ts` | Client mutation Server Actions |
| `app/(dashboard)/clients/loading.tsx` | Clients loading skeleton |
| `app/(dashboard)/clients/[id]/page.tsx` | Client detail Server Component |
| `app/(dashboard)/clients/[id]/client-detail-client.tsx` | Client detail client UI |
| `app/(dashboard)/clients/[id]/actions.ts` | Client activity mutation Server Actions |
| `app/(dashboard)/finance/page.tsx` | Finance Server Component |
| `app/(dashboard)/finance/finance-client.tsx` | Finance client UI |
| `app/(dashboard)/finance/actions.ts` | Finance mutation Server Actions |
| `app/(dashboard)/journal/page.tsx` | Daily log Server Component |
| `app/(dashboard)/journal/journal-client.tsx` | Daily log client UI |
| `app/(dashboard)/journal/actions.ts` | Daily log mutation Server Actions |
| `app/(dashboard)/projects/page.tsx` | Projects Server Component |
| `app/(dashboard)/projects/projects-client.tsx` | Projects client UI |
| `app/(dashboard)/projects/actions.ts` | Project/planning/revision mutation Server Actions |
| `app/(dashboard)/projects/loading.tsx` | Projects loading skeleton |
| `app/(dashboard)/projects/[id]/page.tsx` | Project detail Server Component |
| `app/(dashboard)/projects/[id]/project-detail-client.tsx` | Project detail client UI |
| `app/(dashboard)/projects/[id]/loading.tsx` | Project detail loading skeleton |
| `app/(dashboard)/settings/page.tsx` | Settings client screen |
| `app/(dashboard)/settings/actions.ts` | Profile/password mutation Server Actions |
| `app/(dashboard)/tasks/page.tsx` | Tasks Server Component |
| `app/(dashboard)/tasks/tasks-client.tsx` | Tasks kanban/client UI |
| `app/(dashboard)/tasks/actions.ts` | Task mutation Server Actions |
| `app/(dashboard)/tasks/loading.tsx` | Tasks loading skeleton |
| `app/portal/layout.tsx` | Client portal auth guard and shell |
| `app/portal/page.tsx` | Portal home |
| `app/portal/projects/page.tsx` | Portal projects list |
| `app/portal/projects/[id]/page.tsx` | Portal project detail Server Component |
| `app/portal/projects/[id]/portal-project-client.tsx` | Portal project detail client UI |
| `app/portal/projects/[id]/actions.ts` | Portal revision Server Action |
| `app/portal/revisions/page.tsx` | Portal revisions list |
| `app/portal/tasks/page.tsx` | Portal tasks list |
Route Handlers:
| Route | Dosya | Mevcut sorumluluk |
| --- | --- | --- |
| `POST /api/chat` | `app/api/chat/route.ts` | Chat message persistence, settings read, AI response |
| `POST /api/create-client-user` | `app/api/create-client-user/route.ts` | Freelancer'ın client auth user oluşturması |
| `POST /api/finance-analysis` | `app/api/finance-analysis/route.ts` | Finance transactions üzerinden AI analiz |
| `GET /api/health` | `app/api/health/route.ts` | Basic liveness JSON |
| `POST /api/project-risk` | `app/api/project-risk/route.ts` | Project/tasks üzerinden AI risk analizi |
## Supabase kullanım envanteri
Browser Supabase client kullanan dosyalar:
- `app/(dashboard)/chat/page.tsx`: session/message CRUD ve `auth.getUser`.
- `app/(dashboard)/settings/page.tsx`: profile/settings read ve `app_settings` upsert.
- `lib/supabase/client.ts`: `createBrowserClient` factory.
Server Supabase client kullanan başlıca dosyalar:
- Auth/setup: `app/login/actions.ts`, `app/register/page.tsx`, `lib/auth/first-admin-setup.ts`, `lib/supabase/server.ts`, `lib/supabase/middleware.ts`.
- Dashboard reads: `app/(dashboard)/page.tsx`, `analytics/page.tsx`, `clients/page.tsx`, `clients/[id]/page.tsx`, `projects/page.tsx`, `projects/[id]/page.tsx`, `tasks/page.tsx`, `calendar/page.tsx`, `finance/page.tsx`, `journal/page.tsx`, business pages.
- Dashboard mutations: `clients/actions.ts`, `clients/[id]/actions.ts`, `projects/actions.ts`, `tasks/actions.ts`, `calendar/actions.ts`, `finance/actions.ts`, `journal/actions.ts`, `settings/actions.ts`.
- Portal reads/mutations: `app/portal/**/*.tsx`, `app/portal/projects/[id]/actions.ts`.
- AI/routes: `app/api/chat/route.ts`, `app/api/finance-analysis/route.ts`, `app/api/project-risk/route.ts`, `lib/ai/embeddings.ts`.
Service-role kullanımı:
- `lib/supabase/admin.ts`: `SUPABASE_SERVICE_ROLE_KEY` ile admin client.
- `lib/auth/internal-users.ts`: `request_internal_auth_creation` RPC ve Supabase Admin `auth.admin.createUser`.
- `app/api/create-client-user/route.ts`: internal user creation flow.
- `app/(dashboard)/projects/actions.ts`: `project-assets` upload için service-role client.
- `app/(dashboard)/projects/page.tsx` ve `app/(dashboard)/projects/[id]/page.tsx`: project-assets public URL/asset erişim path'i.
- `app/(dashboard)/settings/actions.ts`: avatars upload ve public URL üretimi.
RPC kullanımı:
- `is_first_admin_setup_available`: first admin setup guard.
- `request_internal_auth_creation`: service-role guard ile internal user creation.
- `match_documents`: pgvector similarity search.
- `get_dashboard_metrics`: dashboard aggregate.
- `get_analytics_metrics`: analytics aggregate.
Storage kullanımı:
- `avatars`: public avatar URL'leri; settings flow.
- `project-assets`: private project cover/assets; path convention user id folder'ı üzerinden.
## Poyraz UI import envanteri
Poyraz UI kullanılan dosya sayısı: 36 uygulama/component dosyası.
Kullanılan namespace'ler:
- `poyraz-ui/atoms`: `Badge`, `Button`, `Card`, `CardContent`, `Input`, `Label`, `Textarea`, `Typography`.
- `poyraz-ui/molecules`: `Dialog*`, `DropdownMenu*`, `Select*`, `Tabs*`, `toast`, `Toaster`.
- `poyraz-ui/organisms`: dashboard ve portal shell navigation bileşenleri.
Yoğun kullanım alanları:
- Layout shell: `components/layout/dashboard-shell.tsx`, `components/layout/portal-shell.tsx`.
- Auth: `components/auth/*`, `app/login/page.tsx`, `app/register/page.tsx`.
- Dashboard features: clients, projects, tasks, calendar, finance, journal, analytics, business.
- Portal: portal home, projects, tasks, revisions.
- Loading states: birden fazla loading skeleton dosyası Poyraz `Card`/`CardContent` kullanıyor.
Replacement yönü:
- Atoms doğrudan `components/ui` primitive'lerine taşınacak.
- Molecules içindeki dialog/select/dropdown/tabs gibi davranışlı bileşenler tek internal UI katmanında izole edilecek.
- Organisms shell'leri Faz 3'te Neta shell olarak yeniden yazılacak.
- `toast`/`Toaster` tek internal feedback API'sine taşınacak.
## Internal UI durumu
Mevcut `components/ui` dizininde şu bileşenler var:
- Aktif internal primitive adayları: `button.tsx`, `card.tsx`, `checkbox.tsx`, `dialog.tsx`, `dropdown-menu.tsx`, `form.tsx`, `icon.tsx`, `input.tsx`, `label.tsx`, `select.tsx`, `separator.tsx`, `skeleton.tsx`, `textarea.tsx`, `toast.tsx`, `toaster.tsx`.
- Geçiş yardımcıları: `pending-link.tsx`, `pending-submit-button.tsx`, `offline-indicator.tsx`.
- Not: `components/ui/pending-submit-button.tsx` hâlâ `poyraz-ui/atoms` `Button` import ediyor; UI katmanı tamamen bağımsız değil.
## Dexie/IndexedDB ve PWA baseline
Dexie:
- `lib/db.ts` içinde IndexedDB prototipi var.
- `rg` sonucunda aktif app import'u görünmüyor.
- Tip düzeyi eski task status değeri `completed` içeriyor; yeni task kanoniğiyle uyumsuz.
- Hedef redesign'da kaldırılacak veya Faz 0 sonrası ayrı bir cleanup commit'inde silinecek.
PWA:
- `next.config.ts` içinde `@ducanh2912/next-pwa` aktif.
- Production build'de `public` altına service worker asset'leri üretmesi beklenir.
- `app/layout.tsx` manifest olarak `/manifest.json` tanımlıyor.
- Offline-first veri sync hedef dışı olduğu için ilk self-hosted release'te PWA kaldırma kararı ADR'de sabitlenmiştir.
## Dependency sınıflandırması
Kalacak:
- `next`, `react`, `react-dom`, `typescript`, `eslint`, `eslint-config-next`
- `zod`, `react-hook-form`, `@hookform/resolvers`
- `date-fns`, `clsx`, `tailwind-merge`
- `lucide-react`
- AI provider paketleri, yalnızca server tarafına izole edilmek şartıyla
Kaldırılacak:
- `@supabase/ssr`, `@supabase/supabase-js`: runtime Supabase bağımlılığı kaldırılacak.
- `poyraz-ui`: internal UI ile değiştirilecek.
- `dexie`, `dexie-react-hooks`: aktif akışta kullanılmıyor.
- `@ducanh2912/next-pwa`: ilk self-hosted release'te offline/PWA kapsam dışı.
- `shadcn`: runtime ihtiyacı yoksa kaldırılacak.
- Duplicate/unused Radix veya umbrella `radix-ui` paketleri, internal UI kararından sonra temizlenecek.
Değerlendirilecek:
- `@base-ui/react`: tek headless primitive katmanı olarak kalabilir veya Radix ile karşılaştırılıp kaldırılabilir.
- `@radix-ui/*`: internal UI behavior için seçilecek tek headless katmana göre azaltılacak.
- `framer-motion`: kritik UX değeri yoksa kaldırılacak; varsa reduced-motion standardıyla izole edilecek.
- `recharts`: analytics UI redesign kapsamına göre kalabilir.
- `@iconify/react`: lucide-react yeterliyse kaldırılacak.
- `tailwindcss-animate`: animasyon stratejisine göre değerlendirilecek.
- `uuid` ve `@types/uuid`: Web Crypto UUID yeterliyse kaldırılacak.
## Davranış baseline matrisi
Bu davranışlar yeni sistemde korunacak veya bilinçli olarak iyileştirilecek:
| Akış | Mevcut davranış | Redesign notu |
| --- | --- | --- |
| İlk admin setup | `/register`, `is_first_admin_setup_available`, signup sonrası profile role `freelancer` | Better Auth setup route'u public registration'ı ilk kullanıcıdan sonra kapatacak |
| Login/logout | Supabase `signInWithPassword`, `signOut`, middleware session refresh | Better Auth DB session ve server-side session helper |
| Client portal hesabı | Freelancer route handler üzerinden client auth user yaratıyor | Invite token ve client self-activation tercih edilecek |
| Client CRUD | Server Actions + Supabase RLS | Service/repository owner filter |
| Project CRUD | Server Actions, cover image storage, planning sections | Local storage metadata + transaction boundary |
| Task kanban | Client optimistic-ish state, Server Actions, `done` kanoniği | Transaction içinde auto progress ve rollback testleri |
| Calendar | Server read + Server Actions | Visible range/pagination zorunlu |
| Finance | Server read + Server Actions, decimal amount | Minor unit integer standardı |
| Business docs | Page-level read-only/dummy-ish client UIs | Faz 5'te gerçek mutation ve constraints gözden geçirilecek |
| Journal | `daily_logs` aktif; `journals` legacy | `journal_entries` kanonik hedef |
| Analytics/dashboard | Supabase RPC aggregate | Drizzle/SQL aggregate repository |
| AI chat | Browser Supabase client + `/api/chat` route | Browser DB erişimi kaldırılacak |
| Settings/profile | Browser Supabase client + Server Action mix | Server-only settings service |
| Portal revisions | UI warning var, server quota enforcement eksik | Server action quota ve ownership negatif test |
## Bilinen problem baseline
Regression maddesine çevrilecek mevcut problemler:
- Portal project detail `project_planning_sections` için `order_index` ile order ediyor; schema alanı `sort_order`.
- Project risk route `completed` task status sayıyor; aktif kanonik status `done`.
- Portal project client UI section tipi olarak `type` okuyor; schema alanı `category`.
- Revision quota sadece UI tarafında uyarı olarak var; server action `createRevisionRequest` quota uygulamıyor.
- Portal revision insert policy project/client eşleşmesini tam doğrulamıyor; client kendi `client_id` değeriyle başka projeye request deneyebilir.
- `app/(dashboard)/chat/page.tsx` ve `app/(dashboard)/settings/page.tsx` browser Supabase client ile auth/veri erişimi yapıyor.
- Bazı listeler pagination yerine tüm kullanıcı datasını veya geniş tarih aralığını çekiyor.
- AI API key `app_settings.api_key` içinde düz metin tutuluyor ve settings client'ına geri okunuyor.
## Performans baseline
Local static baseline:
- Dashboard ve analytics aggregate RPC ile payload azaltılmış; ancak bazı detail/list sayfalarında birden fazla Supabase query paralel çalışıyor.
- Client JS yüzeyi ağır: Poyraz UI, motion, charts, AI chat, kanban ve portal client bileşenleri geniş kullanımda.
- `app/api/chat/route.ts` kullanıcı context'i için `tasks`, `projects`, `finance_transactions`, `daily_logs` sorgularında explicit `.limit()` kullanıyor.
- `app/(dashboard)/journal/page.tsx` 180 kayıt limiti kullanıyor.
- `app/(dashboard)/page.tsx` recent projects/clients için 5 kayıt limiti kullanıyor.
- Calendar visible range filtresi yerine current implementation daha geniş veri çekme riski taşıyor; Faz 4'te görünür aralık zorunlu olacak.
Ölçülmemiş production baseline:
- Gerçek TTFB, payload boyutu, route client JS boyutu ve click-to-feedback metrikleri production veya seeded local environment ayağa kaldırılmadan tamamlanmış sayılmaz.
- Bu metrikler Faz 1 spike container'ı ve import rehearsal sonrası tekrar ölçülecek.
## Local doğrulama sonuçları
2026-07-10 local workspace sonuçları:
| Komut | Sonuç | Not |
| --- | --- | --- |
| `npm.cmd run lint` | Başarısız | 34 error, 25 warning. Mevcut baseline: `any`, React `setState-in-effect`, unused import, unescaped entities, `<img>` warnings. Faz 0 doküman değişiklikleri app code değiştirmedi. |
| `.\node_modules\.bin\tsc.cmd --noEmit` | Başarılı | TypeScript typecheck temiz. |
| `npm.cmd run build` | Başarılı | Next.js 16.2.7 Turbopack production build geçti. Edge runtime static generation warning'i mevcut build uyarısı olarak görüldü. |
Local environment:
- Node: `v24.11.1`
- npm: `11.6.2`
- CPU identifier: `Intel64 Family 6 Model 167 Stepping 1, GenuineIntel`
- Logical processor count: `16`
- Disk root: `D:\`
- RAM: WMI/CIM erişimi sandbox içinde reddedildiği için ölçülemedi.
## Faz 0 istisnaları
Production erişimi gerektiren ve bu local çalışmada tamamlanmayan kalemler:
- Production Supabase backup alma ve restore doğrulama.
- Production tablo/status dağılımlarını sayma.
- Storage bucket dosya sayısı, toplam boyut ve orphan path raporu.
- Gerçek kullanıcı akışı ekran kayıtları.
- Gerçek route timing, payload ve bundle ölçümleri.
- Ürün sahibi onayı gereken ADR ve kritik akış matrisi.
@@ -0,0 +1,193 @@
---
title: Phase 0 Data Mapping
description: Supabase kaynak modelinden SQLite/Next.js hedef modeline ilk mapping ve production audit sorguları.
status: active
last_updated: 2026-07-10
---
# Phase 0 Data Mapping
Bu dosya Faz 0 için source-to-target veri mapping taslağıdır. Production verisine bağlanmadan status dağılımı ve gerçek satır sayısı tamamlanamaz; bu nedenle her production-only madde audit sorgularıyla birlikte bırakılmıştır.
## Kanonik veri standartları
- ID: Mevcut UUID değerleri korunacak.
- Para: `numeric(12,2)` kaynak değerleri hedefte integer minor unit olarak saklanacak.
- Tarih: Kullanıcı iş tarihi `YYYY-MM-DD`, timestamp alanları UTC epoch veya ISO storage standardı ile tekleştirilecek.
- Sahiplik: Supabase RLS yerine `owner_user_id`/session role filtreli repository sorguları kullanılacak.
- Client portal: client auth user ile `clients.client_auth_id` ilişkisi hedefte invitation/account relation olarak modellenir.
- File path: Supabase bucket path değerleri hedefte `files` metadata tablosu ve `/app/data/uploads` altındaki relative path ile tutulur.
- AI secrets: `app_settings.api_key` düz metin olarak taşınmaz; yeni sistemde env veya encrypted-at-rest secret store kararı uygulanır.
## Source-to-target mapping
| Supabase kaynak | Mevcut amaç | Hedef model | Karar |
| --- | --- | --- | --- |
| `auth.users` | Supabase auth principal, freelancer/client user | Better Auth user/session/account tabloları | Password/session taşınmayacak; reset veya yeniden davet |
| `profiles` | Ad, avatar, role | `profiles` veya Better Auth user extension | `role: freelancer|client` korunur |
| `clients` | CRM client ve portal auth link | `clients` | `client_auth_id` nullable relation olarak korunur |
| `client_activities` | Client timeline | `client_activities` | Sahiplik `owner_user_id` ile |
| `projects` | Proje kayıtları, progress, quota, cover image | `projects` | `budget_amount` minor unit; cover image file metadata |
| `project_planning_sections` | Project planning content | `project_planning_sections` | `category` ve `sort_order` kanonik |
| `project_revisions` | Portal revision requests | `project_revisions` | Quota ve project/client eşleşmesi service transaction içinde |
| `tasks` | Task/kanban | `tasks` | Status kanoniği `todo|in_progress|done|cancelled`; legacy `completed` normalize edilir |
| `calendar_events` | Takvim | `calendar_events` | Visible range indexleri |
| `finance_transactions` | Gelir/gider | `finance_transactions` | Amount integer minor unit |
| `proposals` | Teklifler | `proposals` | Faz 5'te gerçek mutation ihtiyacı doğrulanacak |
| `contracts` | Sözleşmeler | `contracts` | Faz 5'te gerçek mutation ihtiyacı doğrulanacak |
| `invoices` | Faturalar | `invoices` | Amount/tax minor unit |
| `subscriptions` | Recurring expenses | `subscriptions` | Billing cycle enum korunur |
| `daily_logs` | Aktif journal ekranı | `journal_entries` | `journals` ile birleştirilecek |
| `journals` | Legacy AI journal | `journal_entries` archive/import source | Aynı güne denk gelen kayıtlar merge rule ile |
| `chat_sessions` | AI chat session | `chat_sessions` | Browser DB erişimi kaldırılır |
| `chat_messages` | AI chat messages | `chat_messages` | Message role enum validate edilir |
| `document_embeddings` | pgvector RAG | Archive table veya import dışı | İlk release'te operational vector search yok |
| `app_settings` | Timezone/currency/AI settings | `user_settings` | `api_key` taşınmaz veya sadece masked migration warning |
| `storage.objects` bucket `avatars` | Avatar dosyaları | `files` + `/uploads/avatars` | Public read yerine authorized handler veya static controlled path |
| `storage.objects` bucket `project-assets` | Project cover/assets | `files` + `/uploads/project-assets` | MIME/magic byte/size checks |
| `neta_internal.internal_auth_creations` | Service-role guarded auth creation handshake | Kaldırılır | Better Auth invite/setup flow ile gerek kalmaz |
## Enum/status baseline
Kaynak schema ve koddan görülen status/type değerleri:
- `profiles.role`: `freelancer`, `client`
- `clients.status`: `active`, `paused`, `archived`
- `clients.pipeline_stage`: `lead`, `contacted`, `proposal_sent`, `won`, `lost`
- `projects.type`: `client_project`, `side_project`
- `projects.status`: `planning`, `active`, `paused`, `completed`, `cancelled`
- `projects.progress_type`: `manual`, `auto`
- `tasks.status`: aktif kod `todo`, `in_progress`, `done`; legacy `lib/db.ts` ve portal UI'da `completed` toleransı var
- `tasks.priority`: `low`, `medium`, `high`, `urgent`
- `calendar_events.type`: `meeting`, `focus`, `deadline`, `personal`, `finance`
- `finance_transactions.type`: `income`, `expense`
- `finance_transactions.payment_status`: `planned`, `pending`, `paid`, `cancelled`
- `proposals.status`: `draft`, `sent`, `accepted`, `rejected`
- `contracts.status`: `draft`, `active`, `completed`, `cancelled`
- `invoices.status`: `draft`, `sent`, `paid`, `overdue`, `cancelled`
- `subscriptions.billing_cycle`: `monthly`, `yearly`, `weekly`
- `subscriptions.status`: `active`, `cancelled`
- `client_activities.type`: `note`, `call`, `meeting`, `email`
- `project_planning_sections.category`: `overview`, `problem`, `goal`, `audience`, `scope`, `design_system`, `color_palette`, `typography`, `assets`, `notes`
- `project_revisions.status`: `pending`, `in_progress`, `completed`, `rejected`
## RLS policy matrix
Hedefte RLS yok; bu policy'ler service/repository authorization testlerine çevrilecek:
| Resource | Freelancer policy | Client policy | Hedef test |
| --- | --- | --- | --- |
| `profiles` | Own profile CRUD | Own profile read | Role-based route access and profile owner filter |
| `clients` | Own client CRUD | Own linked client read | Cross-owner UUID reject |
| `projects` | Own project CRUD | Linked client projects read | Client cannot read unrelated project |
| `tasks` | Own task CRUD | Public tasks of linked projects read | Client cannot see private task |
| `project_planning_sections` | Own section CRUD | Sections of linked projects read | Category/sort order query |
| `project_revisions` | Manage project revisions | Insert/view own revisions | Quota and project/client relation |
| `calendar_events` | Own event CRUD | None | Client role reject |
| `finance_transactions` | Own transaction CRUD | None | Client role reject |
| `daily_logs`/`journals` | Own journal CRUD | None | Client role reject |
| `chat_sessions/messages` | Own chat CRUD | None | Session owner filter |
| `document_embeddings` | Own embedding CRUD | None | First release archive/no runtime search |
| `app_settings` | Own settings CRUD | None | Secret never returned to client |
| `storage.objects/avatars` | Public select, owner write | None | Authorized upload and safe public avatar URL |
| `storage.objects/project-assets` | Owner/service-role path access | Portal read via project relation | File route validates owner/project relation |
## Production audit sorguları
Bu sorgular Supabase SQL Editor veya read-only connection ile çalıştırılmalı. Secret değerleri loglanmamalı.
```sql
select 'profiles' as table_name, count(*) from public.profiles
union all select 'clients', count(*) from public.clients
union all select 'projects', count(*) from public.projects
union all select 'tasks', count(*) from public.tasks
union all select 'calendar_events', count(*) from public.calendar_events
union all select 'finance_transactions', count(*) from public.finance_transactions
union all select 'daily_logs', count(*) from public.daily_logs
union all select 'journals', count(*) from public.journals
union all select 'project_planning_sections', count(*) from public.project_planning_sections
union all select 'project_revisions', count(*) from public.project_revisions
union all select 'chat_sessions', count(*) from public.chat_sessions
union all select 'chat_messages', count(*) from public.chat_messages
union all select 'document_embeddings', count(*) from public.document_embeddings
union all select 'app_settings', count(*) from public.app_settings;
select role, count(*) from public.profiles group by role order by role;
select status, count(*) from public.clients group by status order by status;
select pipeline_stage, count(*) from public.clients group by pipeline_stage order by pipeline_stage;
select status, type, progress_type, count(*) from public.projects group by status, type, progress_type order by status, type, progress_type;
select status, priority, count(*) from public.tasks group by status, priority order by status, priority;
select type, count(*) from public.calendar_events group by type order by type;
select type, currency, payment_status, count(*) from public.finance_transactions group by type, currency, payment_status order by type, currency, payment_status;
select status, count(*) from public.proposals group by status order by status;
select status, count(*) from public.contracts group by status order by status;
select status, count(*) from public.invoices group by status order by status;
select billing_cycle, status, count(*) from public.subscriptions group by billing_cycle, status order by billing_cycle, status;
select category, count(*) from public.project_planning_sections group by category order by category;
select status, count(*) from public.project_revisions group by status order by status;
select count(*) filter (where api_key is not null and btrim(api_key) <> '') as settings_with_api_key from public.app_settings;
```
Storage audit:
```sql
select bucket_id, count(*) as file_count, coalesce(sum(metadata->>'size')::bigint, 0) as total_size
from storage.objects
where bucket_id in ('avatars', 'project-assets')
group by bucket_id
order by bucket_id;
select p.id, p.avatar_url
from public.profiles p
where p.avatar_url is not null and p.avatar_url <> '';
select p.id, p.cover_image_path
from public.projects p
where p.cover_image_path is not null and p.cover_image_path <> '';
```
Constraint/index/policy audit:
```sql
select conrelid::regclass as table_name, conname, contype, pg_get_constraintdef(oid) as definition
from pg_constraint
where connamespace in ('public'::regnamespace)
order by table_name::text, conname;
select schemaname, tablename, indexname, indexdef
from pg_indexes
where schemaname in ('public', 'neta_internal')
order by schemaname, tablename, indexname;
select schemaname, tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname in ('public', 'storage')
order by schemaname, tablename, policyname;
```
## Merge kuralları
`journals` + `daily_logs`:
- Hedef tablo adı: `journal_entries`.
- Aynı user + aynı date/log_date günü için önce `daily_logs` kanonik kabul edilir.
- `journals.content` doluysa `journal_entries.note` içine conflict suffix veya related legacy note olarak eklenir.
- `journals.ai_*` alanları ilk release'te ayrı `journal_ai_metadata` JSON alanına veya archive tablosuna taşınır; runtime AI analiz için şart değildir.
Task status:
- `completed` değerleri import sırasında `done` olarak normalize edilir.
- Tanınmayan status varsa import fail eder; silent fallback yok.
Planning section:
- Kaynak `category` korunur.
- UI'da görülen `type` referansı import alanı değildir.
- Kaynak `sort_order` korunur.
- `order_index` hiçbir hedef query'de kullanılmaz.
Money:
- `amount numeric(12,2)` ve `budget_amount numeric(12,2)` hedefte `amount_minor integer` veya `budget_amount_minor integer`.
- Currency her row'da ISO-like text olarak korunur; kullanıcı default currency migration sırasında sadece fallback.
@@ -0,0 +1,85 @@
---
title: Phase 0 Fixture Strategy
description: Self-hosted redesign boyunca kullanılacak küçük ve stres veri setlerinin kapsamı.
status: active
last_updated: 2026-07-10
---
# Phase 0 Fixture Strategy
Faz 0'da hedef DB schema henüz oluşmadığı için kesin SQLite seed dosyası üretilmez. Bu dosya, import rehearsal ve Faz 1-5 testlerinde kullanılacak küçük/stres veri setlerinin kapsamını sabitler. İlk gerçek seed, Drizzle schema oluştuktan sonra `scripts/seed.mjs` veya TypeScript seed runner ile üretilecek.
## Mevcut seed referansı
Kaynak:
- `supabase/seeds/0001_demo_freelancer_os_data.sql`
- `supabase/seed.sql`
Bu seed'ler Supabase schema için referanstır; hedef SQLite seed'i doğrudan bu dosyaları çalıştırmayacak.
## Küçük fixture kapsamı
Amaç: Core smoke ve regression testleri hızlı çalışsın.
Önerilen içerik:
- 1 freelancer user
- 2 clients
- 1 portal client user
- 3 projects: `planning`, `active`, `completed`
- 1 project `progress_type=auto`
- 8 tasks: `todo`, `in_progress`, `done`, private/public portal task karışık
- 4 calendar events: meeting/focus/deadline/finance
- 6 finance transactions: income/expense ve payment status çeşitleri
- 2 daily journal entries
- 2 planning sections: `overview`, `scope`, sıralı `sort_order`
- 2 revision requests: `pending`, `completed`
- 1 avatar metadata kaydı
- 1 project cover file metadata kaydı
- 1 chat session ve 2 message
Bu fixture REG-001, REG-002, REG-003, REG-004, REG-005 ve cross-owner negatif testleri desteklemeli.
## Stres fixture kapsamı
Amaç: Dashboard/list query performansını ve pagination zorunluluğunu test etmek.
Önerilen içerik:
- 1 freelancer user
- 100 clients
- 250 projects
- 2,500 tasks
- 1,000 calendar events
- 2,000 finance transactions
- 365 journal entries
- 1,000 chat messages
- 500 planning sections
- 250 revisions
- 500 file metadata row
Performans hedefleri:
- Dashboard DB aggregate hedefi: 50 ms altı, referans makine ve fixture ile.
- Core mutation DB bölümü: 100 ms altı.
- Liste endpoint/page payload'ı pagination veya visible range ile bounded kalmalı.
## Production import fixture
Cutover öncesi en az iki import rehearsal yapılacak:
- Rehearsal 1: Supabase export snapshot'ından local SQLite import.
- Rehearsal 2: Fresh export snapshot'ından temiz volume import + smoke.
Her rehearsal çıktısı:
- Export timestamp.
- Source row counts.
- Target row counts.
- Skipped/archived rows.
- Normalization warnings.
- File checksum manifest.
- Restore command.
- Smoke test sonucu.
@@ -0,0 +1,81 @@
---
title: Phase 0 Regression and Phase 1 Spike Criteria
description: Mevcut hatalardan türetilen regression testleri ve Faz 1 runtime spike kabul kriterleri.
status: active
last_updated: 2026-07-10
---
# Phase 0 Regression and Phase 1 Spike Criteria
## Regression test backlog
Bu maddeler davranış paritesi değildir; redesign içinde düzeltilmesi zorunlu eski problem testleridir.
| ID | Problem | Test tanımı | Beklenen sonuç |
| --- | --- | --- | --- |
| REG-001 | Portal planning query `order_index` kullanıyor | Portal project detail planning sections ordered query çalıştırılır | Query `sort_order` kullanır; missing column hatası yok |
| REG-002 | Project risk route `completed` sayıyor | `done` task içeren project için risk analysis çalıştırılır | Completed count `done` taskları sayar |
| REG-003 | Portal UI `section.type` okuyor | `category: scope` olan section portalda render edilir | Label/category doğru görünür, undefined fallback yok |
| REG-004 | Revision quota server'da uygulanmıyor | Quota dolu project için portal revision request gönderilir | Server action reject eder; DB insert olmaz |
| REG-005 | Revision insert project/client ilişkisini tam doğrulamıyor | Client A kendi `client_id` ile Client B project id'sine request dener | Authorization error; DB insert olmaz |
| REG-006 | Browser Supabase client DB erişimi | Chat/settings ekranında client bundle import analizi yapılır | Browser DB/auth SDK import yok |
| REG-007 | Settings API key client'a dönüyor | Settings page data contract incelenir | Secret value response payload'da yok, sadece masked/existence flag var |
| REG-008 | Limitsiz listeler payload büyütüyor | Projects/tasks/clients/calendar list query contract incelenir | Pagination veya visible date range zorunlu |
| REG-009 | Auto progress transaction dışı kalabilir | Task status değişimi auto progress project'te yapılır | Task update ve project progress aynı transaction sonucu |
| REG-010 | Cross-owner UUID erişimi | Başka owner'a ait project/task/client UUID ile query/mutation yapılır | 404 veya authorization error; veri dönmez |
## Behavior smoke matrix
Faz 0 sırasında ekran kaydı alınması gereken kritik akışlar:
| Akış | Kayıt adı önerisi | Kapsam |
| --- | --- | --- |
| İlk admin setup | `setup-first-admin` | `/register`, signup, dashboard redirect |
| Login/logout | `auth-login-logout` | Login, protected route, sign out |
| Client portal hesabı | `client-user-create` | Freelancer client user creation route |
| Client CRUD | `clients-crud` | Create, edit, archive, pipeline stage |
| Project CRUD/planning | `projects-crud-planning` | Create, cover image, planning section, progress settings |
| Task kanban | `tasks-kanban` | Create, drag/status update, complete, delete |
| Calendar | `calendar-crud` | Create/edit/delete, date range |
| Finance | `finance-crud-filter` | Create income/expense, update payment status |
| Business docs | `business-docs` | Proposal/invoice/contract/subscription current behavior |
| Journal | `journal-crud` | Create/edit/delete daily log |
| Analytics | `analytics-range` | Date filter and chart result |
| AI chat | `chat-session-message` | Session create/delete, message send |
| AI analysis | `ai-analysis` | Finance analysis and project risk |
| Settings | `settings-profile-secret` | Avatar, profile, password, API key |
| Portal | `portal-project-revision` | Project, public tasks, revision request |
## Faz 1 spike kabul kriterleri
Faz 1'e başlandığında ilk teknik spike feature taşımaz. Sadece runtime kararlarını kanıtlar.
Başarı kriterleri:
- Next.js standalone production build alınır.
- Tek container boot eder.
- `DATA_DIR` altında SQLite DB ve uploads dizini oluşur.
- Drizzle migration sıfır DB'ye uygulanır.
- Migration ikinci kez çalıştırıldığında schema bozulmaz.
- `better-sqlite3` native module production image içinde çalışır.
- `/api/health/live` process ayakta olduğu sürece 200 döner.
- `/api/health/ready` DB yazılabilir değilse unhealthy döner.
- Container restart sonrası sample row korunur.
- Non-root runtime ile `/app/data` yazılabilir.
- Backup proof-of-concept sample DB ve uploads manifest'i üretir.
- `lint`, typecheck ve production build geçer.
Başarısızlık kriterleri:
- Native SQLite module image içinde yüklenemiyor.
- Restart veri kaybettiriyor.
- Migration request sırasında çalışıyor veya idempotent değil.
- Readiness DB bozuk/yazılamaz durumda healthy dönüyor.
- Tek persistent volume dışında zorunlu servis ihtiyacı çıkıyor.
Rollback:
- Faz 1 spike production'a deploy edilmez.
- Eski Supabase app untouched kalır.
- Spike branch silinebilir veya ADR yeniden açılır.
@@ -0,0 +1,125 @@
---
title: Phase 1 Runtime, SQLite and Deploy Skeleton
description: Faz 1 kapsamında eklenen Next.js standalone, SQLite, Drizzle migration, health, Docker ve backup/restore kanıtları.
status: active
last_updated: 2026-07-10
---
# Phase 1 Runtime, SQLite and Deploy Skeleton
Bu dosya Faz 1 için uygulanan runtime iskeletini ve local doğrulama sonuçlarını kaydeder. Bu faz feature migration yapmaz; Supabase ve Poyraz UI runtime'ı henüz kaldırılmadı.
## Eklenen runtime yüzeyi
- `server/config.ts`: Zod ile server config parse eder, `DATA_DIR` ve `DATABASE_PATH` yollarını çözer.
- `server/db/client.ts`: `better-sqlite3` singleton connection, Drizzle client ve SQLite PRAGMA ayarlarını yönetir.
- `server/db/transaction.ts`: nested çağrılarda yeni transaction açmadan aynı connection üzerinde çalışan synchronous transaction helper.
- `server/db/health.ts`: readiness için data dir yazılabilirlik, `SELECT 1` ve migration table kontrolü.
- `server/db/schema/*`: Faz 1 runtime smoke tabloları.
- `server/db/migrations/*`: Drizzle tarafından üretilen ilk SQLite migration ve journal metadata.
- `app/api/health/live/route.ts`: process liveness endpoint'i.
- `app/api/health/ready/route.ts`: DB/data/migration readiness endpoint'i.
- `scripts/migrate.mjs`: production startup öncesi migration runner.
- `scripts/backup.mjs`: SQLite backup API kullanan backup POC.
- `scripts/restore.mjs`: overwrite guard içeren restore POC.
- `scripts/phase1-smoke.mjs`: migration, persistence, backup ve restore smoke.
- `Dockerfile`: multi-stage Next standalone image.
- `docker-compose.yml`: tek app service, named volume ve readiness healthcheck.
## Config davranışı
- `DATA_DIR` verilmezse development/test için `.data`, production için `/app/data` kullanılır.
- `DATABASE_PATH` verilmezse `DATA_DIR/neta.db` kullanılır.
- Relative path değerleri `process.cwd()` üzerinden absolute hale getirilir.
- Uygulama `data`, `uploads`, `backups` ve `tmp` dizinlerini kontrollü oluşturur.
- Secret değerler config validation çıktısına yazılmaz; mevcut config modülü boot sırasında değer dump etmez.
## SQLite davranışı
Connection açılırken uygulanan PRAGMA değerleri:
- `foreign_keys = ON`
- `journal_mode = WAL`
- `synchronous = NORMAL`
- `busy_timeout = 5000`
Connection `globalThis.__netaSqliteConnection` ile process içinde singleton tutulur. Development hot reload sırasında aynı process içinde duplicate connection oluşmaması hedeflenir.
## Migration davranışı
İlk migration:
- `server/db/migrations/0000_wise_reaper.sql`
- Tablolar: `runtime_checks`, `runtime_events`
- Journal: `server/db/migrations/meta/_journal.json`
Migration runner request sırasında çalışmaz. Docker startup komutu önce `node scripts/migrate.mjs`, sonra `node server.js` çalıştırır.
## Health endpoint sonuçları
Local dev server:
- Komut: `npm.cmd run dev -- --hostname 127.0.0.1 --port 3010`
- `/api/health/live`: HTTP 200, `{"status":"ok", ...}`
- `/api/health/ready`: HTTP 200, `dataDirWritable=true`, `databaseReachable=true`, `migrationsApplied=true`
Health response DB path, data path, schema path veya secret döndürmez.
## Doğrulama sonuçları
2026-07-10 local sonuçları:
| Komut | Sonuç | Not |
| --- | --- | --- |
| `npm.cmd run db:generate` | Başarılı | İlk Drizzle migration üretildi. |
| `npm.cmd run db:migrate` | Başarılı | `.data/neta.db` üzerine migration uygulandı. |
| `npm.cmd run db:migrate` ikinci çalıştırma | Başarılı | Aynı DB üzerinde idempotency smoke geçti. |
| `npm.cmd run phase1:smoke` | Başarılı | Temp data dir, migration, persistence, backup ve restore geçti. |
| `npm.cmd run typecheck` | Başarılı | TypeScript temiz. |
| `npm.cmd run build` | Başarılı | Next.js production build geçti, standalone output etkin. |
| `npm.cmd run lint` | Başarısız | Faz 0 baseline ile aynı 34 error, 25 warning; yeni Faz 1 dosyaları lint çıktısında görünmedi. |
| `docker compose build` | Başarısız | Docker Desktop/Linux engine çalışmıyor: daemon pipe bulunamadı. Docker smoke henüz doğrulanmadı. |
## Backup/restore POC
`scripts/backup.mjs`:
- SQLite `backup()` API kullanır.
- Backup klasörü `DATA_DIR/backups/neta-<timestamp>` altında oluşur.
- `neta.db`, `uploads/` kopyası ve `manifest.json` üretir.
- Manifest her dosya için byte ve SHA-256 içerir.
`scripts/restore.mjs`:
- `--from <backup-dir>` zorunlu.
- `--target <data-dir>` ile ayrı restore hedefi verilebilir.
- Var olan DB üzerine yazmak için `--force` gerekir.
Eksik kalan restore sertliği:
- Restore manifest checksum doğrulaması henüz uygulanmadı.
- Docker içinde native SQLite smoke henüz çalıştırılamadı.
## Docker durumu
Dockerfile kararları:
- `node:22-bookworm-slim`
- `npm ci`
- `next.config.ts` içinde `output: "standalone"`
- Runtime user: `nextjs`
- Persistent volume: `/app/data`
- Startup: migration, sonra standalone `server.js`
Compose kararları:
- Tek application service: `neta`
- Named volume: `neta-data:/app/data`
- Healthcheck: `/api/health/ready`
- Restart policy: `unless-stopped`
- Replica sayısı tanımlı değil; hedef single instance.
Docker doğrulaması açık istisna:
- Local Docker daemon çalışmadığı için `docker compose build`, `docker compose up`, native SQLite Linux runtime ve container restart persistence testleri yapılamadı.
+39
View File
@@ -0,0 +1,39 @@
# Phase 2 Auth Implementation Notes
## Kapsam
Faz 2'de Supabase Auth yerine Better Auth + Drizzle SQLite temelli ilk auth katmanı eklendi. Bu faz, veri ekranlarının tamamını Supabase'ten taşımıyor; koruma noktalarını ve yeni session contract'ını hazır hale getiriyor.
## Eklenen runtime parçaları
- `server/auth/auth.ts`: Better Auth server-only config.
- `app/api/auth/[...all]/route.ts`: Better Auth GET/POST Route Handler.
- `server/db/schema/auth.ts`: Better Auth auth tabloları, Neta profile tablosu, setup lock, portal invitation ve audit log tabloları.
- `server/auth/session.ts`: request içi memoize edilen `getSessionContext`, `requireSession`, `requireFreelancer`, `requireClientUser`.
- `server/auth/setup.ts`: ilk freelancer setup durumu, atomic setup guard ve audit yazımı.
- `server/auth/authorization.ts`: role ve owner assertion helper'ları.
## Güvenlik kararları
- `BETTER_AUTH_SECRET` production runtime'da zorunludur. Build sırasında placeholder kullanılır; runtime'da env yoksa uygulama hata verir.
- `TRUSTED_ORIGINS` wildcard kabul etmez.
- Auth cookie'leri production'da `Secure`, tüm ortamlarda `HttpOnly`, `SameSite=Lax`, `Path=/` ayarlarıyla üretilir.
- Public sign-up endpoint'i `databaseHooks.user.create.before` ile ilk freelancer setup guard'ına bağlıdır. İlk freelancer oluştuktan sonra doğrudan `/api/auth/sign-up/email` çağrısı da kullanıcı oluşturamaz.
- Login hatası genel mesaj döndürür; email varlığı sızdırılmaz.
## Bilinen sınırlar
- Portal client kullanıcı üretimi bu fazda sadece token modeli seviyesindedir; gerçek client invitation tüketimi sonraki veri/API fazında tamamlanacak.
- Eski dashboard ve portal feature sayfalarının veri sorguları hâlâ Supabase kullanıyor. Layout koruması Better Auth'a taşındı, veri okuma/yazma Faz 4-6 kapsamındadır.
- Reverse proxy/TLS altında cookie testi Docker daemon çalışmadığı için bu turda kapatılmadı.
## Doğrulama
- `npm run db:generate`
- `npm run db:migrate`
- `npm run phase2:smoke`
- `npm run typecheck`
- `npm run build`
- `npm run lint`
`npm run lint` mevcut proje baseline'ındaki eski hatalar nedeniyle başarısız kalabilir; Faz 2 dosyalarında yeni lint bulgusu bırakılmamalıdır.