Files
neta/docs/self-hosted-redesign/phase-0-data-mapping.md
T

13 KiB
Raw Blame History

title, description, status, last_updated
title description status last_updated
Phase 0 Data Mapping Supabase kaynak modelinden SQLite/Next.js hedef modeline onaylı mapping ve production audit sorguları. active 2026-07-16

Phase 0 Data Mapping

Bu dosya Faz 0 için onaylı source-to-target veri mapping'idir. 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. Alan düzeyi nihai Drizzle schema Faz 2'de üretilecek, ancak tablo kaderi ve normalization kuralları bu belgeyle kilitlenmiştir.

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 app_profiles `role: freelancer
clients CRM client ve portal auth link clients auth_user_id nullable; invitation kabulünde transaction ile kurulur
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
calendar_events Takvim calendar_events Visible range indexleri
finance_transactions Gelir/gider finance_transactions Amount integer minor unit
proposals Teklifler proposals veya açık archive eşleniği Veri korunur; UI/CRUD release-blocker değildir
contracts Sözleşmeler contracts veya açık archive eşleniği Veri korunur; UI/CRUD release-blocker değildir
invoices Faturalar invoices veya açık archive eşleniği Amount/tax minor unit; UI/CRUD release-blocker değildir
subscriptions Recurring expenses subscriptions veya açık archive eşleniği Billing cycle enum korunur; UI/CRUD release-blocker değildir
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_preferences + server-side AI secret config api_key taşınmaz; yalnızca migration warning/count
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

Hedefte kaynaktan türemeyen yeni tablolar:

Hedef model Amaç Faz 0 kararı
instance_settings Instance adı, support ve genel davranış Singleton instance config
instance_branding Logo, renk, mode ve radius ayarları Poyraz UI semantic token girdisi
user_preferences Tema tercihi, timezone, currency ve UI tercihleri Kullanıcı bazlı; secret içermez
files Yerel dosya metadata ve güvenli relative path İçerik /app/data/uploads altında
portal_invitations Süreli ve hash'lenmiş client invite token Better Auth client self-activation
auth_audit_events Setup/login/logout güvenlik olayları Mevcut SQLite model korunur

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ı.

2026-07-16 durumu: workspace'te yalnızca .env.example bulunduğu için bu sorgular production üzerinde çalıştırılmamıştır. Kaynak schema envanteri 19 ürün/public tablo, 1 internal tablo ve 2 storage bucket olarak doğrulanmıştır; gerçek satır/dosya sayıları açık kalmıştır.

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:

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:

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.