sozsoft-platform/README.md

2297 lines
180 KiB
Markdown
Raw Normal View History

# Sozsoft Platform
2026-06-09 12:54:05 +00:00
**Çalışma zamanında yapılandırılabilen, çok kiracılı (multi-tenant) low-code uygulama motoru.**
2026-06-09 12:54:05 +00:00
Sozsoft Platform, klasik bir "kod yaz–derle–yayınla" uygulaması değildir. Ekranlar, menüler,
route'lar, yetkiler, veri kaynakları, iş akışları ve hatta API endpoint'leri **veritabanındaki
konfigürasyon kayıtlarından** üretilir. Yeni bir modül veya ekran eklemek çoğu durumda tek satır
React/C# kodu yazmadan, yalnızca konfigürasyon ile mümkündür.
2026-06-09 12:54:05 +00:00
> **Temel ilke:** Configuration first, code last.
> Bu ilkenin operasyonel karşılığı ve yapay zeka ajanları için bağlayıcı kurallar
> [`.github/instructions/ai.instructions.md`](.github/instructions/ai.instructions.md) dosyasındadır.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## İçindekiler
1. [Platform Ne Yapar?](#1-platform-ne-yapar)
2. [Mimari Genel Bakış](#2-mimari-genel-bakış)
3. [Teknoloji Yığını](#3-teknoloji-yığını)
4. [Depo Yapısı](#4-depo-yapısı)
5. [Hızlı Başlangıç](#5-hızlı-başlangıç)
6. [Yapılandırma (Configuration)](#6-yapılandırma-configuration)
7. [Low-Code Motoru: ListForm Ekosistemi](#7-low-code-motoru-listform-ekosistemi)
8. [Developer Kit](#8-developer-kit)
9. [Dinamik Menü, Route ve Yetki Modeli](#9-dinamik-menü-route-ve-yetki-modeli)
10. [Modül Kataloğu](#10-modül-kataloğu)
11. [Kimlik, Oturum ve Güvenlik](#11-kimlik-oturum-ve-güvenlik)
12. [Çok Kiracılılık (Multi-Tenancy)](#12-çok-kiracılılık-multi-tenancy)
13. [Bildirim ve Entegrasyonlar](#13-bildirim-ve-entegrasyonlar)
14. [Arka Plan İşleri](#14-arka-plan-işleri)
15. [Raporlama](#15-raporlama)
16. [Dosya Yönetimi ve CDN](#16-dosya-yönetimi-ve-cdn)
17. [Gerçek Zamanlı Özellikler](#17-gerçek-zamanlı-özellikler)
18. [Frontend Mimarisi](#18-frontend-mimarisi)
19. [API ve Swagger](#19-api-ve-swagger)
20. [Dağıtım (Deployment)](#20-dağıtım-deployment)
21. [Geliştirme Standartları ve Sık Kullanılan Komutlar](#21-geliştirme-standartları-ve-sık-kullanılan-komutlar)
22. [Sorun Giderme](#22-sorun-giderme)
23. [Bilinen Sınırlar ve Notlar](#23-bilinen-sınırlar-ve-notlar)
---
## 1. Platform Ne Yapar?
| Yetenek | Açıklama |
| --- | --- |
| **Dinamik liste/form ekranları** | `ListForm` + `ListFormField` kayıtlarıyla tanımlanan; grid, pivot, ağaç, grafik, gantt, scheduler, kart ve todo görünümlerini destekleyen ekranlar. |
| **Dinamik menü ve route** | Menüler ve route'lar veritabanından okunur; frontend bunları çalışma zamanında React Router'a bağlar. |
| **Yetki tabanlı görünürlük** | Menü, ekran, sütun ve buton seviyesinde ABP permission kontrolü. |
| **SQL Query Manager** | Tarayıcı içinden SQL nesne gezgini, sorgu editörü, sonuç grid'i ve tablo tasarımcısı. |
| **Custom / CRUD Endpoint** | Kod yazmadan, SQL veya tablo tanımından REST endpoint üretimi. |
| **Dynamic Service** | Roslyn ile çalışma zamanında derlenen ve controller olarak kaydedilen C# ApplicationService'ler. |
| **Custom Component** | Babel ile tarayıcıda derlenen, veritabanında saklanan React bileşenleri ve bunların route'ları. |
| **İş akışı / onay** | ListForm üzerinde görsel workflow tasarımcısı, koşullu dallanma ve çok kişili onay adımları. |
| **Çok kiracılılık** | Tenant başına ayrı bağlantı dizesi (ayrı veritabanı) desteği, şube (branch) ve organizasyon birimi kırılımı. |
2026-09-24 07:24:48 +00:00
| **Bildirim ve entegrasyon** | SMS, e-posta, WhatsApp, Telegram, Rocket.Chat, masaüstü ve UI kanallarına kural tabanlı bildirim. |
| **Raporlama** | DevExpress Report Designer/Viewer + ListForm'dan otomatik üretilen dinamik raporlar. |
| **Arka plan işleri** | Hangfire + ABP Background Worker; zamanlanmış SQL, mail kuyruğu, yedekleme, oturum temizliği. |
| **Gerçek zamanlı** | SignalR tabanlı messenger ve video oda (WebRTC/TURN) altyapısı. |
| **Intranet & Forum** | Duyuru, anket, sosyal duvar, etkinlik; kategori/konu/gönderi tabanlı forum. |
| **Public web sitesi** | Menüsü ve sayfaları veritabanından yönetilen ziyaretçi sitesi: sayfalar Custom Component, menü `Pub` modülü (bkz. §10.4). Ürün/ödeme ve demo akışları fiziksel sayfalardır. |
| **PWA** | Service worker ile offline kabuk, kontrollü sürüm güncellemesi ve changelog bildirimi. |
---
## 2. Mimari Genel Bakış
```
┌──────────────────────────────────────────────────────────────────────┐
│ UI — React 19 + Vite + TypeScript + DevExtreme + Tailwind (PWA) │
│ ├─ DynamicRouter (route kayıtlarından React Router üretimi) │
│ ├─ ListForm runtime (Grid / Pivot / Tree / Chart / Gantt / ...) │
│ ├─ Form runtime (DevExtreme Form, metadata ile alan üretimi)│
│ ├─ Developer Kit UI (SQL, endpoint, component, dynamic service) │
│ └─ easy-peasy store + React Query + SignalR │
└───────────────┬──────────────────────────────────────────────────────┘
│ HTTPS / OAuth2 (OpenIddict) / SignalR
┌───────────────▼──────────────────────────────────────────────────────┐
│ API — ASP.NET Core (.NET 10) + ABP Framework 10 │
│ ├─ HttpApi.Host : Swagger, OpenIddict, Hangfire, SignalR Hub, │
│ │ DevExpress Reporting, Setup/Migration akışı │
│ ├─ Application : ListForms, DeveloperKit, Identity, Tenants, │
│ │ Menu, Routes, DataSource, FileManagement, ... │
│ ├─ Domain : Entity'ler, dinamik veri erişimi, seed'ler │
│ └─ EF Core : Migration'lar, dinamik DbContext, tenant DB │
│ │
│ Modüller: Languages · Settings · Notifications · MailQueue · │
│ Sender · SqlQueryManager │
└───────────────┬──────────────────────────────────────────────────────┘
│
┌───────────────▼──────────────────────────────────────────────────────┐
│ Veri ve Altyapı │
│ SQL Server / PostgreSQL · Redis (cache + SignalR backplane) · │
│ Hangfire (job storage) · Dosya sistemi tabanlı BLOB/CDN · │
│ coturn (TURN) · nginx · Elasticsearch (opsiyonel) │
└──────────────────────────────────────────────────────────────────────┘
```
**Akışın özeti:** Kullanıcı giriş yapar → ABP `application-configuration` yanıtına
`PlatformApplicationConfigurationContributor` menüleri (`extraProperties.menus`: `public` ziyaretçi
menüsü, `protected` kullanıcının yetkili yönetim navigasyonu — ayrı bir `/api/app/menu` isteği yoktur,
süzgeç `MenuNavigationProvider`), route ve yetkileri ekler → `DynamicRouter`
route kayıtlarını React bileşenlerine bağlar → `/admin/list/{ListFormCode}` gibi bir ekran
açıldığında `ListFormsAppService` metadata'yı, `ListFormDataAppService` ise veriyi döner →
DevExtreme bileşenleri bu metadata'ya göre kendini kurar.
---
## 3. Teknoloji Yığını
### Backend
| Bileşen | Sürüm / Notlar |
| --- | --- |
| .NET | `net10.0` (SDK 10.0.300, `global.json` ile sabit) |
| ABP Framework | 10.0.0 (Identity, TenantManagement, PermissionManagement, AuditLogging, OpenIddict, BlobStoring, BackgroundWorkers) |
2026-10-17 18:51:10 +00:00
| Veritabanı | SQL Server 2022 veya PostgreSQL 17 (`SettingsConsts.DefaultDatabaseProvider`) |
| ORM | Entity Framework Core |
| Kimlik | OpenIddict (password + refresh token, `/connect/token`) |
| Cache | Redis (opsiyonel; `Redis:IsEnabled`) |
| Job | Hangfire (`default`, `platform` kuyrukları) + ABP Background Workers |
| Raporlama | DevExpress Reporting (Web Document Viewer + Report Designer) |
| Log | Serilog |
| Dinamik derleme | Roslyn (`DynamicServiceCompiler`) |
| Şablon | Scriban (mail şablonları), MailKit/MimeKit |
| Gerçek zamanlı | SignalR (Redis backplane destekli) |
### Frontend
| Bileşen | Sürüm / Notlar |
| --- | --- |
2026-10-16 21:13:31 +00:00
| React | 19.3 |
| Build | Vite 8 + TypeScript 7.0 (native `tsc`), lint: oxlint |
| UI kütüphaneleri | DevExtreme 26.1 (React), Tailwind CSS 4.3 (`@tailwindcss/vite`, CSS-first yapılandırma), kendi `components/ui` tasarım sistemi |
| Routing | react-router-dom 7 (dinamik route üretimi) |
2026-10-16 21:13:31 +00:00
| State | easy-peasy (global store) |
| Form | Formik + Yup, DevExtreme Form |
| Kod editörü | Monaco Editor |
2026-10-16 21:13:31 +00:00
| Runtime derleme | @babel/standalone 8 (Custom Component; JSX `classic` runtime) |
| Takvim/Gantt | DevExtreme Scheduler/Gantt |
| Gerçek zamanlı | @microsoft/signalr |
2026-10-16 21:13:31 +00:00
| PWA | vite-plugin-pwa 2 (Workbox 7) |
| Node | 24.22 / npm 12 (`volta` ile sabitlenmiş; kurulum betikleri `allowScripts` ile yönetilir) |
---
## 4. Depo Yapısı
```
sozsoft-platform/
├── api/ # .NET çözümü (Sozsoft.Platform.sln)
│ ├── src/
│ │ ├── Sozsoft.Platform.Domain.Shared/ # Sabitler, enum'lar, permission kodları (AppCodes)
│ │ ├── Sozsoft.Platform.Domain/ # Entity'ler, dinamik veri erişimi, repository'ler
│ │ ├── Sozsoft.Platform.Application.Contracts/ # DTO'lar, servis arayüzleri, permission tanımları
│ │ ├── Sozsoft.Platform.Application/ # Uygulama servisleri (iş mantığı)
│ │ ├── Sozsoft.Platform.EntityFrameworkCore/ # DbContext, migration'lar, dinamik veri katmanı
│ │ ├── Sozsoft.Platform.HttpApi/ # Controller'lar (conventional)
│ │ ├── Sozsoft.Platform.HttpApi.Host/ # Host: Swagger, auth, Hangfire, SignalR, raporlama
│ │ └── Sozsoft.Platform.DbMigrator/ # Migration + seed uygulaması
│ ├── modules/ # Bağımsız ABP modülleri
│ │ ├── Sozsoft.Languages/ # Dil, dil anahtarı, çeviri metinleri
│ │ ├── Sozsoft.Settings/ # Ayar tanımları ve ayar UI'ı
│ │ ├── Sozsoft.Notifications/ # Bildirim tipleri, kuralları, kullanıcı bildirimleri
│ │ ├── Sozsoft.MailQueue/ # Şablonlu mail kuyruğu ve ekleri
│ │ ├── Sozsoft.Sender/ # Mail (SES), SMS, WhatsApp, Rocket.Chat göndericileri
2026-09-17 09:01:14 +00:00
│ │ ├── Sozsoft.SqlQueryManager/ # SQL nesne yönetimi ve sorgu çalıştırma
2026-09-18 13:47:23 +00:00
│ │ └── Sozsoft.Mcp/ # MCP sunucusu: /mcp ucu, şema keşfi, sorgu ve geliştirme araçları
│ └── test/ # Test projeleri
├── ui/ # React uygulaması
│ └── src/
│ ├── routes/ # dynamicRouter, dynamicRouteLoader, route.constant
2026-10-17 18:51:10 +00:00
│ ├── views/ # list, form, admin, developerKit, public, intranet, forum, report, auth, setup, settings, menu, branch, purchasing, ai, version, shared
│ ├── components/ # ui (tasarım sistemi), template (layout), importManager, visualDesigner, componentEditor
│ ├── services/ # API istemcileri (her modül için ayrı servis dosyası)
│ ├── store/ # easy-peasy modelleri (auth, theme, locale, abpConfig, admin)
2026-10-17 18:51:10 +00:00
│ ├── contexts/ # ComponentContext (runtime bileşen kaydı), DemoContext
│ └── utils/ # editorScriptRuntime, workflow, hooks, hoc, biçimlendirme yardımcıları
├── configs/
│ ├── docker/ # Yerel geliştirme için veri katmanı compose dosyaları ve scriptler
│ ├── deployment/ # Dev/Prod compose dosyaları, nginx/redis/turn konfigürasyonu, deploy scriptleri
2026-10-05 12:58:59 +00:00
│ ├── seeds/ # Runtime'da düzenlenebilen seed'ler; host ve tenant'lar için tek klasör
│ │ # {crud,wizard,data,docs/modules}
2026-08-17 09:23:47 +00:00
│ │ # {sql|postgres}/{object,execute}
2026-09-17 12:24:51 +00:00
│ └── ai/ # AI entegrasyonu (n8n akış tanımları, Ollama model kurulum notları)
├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları)
│ # lowcode.instructions.md (artefakt üretim referansı: şemalar, örnekler)
2026-10-17 18:51:10 +00:00
│ # lowcode-reference.instructions.md (yetenek envanteri: JSON kolon sözleşmeleri, hazır ekranlar)
│ # dotnet.instructions.md (api/ için bağlayıcı .NET/ABP kod standardı)
│ # list.instructions.md (modül/liste ekleme prosedürü)
├── CLAUDE.md # Claude Code için operasyon kuralları
└── README.md
```
---
## 5. Hızlı Başlangıç
### 5.1. Önkoşullar
- .NET SDK **10.0.300+**
- Node.js **24.x** ve npm **11.x** (Volta önerilir)
- Docker Desktop (veritabanı ve Redis için)
- DevExpress lisansı (`api/DevExpress_License.txt`) ve DevExtreme npm erişimi
### 5.2. Veri katmanını ayağa kaldırma
```powershell
# SQL Server + Redis
.\configs\docker\start_sql.bat
# veya PostgreSQL + Redis
.\configs\docker\start_postgres.bat
```
Compose dosyaları `configs/docker/docker-compose-data.yml` içindedir; `sql` ve `postgres`
profilleri ile hangi veritabanının kalkacağı seçilir. Redis her iki profilde de çalışır.
### 5.3. Backend
```powershell
cd api
dotnet restore
dotnet run --project src/Sozsoft.Platform.HttpApi.Host
```
Uygulama `https://localhost:44344` adresinde açılır. Swagger UI kök adreste yayınlanır.
2026-10-18 21:47:36 +00:00
**Veritabanı ilk kurulum:** Açılışta veritabanı yoksa host, tam ABP yığını yerine minimal bir
"setup" uygulaması ile açılır (`DbStartup/SetupAppRunner`). Bu durumda:
- `GET /api/setup/application-status` — veritabanının hazır olup olmadığını döner (anonim).
2026-10-18 21:47:36 +00:00
`setupMode: true` ise UI `/setup`'a yönlendirir; katalog ya da çekirdek şema yokken tam yığın da
bunu döner (`DatabaseReadiness.RequiresSetup`), bağlantı hatası ise bakım ekranında kalır.
- Veritabanı tam yığın çalışırken silinirse süreç kurulum moduna geçmez; `DatabaseReadinessGate`
kurulum dışı her isteğe 503 döner, `/api/setup/*` isteklerini veritabanına dokunan ara katmanları
(dil, tenant, oturum) atlayarak geçirir ve Authorization başlığını düşürür — migration yalnızca
kurulum parolasıyla çalışır, bitince uygulama kendini durdurur (process yöneticisi yeniden başlatır).
- UI tarafında `/setup` route'u (`views/setup/DatabaseSetup.tsx`) migration'ı tetikleyen ekranı gösterir.
2026-10-19 14:35:08 +00:00
- Migration için `Setup:MigrationPassword` yapılandırması veya `Sas.Setup.Migrate` yetkisi gerekir.
2026-10-06 11:24:30 +00:00
- Ekran DbMigrator'ü `SetupMigrationRunner` ile çalıştırır. `Setup:MigratorPath` publish çıktısını
2026-10-07 12:45:18 +00:00
gösteriyorsa DLL doğrudan çalışır; kaynak proje klasöründe (geliştirme) `bin/Debug` altındaki var olan
DLL çalışır. Migrate restore/build yapmaz: DLL yoksa hata verir, `api/` kaynaklarından eskiyse yalnızca
uyarı yazar — kod değişikliğinin migrate'e girmesi için DbMigrator elle derlenir.
Alternatif olarak migration + seed'i doğrudan çalıştırabilirsiniz:
```powershell
dotnet run --project api/src/Sozsoft.Platform.DbMigrator
```
DbMigrator; şema migration'larını uygular ve ardından menü, route, permission, dil,
host ayarları, ülke/şehir/ilçe verileri ile seed edilmiş ListForm ekranlarını yükler
2026-08-17 05:56:52 +00:00
(kodla versiyonlanan sabit seed'ler `api/src/Sozsoft.Platform.DbMigrator/Migrations/`,
runtime'da düzenlenebilen seed'ler `configs/seeds/` altındadır — bkz. `App:SeedsPath`).
2026-10-05 12:58:59 +00:00
**Tek tenant seed'i:** Tenants ekranındaki bağlantı diyaloğunun Seed sekmesi
`POST /api/setup/tenants/{id}/seed` ucunu çağırır (`AbpTenantManagement.Tenants.Update`, yalnızca host
kullanıcısı). Uç DbMigrator'ü migrate ile aynı kilit ve SSE akışıyla çalıştırır; `TenantSeed:TenantId`,
`TenantSeed:AdminEmail`, `TenantSeed:AdminPassword` ortam değişkeniyle geçer (`TenantSeedOptions`) ve
`PlatformDbMigrationService` yalnızca o tenant'ın şema + seed + `execute` adımlarını yürütür. Loglar
DB Migrate'teki `DbMigrateLogPanel` ile gösterilir.
2026-10-05 14:11:06 +00:00
`configs/seeds` tek klasördür; tenant'a özel bir seed kapsamı yoktur. Kendi veritabanı olan
tenant'ta ekran tanımları, menüler ve yetki tanımları gibi kiracısız varlıklar host'ta kalır,
çünkü ABP'nin EF repository'si `IMultiTenant` olmayan varlığı her zaman host bağlantısıyla okur.
Kiracının veritabanına iki şey yazılır: `IMultiTenant` varlıklar (kullanıcı, rol, yetki ataması)
ve kiracı tablolarının satırları. Bu satırlar `Code = tenant adı` olan `DataSource` üzerinden
yazılır (`DataSourceManager`). Tabloların scriptleri ile veri dosyalarının kiracı satırları o
veritabanında da gerektiği için host ve kendi veritabanı olan her tenant aynı dosyalarla kurulur.
Kiracısız satırlar host'ta zaten olduğundan bu adımda atlanır. Host veritabanını paylaşan (bağlantı cümlesi olmayan) tenant'ta dosyalar
2026-10-05 12:58:59 +00:00
yeniden uygulanmaz, çünkü host seed'i onları aynı veritabanına zaten yazmıştır. Karar
`SeedFileScope`'tadır: migrate kararı `DataSeedContext`'e yazar, dosya okuyan her seeder
(`SqlDataSeeder`, `CrudDataSeeder`, `WizardDataSeeder`, `ListFormSeedDataApplier`, `FinalSeeder`)
aynı kararı uygular. İçerik türüne göre klasörler:
2026-08-17 09:23:47 +00:00
```
configs/seeds/
2026-10-05 12:58:59 +00:00
crud/ wizard/ data/ docs/
sql/ { object/ execute/ host/{ object/ execute/ } }
postgres/ { object/ execute/ host/{ object/ execute/ } }
2026-08-17 09:23:47 +00:00
```
2026-10-05 12:58:59 +00:00
Dosyalar ürünün varsayılanıdır ve **yalnızca host bağlamında yazılır**. Tenant bağlamındaki bir
kayıt veritabanında kalır, seed dosyasına yansımaz; yansısaydı bir tenant'ın verisi bütün
veritabanlarına yayılırdı. Bunu `ListFormSeedDataSynchronizer` ve `WizardSeedSynchronizer`
uygular. Wizard, SQL Query Manager, CRUD Endpoint ve Custom Components yetkileri zaten yalnızca
host tarafındadır.
Bekleyen wizard kurulumu (`Pending`) dosyaları uygulayan **son** veritabanında kapanır. Tam
migrate'te bu, kendi veritabanı olan son tenant'tır; öyle bir tenant yoksa host'tur. Host'ta
kapansaydı, ekranın eski sürümü kurulu olan tenant ekranı "kurulu" sayıp güncellemezdi. Tek
tenant seed'i (Tenants › Seed) bayrağı kapatmaz.
2026-10-06 11:24:30 +00:00
Aktif veritabanı sağlayıcısına göre `sql/` (SQL Server) ya da `postgres/` okunur. İki klasör
birebir eştir: her `sql/…/X.sql` dosyasının aynı adlı `postgres/…/X.sql` karşılığı vardır (PG'de şema
`public`; çeviri kuralları `lowcode.instructions.md` §8.3.1). EF modeli SQL Server sözdizimiyle
yazılır, PostgreSQL'de `PostgreSqlModelSyntax` ile çevrilir (`PlatformDbContext.ConfigureProviderSpecificModel`);
`Order.IsActive` SQL Server'da lisans bitişinden hesaplanan kolondur, PG'de hesaplanan kolon
`CURRENT_DATE` alamadığı için saklanan kolondur (varsayılan `true`). Her
2026-08-17 09:23:47 +00:00
`.sql` dosyası amacını belirten bir klasörün altındadır; klasör köklerinde dosya bulunmaz:
2026-08-17 05:56:52 +00:00
| Klasör | Ne zaman çalışır |
| --- | --- |
2026-10-05 12:58:59 +00:00
| `{sql\|postgres}/object` | Seed sırasında nesneyi oluşturur/günceller. SqlTableDesigner ve SqlQueryManager, deploy ettiği script'i bu klasöre yazar. |
| `{sql\|postgres}/execute` | Nesneyi oluşturur; ayrıca tüm migration ve seed'ler bittikten sonra `FinalSeeder` dosya adından türettiği procedure'ü çalıştırır (dosya adı = procedure adı, `schema.Procedure.sql` de olur). |
2026-10-05 12:58:59 +00:00
| `{sql\|postgres}/host/{object\|execute}` | Aynı iki kural, **yalnızca host veritabanında**. Tenant veritabanında karşılığı olmayan nesnelere dayanan script buraya konur — örneğin HangFire tabloları tenant migrate'inde de yalnızca host'a kurulduğu için `Sas_H_BackgroundWorker_JobFlow` view'ı buradadır. Sıra: `object` → `host/object` → `execute` → `host/execute`. SqlTableDesigner ve SqlQueryManager her zaman `object/`'e yazar; yalnızca host'ta çalışması gereken script File Manager'dan buraya taşınır. Wizard export/import bu klasörü de taşır. |
2026-10-09 17:54:08 +00:00
| `wizard` | ListForm Wizard'ın ürettiği `.json` konfigürasyonları; `WizardDataSeeder` okur. Contributor değildir (`IWizardDataSeeder`): her dosyayı kendi transaction'ında kurduğu için ABP contributor'ları commit edildikten sonra, `data/` dosyalarından önce çalışır — açık contributor transaction'ı içinde ayrı bağlantı, commit edilmemiş satırları bekleyip zaman aşımına düşer. |
2026-10-05 12:58:59 +00:00
| `crud` | CrudEndpoint konfigürasyonları; `CrudDataSeeder` dosyaları `ICrudEndpointSeedApplier` üzerinden uygular. Politika **damga karşılaştırmalıdır**: eksik endpoint her zaman eklenir, mevcut bir kayıt yalnızca dosyanın `GeneratedAt` damgası kaydın `LastModificationTime`/`CreationTime` değerinden **yeniyse** güncellenir ya da silinir; aksi halde kayıt korunur ve log'a drift uyarısı düşer. Böylece CRUD Endpoint Manager'dan yapılan bir revizyon depodan gelen eski bir dosya yüzünden migrate sırasında geri alınmaz. Damgası olmayan (eski biçimde yazılmış) dosya hiçbir zaman yeni sayılmaz. |
2026-10-21 20:40:03 +00:00
| `data` | Ekranlardan girilen listelerin veri aynası: tek dosya (`{ListFormCode}.json`) ya da satır başına dosya tutan klasör (`{ListFormCode}/{anahtar}.json`; klasör özyinelemeli taranır ve tamamı tek unit of work'te uygulanır). Biçimi yalnızca ekranın `SeedFilePath` tanımı belirler; hedefi okuyan/yazan her yol `ListFormSeedTarget` üzerinden gider ve tanımın dışında kalan dosya uygulanmaz. `ListForm.SeedFilePath` dolu olan ekranda `SeedSyncInsert/Update/Delete` bayraklarından işaretli olan işlemler dosyaya yansır (`ListFormSeedDataSynchronizer`); migrate/seed sırasında bütün contributor'lar bittikten sonra `ListFormSeedDataApplier` dosyayı geri uygular: anahtarı veritabanında **olmayan** satırları ekler, var olanlara dokunmaz. Yeni satırlar toplu komutlarla yazılır; satır sayısı büyüyen ekranın `IListFormSeedBulkInserter` yazıcısı varsa (dil metinleri: `LanguageTextSeedBulkInserter`) satırlar ekranın `InsertCommand`'ının kuralları bellekte uygulanarak `IDynamicDataRepository.BulkInsertAsync` ile (SQL Server bulk copy, PostgreSQL binary COPY) yazılır. Aynı uygulayıcı wizard import'unun kapanışında da (`CompleteImport`) yalnızca o import'ta yazılan dosyalar için, `overwriteExisting` açık olarak çalışır — orada var olan satır dosyadaki değerlerle güncellenir. |
2026-08-17 05:56:52 +00:00
Klasör kökünde kalmış eski `.sql` dosyaları geriye dönük uyumluluk için hâlâ işlenir,
fakat `SqlDataSeeder` bunları uyarı ile loglar — ilgili klasöre taşınmaları beklenir.
### 5.4. Frontend
```powershell
cd ui
npm install
2026-08-12 12:20:00 +00:00
npm start # vite dev server (http://localhost:3000)
```
2026-08-12 12:20:00 +00:00
Visual Designer ve Custom Component altyapısının kullandığı bileşen kataloğu
`src/components/visualDesigner/generated/componentProps.json` dosyasında tutulur ve repoya
dahildir. `src/components/ui` altındaki bileşen prop'ları değiştiğinde bu dosya elle
güncellenmelidir.
### 5.5. İlk giriş
Seed edilen host yöneticisi ile `/login` üzerinden giriş yapılır (kullanıcı bilgileri
2026-09-18 06:28:25 +00:00
`PlatformIdentityDataSeeder` ile belirlenir). Giriş sonrası
`/admin/dashboard` açılır.
2026-10-05 12:58:59 +00:00
Yöneticinin ilk yetkileri, yöneticinin hiç yetkisi yokken bir kez atanır. Host yöneticisi kimlik
yetkileri (rol/kullanıcı) ve `Setup.Migrate` ile başlar. Kiracı yöneticisi ise host'taki tanım
tablosunda (`AbpPermissions`) kiracı tarafına açık bütün yetkileri alır. Sonradan deploy edilen
ekranların yetkileri migrate ile atanmaz.
Kendi veritabanı olan kiracıda yalnızca `IMultiTenant` varlıklar (kullanıcı, rol, yetki ataması)
o veritabanındadır. ListForm, menü ve yetki tanımı gibi kiracısız varlıkları ABP her zaman host'ta
okur. Kiracı tablolarının satırları, kiracı adıyla kayıtlı veri kaynağından okunup yazılır
(`DataSourceManager`).
---
## 6. Yapılandırma (Configuration)
### 6.1. Backend (`appsettings.json`)
| Anahtar | Açıklama |
| --- | --- |
| `App:SelfUrl` | API'nin kendi adresi (OpenIddict authority ile uyumlu olmalı). |
| `App:ClientUrl` | React uygulamasının kök adresi (mail linkleri bu adresi kullanır). |
2026-10-05 12:58:59 +00:00
| `App:CorsOrigins` | Virgülle ayrılmış izinli origin listesi; wildcard subdomain destekli (`https://*.alan.com`). |
| `App:BaseDomain` | Production'da ana alan adı ve tüm alt alan adları (yalnızca `https`), geliştirmede `dev.{alan}` CORS'a açılır. Tek politika `PlatformCorsOriginValidator`'dadır: origin bu kurala ya da `App:CorsOrigins`'e uyuyorsa izinlidir; geliştirmede `localhost`/`127.0.0.1`/`[::1]` her zaman açıktır. |
| `App:RedirectAllowedUrls` | OAuth yönlendirme için izinli adresler. |
| `App:CdnUrl` / `App:CdnPath` | Dosya servisinin adresi ve BLOB kök dizini. |
| `App:AttachmentsPath` | Mail kuyruğu eklerinin yazıldığı dizin. |
| `App:BackupPath` | Veritabanı yedek dizini (BackupWorker kullanır). |
| `App:Version` | Uygulama sürümü (UI sürüm kontrolü ile eşleşir). |
| `ConnectionStrings:SqlServer` / `PostgreSql` | Host veritabanı bağlantıları. |
| `Redis:IsEnabled` / `Redis:Configuration` | Dağıtık cache ve SignalR backplane. |
| `AuthServer:Authority` / `SwaggerClientId` | OpenIddict authority ve Swagger istemcisi. |
| `OpenIddict:TokenLifetimes:*` | Access/refresh token ömrü (dakika). |
| `Setup:MigrationPassword` | Setup ekranından migration tetiklemek için parola. |
| `StringEncryption:DefaultPassPhrase` | ABP string şifreleme anahtarı. |
| `Serilog:MinimumLevel` | Log seviyeleri. |
2026-06-09 12:54:05 +00:00
> **Güvenlik:** Depodaki `appsettings.json` dosyaları geliştirme değerleri içerir.
> Üretimde bağlantı dizeleri, `MigrationPassword`, `DefaultPassPhrase` ve entegrasyon
> anahtarları **ortam değişkeni veya secret yöneticisi** ile verilmelidir; repoya yazılmamalıdır.
2026-06-09 12:54:05 +00:00
### 6.2. Frontend (`ui/.env`, `.env.dev`, `.env.production`)
2026-06-09 12:54:05 +00:00
| Değişken | Açıklama |
2026-06-09 12:54:05 +00:00
| --- | --- |
| `VITE_API_URL` | API kök adresi. |
| `VITE_CDN_URL` | Dosya/CDN kök adresi. |
| `VITE_AI_URL` | AI asistanının n8n webhook kökü. |
| `VITE_USE_POLLING` | Dosya izlemede polling (WSL/Docker senaryoları). |
| `VITE_PWA_DEV` | Geliştirmede service worker'ı açar (varsayılan kapalı). |
2026-06-09 12:54:05 +00:00
### 6.3. Uygulama içi ayarlar
2026-06-09 12:54:05 +00:00
Çalışma zamanı ayarları veritabanında tutulur ve **Ayarlar** ekranından (`/admin/settings`)
yönetilir. `Sozsoft.Settings` modülü ayar tanımlarını, `SettingUiAppService` ise bu tanımların
UI gösterimini yönetir. Şifre politikası, oturum kuralları, gönderici kimlik bilgileri (SMS,
mail, WhatsApp, Rocket) ve AI bot yapılandırması bu yolla değiştirilir — kod dağıtımı gerekmez.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 7. Low-Code Motoru: ListForm Ekosistemi
2026-06-09 12:54:05 +00:00
Platformun kalbi budur. Bir iş ekranı; bir `ListForm` kaydı, ona bağlı `ListFormField`
kayıtları ve bir menü/route/permission üçlüsünden oluşur.
2026-06-09 12:54:05 +00:00
### 7.1. Kavramlar
2026-06-09 12:54:05 +00:00
| Varlık | Rolü |
2026-06-09 12:54:05 +00:00
| --- | --- |
| `DataSource` | Hedef veritabanı bağlantısı (MSSQL / PostgreSQL). Ekranlar `DataSourceCode` ile bağlanır. |
| `ListForm` | Ekranın tüm davranışı: veri kaynağı, sütun/filtre/sayfalama/düzenleme ayarları, yetkiler, alt formlar, iş akışı, grafik ayarları. |
| `ListFormField` | Tek bir sütun/alanın davranışı: başlık, tip, genişlik, editör, lookup, doğrulama, biçim, yetki. |
| `ListFormCustomization` | Kullanıcı bazlı kişiselleştirme: kayıtlı filtre, grid state, sunucu tarafı join/where. |
| `ListFormWorkflow` | Onay ve koşul düğümlerinden oluşan iş akışı grafiği. |
| `ListFormImport` / `ImportLog` | Excel/CSV içe aktarma tanımı ve satır bazlı sonuç günlüğü. |
2026-06-09 12:54:05 +00:00
### 7.2. `ListForm` anatomisi
2026-06-09 12:54:05 +00:00
Ekran davranışı büyük ölçüde JSON kolonlarında saklanır. Öne çıkanlar:
2026-06-09 12:54:05 +00:00
| Alan | İşlevi |
2026-06-09 12:54:05 +00:00
| --- | --- |
| `SelectCommandType` + `SelectCommand` | Veri kaynağı türü: `Table`, `View`, `TableValuedFunction`, `Query`, `StoredProcedure`. |
| `KeyFieldName` / `KeyFieldDbSourceType` | Birincil anahtar alanı ve tipi. |
| `PermissionJson` | Ekranın create/read/update/delete/export/import/note yetki eşlemesi. |
| `EditingOptionJson` / `EditingFormJson` | Düzenleme modu (row/cell/batch/form/popup) ve düzenleme formu düzeni. |
| `FilterRowJson`, `HeaderFilterJson`, `FilterPanelJson`, `SearchPanelJson`, `GroupPanelJson` | Filtreleme, arama ve gruplama davranışı. |
| `ColumnOptionJson`, `PagerOptionJson`, `SelectionJson`, `StateStoringJson` | Sütun, sayfalama, seçim ve durum saklama ayarları. Durum kullanıcı bazlıdır ve grid, pivot, ağaç, gantt, todo ile form sayfasının alan sırasını (Form Düzeni) kapsar. |
| `Insert/Update/DeleteCommand` (+ `Before` / `After`) | CRUD komutları ve öncesi/sonrası çalışacak SQL kancaları. |
| `Insert/Update/DeleteServiceAddress` | Varsayılan olarak `list-form-data/*`; özel endpoint'e yönlendirilebilir. |
2026-10-05 12:58:59 +00:00
| `SeedFilePath` | Doluysa seed sırasında listenin verisi seed klasöründeki bu hedeften geri yüklenir: `.json` ile biten değer tek dosya (`data/{ListFormCode}.json`), uzantısız değer satır başına dosya tutan klasördür (`data/{ListFormCode}/{anahtar}.json`). |
2026-09-04 09:05:26 +00:00
| `SeedSyncInsert` / `SeedSyncUpdate` / `SeedSyncDelete` | Ekranda yapılan hangi işlemin seed dosyasına yansıyacağı; kapalıysa o işlem dosyaya dokunmaz. |
| `SubFormsJson` | Ana-detay ilişkisi (ParentFieldName → ChildFieldName eşlemesi). |
2026-09-30 20:52:33 +00:00
| `WidgetsJson` | Ekranın üstünde gösterilecek KPI kartları. Alt form olarak üretilen ekrana (menüsüz ya da menülü) yazılmaz; menüsüz wizard'da deploy boşaltır, alt form sekmesinde çizilmez. |
| `WorkflowJson` | İş akışı bağlaması. |
| `ExtraFilterJson` | Ekrana özel ek filtre araç çubuğu. |
| `PivotOptionJson`, `TreeOptionJson`, `GanttOptionJson`, `SchedulerOptionJson`, `TodoOptionJson` | Alternatif görünümlerin ayarları. |
| Grafik alanları (`SeriesJson`, `LegendJson`, `ArgumentAxisJson`, `ValueAxisJson`, `TooltipJson`, `ZoomAndPanJson`, …) | DevExtreme Chart yapılandırması. |
| `CustomJsSourcesJson` / `CustomStyleSourcesJson` | Ekran yüklendiğinde çalışacak JS/CSS. |
| `IsTenant`, `IsBranch`, `IsOrganizationUnit` | Otomatik tenant/şube/organizasyon birimi filtrelemesi. |
| `UserId`, `RoleId`, `CultureName` | Aynı ekranın kullanıcı, rol veya dil bazlı varyantları. |
2026-06-09 12:54:05 +00:00
### 7.3. Görünüm tipleri
2026-06-09 12:54:05 +00:00
Aynı `ListForm` tanımı, aynı veri hattı üzerinden birden fazla görünümle sunulabilir:
2026-06-09 12:54:05 +00:00
`Grid` · `Pivot` · `Tree` · `Chart` · `Gantt` · `Scheduler` · `CardView` · `TodoBoard (Kanban)` ·
`Widget Group`
2026-06-09 12:54:05 +00:00
2026-10-11 19:57:23 +00:00
Kullanıcı görünümler arasında geçiş yapabilir (seçilen görünüm tarayıcıda saklanır); grid durumu
(sütun sırası, genişlik, filtre, gruplama) toolbar'dan elle kaydedilir, sunucuda kullanıcı > rol > genel
önceliğiyle saklanır ve sıfırlanabilir. Hazır toolbar/satır butonları ve URL parametreleri:
`lowcode-reference.instructions.md` §5.8.
2026-06-09 12:54:05 +00:00
**TodoBoard artık bir Kanban panosudur:** kolonlar `TodoOptionJson` içindeki `statusExpr`
değerlerinden üretilir, kartlar kolonlar arasında sürüklenerek durum değiştirir ve panodan yeni
kolon (durum) eklenebilir. Kart içeriği `titleExpr`, `descriptionExpr`, `dueDateExpr`, `tagExpr`
ve atanan kullanıcı alanlarından kurulur.
### 7.4. Alan (field) davranışı
2026-06-09 12:54:05 +00:00
- **Editör tipleri:** `dxTextBox`, `dxTextArea`, `dxNumberBox`, `dxDateBox`, `dxDateRangeBox`,
`dxCalendar`, `dxCheckBox`, `dxSwitch`, `dxRadioGroup`, `dxSelectBox`, `dxTagBox`, `dxLookup`,
`dxDropDownBox`, `dxGridBox`, `dxAutocomplete`, `dxColorBox`, `dxHtmlEditor`, `dxSlider`,
`dxRangeSlider` — ayrıca görsel yükleme/görüntüleme editörleri ve yıldızlı puan (`dxRating`).
- **Lookup kaynakları:** `StaticData`, `Query`, `WebService`; ebeveyn–çocuk (cascade) desteklidir.
Cascade liste popup'ında, satır içi düzenlemede ve form sayfasında (`/admin/form/...`, `FormDevExpress`)
aynı kuralla çalışır: alan değişince ebeveyni o alan olan çocuklar ve alanın `CascadeEmptyFields`
listesi boşaltılır.
Lookup'lı seçim kutusunda yazarak arama ikisinde de varsayılan açıktır; alanın `EditorOptions`'ında
`{"searchEnabled": false}` kapatır.
- **Popup ↔ form sayfası eşliği:** `EditorOptions` varsayılanları, butonlar, `EditorScript`, cascade ve
platform editörleri (TagBox, GridBox, görsel) iki yerde ortak koddan gelir; tablo `lowcode.instructions.md` §4.3.4.
- **Doğrulama:** `ValidationRuleJson` ile required/range/pattern/custom kuralları. Kurallar liste popup'ında ve form sayfasında (`/admin/form/...`) aynı uygulanır; maske (`mask`) `EditorOptions` ile verilir.
- **Editör script:** `EditorScript` alanı ile alan değişiminde çalışan JS (örn. tarih farkından
gün hesaplama, miktar × birim fiyat toplamı). Çalıştırma `utils/editorScriptRuntime.ts` üzerinden yapılır.
- **Editör seçenekleri:** `EditorOptions` alanı DevExtreme editörüne geçirilen JSON'dur
(format, maske, placeholder, yükseklik, buton görünürlüğü, platform editörlerinin tipli
ayarları). Elle JSON yazmak zorunlu değildir; bkz. 7.4.1.
- **Koşullu biçimlendirme:** `ColumnStylingJson`, `ColumnCssClass`/`ColumnCssValue`.
- **Alan bazlı yetki:** `PermissionJson` ile sütun düzeyinde okuma/yazma/dışa aktarma kontrolü.
2026-06-09 12:54:05 +00:00
#### 7.4.1. Script Builder ve Editor Options Builder
`EditorScript` ve `EditorOptions` alanları hem görsel olarak hem de seeder kodundan üretilir;
**iki tarafın çıktısı birebir aynı olmak zorundadır.**
**Editor Script Builder** (`json-row-operations/editor-script/`)
- Kural sözlüğü `scriptRecipes.ts` içindedir; her tarif (recipe) tek satırlık runtime çağrısı üretir.
Gruplar: hesap (`calc`), veri (`data`), görünüm (`view`), bildirim, API.
- Üretilen script'in ilk satırındaki `// @builder {...}` başlığı kuralları saklar. Dialog script'i
regex ile çözmeye çalışmaz, bu başlıktan geri okur; başlık bozulursa/elle düzenlenirse script
"manuel" sayılır ve kural editörü kapanır.
- Kural tetikleyicileri: `change` (varsayılan), `open` (form açılışında — varsayılan değer üretmek
için, script başına `// @runOnOpen` direktifi eklenir), `both`.
- Koşullar `and`/`or` ile birleşir: `equals`, `notEquals`, `contains`, `empty`, `notEmpty`,
`greaterThan`, `lessThan`, `isTrue`, `isFalse`, `always`.
- Dialog'un kendisi ortaktır (`components/scriptBuilder/ScriptBuilderDialog.tsx` + IntelliSense);
ListForm lehçesi `formScriptDialect.ts`, Visual Designer lehçesi `designerScriptDialect.ts`
ile aynı sözleşmeye bağlanır.
**Seeder tarafı (C#)** — `Sozsoft.Platform.Domain.Shared/Editors/`
`scriptRecipes.ts` ve editorOptions sözlüğünün C# portudur. Seeder ile basılan script/JSON,
dialogda açılıp görsel olarak düzenlenebilir kalır. Bir tarif TypeScript tarafında değişirse
buradaki karşılığı da güncellenmelidir.
| Tip | Kullanım |
| --- | --- |
2026-09-21 15:01:40 +00:00
| `EditorScript` | `Multiply`, `Subtract`, `Percent`, `Sum`, `Formula`, `Today`, `Days`, `Hours`, `Copy`, `SetValue`, `Clear`, `ReadOnly`, `Notify`, `Ask`, `OpenUrl`, `ApiToField`, `Location`, `Custom` → `EditorScript.Build(rules)` |
| `EditorScriptRule` | `.When(...)` / `.WhenAny(...)` koşulları, `.OnChange()` / `.OnOpen()` / `.OnOpenAndChange()` tetikleyicisi; `string`'e örtük dönüşür |
| `EditorScriptCondition` | `Is`, `IsNot`, `Contains`, `IsEmpty`, `IsNotEmpty`, `GreaterThan`, `LessThan`, `IsTrue`, `IsFalse`, `Always` |
| `EditorOptions` | Hazır başlangıçlar: `Disabled()`, `ReadOnly()`, `ShowClearButton()`, `Multiline(h)`, `Number(precision)`, `Percent()`, `Date()`, `DateTime()`, `Time(interval)`, `Phone()`, `Slider()`, `ImageUpload()`, `Html(h)` |
| `EditorOptionsBuilder` | Akıcı ekleme: `.Placeholder()`, `.MaxLength()`, `.Height()`, `.Width()`, `.Format()`, `.Mask()`, `.Flag()`, `.Text()`, `.Number()`, `.Json()`; `string`'e örtük dönüşür |
```csharp
EditorOptions = EditorOptions.Number(4).ShowClearButton(),
EditorOptions = EditorOptions.Multiline(60).Placeholder("Açıklama"),
EditorScript = EditorScript.Build(
EditorScript.Multiply("Quantity", "UnitPrice", "Total"),
EditorScript.Percent("Total", "VatRate", "VatAmount", mode: EditorScriptPercentMode.Add)
.When(EditorScriptCondition.IsNotEmpty("VatRate"))),
```
**Editor Options Builder** (`json-row-operations/editor-options/`)
- UI tamamen `optionSpecs.ts` veri sözlüğünden üretilir; yeni bir ayar eklemek için oraya tek
satır yazmak yeterlidir. Her ayar `path` (örn. `format.precision`), tip (`boolean` üç durumlu /
`number` / `text` / `select` / `size` / `stringList` / `json`), grup ve hangi editörlerde
anlamlı olduğu bilgisini taşır.
- `platform: true` işaretli ayarlar backend'in tipli DTO'ya (`GridBoxOptionsDto`,
`TagBoxOptionsDto`, `ImageUploadOptionsDto`) deserialize ettiği alanlardır; yanlış tipte
yazılırsa sessizce yok sayılır.
- `presets.ts` hazır kalıplar sunar (HTML editör tam araç çubuğu, telefon maskesi, tarih/saat…);
kalıp mevcut JSON ile **birleştirilir**, diğer ayarları silmez.
### 7.5. İş akışı ve onay
2026-06-09 12:54:05 +00:00
`FormTabWorkflow` ve `views/admin/listForm/workflow/WorkflowDesigner.tsx` üzerinden görsel
tasarlanır. Düğüm türleri koşul karşılaştırması (`CompareColumn`/`CompareOperator`/`CompareValue`)
ve onay adımlarıdır; her adım için `NextOnTrue`, `NextOnFalse`, `NextOnApprove`, `NextOnReject`
2026-10-10 20:11:12 +00:00
geçişleri tanımlanır. Onaycı birden fazla kişi olabilir. Onay düğümü reddi yalnızca açıklamayla ya da
bir tablodan seçilen nedenle (Id + metin kolonu) aldırabilir (`lowcode.instructions.md` §4 › Red nedeni
listesi). Formlar içinde iş akışına bağlı özel butonlar gösterilebilir.
2026-06-09 12:54:05 +00:00
### 7.6. Yeni ekran ekleme akışı (Wizard)
2026-06-09 12:54:05 +00:00
`/admin/listform/wizard` altındaki sihirbaz, aşağıdaki artefaktları tek akışta üretir:
2026-06-09 12:54:05 +00:00
1. **ListForm** kaydı (kod, ad, başlık, veri kaynağı, select komutu, anahtar alan)
2. **ListFormField** kümesi (sütunlar, editörler, lookup'lar, doğrulamalar)
3. **Route** kaydı (`key`, `path`, `componentPath`, `routeType`, `authority`)
4. **Menu** kaydı (`ParentCode`, `Code`, `DisplayName`, `Url`, `Icon`, `RequiredPermissionName`, `Order`)
2026-10-14 09:07:11 +00:00
5. **Permission** kayıtları (`.Default`, `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`; `.Note` yalnızca kayıt kaynağı Table ise)
6. **Dil anahtarları** (menü, başlık, açıklama; EN + TR)
7. Gerekiyorsa **ayar/entegrasyon** bağımlılıkları
2026-10-20 08:52:23 +00:00
8. **Seed dosyası** — `configs/seeds/wizard/{Ad}.json` (tek `Wizard` bölümü: wizard cevapları = ekranın bütün sözleşmesi)
9. **Doğrulama ve geri alma** notları
**Adım bileşenleri.** Adımlar numaralı dosya adları yerine işlevleriyle adlandırılır ve
`React.lazy` ile yüklenir; aynı anda yalnızca görünen adım indirilir, sihirbazın açılış maliyeti
adım sayısından bağımsızdır.
| Adım | Dosya | İçerik |
| --- | --- | --- |
| Menü ve kimlik | `WizardStepMenu.tsx` | Wizard adı, ListForm kodu, menü ağacı/üst menü, ikon, sıra, izin grubu, dil metinleri |
| Veri ayarları | `WizardStepDataSettings.tsx` | Veri kaynağı, select komutu tipi, anahtar alan, CRUD endpoint diyaloğu |
2026-10-20 08:52:23 +00:00
| Alanlar | `WizardStepFields.tsx` · `WizardFieldDetailDialog.tsx` | Sütun grupları, editör tipleri, lookup, doğrulama; kartta ek ayarlar (görünürlük/aktif/salt okunur/arama/düzenleme/ekleme, genişlik, hizalama, biçim, sıralama, varsayılan, yer tutucu, bant); ⚙ ile alan detayının bütün sekmeleri, sütun sırası paneli |
| Liste Ayarları · Düzenleme ve Veri · Grafik | `WizardStepScreenTabs.tsx` | ListForm editörünün sekmeleri taslak modunda (`edit/listFormDraftContext.ts`); eşleme `wizardScreen.ts` |
| Alt formlar | `WizardStepSubForms.tsx` | Ana–detay ilişki eşlemesi |
| Widget'lar | `WizardStepWidgets.tsx` | KPI kartları |
| İş akışı | `WizardStepWorkflow.tsx` | Onay/koşul grafiği |
| Düzen adımları | `WizardStepTodoLayout` · `WizardStepTreeLayout` · `WizardStepGanttLayout` · `WizardStepSchedulerLayout` | Yalnızca ilgili görünüm açıksa görünür |
| Bileşen | `WizardStepComponent.tsx` · `WizardStepCustomComponent.tsx` | Custom yolunda bağlanacak bileşen |
| Yayınlama | `WizardStepDeploy.tsx` | Özet, doğrulama ve seed dosyalarının üretimi; ardından Wizard Manager'a dönülür |
2026-10-20 08:52:23 +00:00
Alanlar adımından sonra üç ek adım — **Liste Ayarları** (Detay, Sütunlar, Sıralama, Filtreleme, Satır,
Arama, Gruplama, Seçim, Sayfalama, Durum, Pivot), **Düzenleme ve Veri** (Düzenleme, Veritabanı
Select/Insert/Update/Delete, Butonlar, Ek Filtreler) ve **Grafik** (Chart görünümü açıkken, İş Akışı
ve düzen adımlarından sonra) — ListForm editörünün sekmelerini **taslak modunda** gösterir
(sekmede Kaydet yoktur; adıma girişte ilk sekme açılır); Alanlar adımındaki alan
kartı da alan detayının sekmelerini (Filtreleme, Başlık, Gruplama, Özetler, Join, Doğrulama, Stil,
CSS, Yetki, Pivot…) ve bir sütun sırası paneli açar. Değişiklikler veritabanına değil wizard
cevabına toplanır ve deploy'da dosyaya yazılır.
Menü ağacı yardımcıları (`menuTree.ts`) adım bileşenlerinden ayrı bir modüldedir; böylece Wizard
ve `SqlTableDesignerDialog` bu fonksiyonları kullanırken adım bileşenlerini pakete çekmez.
2026-10-20 08:52:23 +00:00
**Seed dosyası ekranın kendisini taşır — tek `Wizard` bölümüyle.** ListForm detayının her sekmesi
`Wizard` bölümünde bir bloktur (`Editing`, `Insert`/`Update`/`Delete`, `FormDefaults`, `FilterRow` …
`ChartOptions`), alan detayının her sekmesi `Groups[].Items[]` öğesinin bir alanıdır; ayrı
`ListForm`/`Fields` bölümü yoktur. Deploy `ListFormWizardDto.ApplyDefaults` ile boş blokları
doldurur, `BuildListForm()/BuildFields()` ile ekranı üretir ve `RefreshFrom` ile cevaplara geri yazar;
`WizardDataSeeder` ekranı aynı iki metotla dosyadan kurar. **Deploy veritabanına yazmaz:**
wizard dosyasının yanında menü, modül, izin grubu, yetki ve dil metinleri platform seed
2026-10-19 14:35:08 +00:00
dosyalarına (`data/Sas.Menus.{Menu,Module}.json`, `data/Abp.Identity.Permission{Groups,s}.json`,
2026-10-21 20:40:03 +00:00
`data/Sas.Languages.LanguageText/{CultureName}.json`) satır olarak yazılır (`WizardSeedRowStore`) ve hepsi DB
Migrate ile kurulur. Deploy/import dosyaya `Pending` yazar; Wizard Manager dosyayı "Migrate
bekliyor" rozetiyle gösterir, migrate bitince liste ve menü yenilenir. Yetkiler hiçbir role
otomatik atanmaz. Böylece Wizard ile ListForm
detayı aynı veri yapısını konuşur ve tasarımcıdaki değişiklikler wizard'a geri yazılabilir.
2026-10-20 08:52:23 +00:00
Wizard alanları dosyada `Menu`, `Language`, `Data`, `Details`, `Layout` başlıkları altında toplanır,
bloklar ve `Groups` kök seviyededir (`WizardSeedFileDto.ToSeedJson` / `FromSeedJson`); kısayol cevaplar
(`AllowAdding` …, `SeedData`/`SeedSync`, öğedeki `IsRequired`) dosyaya yazılmaz, sahibi olan bloktan
türetilir. Eski biçimdeki `ListForm`/`Fields` bölümleri okunurken `Wizard`'a aktarılır ve bir sonraki
yazımda düşer. Dosya adı `{Ad}.json`'dır (zaman damgası yok);
uygulama sırası `Wizard.SeededAt` damgasından gelir. MCP `deploy_wizard` dosyaya etkin talebi
`DevRequest` (`Id`, `Title`) olarak yazar; elle yapılan kayıt bu bağı korur, talepsiz wizard'da bölüm yoktur.
**İki yol: `ComponentKind`.** `ListFormWizardDto.ComponentKind` sihirbazın yol ayrımıdır ve seed
dosyasında ilk alandır. Varsayılan `List` olduğu için bu alanı taşımayan eski seed dosyaları
eskisi gibi çalışır.
| `ComponentKind` | Üretilen | Menü URL'i | Yetkiler |
| --- | --- | --- | --- |
| `List` | ListForm + ListFormField (+ iş akışı) + Menu + Permission + dil anahtarları (rota üretilmez; genel `/admin/list/{ListFormCode}` rotası kullanılır) | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
| `Custom` | Menü + Permission + dil anahtarları; ekran seçilen Custom Component'tir | Component'in `RoutePath` değeri | Yalnızca `.Create`, `.Update`, `.Delete` (Export/Import/Note ListForm'a özgüdür) |
**Menüsüz wizard (`CreateMenu = false`).** Menü (ve gerekiyorsa üst menü) kaydı hiç üretilmez;
ListForm, permission ve dil anahtarları her iki durumda da üretilir. Menüsüz wizard'lar başka bir
ekranda SubGrid/parça olarak kullanılmak üzere tanımlanır. Alan varsayılanı `true` olduğundan bu
alanı taşımayan eski seed dosyaları etkilenmez.
**İzin grubu seçimi.** Menü adımında izin grubu bir seçim listesinden gelir
(`ListFormWizardAppService.GetPermissionGroups`, `/api/app/list-form-wizard/permission-groups`). Görünen adlar, grup adıyla aynı olan dil anahtarının metinlerinden
okunur; seçim yapıldığında `PermissionGroupEn/Tr` forma doldurulur. Yeni bir grup
tanımlanırsa grup adıyla aynı dil anahtarına bu metinler yazılır; mevcut grup seçilip alanlar boş
bırakılırsa sunucu veritabanındaki değeri geri yazar. Böylece seed dosyası her durumda iki dilli
görünen adı taşır.
2026-06-09 12:54:05 +00:00
Dinamik liste ekranlarının route deseni `/admin/list/{ListFormCode}` biçimindedir; form
2026-10-11 19:57:23 +00:00
görünümleri `/admin/form/{listFormCode}/:id`, grafik `/admin/chart/{listFormCode}` yollarını kullanır;
pivot ayrı bir route değil, liste ekranının `LayoutJson.Pivot` görünümüdür.
2026-06-09 12:54:05 +00:00
Var olan bir ekranı düzenlemek için `/admin/listform/edit/{listFormCode}` — tüm JSON
yapılandırması sekmeli bir editörde yönetilir (Veritabanı, Sütunlar, Filtreler, Düzenleme,
Yetkiler, Alt Form, Pivot, Tree, Gantt, Scheduler, Todo, Widget, Workflow, Durum, Grafik sekmeleri).
2026-10-04 15:04:16 +00:00
**Sütunlar** sekmesinde alanlar sürüklenerek sıralanır (`form-fields/fieldOrder.ts`): liste görünümünde
`ListOrderNo` bütün alanlar arasında, form görünümünde `EditOrderNo` yalnızca aynı form grubundaki alanlar
arasında yeniden 1'den dağıtılır; grup değiştirmek Options sekmesindedir. Kültür/rol/kullanıcı kayıtları kendi
sırasını taşır, yalnızca değeri değişen alanlar kaydedilir. **Veritabanı** sekmesindeki varsayılan değer
satırlarında alan adı Select Command kolonlarından seçilir ve `FieldDbType` kolon tipinden dolar; liste
kısıtlamaz — sorgu tipli kaynağın kolonları katalogdan okunamadığı için ad elle de yazılabilir.
2026-06-09 12:54:05 +00:00
**Wizard File Manager** (`/admin/listform/wizardManager`; sihirbazın kendisi `/admin/listform/wizard`) üretilmiş seed dosyalarını yönetir:
liste/kart görünümü arasında geçiş (tercih diğer listelerle aynı yerde, `admin.lists.states`
2026-10-04 15:04:16 +00:00
altında saklanır), arama, ağacın tümünü açma/daraltma (arama sırasında gruplar zaten açıktır), düzenleme (dosyanın yerine geçen yeni bir çalıştırma), silme, veritabanı
migrate tetikleme, dışa aktarma ve içe aktarma. Liste, wizard'ın ilk deploy damgasına
(`Wizard.SeededAt`) göre **tersine** sıralıdır — en son üretilen ekran en üstte durur; damgası
okunamayan dosyalar sona düşer. Silme, bağlı wizard'ı olan dosyada reddedilir; onay diyaloğu önce
`DeleteAnalysis` ile düşürülebilecek tabloyu sorar, sonuç her kaydın akıbetiyle (`Deleted` / `Kept` +
kullananlar / `NotFound`) rapor olarak gösterilir (`WizardRecordCleaner`, `WizardTableCleaner`;
2026-10-19 14:35:08 +00:00
`lowcode.instructions.md` §4.5). Butonlar `Sas.Listforms.Wizard.*` yetkileriyle gizlenir; asıl
kontrol `ListFormWizardAppService` üzerindedir.
### 7.7. İçe/dışa aktarma
2026-06-09 12:54:05 +00:00
**Veri (ekran içi)**
2026-10-11 19:57:23 +00:00
- **Dışa aktarma:** Grid'de pdf ve xlsx, Pivot'ta xlsx (`ExportJson.Enabled` + `PermissionJson.E`; grid durumuna ve görünür sütunlara saygı duyar).
- **İçe aktarma:** `components/importManager` altındaki dosya yükleme → önizleme → ilerleme →
sonuç akışı; sonuçlar `ListFormImportLog` üzerinde satır bazında raporlanır. Şablon dosyası
2026-10-11 19:57:23 +00:00
ekran tanımından üretilir (`.xlsx/.xls/.csv`, en çok 10 MB; yalnız INSERT, `Insert*Command` kancaları
çalışır, `InsertServiceAddress` atlanır).
2026-06-09 12:54:05 +00:00
**Ekran tanımı (wizard seed paketi)**
Bir ekranın tanımı, bağımlılıklarıyla birlikte tek bir zip olarak taşınır. Zip içindeki yollar seed
2026-10-05 12:58:59 +00:00
klasörünün kökü baz alınarak yazılır; arşiv başka bir ortamda aynı klasör düzenine doğrudan
açılabilir. Eski arşivlerdeki `host/` öneki import'ta yok sayılır.
Toplama **özyinelemelidir** (`WizardExportCollector`): alt formların wizard dosyaları kuyruğa
eklenir ve onların bağımlılıkları da aynı kurallarla toplanır. Böylece zip tek başına çalışır.
**Talep export'u** (`api/app/list-form-wizard/export-dev-request{,-analysis}`) bir geliştirme
talebinin `DevRequest` bağı taşıyan bütün wizard'larını aynı toplayıcıda tek kapanışa koyar;
paylaşılan dosyalar arşive bir kez, birleşik satırlarla girer ve import arşivi tek oturumda uygular.
| Yol | İçerik |
| --- | --- |
| `wizard/{dosya}.json` | Wizard tanımının kendisi **ve** `SubFormsDto[].Code` ile bağlı alt form ekranlarının wizard dosyaları (özyinelemeli) |
2026-10-19 14:35:08 +00:00
| `data/Sas.DeveloperKit.CustomComponents/{Id}.json` | Custom yolunda bağlı component ve bağımlılıkları — yalnızca bu bileşenlerin satır dosyaları |
| `data/{ListFormCode}.json` ya da `data/{ListFormCode}/{anahtar}.json` | Ekranın `SeedFilePath` değeri doluysa kendi veri aynası (dosya ya da klasör) |
| `crud/{entity}.json` | Component'lerin/nesnenin kullandığı CRUD endpoint tanımları |
2026-10-05 12:58:59 +00:00
| `{sql\|postgres}/[host/]{object\|execute}/{nesne}.sql` | `SelectCommand`'a karşılık gelen SQL nesnesi, lookup sorgularının (`LookupDto.LookupQuery`) `FROM`/`JOIN` ile işaret ettiği nesneler ve Custom yolunda CRUD endpoint'lerinin dayandığı tablolar |
2026-10-05 14:11:06 +00:00
Script dosyaları sabit bir sağlayıcı/klasör kombinasyonundan **tahmin edilmez**: seed kökündeki
script klasörleri (`sql/object`, `sql/host/execute`, `postgres/execute` …) diskten taranır ve nesne adıyla eşleşen her
dosya arşive girer. Yeni bir sağlayıcı ya da script klasörü eklendiğinde toplayıcı değişmez.
Script bulunamaması **uyarı üretmez**: her tablo seed ile yönetilmez — ABP'nin kendi tabloları
(`AbpUsers`, `AbpRoles` …) EF migration'larıyla kurulur ve seed script'leri yoktur. "Bu tablo
hedefte var mı" sorusunun tek doğru cevabı hedef veritabanındadır; kararı import analizi verir,
export varsayımda bulunmaz.
Çözülemeyen bir **kayıtlı referans** (seed dosyasında adı geçen ama karşılığı bulunmayan alt form,
`SeedFilePath` veri aynası, `DataSources[].SeedFile` CRUD dosyası, custom component satırı)
indirmeyi engellemez ama sessiz de kalmaz: `ExportAnalysis` ucu indirmeden önce arşivin tam
içeriğini ve eksikleri döndürür, Wizard File Manager eksik varsa önce bu listeyi gösterir.
**İçe aktarma dört adımlı bir sihirbazdır** (`WizardImportDialog`, `Steps` bileşeni):
| Adım | İçerik | Geçiş koşulu |
| --- | --- | --- |
| **Dosyalar** | Zip girdileri ve durumları; çakışanlar Monaco `DiffEditor` ile çözülür | Çözülmemiş çakışma kalmamalı |
| **Kontroller** | Analiz bulguları ve zip'te atlanan girdiler. Engelleyiciler iki başlıkta ayrılır — **arşiv eksik ya da bozuk** (burada giderilemez, kaynak ortamda export tekrarlanmalı; düzeltme düğmesi yoktur) ve **ortamda giderilebilir**. Tabloya dokunan bulgularda mevcut `SqlTableDesignerDialog` açılır — şema farkında **Tabloyu düzenle** (düzenleme modu), eksik tabloda **Tabloyu oluştur** (oluşturma modu, ad ön dolu); deploy sonrası analiz kendiliğinden yenilenir (önceki oturum bırakılır) | Engelleyici (`Error`) bulgu kalmamalı |
| **Yapılacaklar** | Onaylandığında ne olacağının tam listesi: yazılacak dosyalar, çalışacak SQL script'leri, eklenecek kolonlar (cümleleriyle), uygulanacak CRUD ve veri dosyaları, kurulacak ekranlar. Sıra sunucudaki kapanış sırasıyla aynıdır | Onay |
| **Uygulama** | Dosya dosya yazım, kapanış adımlarının sonucu | — |
Geriye dönüş her zaman serbesttir; ileri atlama bağımlılık koşullarına bağlıdır. Adım başlıklarına
tıklanarak da gezinilebilir.
İçe aktarma **toplu (batch) bir işlemdir**: engelleme oturum genelindedir, dosya bazlı değildir.
Bir tek engelleyici bulgu tüm zip'i durdurur ve o dosyanın işaretini kaldırarak aşılamaz — yarım
kurulum üretmemenin tek güvenli yolu budur. Karşılığında, tabloya dair her engel diyalogdan
çıkmadan giderilebilir (tablo tasarımcısı → deploy → analiz yenilenir). Tabloyla ilgisi olmayan
engeller (eksik alt form, eksik custom component bağımlılığı, bozuk JSON) zip'in kendisinin eksik
olduğunu gösterir; bunlar kaynakta düzeltilip yeniden export edilmelidir.
Adımların altında yatan üç sunucu işlemi şunlardır:
1. **Analiz** (`AnalyzeImport`) — zip, seed kökü altındaki `.imports/{importId}/staged` klasörüne
açılır (nokta ile başladığı için seeder taramalarına girmez) ve her dosya hedefteki karşılığıyla
karşılaştırılır: `New` (hedefte yok), `Identical` (aynı, sorulmaz), `Conflict` (farklı).
Çakışan dosyalar Monaco `DiffEditor` ile karşılaştırılıp birleştirilir.
`data/*.json` girdilerinde karşılaştırma **satır bazlıdır**: hedef dosyadan yalnızca gelen
satırların anahtarları süzülür, dosya düzeyindeki `GeneratedAt`/`Order` karşılaştırmaya girmez.
Böylece aktarılan kaydın dışındaki satırlar fark olarak görünmez; gelen satırların hiçbiri
hedefte yoksa dosya var olsa bile durum `New` (ekleme), varsa ve içerik aynıysa `Identical`
(hiç aktarılmaz), farklıysa `Conflict` (güncelleme) olur.
2. **Uygulama** (`ApplyImport`) — dosyalar tek tek yazılır, ilerleme satır satır gösterilir. Yazılan
her dosyanın önceki hâli `.imports/{importId}/backup` altına alındığı için hata durumunda
`RollbackImport` ile toplu işlem geri alınabilir.
3. **Kapanış** (`CompleteImport`) — oturum kapatılmadan önce zip'in getirdiği dosyalar
**bağımlılık sırasıyla** veritabanına uygulanır. Sıra bilinçli ve sabittir:
**(a)** `{sql|postgres}/{object,execute}/*.sql` scriptleri çalıştırılır (`object` önce) — tablo
yoksa burada oluşur, aksi halde sonraki iki adım eksik tablo yüzünden baştan düşerdi;
**(b)** hedefte eksik olan kolonlar eklenir (aşağıya bakın);
**(c)** `crud/*.json` dosyaları `ICrudEndpointSeedApplier` ile uygulanır;
**(d)** `data/*.json` dosyaları
`IListFormSeedDataApplier.ApplyFilesAsync(..., overwriteExisting: true)` ile veritabanına
uygulanır: tabloda olmayan satır eklenir, var olan satır dosyadaki değerlerle **güncellenir**
(migrate/seed akışından farkı budur — orada dosya bayat olabileceği için canlı kayda
dokunulmaz). Kayıt daha önce **soft delete** edilmişse silinme izleri de temizlenir
(`IsDeleted`, `DeletionTime`, `DeleterId`); aksi halde satır tabloda durur ama ekranın
varsayılan süzgeci (`"IsDeleted" = 'false'`) onu gizlemeye devam ederdi. Hangi kolonların
sıfırlanacağı ekranın `DeleteCommand`'ından okunur, hard delete yapan ekranda hiçbir şey
eklenmez. Hedefle **zaten aynı** olan (`Identical`) veri dosyaları da uygulanır: yazacak bir şey
yoktur ama satırlar veritabanında eksik ya da silinmiş olabilir. Kullanıcının "mevcudu koru"
dediği (farklı olup yazılmayan) dosyaya dokunulmaz. Anahtarı olmayan satır güncellenemez,
atlanır; eklenen/güncellenen/atlanan satır
sayısı dialogda raporlanır. Bu adım `RollbackImport`
kapsamında **değildir** — geri alma bu adımdan önce yapılır.
5. **Ekran tanımları** (`wizard/*.json`) — ekran import'ta **kurulmaz**: dosyaya `Pending` yazılır,
Wizard Manager onu "Migrate bekliyor" gösterir ve ekran, deploy'daki gibi, DB Migrate'te
2026-10-20 08:52:23 +00:00
`WizardDataSeeder` tarafından kurulur (hedefte kurulu olsa da dosyadaki hâliyle). Ekranın bütün
ayarları dosyanın `Wizard` bölümündeki bloklardadır; seeder onları olduğu gibi uygular
(`BuildListForm/BuildFields`), **yeniden türetmez**. Wizard'ın menü/yetki/dil satırlarını taşıyan platform dosyaları 4. adımda
uygulanmaz; onları da migrate ekler.
**Analiz doğrulama da yapar** (`WizardImportValidator`). Zip'in hedef ortamda yarım bir kurulum
bırakıp bırakmayacağı dosyalar yazılmadan **önce** denetlenir; bulgular `Error`/`Warning` olarak
raporlanır ve en az bir `Error` varken import başlatılamaz — sunucu oturuma `blocked` işaret
dosyası bırakır, dolayısıyla `ApplyImport` ucu doğrudan çağrılsa bile reddeder.
| Kural | Ağırlık |
| --- | --- |
| Bozuk JSON (`wizard`, `crud`, `data`) | Error |
2026-10-20 08:52:23 +00:00
| Wizard bölümü ya da adı olmayan wizard dosyası; `Data` başlığı (`SelectCommand`/`KeyFieldName`) eksik List wizard'ı | Error |
| Ekranın tablosu (`SelectCommand`, `Table`/`View`) hedefte **yok** ve zip onu oluşturan `.sql` dosyasını **taşımıyor** | Error |
| `crud/{Entity}.json`'un tablosu hedefte yok ve zip'te oluşturan script yok | Error |
| `data/{code}.json`'un ekranı ne zip'te ne hedefte var | Error |
| Custom component'in `Dependencies` ya da `DataSources[].SeedFile` bağımlılığı ne zip'te ne hedefte var | Error |
| Custom wizard'da bileşen adı / rota yolu boş | Error |
| Alt form (`SubFormsDto[].Code`) ne zip'te ne hedefte var | Error |
| Nesne varlığı sorgulanamadı (bağlantı/izin) ve script de yok | Warning |
| Zip yalnızca karşı sağlayıcının (`sql` ↔ `postgres`) scriptlerini taşıyor | Warning |
| Ekranın `SeedFilePath` veri dosyası ne zip'te ne hedefte var; boş CRUD/veri dosyası | Warning |
| Script'in kurduğu tablo hedefte **var** ama bir kolonu eksik | Warning — kapanışta eklenir |
| Eksik kolon `NOT NULL`, varsayılanı yok ve tabloda kayıt var | Error (otomatik eklenemez) |
| Var olan kolonun uzunluk / kesinlik / ondalık / nullability değeri script'ten farklı | Error |
| Hedefte script'te olmayan fazladan kolon var | Warning (dokunulmaz) |
Eksik tablo ölçütü "veritabanında var" değil, "veritabanında var **ya da** zip onu oluşturan
scripti taşıyor"dur — kapanış scriptleri veri satırlarından önce çalıştırdığı için zip'te scripti
olan tablo eksik sayılmaz.
#### Şema karşılaştırması — tablo var ama yapısı farklıysa
Üretilen tablo script'leri `IF OBJECT_ID(...) IS NULL BEGIN CREATE TABLE ... END` kalıbıyla sarılıdır:
hedefte tablo varsa `CREATE` bloğu **tamamen atlanır**. Dolayısıyla eski şemalı bir tabloya yeni bir
kolon kendiliğinden gelmez; ekran o kolonu bir alan olarak tanımladığı için açılışta "geçersiz kolon"
hatası verir. Bu sessiz uyuşmazlığı analiz adımı yakalar (`SqlCreateTableParser` +
`WizardImportValidator`): zip'in getirdiği `CREATE TABLE` bildirimi, hedef tablonun
`INFORMATION_SCHEMA.COLUMNS` üzerinden okunan canlı şemasıyla karşılaştırılır.
Ayrım **kayıp riskine** göre yapılır:
- **Eksik kolon eklenir.** Kolon eklemek kayıpsız bir işlemdir; analiz "şu kolon eklenecek" diye
uyarır, çalıştırılacak cümleyi gösterir ve kapanışta (SQL script'lerinden sonra, CRUD ve veriden
önce) uygular. Tek istisna: `NOT NULL` olup varsayılan değeri olmayan bir kolon dolu bir tabloya
eklenemez — kolona hangi değerin yazılacağı bir iş kararıdır, bu durumda import durdurulur ve
cümle kullanıcıya bırakılır. Tablonun boş olup olmadığı çalışma anında sorgulanır; boş tabloya
böyle bir kolon güvenle eklenir.
- **Var olan kolon değiştirilmez.** Uzunluk/kesinlik/ondalık/nullability farkında import durur.
Daraltma veri keser, tip dönüşümü başarısız olabilir; doğru işlem veriye bakmayı gerektirir.
Fark somut olarak bildirilir ("uzunluk hedefte 200, script'te 300"), düzeltmeyi kullanıcı yapar.
- **Hiçbir kolon düşürülmez, hiçbir tablo `DROP` edilmez.** Hedefteki fazla kolon yalnızca
bilgilendirme olarak listelenir.
Karşılaştırma **tip adına bakmaz**. Sağlayıcı kataloğu kendi kanonik adını raporlar (PostgreSQL'de
`VARCHAR` → `character varying`, `TIMESTAMP` → `timestamp without time zone`); script'teki yazılışla
metin olarak karşılaştırmak her `VARCHAR` kolonunda sahte fark üretirdi. Yalnızca her iki
sağlayıcıda da aynı anlamı taşıyan ölçüler karşılaştırılır: uzunluk, kesinlik/ondalık, nullability.
Script bir tablo kurmuyorsa (procedure, view) ya da `CREATE TABLE` çözülemiyorsa karşılaştırma
sessizce atlanır — tahmin yürütülmez.
`data/*.json` dosyaları kapsamdaki bütün satırları taşıdığı için üzerine yazılmaz: satırlar anahtar
alan üzerinden birleştirilir (gelen satır varsa günceller, yoksa eklenir, hedefteki diğer satırlar
korunur). Diff ekranında düzenlenen içerik de aynı sözleşmeyi taşıdığı için elle birleştirme bu
davranışı bozmaz.
Güvenlik sınırları: yalnızca `wizard`, `crud`, `data`, `sql`, `postgres` kök klasörleri
(SQL sağlayıcı klasörlerinin altında yalnızca `object` ve `execute`) kabul edilir; dosya başına
5 MB, arşiv başına 50 MB, en çok 500 girdi. Kabul edilmeyen girdiler analiz sonucunda uyarı
2026-10-19 14:35:08 +00:00
olarak listelenir. Yetkiler: `Sas.Listforms.Wizard.Export` / `Sas.Listforms.Wizard.Import`.
---
2026-06-09 12:54:05 +00:00
## 8. Developer Kit
2026-06-09 12:54:05 +00:00
Teknik kullanıcılar için `/admin/developerkit` altında toplanan araçlar:
2026-06-09 12:54:05 +00:00
| Araç | Route | Ne işe yarar |
2026-06-09 12:54:05 +00:00
| --- | --- | --- |
| **SQL Query Manager** | `/admin/sqlQueryManager` | Nesne gezgini, Monaco tabanlı SQL editörü, sonuç grid'i, **tablo tasarımcısı**, **view tasarımcısı** ve **CRUD endpoint yönetimi**. |
2026-10-19 14:35:08 +00:00
| **Custom Endpoint** | `/admin/list/Sas.DeveloperKit.CustomEndpoints` | SQL veya servis tanımından REST endpoint üretimi; GET/POST/PUT/DELETE için ayrı yetki kapıları (`Sas.DeveloperKit.CustomEndpoints.*`) ve kullanıcı/rol/global erişim kuralları. |
| **CRUD Endpoint** | SQL Query Manager içinde | Tablo tanımından tam CRUD endpoint kümesi üretimi. Ayrı ekranı yoktur; bkz. aşağıdaki not. |
2026-10-19 14:35:08 +00:00
| **Dynamic Service** | `/admin/list/Sas.DeveloperKit.DynamicServices` | Liste bir ListForm ekranıdır (KPI şeridi, Swagger butonu, kart/ızgara görünümü); kod yazma listeden açılan `DynamicServiceEditor` diyalogundadır: Monaco editörü, Roslyn ile derleme (`TestCompile`), yayınlama (`Publish`) ve çalışan uygulamaya controller olarak kaydetme. Yetkiler: Create/Edit/Delete/Manage/TestCompile/Publish/ViewCode. |
| **Custom Component** | `/admin/list/Sas.DeveloperKit.CustomComponents` | React bileşenini veritabanında saklama; `@babel/standalone` ile tarayıcıda derleyip route'a bağlama. Liste bir ListForm ekranıdır (KPI şeridi, ızgara/kart görünümü, grid'in kendi ekleme/düzenleme formu); satırdaki **Design** butonu `/admin/developerkit/components/design/{id}` tasarımcısını, **Open URL** butonu bileşenin `RoutePath`'ini açar. Kayıtlar `configs/seeds/data/Sas.DeveloperKit.CustomComponents/{Id}.json` klasörüne senkronlanır. Yetkiler `Sas.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`. |
| **Visual Designer** | `/admin/developerkit/components/design/:id` | Custom Component listesindeki **Design** butonundan açılır; sürükle-bırak kanvas ile bileşen üretimi ve kod üretimi (`visualDesigner/codeGenerator.ts`). |
2026-10-19 14:35:08 +00:00
| **ListForm** | `/admin/list/Sas.Listforms.Listform` | Var olan ekranların ham tanım listesi; buradan `/admin/listform/edit/{kod}` sekmeli editörüne geçilir. |
| **Wizard Manager** | `/admin/listform/wizardManager` | Üretilmiş wizard seed dosyalarının listesi/kartı, düzenleme, silme, DB migrate, export/import. Menüsüz alt formlar onları sekme olarak açan ekranın altında iç içe görünür; kendi menüsü olan ekran başka bir ekranın sekmesi olsa da kökte kalır (export kapanışı onu yine taşır). Wizard'ı üreten talep listede görünür ve kaydına bağlanır; talebin bütün wizard'ları tek arşivde export edilir (`export-dev-request`) ya da bağımlılık sırasıyla tek işlemde silinir (`delete-dev-request`; alt formlar önce, talebin ekranları birbirini kullanım saymaz, tablolar en sonda — `lowcode.instructions.md` §4.5). Yetki `Sas.Listforms.Wizard{,.Create,.Update,.Delete,.Export,.Import}`. |
> Bu ekranların artefakt şemaları, enum değerleri ve çalışan örnekleri
> [`.github/instructions/lowcode.instructions.md`](.github/instructions/lowcode.instructions.md)
> dosyasındadır.
### 8.1. SQL View Designer
`SqlViewDesignerDialog` (+ `sqlViewDesigner/`), SSMS Query Designer düzeninde görsel view
oluşturma aracıdır. Üç panel:
- **Diagram** (`DiagramPane.tsx`) — `ViewSource` kutuları (x/y konumlu, daraltılabilir, yalnızca
seçili kolonları gösterebilir) ve aralarındaki JOIN okları. Kaynak bir tablo/view olabileceği
gibi `CROSS/OUTER APPLY` ya da türetilmiş bir alt sorgu da olabilir; alt sorgunun gövdesi
olduğu gibi saklanır. JOIN türleri: `INNER`, `LEFT`, `RIGHT`, `FULL`, `CROSS`; koşul
operatörleri `=`, `<>`, `>`, `>=`, `<`, `<=`.
- **Criteria grid** (`CriteriaGrid.tsx`) — Column / Alias / Output / Group By / Sort / Filter /
Or… satırları. Bir satır ya `sourceId + columnName` referansıdır ya da serbest bir SQL
ifadesidir. Group By sütunu `GroupBy`, `Where` (satır çıktıya girmez, yalnızca filtre taşır) ve
`SUM`/`COUNT`/`COUNT_DISTINCT`/`AVG`/`MIN`/`MAX` değerlerini alır. Filtre hücreleri SSMS'teki
gibi serbest yüklemdir (`> 100`, `LIKE '%abc%'`, `IS NULL`).
- **T-SQL önizleme** — model → SQL üretimi (`generateViewSql`).
Tasarımcı tek yönlü çalışır: **model → T-SQL**. Mevcut bir view açılırken `parseViewSql` ile geri
okunmaya çalışılır; üretilen kanonik şekle uymayan bir tanım gelirse dialog **ham SQL moduna**
düşer ve kullanıcı Query Editor'dan devam eder (`onOpenInEditor`). PostgreSQL bağlantılarında
`isPostgreSql` bayrağı ile söz dizimi buna göre üretilir.
2026-06-09 12:54:05 +00:00
> **CRUD Endpoint, SQL Query Manager ile birleştirildi.** Ayrı `/admin/developerkit/endpoints`
> ekranı ve menüsü kaldırıldı. Endpoint üretme, aktif/pasif etme, test etme ve silme işlemleri
> artık iki yerden yapılır: SQL Query Manager nesne gezgininde tablo satırının **CRUD Endpoints**
> aksiyonu (ve seçili tablolar için toolbar'daki toplu üretim butonu), ayrıca ListForm
> sihirbazının **Adım 2 – Veri Kaynağı** ekranındaki aynı diyalog. Ayrıca **tablo
> tasarımcısında deploy öncesi bir adım** vardır: endpoint üretilip üretilmeyeceği ve
> hangi operasyonların (GetList/GetById/Create/Update/Delete) aktif başlayacağı seçilir
> ("tümünü seç" destekli); seçilmeyenler pasif olarak kaydedilir, sonradan diyalogdan
2026-10-19 14:35:08 +00:00
> açılabilir. Hepsi `CrudEndpointDialog` bileşenini kullanır. Yetki `Sas.SqlQueryManager` altındaki
> `Sas.SqlQueryManager.CrudEndpoints`'tir; izin verilmemişse butonlar görünmez ve
> `crud-endpoint-generate` uçlarının tamamı (base CRUD metotları dahil) reddedilir.
2026-10-05 12:58:59 +00:00
> Üretilen endpoint'ler `configs/seeds/crud/{EntityName}.json` olarak
> seed dosyasına yazılır; `CrudDataSeeder` aynı dosyaları okuyup endpoint'leri geri yükler.
> **Tablo tasarımı varsayılanı:** Yeni tablo oluştururken tenant ve tam denetim (audit) kolonları
> varsayılan olarak eklenir: `Id`, `TenantId`, `CreationTime`, `CreatorId`, `LastModificationTime`,
> `LastModifierId`, `IsDeleted`, `DeletionTime`, `DeleterId`. Kullanıcı açıkça istemedikçe bu
> kolonlar çıkarılmaz.
2026-06-09 12:54:05 +00:00
### 8.2. Custom Component ve Visual Designer
**Saklanan tanım.** Bir custom component `Name`, `RoutePath`, `Code` (JSX), `Props`,
`Dependencies` ve `DataSources` alanlarından oluşur. Görsel tasarımcı dokümanı iki yerde tutulur:
`Props.visualDesigner` içinde (nodes, canvas, lifecycle, dataSources) ve üretilen kodun ilk
satırındaki `/*__SOZSOFT_VISUAL_DESIGNER__…__*/` yorumunda. Böylece yalnızca kod elinizdeyse bile
tasarımcı dokümanı geri okunabilir. `sourceMode` alanı `visual` ya da `code` olur; kod moduna
geçilen bir bileşen kanvasa geri dönmez.
**Seed senkronizasyonu.** Bileşenlerin seed kaynağı Custom Components ekranının veri klasörüdür:
2026-10-19 14:35:08 +00:00
`configs/seeds/data/Sas.DeveloperKit.CustomComponents/{Id}.json` — her
bileşen kendi dosyasındadır; bileşen kodu satırın içinde durduğu için tek dosya hızla büyürdü.
Bileşene yazan **iki yol** da bu klasöre işlenir (`ListFormSeedDataSynchronizer`): listeden yapılan
ekleme/güncelleme/silme ve Visual Designer'ın kaydı (`CustomComponentAppService`). Veritabanı
sıfırlandığında `ListFormSeedDataApplier` satırları geri yazar; `Code`, `Props` ve `DataSources`
kolonları bu yüzden ekranda gizli alan olarak tanımlıdır. `DataSources` sütunu tasarımcı
dokümanından türetilir (`CustomComponentDataSourceResolver`): Data sekmesindeki her endpoint
`method + path` ile CRUD endpoint kataloğunda aranır, eşleşenler `EntityName` +
`crud/{EntityName}.json` referansını taşır, elle yazılmış olanlar listede kalır ama bu alanları
boş gelir. Wizard export'u bu referansları izleyerek bağlı `crud/*.json` dosyalarını zip'e ekler.
**Toolbox.** Kanvasa bırakılabilecekler beş aileye ayrılır:
| Aile | İçerik |
| --- | --- |
| `layout` | `PageContainer`, `FlexRow` (kolon sayısı, ilk kolon genişliği, hizalama, gap, wrap), `Spacer` |
| `data` | `Form` — ASP.NET'in Form + FormView karşılığı; dört CRUD endpoint'ini sahiplenen kapsayıcı |
| `platform` | `ListView`, `DataGridView`, `TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` — hepsi `listFormCode` ile bir ListForm ekranını gömer |
| `html` / `ui` | Ham HTML etiketleri ve `components/ui` tasarım sistemi bileşenleri (sözleşmeleri metadata'dan okunur) |
| `custom` | Başka custom component'ler (bağımlılık olarak kaydedilir) |
**Form bileşeni.** `selectEndpoint` / `insertEndpoint` / `updateEndpoint` / `deleteEndpoint`,
`keyFieldName`, `collectionPath`, `keySource` + `keyParamName`, `previewKeyValue`, `autoLoad`,
`showToolbar`, `columnCount`, `gap` özellikleriyle yapılandırılır. İçine bırakılan her bileşen
Select sonucunun bir kolonuna bağlanır ve Save/Delete üzerinden geri yazar.
**Veri bağlama.** `DesignerBinding` bir kaynağa (`sourceId`) ve yola (`path`) bağlanır; `labelPath`
/ `valuePath` seçim bileşenleri içindir. `columns` ile ekranda görünmeyen ek sütunlar da taşınır
ve script'ten `refs.<ad>.getColumn('sutun')` ile okunup başka bir bileşenin değerine ya da Form
kaydına yazılır. Filtreler CrudEndpoint `GetList` sözleşmesine göre üretilir: `eq` çıplak query
parametresi (`?RoleId=…`), diğerleri son ek taşır (`?Name.contains=…`). Operatörler: `eq`, `ne`,
`contains`, `startswith`, `endswith`, `gt`, `gte`, `lt`, `lte`, `in`, `isnull`, `notnull`.
**Yetki modeli.** İki katmanlıdır:
- **Node görünürlüğü** — `designerPermission` prop'u dolu olan bir düğüm, yetki verilmemişse
render edilmez.
- **Form komutları** — her komut (`selectPermission`, `insertPermission`, `updatePermission`,
`deletePermission`) ya serbesttir (boş) ya da bir yetkiye bağlıdır. `Otomatik` modda temel,
Wizard'ın bu bileşen için ürettiği okuma yetkisidir (menü kaydının korunduğu yetkinin aynısı) ve
komutlara `''` / `.Create` / `.Update` / `.Delete` son ekleri eklenir. `Özel` modda yetki adı
elle yazılır.
**Script.** Tasarımcı, ListForm Editor Script ile aynı ortak dialog'u kullanır
(`designerScriptDialect.ts` + `designerScriptRecipes.ts`); tarif grupları bileşen erişimi, API
çağrıları, form ve olay/sayfa başlıkları altında toplanır.
**Kanvas doğruluğu.** Tasarım zamanında görülen, canlıda görülendir; bunu iki mekanizma sağlar.
(1) *Yerleşim sınıflarının taşınması*: kanvas her düğümü seçim/sürükleme için bir sarmalayıcıya
alır ve üst öğe grid/flex olduğunda asıl öğe o sarmalayıcı olur. `splitLayoutClasses` düğümün
yerleşim sınıflarını sarmalayıcıya geçirir — `col-span`/`row-span`/`order`/`self`/`flex-1`/`basis`,
sabit ve kesirli `w-*`, kenar boşlukları taşınır; konum sınıfları (`absolute`, `inset-*`, `z-*`,
`w-full`) her ikisinde de kalır. Görünürlük sınıfları da sarmalayıcıya kopyalanır (`hidden lg:flex`
→ sarmalayıcıda `hidden lg:block`): aksi hâlde mobilde gizlenmesi gereken masaüstü şeridi yine bir
grid hücresi işgal eder ve komşularını yanlış hücreye iter. `fixed` kanvasta akışa çevrilir, yoksa
düğüm tasarım yüzeyinden taşardı. (2) *Cihaz çerçevesi*: araç çubuğundan masaüstü/tablet/mobil seçildiğinde kanvas kendi
belgesinde çizilir (`CanvasFrame`), böylece `md:`/`lg:` kuralları tarayıcı penceresine değil seçilen
genişliğe göre çözülür. Cihaz modunda tasarım yüzeyi kenar boşluğunu bırakır: kanvas o cihazın ekranıdır. `Responsive`
seçimi aynı sayfada, çerçevesiz ve boşluklu çizer. (3) *Gezinme kapalıdır*: kanvastaki bir bağlantıya
tıklamak seçim yapar, sayfayı değiştirmez — tıklama yakalama evresinde durdurulur, `react-router`'ın
`Link`'i de `defaultPrevented` görünce gezinmez.
**Diğer notlar.** `PlatformIcon` tasarımcı dokümanındaki ikon adını çözer; `selectComponents.ts`
`Select.componentAs` için saklanan adı (`ReactSelect`, `CreatableSelect`, `AsyncSelect`) gerçek
bileşene çevirir — kanvas ve üretilen bileşenin aynı adı aynı şekilde yorumlamasını sağlayan tek
2026-10-19 14:35:08 +00:00
nokta budur. Yeni bileşen `configs/seeds/data/Sas.DeveloperKit.CustomComponents/` klasöründe
kendi `{Id}.json` satır dosyası olarak başlar.
**Dinamik servis yaşam döngüsü:** `DynamicServiceCompiler` kodu derler →
`DynamicAssemblyRegistrationService` assembly'yi tenant bağlamıyla kaydeder →
`ActionDescriptorChangeProvider` MVC'ye route tablosunun değiştiğini bildirir →
`DynamicControllerActivator` bağımlılıkları enjekte eder. Uygulama yeniden başlatılmaz.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 9. Dinamik Menü, Route ve Yetki Modeli
2026-06-09 12:54:05 +00:00
```
Permission (ABP) ──┐
├──► Menu.RequiredPermissionName ──► menüde görünürlük
Route.Authority ──┘ └► PermissionGuard ile erişim
```
2026-06-09 12:54:05 +00:00
2026-10-19 14:35:08 +00:00
- **Route** (`Entities/Tenant/Adm/Route.cs`): `Key`, `Path`, `ComponentType`,
`ComponentPath`, `RouteType` (`public` / `protected`), `Authority[]`.
- **Menu** (`Menu.cs`): `Code`, `ParentCode`, `DisplayName`, `Url`, `Icon`, `Order`,
`RequiredPermissionName`, `Target`, `IsDisabled`, ayrıca `UserId` / `RoleId` / `CultureName`
ile kullanıcı, rol ve dil bazlı menü varyantları.
2026-10-21 20:40:03 +00:00
- **Menü yöneticisi** (`/admin/menuManager`): sürükle-bırak ağaç ile menü düzenleme. Kayıtlar `Sas.Menus.Menu` ekranıyla aynı seed dosyasına (`data/Sas.Menus.Menu.json`, `SeedSync*` bayraklarına bağlı) yazılır; menü adları `Sas.Languages.LanguageText/{CultureName}.json`'a.
- Frontend'de `dynamicRoutesContext` route'ları çeker, `dynamicRouteLoader` bunları fiziksel
view'lara veya runtime derlenmiş Custom Component'lere eşler, `DynamicRouter` React Router
ağacını kurar; her korumalı route `ProtectedRoute` + `PermissionGuard` ile sarılır.
- Aynı yolu bir fiziksel route ile bir Custom Component paylaşıyorsa **Custom Component kazanır**;
karşılığı olan dosyası bulunmayan rota kaydı hiç yüklenmez. Public site menüsü bu modelin
dışındadır: `Pub` modülü altında tutulur ve uygulama konfigürasyonuyla taşınır (bkz. §10.4).
2026-06-09 12:54:05 +00:00
2026-09-29 11:46:39 +00:00
**Modüller ve kök menüler.** Kök menüler bir modüle bağlıdır (`Menu.ModuleId` →
2026-10-19 14:35:08 +00:00
`data/Sas.Menus.Module.json`): `Sas` → `Sas` (Saas), `Adm` → `Adm`,
`Hr` → `Hr` (İK), `Pub` → `Pub.Header` (public site). Tablo ön eki aynı modülden gelir
2026-09-29 11:46:39 +00:00
(`MenuPrefix`: `Sas_`, `Adm_`, `Hr_`; `TableNameResolver`). Her yetki satırı `Modules`
(pipe ile ayrılmış) değeri taşır; kiracının modül listesi doluysa menü (`MenuNavigationProvider`;
hem `menus.protected` hem Menü Yönetimi listesi bu süzgeçten geçer) ve rol
2026-09-29 11:46:39 +00:00
yetki ekranı (`RolesPermission.tsx`) yalnızca kesişen modüllerin yetkilerini gösterir. Sözleşme:
`Sas` host'ta görünürlüğü, `Adm` ve `Hr` kiracıdaki görünürlüğü belirler — Saas/Administration
yetkileri `Sas|Adm`, İK yetkileri `Sas|Hr` taşır.
2026-10-19 14:35:08 +00:00
**İK (Hr) modülü.** Menü `Hr` → `Hr.Definitions` (Yetenek Tipleri `Hr.SkillType`,
Departmanlar `Hr.Department`, Pozisyonlar `Hr.JobPosition`) ve doğrudan
Organizasyon Şeması `Hr.OrgChart` (`/admin/organization`; menü ve route aynı yetkiyi ister).
Yetki grubu `Hr`; ListForm kodu = menü kodu = yetki adı = dil anahtarı. Tablolar
2026-09-29 14:18:47 +00:00
`Hr_T_SkillType` / `Hr_T_SkillLevel` / `Hr_T_Skill` / `Hr_T_Department` / `Hr_T_JobPosition`
(hepsi kiracı, `IMultiTenant`, `Guid` anahtar); ekranlar `ListFormSeeder_Hr.cs`'tedir, entity'ler
`Entities/Tenant/Hr/Definitions`. Departman ve pozisyon **şube bazlıdır**: `BranchId` zorunlu kolondur
(`Guid`, `NOT NULL`), listenin ilk sütunu Şube Adı'dır (kullanıcının şubeleri `LookupQueryValues.UserBranchValues`,
yeni kayıt ilk şubeyle açılır `UserDefaultBranchValue`);
2026-09-29 11:46:39 +00:00
departman lookup'ı şubeye göre süzülür (`BranchDepartmentValues`). Host varsayılan şubesi sabit
kimlikle kurulur (`PlatformConsts.Branches.BranchIdDefaultValue`) ve
2026-10-19 14:35:08 +00:00
`data/Hr.{Department,JobPosition}.json` satırları bu kimliğe bağlıdır. Numaratör
(`Adm.Definitions.Sequence`) Administration › Definitions altındadır. Intranet de İK'dadır: menü
`Hr.Intranet`, ekran/yetki kodları `Hr.Intranet.*`, tablolar `Hr_T_*` (mesajlaşma dahil),
entity'ler `Entities/Tenant/Hr/Intranet`, seed `data/Hr.Intranet.*.json`; pano widget kaydı
(`Sas_T_IntranetWidget`) Saas'ta kalır. Vardiya, çalışma takvimi, olay günlüğü, Eğitimler
2026-10-19 14:35:08 +00:00
(`Hr.Training`), Araçlar (`Hr.Vehicles`), disiplin dosyaları ile izin/masraf/yan hak/disiplin cezası türleri wizard ile
2026-10-04 08:14:38 +00:00
üretilmiştir — entity yoktur, ekran `wizard/{Ad}.json`, tablo `sql/object/Hr_T_*.sql`. **İşe Alım**
2026-10-19 14:35:08 +00:00
(`Hr.Recruitment`) da wizard ile üretilmiştir: kadro talebi (Grid Workflow onayı) → ilan → aday (CV,
2026-10-04 08:14:38 +00:00
KVKK açık rızası zorunlu — tabloda `CHECK`) → başvuru (aşama kanbanı) → mülakat (takvim, kriter
ağırlıklı puan) → teklif (Grid Workflow onayı, teklif mektubu `DynamicReport`). Aday kaynağı, ret
2026-10-19 14:35:08 +00:00
sebebi ve değerlendirme kriteri tanımları İK › Tanımlar (`Hr.Definitions`) altındadır. İşe alınan adayın
2026-10-04 08:14:38 +00:00
kullanıcı (personel) kaydını BT elle açar; İK aday kaydını `UserId` ile o kullanıcıya bağlar. Ayrıntı:
2026-10-19 14:35:08 +00:00
`lowcode-reference.instructions.md` §12.4. **Performans Yönetimi** (`Hr.Performance`) wizard +
2026-10-04 15:04:16 +00:00
custom component ile üretilmiştir: İK menüsü şartları ve takibi taşır, çalışana ait listeler menüsüzdür ve
2026-10-19 14:35:08 +00:00
Intranet panosundaki widget'lardan açılır; çalışma şekli [Hr › Performans Yönetimi](configs/seeds/docs/modules/Hr.md).
**İzin Yönetimi** (`Hr.Leave`) aynı kalıptadır (talep, Müdür → İK Grid Workflow onayı, İş Kanunu md.53 bakiyesi,
rapor ↔ Sağlık Kartı eşlemesi, kullanıcı detayında İzinler sekmesi); çalışma şekli [Hr › İzin Yönetimi](configs/seeds/docs/modules/Hr.md).
**Puantaj ve Devam Takibi** (`Hr.Attendance`) hareketlerden (PDKS ucu, Excel, elle) günlük ve aylık puantaj kurar;
2026-10-04 21:18:13 +00:00
izin, rapor, çalışma takvimi ve vardiyadan beslenir, kullanıcı detayında Puantaj sekmesi ve Intranet'te Puantajım widget'ı vardır;
2026-10-19 14:35:08 +00:00
çalışma şekli [Hr › Puantaj ve Devam Takibi](configs/seeds/docs/modules/Hr.md).
2026-09-29 11:46:39 +00:00
**Yetki kodları** `PlatformConsts.AppCodes` içinde merkezî olarak tanımlıdır ve
2026-10-05 12:58:59 +00:00
`configs/seeds/data/Abp.Identity.Permissions.json` ile seed edilir (gruplar:
2026-10-19 14:35:08 +00:00
`Abp.Identity.PermissionGroups.json` — `Sas` (1), `Adm` (2), `Hr` (3)).
Ana yetki kökleri: `Sas`, `Sas.Branches`,
`Sas.Definitions.*`, `Adm.Definitions.*`, `Hr.*` (Intranet dahil, `Hr.Intranet.*`), `Adm.Restrictions.*`, `Sas.Languages.*`, `Sas.Listforms.*`,
`Adm.Notifications.*`, `Sas.BackgroundWorkers.*`, `Sas.Menus.*`, `Sas.DeveloperKit.*`,
`Sas.SqlQueryManager.*`,
`Sas.Orders.*`, `Sas.BlogManagement.*`, `Adm.IdentityManagement.*`, `Adm.Reports.*`,
`Adm`, `Adm.Setting`, `Sas.Setup.Migrate`.
2026-06-09 12:54:05 +00:00
Yetki ekranındaki **sıralama** `AbpPermissions.Order` ve `AbpPermissionGroups.Order`
sütunlarından gelir (`Modules` gibi genişletme alanları, `PlatformEfCoreEntityExtensionMappings`);
ABP kayıtları sırasız okuduğu için sıra taşınmazsa ekran her açılışta başka bir dizilim gösterirdi.
Sıra **kardeşler arasındadır**: her üst yetkinin altında 1'den başlar, grubun kök yetkileri de
kendi aralarında 1'den. `PlatformPermissionAppService` sıralama anahtarını kökten yaprağa üst yetki
zincirinden kurar, böylece çocuk her zaman ebeveyninin altında kalır. Değerler seed dosyalarındaki
`Order` alanında tutulur ve Permissions / Permission Groups ekranlarından düzenlenebilir.
2026-09-29 14:55:13 +00:00
**Yetki ağacı menü ağacını izler.** Wizard ekranın okuma yetkisini menüde durduğu dalın altına
yazar: üst menü zincirindeki (`MenuParentPath` + `MenuParentCode`) her seviye, izin grubunun
kendisi hariç, aynı kodlu bir ara yetkidir (`İnsan Kaynakları › Tanımlamalar › Ekran` →
2026-10-19 14:35:08 +00:00
`Hr.Definitions › Hr.Doctor`). Var olan ara yetki kullanılır, eksik olan menü seviyesinin
görünen adıyla wizard'a ait üretilir; başka bir gruptaki aynı adlı yetki atlanır. Kodu bulunamayan
bir seviye önce aynı üstün altındaki dallarla adıyla eşleştirilir (dalın `DisplayName` anahtarı =
verilen kod ya da EN/TR adı aynı) ve eşleşen dalın koduna bağlanır; böylece MCP'nin yalnızca yol
verdiği deploy'da ikinci bir "Tanımlamalar" menüsü ve ara yetkisi oluşmaz
(`ListFormWizardAppService.BindExistingMenuParentsAsync`). Menüsüz wizard'ın
2026-09-30 14:57:25 +00:00
(`CreateMenu: false`, alt form) okuma yetkisi onu `SubForms` içinde taşıyan listenin yetkisine
2026-10-19 14:35:08 +00:00
bağlanır: `ParentName` = listenin yetki kodu (`Hr.HealthCard › Hr.HealthCardFile`), sırası
2026-09-30 14:57:25 +00:00
listenin alt yetkilerinin (Create/Update/…) ardındandır. Alt form listeden önce deploy edildiği için
yetkisi önce grubun köküne yazılır; liste deploy edildiğinde alt formun satırı listenin altına
taşınır (satır alt formun wizard'ına ait kalır, başka bir listeye bağlanmış alt form taşınmaz).
Hiçbir listenin alt formu olmayan menüsüz wizard'ın yetkisi grubun kökünde kalır.
2026-09-29 14:55:13 +00:00
Wizard deploy'u yeni grubu ve yeni yetkileri (seed satırı olarak) veritabanı ile seed
dosyasındaki kardeşlerin en büyük sırasının bir fazlasıyla, alt yetkileri `WizardSeedRowKeys.ChildPermissions` dizilimiyle
2026-10-14 09:07:11 +00:00
(List: Create/Update/Delete/Export/Import + Table kaynağında Note, Custom: Create/Update/Delete) 1'den yazar
(`ListFormWizardAppService.WritePermissionGroupAsync` / `WritePermissionRowsAsync`).
Aksiyon yetkisi olan alt gruplar (UI tarafındaki karşılıkları `constants/permission.constant.ts`
içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppService'lerdedir):
| Grup | Alt yetkiler |
| --- | --- |
2026-10-19 14:35:08 +00:00
| `Sas.Listforms.Wizard` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import` |
| `Sas.DeveloperKit.CustomComponents` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import` |
| `Sas.DeveloperKit.CustomEndpoints` | `.Get`, `.Post`, `.Put`, `.Remove` (dispatcher üzerinden çağrı kapısı; endpoint bazlı User/Role/Global kuralları bunun üstünde denetlenir) |
| `Sas.DeveloperKit.DynamicServices` | `.Create`, `.Edit`, `.Delete`, `.Manage`, `.TestCompile`, `.Publish`, `.ViewCode` |
| `Sas.SqlQueryManager` | `.CrudEndpoints` |
| `Sas.Setup.Migrate` | Migration + seed tetikleme (host tarafı) |
> **Kural:** Yetki sözleşmesi olmayan menü/route önerilmez ve eklenmez.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 10. Modül Kataloğu
2026-06-09 12:54:05 +00:00
### 10.1. Backend modülleri (`api/modules`)
2026-06-09 12:54:05 +00:00
| Modül | Kapsam |
2026-06-09 12:54:05 +00:00
| --- | --- |
2026-10-19 14:35:08 +00:00
| **Sozsoft.Languages** | Dil tanımları, dil anahtarları, çeviri metinleri; veritabanı tabanlı lokalizasyon (`DatabaseResourceLocalizer`). Çeviri okuma (public sayfalar dahil) ABP'nin `application-localization` ucundandır ve yetki istemez; dil/anahtar/metin yönetim uçları ile önbellek temizleme `Sas.Languages.Language*` / `Sas.Languages.LanguageText*` yetkilerine bağlıdır (`LanguagesPermissions`). Önbellek temizleme yalnızca uygulamanın anahtarlarını siler (`DistributedCacheCleaner`). `DatabaseResourceLocalizer` metinleri süreç içinde 30 saniye tutar; ham SQL ile, başka bir API örneğinde ya da DbMigrator'da değişen bir metin en geç 30 saniyede görünür. |
| **Sozsoft.Settings** | Ayar tanımları, sağlayıcılar, şifreleme bayrakları ve ayar ekranı metadata'sı. |
| **Sozsoft.Notifications** | Bildirim tipleri, kuralları ve kullanıcı bildirimleri; kanal bazlı yönlendirme. |
| **Sozsoft.MailQueue** | Şablon (Scriban) tabanlı gövde üretimi, ek dosya yaşam döngüsü, kuyruk çalıştırma ve günlükleme. |
| **Sozsoft.Sender** | Amazon SES (mail), Posta Güvercini (SMS), WhatsApp Cloud API, Rocket.Chat göndericileri. |
| **Sozsoft.SqlQueryManager** | SQL nesne yönetimi, sorgu çalıştırma, tablo/kolon metadata'sı. |
| **Sozsoft.Mcp** | MCP (Model Context Protocol) sunucusu: `/mcp` Streamable HTTP ucu, kiracı bazlı araç seti, şema keşfi, sorgu, veri yazma ve (host) tablo/ekran/bileşen geliştirme. Kiracı kullanıcısı veri okur ve ekranın C/U/D sözleşmesinden geçerek satır yazar; geliştirme araçları yalnızca host'tadır. Bkz. §13.9. |
2026-06-09 12:54:05 +00:00
### 10.2. Uygulama servisleri (`api/src/Sozsoft.Platform.Application`)
2026-06-09 12:54:05 +00:00
`AiBots` · `AuditLogs` · `BackgroundWorker` · `Blog` · `Branch` · `Dashboard` · `DataSource` ·
`DeveloperKit` · `FileManagement` · `Forum` · `GlobalSearch` · `Hr` · `Identity` · `Intranet` ·
2026-10-19 14:35:08 +00:00
`ListForms` (+ `Adm`) · `Menu` · `Messenger` · `Note` · `OrgChart` ·
`OrganizationUnits` · `Public` · `Routes` · `Tenants` · `Videoroom`
2026-06-09 12:54:05 +00:00
### 10.3. Yönetim ekranları (UI)
2026-06-09 12:54:05 +00:00
| Alan | Route | Açıklama |
2026-06-09 12:54:05 +00:00
| --- | --- | --- |
| Dashboard | `/admin/dashboard` | Widget tabanlı özet ekran. |
| Kullanıcı / Rol | `/admin/list/...`, `/admin/users/detail/:userId` | Kullanıcı, rol ve permission atamaları. |
| Organizasyon birimleri | `/admin/ous` | OU ağacı ve üyelik yönetimi. |
| Tenant yönetimi | ListForm ekranları | Tenant kayıtları, bağlantı dizeleri, siparişten otomatik tenant açma. |
| Ayarlar | `/admin/settings` | Sistem ve entegrasyon ayarları. |
| Menü yöneticisi | `/admin/menuManager` | Menü ağacı düzenleme. |
| Dosya yöneticisi | `/admin/files` | Klasör/dosya işlemleri. |
| Aktivite ve denetim kaydı | `/admin/activityLog`, `/admin/changeLog` | Kullanıcı hareketleri ve ABP audit log detayı. |
| Bildirimler | `/admin/profile/notification-settings`, bildirim ekranları | Kanal tercihleri ve bildirim oluşturma. |
2026-10-19 14:35:08 +00:00
| Forum | `/admin/forum` | Forum kullanımı; yönetim `Sas.ForumManagement` altındaki ListForm ekranlarıyla yapılır. |
| Intranet | Intranet dashboard ve widget'ları | Duyuru, anket, sosyal duvar, etkinlik; ayrıca İK modüllerinin çalışana ait özetleri (Performans Yönetimi: Performansım, Ekibimin Performansı, 360 Taleplerim — custom component). Hangi widget'ın hangi kolonda, hangi sırada ve hangi yetkiyle yayınlandığı `Sas_T_IntranetWidget` kaydından gelir; kayıtlar `configs/seeds/data/Sas.DeveloperKit.IntranetWidgets.json` ile senkron tutulur. |
| Video oda | `/admin/videoroom/*` | Oda listesi, detay, planlama, katılımcı ve ekran paylaşımı. |
| Raporlar | `/admin/reports/:report/view|design/...` | DevExpress rapor görüntüleyici ve tasarımcı. `Raporlar` kategorisindeki şablon: `/admin/reports/DynamicReport/view/{ŞablonId}[/{ListFormCode}?recordId=...]` (§15). |
| AI asistanı | `/admin/ai` | n8n webhook üzerinden sohbet arayüzü. |
2026-10-19 14:35:08 +00:00
| Profil | `/admin/profile/*` | Genel bilgiler, çalışan bilgileri, izinlerim, puantajım, performansım, şifre, bildirim ayarları. Genel ve Çalışan Bilgileri `api/account/my-profile` ucundan okunur. ABP'nin eşlemesi yalnızca modül uzantısında tanımlı ek alanları taşıdığı için `PlatformProfileAppService` kullanıcının kendi kaydındaki alanları (`ProfileExtraProperties`; sağlık, ücret ve işten çıkış alanları hariç) ve departman/pozisyon/şube/rol adlarını cevaba ekler. Çalışan Bilgileri salt okunurdur. İzin, puantaj ve performans sekmeleri çekirdek, salt okunur bileşenlerdir: `Hr.My*` persona ekranlarının satırlarını `list-form-select` ile okuyup kart + tablo olarak gösterir, düzenle/sil çizmez (tek aksiyon izin talebi oluşturma). Sekme, ekranlarından birinin yetkisi yoksa görünmez. |
2026-06-09 12:54:05 +00:00
### 10.4. Public site
2026-06-09 12:54:05 +00:00
`/home` · `/about` · `/contact` (Custom Component) · `/products` · `/checkout` · `/payment` ·
`/success` · `/blog` · `/blog/:id` · `/demo` (fiziksel sayfalar).
Ziyaretçiye açık kısım iki parçadan oluşur ve ikisi de veritabanında yaşar:
**Menü.** Public menüler `Menu` tablosunda `ModuleId = "Pub"` modülü altında tutulur ve Menü
2026-10-19 14:35:08 +00:00
Yönetimi ekranından girilir (sözleşme: `PlatformConsts.PublicSite`). `Pub.Header` kökü hem
üst navigasyonu hem de alt bilgideki hızlı bağlantıları besler — ikisi de `PublicNav` düğümüdür,
menü bir kez eklenir iki yerde birden görünür; alt kaydı olan bir menü üstte açılır liste olur.
Alt bilgide `cta` kayıtları (Giriş) yer almaz: oradaki düğüm `source="navigation"` ile çizilir. `Url` sayfa yolu, `Icon`
react-icons adı, `Order` sıra, `IsDisabled` yayın anahtarı, `DisplayName` dil anahtarıdır;
`CssClass` içinde `cta` geçen kayıt düz bağlantı yerine vurgulu buton olarak çizilir. Menü ucu
`[Authorize]` olduğu için menüler uygulama konfigürasyonunun `menus.public` alanıyla taşınır
(`PlatformApplicationConfigurationContributor`), istemcide `usePublicMenu` okur. `Pub` modülü
yönetim navigasyonuna (`menus.protected`) girmez (`MenuNavigationProvider` bu modülü eler; Menü Yönetimi
2026-10-19 14:35:08 +00:00
ekranı elemez, menüler oradan düzenlenir). Seed: `configs/seeds/data/Sas.Menus.Module.json` ve
`Sas.Menus.Menu.json`.
**Sayfa.** Her public sayfa bir Custom Component'tir: `RoutePath` `/admin/` ile başlamadığı sürece
bileşen otomatik olarak public route olur ve `PublicLayout` altında yayınlanır. Tasarım yalnızca
DeveloperKit > Custom Components (Visual Designer) üzerinden değişir. Aynı yolu bir fiziksel route
ile paylaşan custom component kazanır; karşılığı olan dosyası bulunmayan rota kaydı ise hiç
yüklenmez (`hasPhysicalComponent`) — seeder yalnızca eklediği için eski kayıtlar veritabanında
2026-10-19 14:35:08 +00:00
kalsa bile ekran patlamaz. Seed: `configs/seeds/data/Sas.DeveloperKit.CustomComponents/{Id}.json`;
ekran klasör modundadır, her bileşen kendi satır dosyasındadır (dosya adı satırın `Id`'si).
Runtime kapsamı sınırlıdır: bileşen kodundaki `import` satırları derlemede temizlenir,
kullanılabilenler `React`, `ui` kit, `PlatformViewHost`, `apiService`, `axios`, `DOMPurify`,
`translate`, `checkPermission`, `getCulture` ve **katalog düğümleri**
(`components/componentEditor/runtimeNodes.ts`). **`react-icons` ve `react-router` yoktur** — ikon
`<PlatformIcon name="FaUsers" size={40} className="..." />` ile ada göre çizilir (menü ikonlarıyla
aynı sözlük), gezinme `<a href>` ile yazılır. Kullanıcıya görünen metinler dil anahtarı üzerinden
gelir.
**Tailwind sınıf yüzeyi.** Bileşenin kodu veritabanında yaşar; derleme sırasında taranan tek
2026-10-19 14:35:08 +00:00
kaynak seed dosyalarıdır (`configs/seeds/*/data/Sas.DeveloperKit.CustomComponents/{Id}.json`). Bu klasör
depo kökünde, `ui/` dışındadır — derleme bağlamı yalnızca `ui/` klasörü olduğunda (docker, ayrı
checkout) glob boş döner ve seed'e özgü sınıflar (`md:col-span-4`, `bg-gray-950/80`, `opacity-20`)
CSS'e hiç girmez: uygulama yerelde doğru, sunucuda stilsiz görünür. Bu yüzden aynı sınıflar var
2026-10-16 21:13:31 +00:00
olan `ui/safelist.txt` dosyasına da yazılır. Dosyayı `ui/twSafelistGenerator.ts` Vite eklentisi
üretir: seed dosyalarındaki `className` değerlerini toplar, derleme başında yazar ve geliştirmede seed
klasörünü izler. `safelist.txt` `ui/` içinde ve depoda olduğu için her ortamda taranır
(`src/assets/styles/tailwind/index.css` → `@source`). Seed kökü görünmüyorsa liste olduğu gibi
korunur — eksik bir bağlam listeyi silip sorunu büyütmez. Çalışma anında veriden gelen renk/ölçü
sınıfları (`bg-{renk}-{ton}`, `w-1/2` …) dosyaya değil aynı CSS'teki `@source inline(...)` satırlarına
yazılır. Ayrı bir betik ya da ikinci bir safelist dosyası yoktur; liste her `npm run build` /
`npm start` ile tazelenir.
**Tailwind 4 yapılandırması.** `tailwind.config.ts` ve PostCSS zinciri yoktur; tema (`gray` paleti,
`sozsoft-*` renkleri, `1.2`–`1.4` aralıkları, font) `src/assets/styles/tailwind/index.css` içindeki
`@theme` bloğundadır. Aynı dosya v3 davranışını koruyan uyumluluk kurallarını taşır: `dark` sınıf
varyantı, dokunmatikte de çalışan `hover`, gri-200 kenarlık, gri-400 placeholder, butonda `pointer`
imleci, varsayılan 3px mavi `ring`, veritabanındaki eski Custom Component kodu için `flex-shrink-*` /
`flex-grow-*`. Kademelenme v3 ile aynıdır (`app.css`): yalnızca tema ve preflight katmandadır
(en zayıf); `components/` + `template/` stilleri ve en sonda utility'ler katmansızdır. Bileşen
stilleri katmana alınmaz — katmansız CSS her katmanı yendiği için stilini katmansız basan
kütüphanelere (react-select, DevExtreme) yenilirdi. JS'te renk gerektiğinde değer kopyalanmaz,
`themeColor()` (`src/utils/tailwind.ts`) temanın `var(--color-*)` değişkenini döndürür.
**Katalog düğümleri.** Kanvasın doğrudan çizdiği, derlenen bileşen listesinde yer almayan
bileşenler tek bir sözlükte durur (`RUNTIME_NODE_COMPONENTS`): kanvas onu `VisualCanvas` içinde,
üretilen kod ise runtime kapsamı üzerinden kullanır — iki taraf aynı kaynaktan beslendiği için
2026-09-21 15:01:40 +00:00
tasarım ve çalışma zamanı ayrışamaz. Sözlükte `PlatformIcon`, `BarcodeScanField`
2026-09-21 19:53:48 +00:00
(`components/shared/BarcodeScanner`), `BarcodeViewer` (`components/shared/BarcodeViewer.tsx`) ve
public site parçaları (`components/publicSite`) bulunur. Yeni bir düğüm eklerken: bileşen `components/publicSite` (ya da
2026-10-21 20:40:03 +00:00
uygun klasör) → `runtimeNodes.ts` sözlüğü → `catalog.ts` tanımı (`kind: 'custom'`) → `data/Sas.Languages.LanguageText/{CultureName}.json`
açıklama anahtarı.
2026-09-21 19:53:48 +00:00
**Barkod.** Okuma ve çizme ayrı yollardır, ikisi de `utils/barcode.ts` içindedir ve `zxing-wasm`
yalnızca gerektiğinde lazy iner. Okuma: Chromium'da yerleşik `BarcodeDetector`, olmayan tarayıcıda
`zxing-wasm/reader` (`BarcodeScanField`, `BarcodeScannerDialog`). Çizme: `zxing-wasm/writer`
(`writeBarcodeSvg` → `BarcodeViewer`); SVG üretilir, böylece büyütmede ve baskıda kırılmaz.
Desteklenen çizim formatları `BARCODE_WRITE_FORMATS` listesindedir (QRCode, Code39, Code93,
Code128, EAN13/8, UPCA/E, ITF, Codabar, DataMatrix, PDF417, Aztec) ve katalogdaki `format`
seçenekleri de bu listeden üretilir — liste tek yerde değişir. `BarcodeViewer` bir `value` propu
taşıdığı için tasarımcıdaki `Form` düğümünün içinde tıpkı bir `Input` gibi bir kolona bağlanabilir;
kayıt değiştikçe kod yeniden çizilir.
Elle yazılmış bir bileşen Visual Designer'da **kod modunda** açılır: tasarımcı yalnızca kendi
ürettiği kodu düğüm ağacına çevirir (`isDesignerGeneratedCode`), aksi hâlde tanımadığı JSX'i
düşürüp kaydederken kodu yok ederdi.
Public bileşenlerin tamamı — `/home`, `/about`, `/contact` sayfaları ile üst ve alt bilgi
(`PublicHeader`, `PublicFooter`; rotaları yok) — **tasarımcı belgesi** olarak tutulur: `Props` alanındaki
`visualDesigner` düğüm ağacı kaynaktır (`sourceMode: "visual"`), `Code` ondan üretilir
(`generateDesignerCode`) ve runtime da bileşeni bu belgeden derler. Yani hepsi kanvasta görünür,
sürükle-bırak ve özellik paneliyle düzenlenir. Belgeler `div`/`h1`-`h5`/`p`/`span`/`img`/`a`
HTML düğümleri ve `PlatformIcon` ikon düğümüyle kurulmuştur; `::` ile başlayan metinler üretilen
kodda `translate()` çağrısına döner. `RoutePath` boş bırakılabilir; rotası olmayan bir bileşen
yalnızca başka bir ekranın içinde çizilir (alt bilgi gibi).
Üst ve alt bilgi de birer Custom Component'tir (`PublicHeader`, `PublicFooter`; rota yok):
`PublicLayout` ikisini de `renderComponent` ile çizer ve geriye yalnızca rota değişiminde başa
sarma ile demo penceresi kalır. Bileşen kayıtlı değilse o parça çizilmez.
Üst bilginin davranışı (menü listesi, aktif bağlantı, kaydırmada değişen görünüm, mobil panel,
tema/dil/demo kontrolleri) `components/publicSite` altındaki katalog düğümlerinde yaşar; yerleşim
ve sınıflar tasarımcıdadır: `PublicHeaderBar` (kaydırıldığında `scrolledClassName`'e geçen kabuk),
`PublicNav` (`source`: `navigation`/`actions`/`all`, `variant`: `horizontal`/`vertical`),
`PublicMobileMenu` (hamburger + içeriği tasarlanan panel), `PublicLogo`, `PublicThemeToggle`,
`PublicLanguageSelector`, `PublicDemoButton`.
Rotası olmayan bileşende `RoutePath` **NULL** yazılır; `Sas_H_CustomComponent`'in `RoutePath`
tekil indeksi yalnızca dolu değerleri kapsar (`[IsDeleted] = 0 AND [RoutePath] IS NOT NULL`), aksi
hâlde ikinci rotasız bileşen birincisiyle çakışırdı.
`/home`, `/about`, `/contact` bu yolla çalışır (`PublicHomePage`, `PublicAboutPage`,
`PublicContactPage`); üst ve alt bilgi rotasızdır (`PublicHeader`, `PublicFooter`).
2026-09-22 20:59:36 +00:00
Yalnızca ikon taşıyan bir bağlantı ya da düğmenin ekran okuyucuda adı olmaz; `a` ve `button`
düğümleri bunun için `aria-label` özelliği taşır (`componentDefinitions.ts`). Görsel düğümüne
`width`/`height` yazmak da tarayıcının yeri önceden ayırmasını sağlar ve yerleşim kaymasını
(CLS) önler — gösterim ölçüsü `className`'den gelmeye devam eder.
Veriden beslenen public sayfalar (`/products`, `/checkout`, `/payment`, `/success`, `/blog`,
`/blog/:id`, `/demo`) fiziksel kalır; içerik sayfaları Custom Component'tir. Blog listesi ve detayı
`PublicAppService`'in `post-list` / `post-by-slug` uçlarından beslenir, yönetimi
2026-10-19 14:35:08 +00:00
`Sas.BlogManagement.*` ekranlarındadır.
2026-09-22 20:59:36 +00:00
**SEO.** Ziyaretçiye açık yüzeyin arama motoru sözleşmesi üç parçadır:
| Parça | Nerede | Ne taşır |
| --- | --- | --- |
2026-10-19 14:35:08 +00:00
| Genel varsayılanlar | `Adm.SiteManagement.Seo.*` ayarları (Ayarlar ekranı, yetki `Adm.SiteManagement.Seo`) | Site adresi, site adı, varsayılan başlık/açıklama/paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması, sosyal hesaplar, varsayılan anahtar kelimeler (`DefaultKeywords`), sitemap anahtarı (`SitemapEnabled`), sitemap dışı yollar (`SitemapExcludePaths`) |
2026-09-22 20:59:36 +00:00
| Yol bazlı alanlar | Yolu tanımlayan kaydın `Seo*` kolonları: `Route` (`Sas_H_Route`, ekran **Menü Yönetimi › Route**) ve `CustomComponent` (`Sas_H_CustomComponent`, ekran **DeveloperKit › Custom Components**) | Yolun başlığı, açıklaması, anahtar kelimeleri, paylaşım görseli, kanonik adresi, `SeoNoIndex`, sitemap davranışı ve ek JSON-LD |
| İstemci | `components/shared/SeoHead.tsx` | `title`, `description`, `robots`, `canonical`, `og:`/`twitter:` kartı, `og:locale:alternate` ve JSON-LD |
Ayrı bir SEO tablosu yoktur: meta, yolu tanımlayan kaydın üzerinde yaşar. `SeoAppService`
iki kaynağı tek sözleşmede birleştirir — `RouteType = public` olan `Route` satırları ve rota
taşıyan aktif Custom Component'ler. Aynı yolu ikisi de tanımlıyorsa bileşen kazanır; çalışma
zamanında da bileşen fiziksel rotayı gölgeler.
Alanlar ve ayarlar uygulama konfigürasyonunun `seo` alanıyla ziyaretçiye taşınır
(`PlatformApplicationConfigurationContributor` → `SeoAppService.GetConfigAsync`); sayfa ayrı bir
istek atmaz, meta ilk boyamada hazırdır. Yol çözümü daraldıkça öncelik kazanır: birebir yol, sonra
en uzun joker (`/blog/*`). Metin alanları kültür başına çoğalmaz: `::Anahtar` biçimindeki değer
istemcide çevrilir (`resolveSeoText`), düz metin olduğu gibi kullanılır.
Sayfanın kendi `SeoHead`'i, kabuğun (`PublicLayout`) `fallback` kaydını her zaman yener —
`PageTitle` ile aynı çözümleme.
`sitemap.xml` ve `robots.txt` aynı servisten üretilir (`SeoController`, anonim). Sitemap'in
kaynakları: `Pub` menüleri, aktif custom component rotaları, yayınlanmış blog yazıları ve
yolu tanımlayan kayıtların `Seo*` alanları; `/admin` yolları, parametreli rotalar (`/blog/:id`) ve
`SeoNoIndex` işaretli ya da `SeoIncludeInSitemap = false` olan yollar elenir.
2026-10-19 14:35:08 +00:00
`Adm.SiteManagement.Seo.SitemapExcludePaths` (satır başına bir yol, sonu `*` ise önek) hem sitemap'ten
düşer hem `robots.txt`'e `Disallow` olarak yazılır; `robots.txt` ayrıca `/admin/`, `/auth/`,
`/checkout`, `/payment`, `/success` yollarını her zaman kapatır. `SitemapEnabled` kapalıyken
`sitemap.xml` 404 döner ve robots'a `Sitemap:` satırı yazılmaz. Arama motorları bu iki dosyayı **sitenin** kök dizininde aradığı için UI sunucusu her iki
2026-09-22 20:59:36 +00:00
yolu da API'ye geçirir (`vite.config.ts` > `seoProxy`, hem `server` hem `preview`); ayrı bir ters
vekil kuralı gerekmez. `VITE_API_URL` tanımlı değilse proxy kurulmaz ve `ui/public/robots.txt`
içindeki statik varsayılan geçerli kalır.
Yönetim ve oturum yüzeyi `noindex` gelir (`Layouts.tsx`), satın alma akışı (`/checkout`,
`/payment`, `/success`) kendi sayfasında `noIndex` bildirir.
**Ziyaretçiye açık yeni bir yol üreten her iş SEO alanlarını da doldurur**; iki ekranın da
2026-10-19 14:35:08 +00:00
`SeedSync*` bayrakları açık olduğu için ekrandan girilen değer `data/Sas.Menus.Routes.json` ya da
`data/Sas.DeveloperKit.CustomComponents/{Id}.json` dosyasına düşer.
2026-09-22 20:59:36 +00:00
Ayrıntı: `.github/instructions/lowcode.instructions.md` §0.9.
Eski statik içerik sayfaları ve sayfa içi tasarım modu (`?design=1`) kaldırılmıştır:
`views/public/Home|About|Contact|Services`, `views/public/designer/`, bunlara özel istemci
servisleri; sunucuda `Home`/`About`/`Contact`/`Service` entity'leri, `PublicAppService` sayfa
Get/Save uçları ve DTO'ları, `TenantData.json` + `TenantDataSeeder`,
2026-10-19 14:35:08 +00:00
Home/About/Services/Contact sayfa yetkileri ve `.Design` alt yetkileri, `/admin/public/*/designer`
rota ve menüleri.
2026-06-09 12:54:05 +00:00
2026-10-05 12:58:59 +00:00
### 10.5. Modül dokümanları (`configs/seeds/docs/modules/`)
2026-10-04 15:04:16 +00:00
2026-10-07 08:17:46 +00:00
Konfigürasyonla üretilen modüllerin çalışma şekli, keşif analizi ve kararları **kök menü başına tek
dosyadadır**; menünün altındaki her dal dosyada bir bölümdür (`module-doc:section`). "Bu ekran nasıl
çalışıyor?" sorusunun cevabı oradadır (MCP'de `get_module_doc`; ekran ya da dal kodu verilirse
2026-10-19 14:35:08 +00:00
yalnızca o bölüm döner). Dosya adı kök menünün kodudur (`Hr` → `Hr.md`,
`Prc` → `Prc.md`); sabit bir liste yoktur, menü ağacından okunur. Wizard deploy'u
2026-10-07 08:17:46 +00:00
(ekran ya da MCP) yeni kök menünün dosyasını, yeni dalın bölümünü menü sırasındaki yerine açar ve
sondaki "Ek — Ekran envanteri"ni her deploy, silme, tasarımcı kaydı ve import'ta wizard dosyalarından
yeniden yazar; anlatım bölümlerini üreten doldurur (MCP'de `save_module_doc`). Wizard export'u
dokümanı taşır (`lowcode.instructions.md` §0.13). Liste ekranlarının başlığındaki ⓘ simgesi ise
ekranın kendi ayrıntılı açıklamasını gösterir (`DescTr`/`DescEn`, `lowcode.instructions.md` §4.4.2).
| Doküman | Kök menü | Bölümler |
2026-10-04 15:04:16 +00:00
| --- | --- | --- |
2026-10-19 14:35:08 +00:00
| [Sas.md](configs/seeds/docs/modules/Sas.md) | Saas (`Sas`, host) | Tanımlamalar, Public Site Yönetimi, Kiracılar ve Şubeler, Ayar Tanımları, Diller, Veri Kaynakları, Arka Plan İşleri, Menüler, Developer Kit, Forum Yönetimi (elle yazılır; ekranlar C# seeder ile kurulur) |
| [Adm.md](configs/seeds/docs/modules/Adm.md) | Yönetim (`Adm`) | Tanımlamalar (Kontaklar: satış ve satınalmanın ortak müşteri/tedarikçi kartı ve sekmeleri, kontak etiketleri) |
| [Hr.md](configs/seeds/docs/modules/Hr.md) | İnsan Kaynakları (`Hr`) | Tanımlamalar (dizin + genel tanımlar), Organizasyon ve Yetkinlik, Personel Kaydı (Kimlik › Kullanıcılar › detay), İşe Alım, Çalışma Takvimi ve Vardiyalar, İzin Yönetimi, Puantaj ve Devam Takibi, Sağlık Kartları, Eğitimler, Performans Yönetimi, Disiplin Dosyaları, Araçlar, Ziyaretçiler, Olay Günlüğü, Intranet |
| [Prj.md](configs/seeds/docs/modules/Prj.md) | Proje (`Prj`) | Tanımlamalar, İş Paketleri, Günce Listesi, Projeler, Adam/Ay Oranları, Kaynak Listesi, Personel Adam/Ay Oranları, Proje Bütçe Raporu |
| [Prc.md](configs/seeds/docs/modules/Prc.md) | Satınalma (`Prc`) | Tanımlamalar (ödeme/teslimat koşulları, ödeme/teslim yöntemleri, red nedenleri) · Talepler · Siparişler (sözleşme, tedarikçi bazlı teklif talebi ihalesi + fiyat/termin kıyası, satınalma siparişi) |
| [Stk.md](configs/seeds/docs/modules/Stk.md) | Envanter (`Stk`) | Tanımlamalar (ürün kategorileri, nitelikler, ürünler ve varyantlar, reçeteler, reçete patlatma, depolar, konumlar, operasyon türleri), Operasyonlar (alım, teslimat, iç transfer; lot/seri, backorder, iade), Raporlama (stok, hareketler) |
| [Prd.md](configs/seeds/docs/modules/Prd.md) | Üretim (`Prd`) | Tanımlamalar (iş merkezi etiketleri, iş merkezleri, operasyonlar) |
| [Mnt.md](configs/seeds/docs/modules/Mnt.md) | Bakım (`Mnt`) | Tanımlamalar, Bakım |
2026-10-04 15:04:16 +00:00
---
2026-06-09 12:54:05 +00:00
## 11. Kimlik, Oturum ve Güvenlik
2026-06-09 12:54:05 +00:00
### 11.1. Kimlik doğrulama
2026-06-09 12:54:05 +00:00
- **OpenIddict** password + refresh token akışı. Token uç noktası: `POST /connect/token`
(`grant_type=password`, `client_id=Platform_PublicApi`, `scope=offline_access Platform`).
- Token ömürleri `OpenIddict:TokenLifetimes` ile yapılandırılır (varsayılan 60 dk access, 90 dk refresh).
- Swagger için ayrı istemci: `AuthServer:SwaggerClientId`.
- İki aşamalı doğrulama (2FA) ve hesap doğrulama (e-posta/telefon) desteklenir.
- Kayıt ve iletişim formlarında Cloudflare Turnstile tabanlı captcha (`CaptchaMiddleware`).
2026-06-09 12:54:05 +00:00
### 11.2. Oturum yönetimi
2026-06-09 12:54:05 +00:00
- `PlatformSessionValidationMiddleware` her istekte oturumun hâlâ geçerli olduğunu doğrular.
- `PlatformSessionRevocationHandler` token iptalinde oturumu sonlandırır.
- `PlatformSessionCleanupWorker` süresi dolmuş oturumları temizler.
- Eş zamanlı kullanıcı limiti aşıldığında giriş reddedilir.
2026-06-09 12:54:05 +00:00
### 11.3. Giriş engelleri
2026-06-09 12:54:05 +00:00
`UserCannotSignInErrors` altında tanımlı, kullanıcıya lokalize mesajla dönen durumlar:
2026-06-09 12:54:05 +00:00
| Kod | Anlamı |
2026-06-09 12:54:05 +00:00
| --- | --- |
| `LoginNotAllowed_NotVerified` | Hesap doğrulanmamış. |
| `LoginNotAllowed_NotAllowedIp` | IP kısıtlaması (`IpRestriction`). |
| `LoginNotAllowed_WorkHour` | Çalışma saati kısıtlaması (`WorkHour`). |
| `LoginNotAllowed_LoginEndDateDue` | Kullanıcının giriş yetki süresi dolmuş. |
| `ShouldChangePasswordOnNextLogin` / `ShouldChangePasswordPeriodic` | Zorunlu şifre değişimi. |
| `LoginNotAllowed_TenantIsPassive` / `TenantNotFound` | Tenant pasif veya bulunamadı. |
| `LoginNotAllowed_BranchLimit` | Şube lisans limiti aşıldı. |
| `LoginNotAllowed_ConcurrentUserLimit` | Eş zamanlı kullanıcı limiti aşıldı. |
2026-06-09 12:54:05 +00:00
### 11.4. Güvenlik kuralları
2026-06-09 12:54:05 +00:00
- Tüm katmanlarda RBAC ve yetki tabanlı görünürlük zorunludur.
- Dinamik SQL'de **yalnızca parametreli** desenler kullanılır; kullanıcı girdisi string birleştirme ile sorguya eklenmez.
- Her sorgu ve eylemde tenant izolasyonu korunur (`IsTenant`, `IsBranch`, `IsOrganizationUnit` bayrakları ve ABP tenant filtreleri).
- Gerçek kimlik bilgileri, token ve anahtarlar örneklerde/dokümanda yer almaz; yer tutucu kullanılır.
- Denetim: kritik işlemler ABP Audit Log'a yazılır; yüksek frekanslı `list-form-customization` çağrıları denetim dışı bırakılmıştır.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 12. Çok Kiracılılık (Multi-Tenancy)
2026-06-09 12:54:05 +00:00
- `PlatformConsts.IsMultiTenant = true`.
- Tenant çözümleme host adı (subdomain) ve ABP tenant resolver'ları ile yapılır (`utils/subdomain.ts`).
- **Tenant başına ayrı veritabanı:** `TenantConnectionString` kayıtları ile her tenant kendi
2026-10-05 14:11:06 +00:00
veritabanına yönlendirilebilir. Bağlantı kaydedildiğinde kiracı adıyla bir `DataSource` açılır
(`TenantConnectionStringEventHandler`). Şema ve seed DbMigrator ile kurulur: tam migrate
bütün kiracıları dolaşır, Tenants ekranının Seed sekmesi yalnızca seçilen kiracıyı kurar
(`POST /api/setup/tenants/{id}/seed`, `TenantSeedOptions`). EF ile yalnızca `IMultiTenant`
varlıklar kiracı veritabanına gider. ListForm, menü ve yetki tanımı gibi kiracısız varlıkları
ABP her zaman host'ta okur. Kiracı tablolarının satırları `DataSourceManager` üzerinden kiracının
veri kaynağına gider.
- **Şube (Branch):** Tenant altındaki fiziksel/örgütsel kırılım; kullanıcı–şube eşlemesi
2026-10-14 09:07:11 +00:00
`BranchUsers` ile tutulur (atamada kullanıcının o şubedeki varsayılan deposu ve konumu da tutulur —
`DefaultWarehouseId`/`DefaultLocationId`, Kullanıcılar › Yetkiler), lisans limiti girişte kontrol edilir.
- **Organizasyon birimi:** ABP `OrganizationUnit` ağacı; ListForm'lar OU bazlı filtrelenebilir.
2026-10-05 12:58:59 +00:00
- Lokalizasyon: isteğin kültürü (metin, tarih, sayı) `UseAbpRequestLocalization` ile UI'ın gönderdiği
`Accept-Language`'tan kurulur. `TenantLocalizationMiddleware` + `TenantLocalizationInitializer` istek dışı
iş parçacıklarının (Hangfire, worker) varsayılan kültürünü ve `IClock` tipini global `Abp.Localization.DefaultLanguage`
/ `Abp.Timing.TimeZone` ayarlarından tutar; değerler dakikada en fazla bir kez okunur ve yalnızca değişince yazılır.
2026-10-17 18:51:10 +00:00
Sunucuda kullanıcıya giden tarih/sayı tek yoldan biçimlenir: `CultureFormat` (`Domain.Shared/Localization`) ile
`CultureInfo.CurrentCulture` — koda `dd.MM.yyyy` ya da `tr-TR` gibi sabit desen/kültür yazılmaz; bildirim ve worker
2026-10-19 14:35:08 +00:00
metinleri kiracının `DefaultLanguage` ayarıyla üretilir (`INotificationCulture`), rapor metinleri (`Sas.Reports.PageOf`,
2026-10-17 18:51:10 +00:00
Evet/Hayır) localizer'dan gelir.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 13. Bildirim ve Entegrasyonlar
2026-06-09 12:54:05 +00:00
### 13.1. Kanallar
2026-06-09 12:54:05 +00:00
2026-09-24 07:24:48 +00:00
`Sms` · `Mail` · `Rocket` · `Desktop` · `UiActivity` · `UiToast` · `WhatsApp` · `Telegram`
Kanal kayıt defteri iki yerdedir: backend `NotificationChannels` (Domain.Shared), UI
`constants/notification-channel.enum.ts` (kanal değeri + `NotificationChannelColors` +
`NotificationChannelIcons`). Kanal listesi gösteren ekranlar (tercih matrisi, Activity Log filtresi)
2026-10-19 14:35:08 +00:00
listeyi bu kayıt defterinden üretir; etiket her yerde `Sas.NotificationChannel.{Kanal}` dil
2026-09-24 07:24:48 +00:00
anahtarından okunur, kanal lookup'ı (`ListFormSeeder_DefaultJsons`) da aynı anahtarları kullanır.
Yeni kanal = iki kayıt defteri + renk + ikon + dil anahtarı + `NotificationWorker` gönderimi.
2026-06-09 12:54:05 +00:00
### 13.2. Yönlendirme modeli
2026-06-09 12:54:05 +00:00
Bildirim **tip + kanal** eşleşmesiyle yönlendirilir. Alıcı hedefleme seçenekleri:
`All`, `User`, `Role`, `OrganizationUnit`, `Custom`. Kullanıcılar kendi bildirim tercihlerini
`/admin/profile/notification-settings` üzerinden kanal bazında yönetir.
2026-06-09 12:54:05 +00:00
2026-10-04 19:17:16 +00:00
Bildirim panelinde, masaüstü bildiriminde ve Activity Log'da başlık kaydın **`Subject`**'idir
(`NotificationDto.Subject`; bildirimi yazan prosedür dil anahtarından çevrilmiş metni yazar). Tip adı
(`PerformansDurumDegisti` gibi) koddur; yalnız `Subject` boşsa gösterilir.
### 13.3. Kayıt durumu: gönderildi → okundu
Bir bildirim kaydı iki bayrak taşır ve sıra değişmez: **önce `IsSent`, sonra `IsRead`**.
Gönderilmemiş bir kayıt okunmuş olamaz.
| Kanal | `IsSent` ne zaman |
| --- | --- |
| `UiActivity` | Kayıt oluşurken (`NotificationManager`); ayrı bir gönderim adımı yoktur, kayıt doğrudan kullanıcının akışına yazılır |
| `UiToast` · `Desktop` | Tarayıcıda gösterildiğinde, UI `updateSentMany` ile işaretler |
| `Mail` · `Sms` · `Rocket` · `WhatsApp` · `Telegram` | `NotificationWorker` sağlayıcıya teslim ettiğinde |
`IsRead` ve `ReadTime` birlikte yazılır (`NotificationAppService.UpdateRead*`): okundu işaretinde
`ReadTime = Clock.Now`, okunmadıya çevirmede `null`. `IsSent = false` olan kayıt okundu
işaretlenmez — tekil ve toplu okuma yolları bu kaydı sessizce atlar, gönderim gerçekleştiğinde
okunabilir olur. Bu yüzden UI işaretlemeden sonra listeyi sunucudan tazeler; iyimser güncelleme
rozette yanlış sayı bırakırdı.
Tarayıcı kanalları tek kümededir (`NotificationChannels.BrowserChannels`); `sent-many` ucu
yalnızca bu kanalları işaretler, böylece istemci Mail/Sms gibi bir kanalı "gönderildi" yapamaz.
Masaüstü bildirimi izin verilmemişse hiç işaretlenmez — işaretlenseydi izin sonradan verildiğinde
de gösterilmezdi.
### 13.4. Gerçek zamanlı iletim
Bildirim üretildiği anda açık istemcilere **SignalR** ile duyurulur: hub `/notificationhub`,
bağlantı kullanıcı başına gruplanır (`NotificationHub.UserGroupName`, kiracı + kullanıcı).
`NotificationManager` kayıtları yazdıktan **sonra** duyurur (`INotificationRealtimeNotifier`);
duyuru yükü yalnızca hangi kanallarda kayıt oluştuğunu söyler, istemci listesini yine sunucudan
okur — okundu/gönderildi bayrakları tek kaynaktan gelir.
Katman sınırı: sözleşme bildirim modülünün Domain katmanındadır (`INotificationRealtimeNotifier`),
SignalR gerçeklemesi ise barındıran uygulamadadır (`Sozsoft.Platform.Application/Notifications/`,
`MessengerHub` ile aynı yer) — modül katmanları ASP.NET Core'a bağlanmaz. Gerçekleme
`[Dependency(ReplaceServices = true)]` ile devreye girer; SignalR olmayan barındırmalarda
(DbMigrator, testler) `NullNotificationRealtimeNotifier` kalır.
Duyuru **en iyi çaba**dır: bağlantı yoksa ya da mesaj kaybolursa bildirim kaybolmaz —
istemcideki yedek yoklama (5 dakika, sekme öne geldiğinde ayrıca) aynı kaydı getirir. Ters
vekilde `/notificationhub` kendi bloğunda WebSocket yükseltmesiyle karşılanır
(`configs/deployment/configs/nginx.conf`); token sorgu dizesinden geldiği için host bu yolu da
`Authorization` başlığına çevirir.
Masaüstü bildirimine tıklamak uygulamaya döner: service worker açık sekmeyi öne alıp bildirimin
`data.url` adresine götürür, sekme yoksa yenisini açar (varsayılan hedef Activity Log).
### 13.5. Kullanıcı tercihleri
Kurallar alıcı kümesini belirler; kullanıcının profilinden yazdığı tercih (`IsCustomized`,
`RecipientType = User`) **kural değildir**, yalnızca sahibinin o tür/kanal aboneliğini kapatır
(`NotificationManager`). Tercihi kural gibi işletmek, bir kullanıcının kendi tercihini yazmasıyla
aynı tür ve kanaldaki herkesin bildirimini kesmesi anlamına gelirdi. `IsFixed` kural zorunludur,
tercih dikkate alınmaz. Aynı kullanıcıyı birden çok kural hedeflerse (All + Role) kanal başına tek
kayıt üretilir.
Tercih ekranı (`/admin/profile/notification-settings`) satırlarını bildirim **tipinin id'si**
üzerinden eşler; tip adı yalnızca gösterimdir. `NotificationType.Description` alanı aynı ekranda
2026-09-24 07:24:48 +00:00
ipucu olarak görünür ve Bildirim Türleri ekranından yazılır. Ekran geniş ekranda tür × kanal
matrisi, dar ekranda kart listesidir; hücre `Switcher`'dır, `IsFixed` kural kilit ikonuyla
gösterilir ve değiştirilemez, kullanıcının değiştirdiği tür işaretlenir. Kayıt sırasında yalnızca
ilgili hücre/satır kilitlenir. Satırı varsayılana döndürmek
`DELETE /api/app/notification-rule/my-notification-rules/{notificationTypeId}` ile kullanıcının o
türdeki tercihlerini siler.
### 13.5.1. Bildirim ekranı ve elle gönderim (Administration › Bildirimler)
2026-10-19 14:35:08 +00:00
Bildirimin tamamı Administration menüsünde `Adm.Notifications` grubundadır:
**Bildirim Türleri**, **Bildirim Kuralları**, **Bildirimler**, **Bildirim Şablonları**. Grup
Agenda'nın üstündedir; Saas altında bildirim ekranı **yoktur**.
Tablolar kiracı kapsamındadır (`Adm_T_NotificationTemplate`, `Adm_T_NotificationType`,
`Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`). Her kiracı kendi türünü, kuralını ve kuyruğunu
görür; host kaydı (`TenantId = null`) yalnızca host oturumunda görünür. Yeni bir kiracı
2026-10-05 12:58:59 +00:00
oluşturulduğunda varsayılan tür ve kural kümesi o kiracıya kopyalanır. Kendi veritabanı olan
kiracı kümeyi seed dosyasından alır (`ListFormSeedDataApplier`). Host veritabanını paylaşan
kiracıya ise `NotificationTenantDefaultsSeedContributor` kopyalar: kaynak
2026-10-19 14:35:08 +00:00
`data/Adm.Notifications.Notification{Types,Rules}.json` dosyalarıdır, satırlar **yeni
kimliklerle** yazılır ve eşleşme ad üzerinden yapıldığı için ikinci çalıştırma bir şey eklemez;
2026-10-05 12:58:59 +00:00
şablonlar kopyalanmaz. İki yol aynı kiracıda çalışmaz, yoksa türler iki kez yazılırdı.
2026-10-19 14:35:08 +00:00
**Bildirimler** ekranı (`Adm.Notifications.Notification`) hem kuyruk günlüğü hem gönderim
ekranıdır: liste kural motorunun ürettiklerini de gösterir, ekleme formu ise elle gönderimdir.
Ayrı bir manuel gönderim kaydı yoktur — gönderimin kaydı bildirimin kendisidir. Ekleme anında
yazma işi `list-form-dynamic-api/notification-insert` ucuna gider (`InsertServiceAddress`); uç
seçilen alıcı × kanal açılımını `INotificationManager.SendManualNotificationAsync` ile kuyruğa
yazar. Bildirim Kuralları ekranındaki `CreateNotification` diyaloğu ise aynı yöneticinin
`SendNotificationAsync` yolunu kullanır: türün kurallarını işletir ve kullanıcı tercihini dikkate
alır; bu uç alıcıyı ekrandan alır ve tercihi okumaz. Güncelleme kapalıdır, silme kaydı kaldırır.
Formdaki `NotificationChannel` ve `UserId` alanları listede tek değerlidir (her satır bir
alıcı × kanal), formda `dxTagBox` ile çoklu seçilir; tipli DTO yolunda çoklu değeri
`MultiValueStringJsonConverter` taşır (`CreateNotificationInput`).
2026-09-21 06:57:02 +00:00
| Kural | Nerede |
| --- | --- |
2026-10-19 14:35:08 +00:00
| Bildirim tipi her zaman `Manuel` | `NotificationConsts.ManualTypeName`; tip kaydı `Adm.Notifications.NotificationTypes.json` |
| `Notification.NotificationRuleId` NOT NULL olduğu için tipin **çapa kuralına** bağlanır (`RecipientType = Custom`, `IsActive = false`) | `Adm.Notifications.NotificationRules.json`; kural pasif olduğu için kural motoru ve profil tercih ekranında görünmez |
2026-09-21 06:57:02 +00:00
| Kullanıcı tercihi okunmaz (zorunlu bildirim gibi davranır) | `NotificationManager.SendManualNotificationAsync` |
| Alıcı listesi kiracı filtresinden geçer | Kullanıcı sorgusu ABP kiracı filtresiyle çalışır; istekteki kimlikler değiştirilse bile başka kiracıya gönderilemez |
2026-10-19 14:35:08 +00:00
| İlgili kanalda adresi olmayan alıcı atlanır | Hiç satır üretilemezse uç `Sas.Notifications.SkippedChannel` gerekçesiyle hata döndürür; kısmi atlamada gönderim sürer |
| Metnin geldiği şablon kayda yazılır | `Notification.NotificationTemplateId` (provenans; tür değil, metin kaynağı) |
2026-09-21 06:57:02 +00:00
| Kanaldan çıkış yine `NotificationWorker` | `UiActivity` oluşurken iletilmiş sayılır, tarayıcı kanallarını istemci işaretler (§13.3) |
**Şablonlar** (`Administration › Bildirimler › Bildirim Şablonları`,
2026-10-19 14:35:08 +00:00
`Adm.Notifications.NotificationTemplates`,
2026-09-21 06:57:02 +00:00
`Adm_T_NotificationTemplate`) mesaj metnini taşır: `Code`, `Name`, `Channel` (boş = tüm kanallar),
`Subject`, `Body`, `IsActive`. Örnek şablonlar seed ile gelir
2026-10-19 14:35:08 +00:00
(`data/Adm.Notifications.NotificationTemplates.json`; ekran `SeedFilePath` ile bu dosyaya
2026-09-21 06:57:02 +00:00
aynalanır). Gönderimde şablon seçilirse ekranda **boş bırakılan** konu ve gövde şablondan
tamamlanır; ekranda yazılan metin şablonu ezer.
Gövdedeki yer tutucular alıcı bazında çözülür (`NotificationMessageTokens`): `{AdSoyad}`,
`{KullaniciAdi}`, `{Eposta}`, `{Tarih}`, `{Saat}`, `{Kiraci}`. Bildirim kaydının `Message` kolonu
2048 karakterdir; yer tutucular çözülünce metin bu sınırda kırpılır.
**Mail konu satırı:** bildirim kaydı `Subject` kolonu taşır (`Adm_T_Notification`, 256).
2026-09-21 06:57:02 +00:00
`NotificationWorker` konuyu bu kolondan alır; kolon boşsa bildirim tipinin adına düşer
(`Platform Bildirim: {tip}`). Kural motorundan gelen bildirimlerde kolon boş kalır, elle
2026-09-21 06:57:02 +00:00
gönderimde ekrandan ya da şablondan gelir — yani konu artık gövdeye gömülmez.
2026-10-12 10:27:54 +00:00
**Mail gövdesi:** `NotificationWorker` maili her zaman HTML gönderir. Etiket taşıyan mesaj (teklif talebi tablosu,
HTML şablon) olduğu gibi, etiketsiz düz metin HTML kodlanıp satır sonları `<br>` yapılarak gider.
### 13.6. Göndericiler (`Sozsoft.Sender`)
2026-06-09 12:54:05 +00:00
| Kanal | Sağlayıcı |
2026-06-09 12:54:05 +00:00
| --- | --- |
| E-posta | ABP Emailing + Amazon SES (`AmazonSesEmailSender`), arka plan iş kuyruğu ile |
| SMS | Posta Güvercini HTTP API |
| WhatsApp | WhatsApp Cloud API (şablon tabanlı) |
| Rocket.Chat | Rocket.Chat HTTP API |
2026-09-24 07:24:48 +00:00
| Telegram | Telegram Bot API (`SozsoftTelegramSender`; alıcı kullanıcının `TelegramChatId` özelliği) |
2026-06-09 12:54:05 +00:00
2026-10-19 14:35:08 +00:00
Tüm sağlayıcı kimlik bilgileri ayar ekranından yönetilir (`Adm.Sender.*` anahtarları);
koda gömülmez.
2026-06-09 12:54:05 +00:00
### 13.7. Mail kuyruğu
2026-06-09 12:54:05 +00:00
`Sozsoft.MailQueue`; şablondan gövde üretir, PDF/XLS/TXT çıktıları ve ek dosyaları yönetir,
kuyruğu arka plan işçisiyle çalıştırır ve sonuçları günlükler.
2026-06-09 12:54:05 +00:00
### 13.8. AI entegrasyonu
2026-06-09 12:54:05 +00:00
2026-09-17 12:24:51 +00:00
`AiBot` kayıtları tenant bazlıdır; n8n webhook üzerinden bir niyet sınıflandırma adımı ve
2026-10-17 18:51:10 +00:00
ardından sohbet ya da SQL dalı çalışır (`configs/ai/chat_SQL_gemma3_4b_Select_Summary_Pivot_Grouping.json`). Model, DevOps makinesindeki
2026-09-17 12:24:51 +00:00
Ollama'da yerel olarak koşar (`http://ollama:11434/api/chat`, varsayılan `qwen2.5:3b`) — dışarıya
model çağrısı yapılmaz. UI tarafında `/admin/ai` ve header'daki asistan bileşeni bu uç noktayı
kullanır.
2026-10-17 18:51:10 +00:00
Ollama'ya yüklü modelleri yönetmek için `configs/ai/install_ollama.txt`, sohbet arayüzü (Open WebUI) ve
alan adı yönlendirmesi için §20 kullanılır. Akıştaki model adı değiştirilirse akış dosyası
2026-09-17 12:24:51 +00:00
içindeki üç varsayılanın (`intentModel`, `chatModel`, `sqlModel`) birlikte güncellenmesi gerekir;
biri kalırsa o dal "model not found" ile düşer.
2026-06-09 12:54:05 +00:00
### 13.9. MCP sunucusu
2026-09-17 09:01:14 +00:00
Yapay zekâ istemcileri (Claude Code, Claude Desktop, ChatGPT) platforma **`/mcp`** ucundan
bağlanır. Uç, API imajının içinde aynı portta (`8080`) yayınlanır; ayrı bir container yoktur.
Modül `api/modules/Sozsoft.Mcp` altında standart ABP katmanlarıyla durur.
**Kimlik ve kiracı.** Kimlik doğrulama mevcut OpenIddict bearer token'ıdır. Uç,
`UseAuthentication` → `UseAbpOpenIddictValidation` → `UseMultiTenancy` → `UseUnitOfWork`
ardışık düzeninden sonra bağlanır ve açıkça bearer şemasına bağlıdır (token'sız istek giriş
sayfasına 302 ile değil, 401 ile döner); kiracı token talebinden ya da `__tenant` başlığından
çözülür. Taşıyıcı **durumsuz** kiptedir
(`HttpServerSessionMode.Stateless`): her istek kendi ABP kapsamında koşar, ters vekil
arkasında oturum yapışkanlığı gerekmez. `/mcp` istek düzeyindeki unit of work'ün **dışındadır**
(`AbpAspNetCoreUnitOfWorkOptions.IgnoredUrls`): cevap SSE ile aktığı için istek UoW'si sonuç
istemciye gittikten sonra commit edilir ve bağlantı kapanınca geri alınırdı — araç başarılı
görünür, EF kayıtları (ListForm, menü, yetki) kaybolur, DDL ve seed dosyası kalırdı. Her MCP
servis çağrısı kendi UoW'sini sonuç dönmeden tamamlar.
2026-09-17 09:01:14 +00:00
**Kiracı bazlı sözleşme.** `Sas_H_McpServer` tablosundaki kayıt (ekran:
2026-10-19 14:35:08 +00:00
`Sas.DeveloperKit.McpServers`) hangi araçların yayınlandığını ve sorguların hangi satır tavanıyla koşacağını
2026-09-18 13:47:23 +00:00
belirler; sorgular her zaman isteğin kiracısının veritabanında koşar. `Tenants` boşsa kayıt tüm
2026-09-17 09:01:14 +00:00
kiracılara açıktır (AiBot ile aynı sözleşme). Araç listesi bu kayda göre süzülür
(`AddListToolsFilter`) **ve** çağrı anında aynı kural yeniden uygulanır — süzme tek başına
bir yetki sınırı değildir.
**Kiracı veri okur ve yazar, geliştirme yapamaz.** Kiracı bağlamındaki istekte açık araçlar
2026-09-18 13:47:23 +00:00
`McpToolNames.TenantTools` kümesiyle sınırlıdır: `server_info`, `list_lists`, `describe_list`,
`search_schema`, `query_list`, `run_select`, `insert_row`, `update_row`, `delete_row`.
`AllowedTools` bu kümeyi yalnızca daraltabilir. Satır araçları ekranın C/U/D sözleşmesinden ve
kiracının kendi veritabanından geçer. `create_table`, wizard, component ve geliştirme talebi
2026-10-19 14:35:08 +00:00
araçları kiracının yetkisi ne olursa olsun listelenmez, çağrılırsa `Sas.Mcp.Error.TenantToolDisabled`
ile reddedilir. `list_queries`/`run_query` da dışarıdadır: procedure ve Custom Endpoint çağrısı
ekran yetkisinden geçmeden veri yazabilir. Host kullanıcısı için tek sunucu anahtarı
2026-09-18 13:47:23 +00:00
`AllowedTools`'tur (boşsa tümü); sınırı yetkiler çizer. Karar `McpToolPolicy.IsAllowed`'dadır,
liste süzgeci ve çağrı kapısı aynı kuralı kullanır.
2026-09-17 09:01:14 +00:00
**Araçlar.**
| Araç | İş |
| --- | --- |
| `server_info` | Bağlantının bağlamı (`scope`: `host` / `tenant`; kiracı adı `server.tenantName`), açık araçlar, veri sözlüğünün tazelik bilgisi. Sözlüğün `dictionaryKey` alanı (host'ta `Sozsoft`) sözlüğün yazıldığı anahtardır, kiracı bağlamını göstermez. Her zaman açıktır. |
2026-10-20 08:52:23 +00:00
| `list_lists` | Listeler: kod, başlık, açıklama, arkasındaki tablo. Host'ta `Sas.Listforms.Wizard` yetkisi olan geliştiriciye veri sözlüğünde henüz olmayan kaynaklar da döner: migrate bekleyen wizard ekranları (`sourceKind: pending`, alanlar wizard dosyasının `Groups[].Items[]` öğelerinden) ve yalnızca seed scripti olan tablolar (`pendingTable`, kolonlar scriptten). `describe_list` ve `search_schema` aynı kümeyi kullanır; model lookup/widget/view SQL'ini bu adlarla yazar. |
2026-09-17 09:01:14 +00:00
| `describe_list` | Bir listenin sütunları: alan adı, görünen başlık, SQL tipi, lookup ilişkisi, `isSensitive`. |
| `search_schema` | Liste ve sütun adları/başlıkları üzerinde aksan ve büyük/küçük harf duyarsız arama. |
| `query_list` | Listenin verisini ekranın kendi sorgusuyla (`GetSelectAsync`) okur: filtre, sıralama, gruplama, özet (DevExtreme biçimi). Sıralama/gruplama/özet JSON'u, arayüzün `Utils.ts`'te yaptığı gibi select ucunun metin biçimine (`Alan desc|Alan2 asc`) `McpLoadOptions` ile çevrilir; select ucu JSON'u tanımaz. Normal kullanıcının veri okuma yolu. |
| `run_select` | Parametreli tek SELECT; çoklu ifade, DDL ve DML reddedilir, satır tavanı sorguya yazılır. Yalnızca SQL Query Manager yetkisiyle. Sağlayıcı hatası mesajıyla döner (`SelectFailed`; bilinmeyen kolonda tabloların gerçek kolonlarıyla `SelectFailedColumns`; veritabanında olmayan ama seed scripti olan tabloda — `create_table` yazdı, DB Migrate kurmadı — scriptteki kolonlarla `SelectTableNotInstalled`); araç açıklaması kolonu varsaymamayı, gerekirse önce `INFORMATION_SCHEMA.COLUMNS`'ı okumayı söyler. |
2026-10-19 14:35:08 +00:00
| `list_queries` | View/procedure/function nesneleri (SQL Query Manager yetkisiyle) + çağrılabilir Custom Endpoint'ler (`Sas.DeveloperKit.CustomEndpoints.Get` ile). |
2026-09-17 09:01:14 +00:00
| `run_query` | Kayıtlı bir nesneyi ya da Custom Endpoint'i parametreleriyle çağırır; yetkiler `list_queries` ile aynı. |
2026-09-18 13:47:23 +00:00
| `insert_row` / `update_row` / `delete_row` | ListForm ekranının insert/update/delete sözleşmesinden geçer. |
| `get_dev_guide` | Üretim sözleşmesi: `lowcode` / `reference` belgelerinin başlık listesi ya da istenen bölümü (numarayla, örn. `4.2`, ya da başlık metniyle). Bölüm önce istenen, sonra diğer belgede aranır; başlıkla eşleşmeyen terimde (örn. `dxImageUpload`) terimin geçtiği bölümler (en çok 15, `DevGuideSearchMatches`), hiç geçmiyorsa başlık listesi döner — hata atılmaz. Çok büyük bir üst bölüm yalnızca giriş metni + alt başlıklarıyla döner. |
2026-10-19 14:35:08 +00:00
| `create_table` | Yeni tablonun scriptini SQL Query Manager tablo tasarımcısıyla aynı biçimde üretir (`McpTableCreator`): `Id` + `TenantId` + tam audit kolonları şablondan gelir, istemci yalnızca iş kolonlarını verir; tip ve varsayılan değer beyaz listeden geçer, istemci SQL yazamaz. `indexes` ve `foreignKeys` ile index/unique/FK aynı scripte varlık kontrollü (tekrar çalışmaya dayanıklı) yazılır; FK'nin hedefi migrate'te önce kurulmalıdır — adı bu tablodan önce sıralanan bir seed tablosu ya da veritabanında kurulu bir platform tablosu (`ForeignKeyTableNotInstalledFirst`). Script `sql/object/{Tablo}.sql` dosyasına, PostgreSQL eşi (`PostgreSqlTableScript`) `postgres/object/{Tablo}.sql` dosyasına yazılır. Veritabanında çalıştırılmaz: tabloyu kullanıcının çalıştırdığı DB Migrate kurar; o zamana kadar `deploy_wizard` kolonları scriptten okur (`PostgreSqlTableScript.ReadSeedColumns`). Yalnızca oluşturur — tablo ya da scripti varsa reddeder. `.Write` + `Sas.SqlQueryManager` ister. |
| `save_sql_object` | View/procedure/function scriptini `sql/object/{Ad}.sql` ve `postgres/object/{Ad}.sql` dosyalarına yazar (`McpSqlObjectWriter`); veritabanında çalıştırılmaz, nesneyi DB Migrate kurar. Gövde diyalektler arasında güvenle çevrilemediği için iki script de istemciden gelir ve aynı nesneyi tanımlamalıdır (`SqlObjectInvalid`); ad scriptten okunur, başlık `CREATE OR ALTER` / `CREATE OR REPLACE`'e çevrilir. Tablo scriptinin üstüne yazmaz. `.Write` + `Sas.SqlQueryManager` ister. |
| `generate_crud_endpoints` | SQL Query Manager CRUD diyaloğuyla aynı endpoint kümesini (`CrudEndpointSeedFileDto.Generate` — diyaloğun servisi de bunu kullanır) yalnızca `crud/{Entity}.json` dosyasına yazar; kayıtları DB Migrate ekler (`CrudEndpointSeedApplier`). `.Write` + `Sas.SqlQueryManager.CrudEndpoints` ister. |
2026-09-18 13:47:23 +00:00
| `list_wizards` / `get_wizard` | Wizard File Manager dosyaları; `get_wizard` seed dosyasını diskteki biçimiyle döndürür. |
2026-10-20 08:52:23 +00:00
| `deploy_wizard` | Wizard'ı Wizard ekranıyla aynı yoldan (`IListFormWizardAppService.Create`) deploy eder. Girdi seed dosyası biçimi (`{"Wizard": {...}}`, başlıklar toplanmış) ya da düz cevaplardır; ikisi de `WizardSeedFileDto.FromDeployJson`'dan geçer. Ekranın her ayarı `Wizard` bölümünün bir bloğu ya da alan öğesinin bir alanıdır; yazılmayan blok deploy'da varsayılanıyla dolar (`ListFormWizardDto.ApplyDefaults`), kısayol cevaplar (`AllowAdding` …, `IsRequired`) bloktan üstündür. Eski biçimin `ListForm`/`Fields` bölümleri reddedilir (`Sas.ListForm.WizardError.LegacySectionsNotAccepted`); kimlik alanları (yetki adları, popup başlığı, durum anahtarı) ekran kodundan yeniden türetilir, tanınmayan anahtar reddedilir. Böylece MCP, ListForm editörünün yapabildiği her ayarı wizard dosyasına yazar ve `replaceFileName` ile güncelleme (`get_wizard` çıktısı düzenlenip gönderilir) editör ayarlarını silmez. Yeni kaydın form varsayılanı kolonun tablodaki sabit `DEFAULT`'udur (katalog ya da seed scripti, `SqlSeedColumn.ConstantValue`; IsActive → true, UomId → Adet, para birimi → TRY tablonun `DEFAULT`'larından gelir); alana özel değer öğenin `DefaultValue`'su ile verilir (`lowcode.instructions.md` §4.3.5.1). Girdide sözleşmede olmayan anahtar adıyla reddedilir, sessizce yok sayılmaz. `StaticData` lookup seçeneklerinin düz metni `Sas.StaticLookup.*` dil anahtarına çevrilir ve EN + TR satırı yazılır (`LocalizeStaticLookupsAsync`, `lowcode.instructions.md` §4.3.4.1). Dosyaya kullanıcının etkin talebi `DevRequest` olarak yazılır (sunucu doldurur). Wizard ekranı gibi yalnızca seed dosyalarını yazar; ekran DB Migrate ile kurulur ve çıkıştaki `isPending` o zamana kadar `true` kalır (model migrate'i beklemez; `verify_wizard` bekleyen dosyada `install` kontrolünde durur). `replaceFileName` doluysa güncellemedir (dosyalar yeniden yazılır, önceki kayıtları migrate kaldırır). İstek demo veri taşıyorsa (prompt'taki satırlar, ekran görüntüsü) wizard `SeedData: true` ile deploy edilir — ekranın `SeedFilePath`'i `data/{ListFormCode}.json` olur (`SeedSync*` bayrakları demo veriyle açılmaz; yalnızca istekte açıkça istenirse wizard'ın `SeedSync: true` cevabıyla) — satırlar aynı çağrıda `seedRows` ile bu dosyaya yazılır (alan adları doğrulanır, `Guid` anahtarı sunucu üretir); migrate ekranı kurduktan sonra satırları ekler (`lowcode.instructions.md` §0.10.1). Widget sorgularındaki her dil anahtarı tanımlı olmalı ya da wizard'ın `Language.Texts` alanında EN + TR verilmeli, yoksa deploy adlarıyla reddeder (`ValidateWidgetLanguageKeysAsync`). `uploadUrl`'süz `dxImageUpload` alanı base64 sakladığı için kolonu sınırlı boyluysa (katalogdaki karakter sınırı) deploy reddedilir; tablo kolonuna bağlı `dxImageViewer` alanı da reddedilir — görsel kolonunun editörü her zaman `dxImageUpload`'dır (`ValidateImageColumnLengths`). Yeni wizard'ın adına (= dosya adı) modül öneki eklenir: `{ModuleId}_{Ad}` (`ListFormWizardDto.ResolveWizardName`; aynı ekran adı başka modülde de geçebilir), çıkıştaki `fileName` gerçek adı verir. `MenuOrder` yok sayılır: yeni ekran kardeşlerinin sonuna, güncelleme mevcut yerine yazılır — lookup kaynağı ekran önce deploy edildiği için onu kullanan ekranın üstünde kalır. |
| `delete_wizard` | Wizard Manager'daki silmeyle aynı: `InsertedRecords` + ekranın veri aynası, bağımlı wizard varsa reddedilir. `dropTables: true` ekranın tablosunu (yalnızca tasarımcı/`create_table` ile kurulmuş ve başka yerde kullanılmayan) CRUD endpoint'leri, `sql/object` ve `crud` dosyalarıyla düşürür; model bunu kullanıcı onayı olmadan göndermez (`WizardTableCleaner`, `lowcode.instructions.md` §4.5). |
2026-10-19 14:35:08 +00:00
| `verify_wizard` | Deploy edilmiş ekranı veri yazmadan doğrular: menü kaydı ve URL'si, menünün istediği yetki tanımı (`AbpPermissions`), List yolunda ekran sorgusu (`GetSelectAsync`, tek satır, çağıranın yetkisiyle), Custom yolunda bileşen (etkin, rota = menü URL'si) ve seed kapanışı (`ExportAnalysis` uyarıları; engellemez). İsteğe bağlıdır, migrate sonrasında kullanılır; `Sas.DevRequests` döngüsünün adımı değildir — döngü `Testing`'de biter, kullanıcı elle test edip `Staging`'e alır (`lowcode.instructions.md` §0.11). |
| `start_dev_request` | Geliştirme talebini Id ya da tam başlıkla üstlenir: yalnızca `Ready to Start` durumundakini `In Progress` durumuna alır, diğerlerini reddeder; talebin metnini döndürür. Açıklamaya yapıştırılmış base64 görseller (`data:image/png|jpeg|gif|webp`) metinden ayıklanır (`McpDevRequestImages`), yerlerinde `[image-N]` kalır ve her biri ayrı MCP resim içeriği (`ImageContentBlock`) olarak döner — model görseli resim olarak görür, görsel hiçbir yere yüklenmez. Sonuç istemcinin 1 MB sınırına sığdırılır: büyük görseller küçültülüp JPEG'e çevrilir (`IMcpImageShrinker`), sığmayan gönderilmez ve metinde bildirilir. `.Write` + `Sas.DevRequests.Update` ister. |
| `get_dev_request` | Talebi durumuna dokunmadan, `start_dev_request` ile aynı biçimde (metin + analiz + resim içerikleri) okur; analizden önce ya da yarım kalan talebe dönmek için. `Sas.DevRequests` ister. |
| `save_dev_request_analysis` | `Backlog`, `Analysis` ya da `Ready to Start` durumundaki talebin analizini (HTML) `Analysis` kolonuna yazar — var olanı değiştirir; `productionReady: true` (Üretime hazır: Evet) talebi `Ready to Start`'a, `false` (şartlı ya da Hayır) `Analysis` durumuna alır; diğer durumları reddeder. `Description`'a dokunmaz. Modül dokümanına yazmaz; doküman üretimde açılır. Analizin standardı `lowcode.instructions.md` §0.12. `.Write` + `Sas.DevRequests.Update` ister. |
| `add_dev_request_subtasks` | Talebin `SubTasks` listesine tamamlanmış satırlar ekler: model yalnızca başlığı verir, atanan kişi bağlanan kullanıcının adı (`userName`), bitiş tarihi bugün (`yyyy-MM-dd`) olarak sunucuda yazılır; var olan satırlar korunur. `.Write` + `Sas.DevRequests.Update` ister. |
| `append_dev_request_note` | Talebin `Analysis` kolonunun sonuna HTML ekler (üretim sonuç notu); `Description` talebin yazıldığı hâliyle kalır. İkisi de `update_row` ile yazılmaz — açıklama base64 görsel taşır. `.Write` + `Sas.DevRequests.Update` ister. |
| `list_components` / `get_component` / `save_component` / `delete_component` | Custom component'ler `ICustomComponentAppService` üzerinden; seed senkronu ve `DataSources` çözümü aynen çalışır. `save_component` ada göre ekler/günceller, tasarımcı belgesi (`Props.visualDesigner`, `sourceMode ≠ code`) taşıyan bileşene kod yazmayı reddeder — kod belgeden üretildiği için tasarımcıdaki ilk kayıtta ezilirdi. |
2026-10-19 14:35:08 +00:00
| `get_module_doc` / `save_module_doc` | Modül dokümanı (`configs/seeds/docs/modules/{Menü}.md`, `IModuleDocAppService`). Kök menü kodu (`Hr`) dosyanın tamamını, ekran/dal kodu ya da wizard dosya adı yalnızca o dalın bölümünü okur/yazar. `save_module_doc` elle yazılan bölümleri kaydeder, "Ek — Ekran envanteri"ni sunucu üretir. Talebi `update_row` ile `Testing`'e almak, talebin wizard'larının bölümü tamamlanmadan (bölüm var, `module-doc:todo` kalmamış) reddedilir. Okuma `Sas.Listforms.Wizard`, yazma ayrıca `Wizards.Update` ister. |
| `list_report_templates` / `get_report_template` / `save_report_category` / `save_report_template` / `delete_report_template` | Rapor kategorileri ve `DynamicReport` şablonları (`McpReportAppService`). Okuma depodan, ekranın okuma yetkisiyle yapılır. Yazma `Adm.Reports.Categories` / `Adm.Reports.ReportTemplates` ekranlarının C/U/D sözleşmesinden (`IListFormDataAppService`) geçer; seed senkronu aynen çalışır. Kayıt ada göre ekler/günceller. `listFormCode` verilirse HTML'deki `{{AlanAdi}}` yer tutucuları o ekranın alanlarına göre doğrulanır ve cevapta satır butonu (`rowButton`) döner (§15). |
**Timesheets.** Talebin `Timesheets` listesini sunucu doldurur (`IMcpDevRequestTimesheetRecorder`):
`get_dev_request` / `start_dev_request` talebi kullanıcının etkin talebi yapar (dağıtık önbellek,
2026-10-07 18:46:45 +00:00
`McpDevRequestActivityCacheItem`, 12 saat); sonraki her MCP yazma işlemi (`create_table`, `save_sql_object`, `generate_crud_endpoints`,
`deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, `save_report_category`,
2026-10-04 15:04:16 +00:00
`save_report_template`, `delete_report_template`, `save_module_doc`, satır araçları,
`save_dev_request_analysis`, `append_dev_request_note`, `add_dev_request_subtasks`) o talebe bir satır
2026-10-19 14:35:08 +00:00
ekler; talebi açan `start_dev_request` yazmaz. Açıklama `Sas.Mcp.Timesheet.*` anahtarından
Türkçe üretilir, süre önceki satırdan ya da talebin açıldığı andan ölçülür (en az 1 dakika, saat
olarak), çalışan bağlanan kullanıcı, tarih bugündür. Etkin talep yoksa satır yazılmaz; bozuk bir
2026-09-29 14:18:47 +00:00
`Timesheets` değeri işlemi geri almaz, yalnızca satır atlanır ve log'a düşer. Satır, aracın unit of
work'ü commit edildikten sonra ayrı bir UoW'de yazılır (`OnCompleted`): satır araçları talebi Dapper
bağlantısındaki transaction ile günceller ve EF'in ayrı bağlantıdan aynı satıra yazması o kilidi
bekleyip zaman aşımına düşerdi.
**Prompt'lar** (`McpPromptNames`, `McpDevelopPrompts`). İstemcide komut olarak görünür; yalnızca
metindir, yetki taşımaz. `analyze_dev_request` tek talebi, `analyze_backlog_requests` bütün
`Backlog` kuyruğunu analiz edip `Analysis` durumuna alır (§0.12); `process_dev_request` tek talebi,
`process_ready_requests` bütün `Ready to Start` kuyruğunu işler (§0.11). Hiçbiri adımları tekrar
etmez, `get_dev_guide` ile `lowcode.instructions.md` ilgili bölümüne yönlendirir.
**Geliştirme taleplerini çalıştırma.** Komutun adı istemcideki sunucu adına göre değişir: aşağıdaki
örnekler `claude mcp add sozsoft ...` ile eklenmiş sunucu içindir (`sozsoft-local` ile eklediysen
`mcp__sozsoft-local__...` yaz). Her durumda talep yalnızca `Ready to Start` durumundaysa işlenir;
değilse sunucu reddeder ve hiçbir şey üretilmez.
| Ne istiyorsun | Claude Code | Claude.ai / Claude Desktop | Düz metin (her istemci) |
| --- | --- | --- | --- |
| Tek talebi analiz et | `/mcp__sozsoft__analyze_dev_request <Id>` | `+` menüsü → `sozsoft` → `analyze_dev_request` | `<Id ya da "başlık"> talebini analiz et` |
| Backlog'u analiz et | `/mcp__sozsoft__analyze_backlog_requests` | `+` menüsü → `sozsoft` → `analyze_backlog_requests` | `Backlog'daki talepleri analiz et` |
| Tek talep | `/mcp__sozsoft__process_dev_request <Id>` | Mesaj kutusundaki `+` menüsü → `sozsoft` → `process_dev_request`, `request` alanına Id ya da tam başlık | `<Id ya da "başlık"> talebini işle` |
| Bütün kuyruk | `/mcp__sozsoft__process_ready_requests` | `+` menüsü → `sozsoft` → `process_ready_requests` | `Ready to Start durumundaki talepleri sırayla işle` |
| Önce kuyruğu görmek | — | — | `Ready to Start durumundaki talepleri Id'leriyle listele` |
```text
/mcp__sozsoft__process_dev_request 3f2a9c1e-5b7d-4e2a-9f10-6c8d2b4a1e77
/mcp__sozsoft__process_ready_requests
```
2026-10-19 14:35:08 +00:00
- **Id nereden gelir:** `Sas.DevRequests` ekranında `Id` kolonu gizlidir. Kolon seçiciden aç ya da
Claude'dan "Id'leriyle listele" ile iste.
- **Başlıkla çağırmak:** Claude Code komut argümanlarını boşluktan böler, bu yüzden boşluklu
başlıkta komut yerine Id ya da düz metin kullan. Başlık birden fazla talebe uyuyorsa sunucu Id ister.
- Düz metin de aynı yoldan geçer: sunucu talimatları modeli `start_dev_request`'e yönlendirir,
durum şartı yine sunucuda uygulanır.
2026-09-17 09:01:14 +00:00
**Yetki modeli: MCP yetki vermez.** Kullanıcı MCP'de uygulamadaki yetkisini taşır; MCP'nin iki
yetkisi yalnızca kapıdır. Bir çağrı üç katmandan geçer ve her katman ayrı reddeder:
1. **Sunucu kaydı** (`AllowedTools`, `Tenants`) — o kiracıda hangi araçların var olduğu. Kiracıdaki
herkes için aynıdır.
2. **MCP kapısı** (`.Invoke`, `.Write`) — kullanıcının MCP'yi okumak / değiştirmek için
kullanabilir olup olmadığı. Rol/kullanıcı bazındadır.
3. **Uygulama yetkisi** — işlemin kendisi: ekranın C/R/U/D yetkisi, Wizard, Custom Components,
SQL Query Manager. Kullanıcı uygulamada yapamadığını MCP'de de yapamaz.
**MCP yetkileri — ne açar, sınırı ne**
| Yetki | Kim alabilir | Ne açar | Açmadığı / sınırı |
| --- | --- | --- | --- |
2026-10-19 14:35:08 +00:00
| `Sas.DeveloperKit.McpServers` | Host, kiracı | MCP Servers ekranını görme (`.Create/.Update/.Delete/.Export/.Import` ekran işlemleri) | `/mcp` ucuna erişim vermez |
| `Sas.DeveloperKit.McpServers.Invoke` | Host, kiracı | `/mcp` bağlantısı ve bütün okuma araçları: `server_info`, `list_lists`, `describe_list`, `search_schema`, `query_list`; host'ta ayrıca `get_dev_guide`, `list_wizards`, `get_wizard`, `verify_wizard`, `get_dev_request`, `get_module_doc`, `list_components`, `get_component` | Hiçbir şey yazmaz. Okuma da ekran yetkisine bağlıdır: `R` yetkisi olmayan liste görünmez, sorgulanamaz. Ham SQL (`run_select`, `list_queries`, `run_query`) ayrıca `Sas.SqlQueryManager` ister |
| `Sas.DeveloperKit.McpServers.Write` | Host, kiracı | MCP üzerinden her değişiklik. **Kiracıda:** `insert_row`, `update_row`, `delete_row`. **Host'ta:** bunlara ek olarak `create_table`, `save_sql_object`, `generate_crud_endpoints`, `deploy_wizard`, `delete_wizard`, `save_component`, `delete_component`, `save_report_category`, `save_report_template`, `delete_report_template`, `save_module_doc`, `start_dev_request`, `save_dev_request_analysis`, `append_dev_request_note`, `add_dev_request_subtasks` | Kendi başına bir şey yazdırmaz: satır araçları ekranın `C`/`U`/`D` yetkisini, geliştirme araçları Wizard / Custom Components / SQL Query Manager / `Sas.DevRequests.Update` yetkisini ayrıca ister. Kiracıda geliştirme araçlarını **açmaz** — kiracı bağlamında bu araçlar yetkiden bağımsız kapalıdır (`McpToolNames.TenantTools`). `Invoke` olmadan işe yaramaz |
**Araç → gereken yetkiler** (Invoke hepsinde aranır, tabloda tekrar yazılmadı)
| Araç | Kiracı | MCP kapısı | Uygulama yetkisi |
| --- | --- | --- | --- |
| `server_info`, `list_lists`, `describe_list`, `search_schema` | ✓ | — | Listelenen her ekran için `PermissionJson.R` |
| `query_list` | ✓ | — | Ekranın `R` yetkisi |
2026-10-19 14:35:08 +00:00
| `run_select` | ✓ | — | `Sas.SqlQueryManager` |
| `list_queries`, `run_query` | — | — | Nesne: `Sas.SqlQueryManager`; Custom Endpoint: `Sas.DeveloperKit.CustomEndpoints.Get` + endpoint'in kullanıcı/rol listesi |
| `insert_row` / `update_row` / `delete_row` | ✓ | `.Write` | Ekranın `C` / `U` / `D` yetkisi |
| `get_dev_guide` | — | — | — |
2026-10-19 14:35:08 +00:00
| `list_wizards`, `get_wizard` | — | — | `Sas.Listforms.Wizard` |
| `verify_wizard` | — | — | `Sas.Listforms.Wizard`; seed kapanışı için `.Export` (yoksa o kontrol atlanır, doğrulama düşmez) |
| `deploy_wizard` | — | `.Write` | `Sas.Listforms.Wizard` + `.Create` (yeni) / `.Update` (`replaceFileName`) |
| `delete_wizard` | — | `.Write` | `Sas.Listforms.Wizard` + `.Delete`; `dropTables` için ayrıca `Sas.SqlQueryManager` |
| `get_module_doc` | — | — | `Sas.Listforms.Wizard` |
| `save_module_doc` | — | `.Write` | `Sas.Listforms.Wizard` + `.Update` |
| `create_table`, `save_sql_object` | — | `.Write` | `Sas.SqlQueryManager` |
| `generate_crud_endpoints` | — | `.Write` | `Sas.SqlQueryManager.CrudEndpoints` |
| `list_components`, `get_component` | — | — | `Sas.DeveloperKit.CustomComponents` |
| `save_component` | — | `.Write` | `Sas.DeveloperKit.CustomComponents.Create` (yeni) / `.Update` |
| `delete_component` | — | `.Write` | `Sas.DeveloperKit.CustomComponents.Delete` |
| `list_report_templates`, `get_report_template` | — | — | `Adm.Reports.ReportTemplates` |
| `save_report_category` | — | `.Write` | `Adm.Reports.Categories.Create` (yeni) / `.Update` |
| `save_report_template` | — | `.Write` | `Adm.Reports.ReportTemplates.Create` (yeni) / `.Update` |
| `delete_report_template` | — | `.Write` | `Adm.Reports.ReportTemplates.Delete` |
| `start_dev_request` | — | `.Write` | `Sas.DevRequests.Update` |
| `get_dev_request` | — | — | `Sas.DevRequests` |
| `save_dev_request_analysis` | — | `.Write` | `Sas.DevRequests.Update` |
| `append_dev_request_note` | — | `.Write` | `Sas.DevRequests.Update` |
| `add_dev_request_subtasks` | — | `.Write` | `Sas.DevRequests.Update` |
Kaynak: MCP kapısı `McpToolNames.RequiredPermissions`, kiracı kümesi `McpToolNames.TenantTools`,
arkadaki servisin attribute ile istediği yetkiler `McpToolNames.ServicePermissions`. `Invoke` her MCP
servisinin sınıf düzeyindeki `[Authorize]` attribute'undadır; `/mcp` ucu yalnızca kimlik doğrulama
ister (ucun kendisinde aranırsa kullanıcı nedeni yazmayan bir 403 alır). Araç listesi aynı
kurallarla süzülür (`GetAvailableToolsAsync`), çağrı anında kontrol tekrarlanır.
2026-10-10 22:03:36 +00:00
**Reddedilen çağrı ne görür.** Ret modelle konuşmadır, istisna değildir: MCP'nin kendi kontrolleri
(kiracıya kapalı araç, `AllowedTools` dışı araç, eksik yetki, hatalı girdi, `start_dev_request` durum
reddi…) `McpProblem` döndürür (`McpOutcome<T>`); araç katmanı onu `McpToolResult.From` ile hata
sonucuna (`CallToolResult.IsError`) çevirir. Debugger durmaz, sunucu logunda yığın izli `ERR` oluşmaz,
model nedeni okuyup girdiyi düzeltir:
2026-10-19 14:35:08 +00:00
- **Yetki reddi** → `Sas.Mcp.Error.PermissionDenied`: "'deploy_wizard' için gereken yetkiniz yok:
Wizard (Sas.Listforms.Wizard). Yöneticinizden bu yetkiyi isteyin." MCP yetkiyi
2026-10-10 22:03:36 +00:00
`McpAppService.CheckPermissionAsync` ile adıyla ister; satır ve `query_list` araçları ekranın
yetkisini de adıyla önceden ister (`IMcpListFormAccessFilter.GetPermissionNameAsync`).
2026-10-19 14:35:08 +00:00
- **İş kuralı / girdi reddi** → ilgili `Sas.Mcp.Error.*` metni.
2026-10-10 22:03:36 +00:00
Arkadaki platform servisinin (wizard, custom component, ListForm verisi) attığı istisna ve sınıf
düzeyindeki `[Authorize]` reddi için `McpToolErrorTranslator` (çağrı ve araç listesi filtresi,
`McpHttpApiModule`) devrededir: yetki reddinde eksik yetkiler aracın bilinen yetkileri
(`McpToolNames.KnownPermissionsOf`) ve reddin taşıdığı yetki adı karşılaştırılarak bulunur (belirlenemezse
`PermissionDeniedUnspecified`), `UserFriendlyException` yerelleştirilmiş mesajıyla döner.
Başka istisnalar çevrilmez; SQL ve yığın ayrıntısı istemciye sızmaz. Tek istisna `run_select`'tir:
2026-10-19 14:35:08 +00:00
sağlayıcı hatası (`DbException`, örn. tabloda olmayan kolon) `Sas.Mcp.Error.SelectFailed` ile
mesajıyla döner ki model sorguyu düzeltebilsin — araç zaten `Sas.SqlQueryManager` ister, hata metni
yeni bilgi sızdırmaz (`McpQueryAppService`).
2026-09-17 09:01:14 +00:00
2026-09-18 13:47:23 +00:00
**Geliştirme rehberi nereden okur.** `get_dev_guide` depodaki
`.github/instructions/lowcode*.instructions.md` dosyalarını runtime'da okur; Claude Code ile MCP
istemcisi aynı sözleşmeyi görür. Klasör `App:DevGuidePath` ayarından (konteynerde
`/etc/api/dev-guide`, compose'da `.github/instructions` salt okunur mount), boşsa çalışma
dizininden yukarı `.github/instructions` aranarak bulunur. İmaja gömülmez: API build bağlamı
`api/` olduğu için `.github` imaja girmez, rehber değiştiğinde imaj derlenmez.
2026-09-17 09:01:14 +00:00
**Şema keşfi nereden okur.** Tablo ve sütun bilgisi canlı katalogdan değil,
`Sas_H_DbTableColumn` tablosundaki JSON veri sözlüğünden gelir — sözlük dil anahtarlarından
çözülmüş başlıkları ve lookup ilişkilerini birlikte taşıdığı için modelin gördüğü adlar
kullanıcının ekranda gördüğü adlarla aynı olur. Sözlük
`dbo.Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns` prosedürüyle kiracı adı başına
üretilir; sözlük yoksa araçlar tablo döndürmez ve `server_info` bunu bildirir.
**OAuth (Claude / ChatGPT connector'ları).** Connector formuna yalnızca ad ve URL girilir;
kimlik doğrulamayı istemci MCP OAuth akışıyla kendisi yürütür:
1. `/mcp` token'sız isteğe 401 + `WWW-Authenticate: Bearer resource_metadata="…"` döner
(`McpAuthenticationChallengeMiddleware`).
2. İstemci `/.well-known/oauth-protected-resource/mcp` (RFC 9728) belgesinden yetkilendirme
sunucusunu (`AuthServer:Authority`) öğrenir, `/.well-known/oauth-authorization-server`
(OpenIddict keşif belgesi, `registration_endpoint` eklenmiş) belgesini okur.
3. `/connect/register` (RFC 7591, `McpOAuthClientManager`):
2026-10-07 08:17:46 +00:00
- İstenen geri dönüş adresleri bir **bilinen istemcide** kayıtlıysa onun kimliği döner:
`Platform_Mcp` (Claude, ChatGPT; onay ekranı çıkmaz) ya da `Platform_Mcp_Local` (yerel
`mcp-remote` proxy'si, `127.0.0.1` callback'i; açık onaylı). Adresler `DbMigrator` ayarı
`OpenIddict:Applications:{ClientId}:RedirectUris`'tedir; ayarı olmayan istemci seed edilmez.
2026-09-17 09:01:14 +00:00
- Değilse (Cursor, VS Code, Claude Code, başka bir AI istemcisi) **dinamik istemci** üretilir:
`mcp_` önekli, PKCE zorunlu, **açık onaylı** OpenIddict uygulaması. Aynı adres kümesi aynı
2026-10-07 08:17:46 +00:00
uygulamaya bağlanır — kayıt kilit altında ve kendi UoW'sinde commit edilir, masaüstü
istemcisinin aynı anda başlattığı proxy süreçleri ikinci kayıt açamaz; tavan `McpConsts.MaxDynamicClients`. Kabul edilen adresler: HTTPS,
2026-09-17 09:01:14 +00:00
localhost HTTP (RFC 8252), uygulama şemaları (`cursor://…`); `javascript:` vb. reddedilir.
Onay ekranındaki ad istemcinin beyanı + geri dönüş adresinin sahibidir (ad doğrulanamaz).
4. Kullanıcı platformun giriş ekranına (`/Account/Login`, kiracı seçimi dahil) yönlenir;
authorization code + PKCE ile token alınır. Kiracı ve yetki giriş yapan kullanıcıdan gelir.
Üç ayrıntı bilinçlidir:
- Yayıncı (`issuer`) `AuthServer:Authority`'den sabitlenir; ters vekil arkasında istekten
türetilirse `http://` olur ve istemci keşif belgesini reddeder. Korumalı kaynak belgesindeki
`authorization_servers` değeri bu `issuer` ile birebir aynıdır (sonda `/` dahil,
`McpOAuthOptions.AuthorizationServer`); RFC 8414 §3.3'ü uygulayan istemci (`mcp-remote`)
farkta `IssuerMismatchError` ile durur.
2026-09-17 09:01:14 +00:00
- MCP istemcisinin gönderdiği RFC 8707 `resource` parametresi yok sayılır
(`McpOAuthServerConfiguration`): OpenIddict kayıtsız kaynağı reddeder ya da token kitlesini
`/mcp` adresine daraltır, API ise `Platform` kitlesini doğrular.
- MCP istemcilerinin (`McpConsts.IsMcpClient`) token'ları kullanıcı oturumuna bağlanmaz
(`PlatformTokenController`). Tek oturum kuralı her girişte diğer oturumları sildiği için,
bağlansaydı connector her token yenilemesinde kullanıcının web oturumunu düşürürdü.
- MCP istemcilerinin yenileme token ömrü uygulama bazında 30 gündür
(`McpConsts.RefreshTokenLifetime`, OpenIddict uygulama ayarı) ve her kullanımda yenilenir;
web istemcisinin dakikalık ömrü etkilenmez. Kullanıcı 30 gün hiç soru sormazsa bağlantıyı
yeniden kurar.
Bağlantı sunucu gerektirir: connector formu bağlantıyı istemci sağlayıcının sunucularından
kurar, `localhost` erişilemez. Forma `https://api.sozsoft.com/mcp` yazılır.
**Bağlanma (token ile, CLI).**
```bash
# sunucu
claude mcp add sozsoft --transport http https://api.sozsoft.com/mcp \n --header "Authorization: Bearer <access_token>"
# localhost (dotnet run / IIS Express)
claude mcp add sozsoft-local --transport http https://localhost:44344/mcp \n --header "Authorization: Bearer <access_token>"
```
Ters vekilde `/mcp` ayrı bir blokta karşılanır (`configs/deployment/configs/nginx.conf`):
Streamable HTTP yanıtları SSE akışıdır, `location /` altındaki tamponlama akışı bekletir.
2026-09-17 12:24:51 +00:00
**Şema tuzağı.** İstek vekilden HTTP olarak gelir. `issuer` ve `registration_endpoint`
yapılandırmadan sabitlenir (`AuthServer:Authority`), ama `authorization_endpoint` ve
`token_endpoint` adreslerini OpenIddict istekten türetir. Uygulama `X-Forwarded-Proto`
başlığını işlemezse bu iki adres keşif belgesine `http://` olarak yazılır ve **OAuth 2.1
istemcileri (MCP connector'ları) TLS'siz uç kabul etmediği için bağlantı kurulamaz** —
istemci tarafında görülen hata genellikle "please register the OAuth client" olur.
Çözüm iki parçalıdır ve ikisi birlikte olmak zorundadır:
- API konteynerinde `ASPNETCORE_FORWARDEDHEADERS_ENABLED=true` (`docker-compose-app.yml`)
- nginx'te **her** `location` bloğunun `X-Forwarded-Proto $scheme` göndermesi. `location /`
bunu `proxy_params` üzerinden yapar; `/mcp`, `/videoroomhub`, `/messengerhub` ve
`/notificationhub` kendi
2026-09-17 12:24:51 +00:00
blokları olduğu için başlığı ayrıca set eder. Bir blok atlanırsa istemcinin gönderdiği
başlık olduğu gibi geçer ve şema taklit edilebilir hale gelir.
Doğrulama:
```bash
curl -s https://api.sozsoft.com/.well-known/oauth-authorization-server \
| tr ',' '\n' | grep -E 'issuer|authorization_endpoint|token_endpoint|registration_endpoint'
```
Dördü de `https://` olmalıdır.
**Yetkiler.** Ne açtıkları ve sınırları yukarıdaki "Yetki modeli" tablosundadır. İkisi de
`MultiTenancySide = Both`'tur. Eskiden geliştirme için ayrı olan `.Develop` yetkisi `.Write` ile
birleştirildi; mevcut veritabanında atamalar migrate sırasında `.Write`'a taşınır ve tanım silinir
(`McpDevelopPermissionMergeDataSeedContributor`).
`AllowedTools` ile yetkiler farklı eksenlerdir: sunucu kaydı bir kiracıda **hangi araçların var
olduğunu** belirler ve o kiracının bütün kullanıcıları için aynıdır; yetkiler **kimin
kullanabileceğini** rol/kullanıcı bazında belirler. Örneğin `delete_row` açık bir kayıtta yalnızca
`.Write` verilmiş rol MCP ile silebilir, diğerleri aynı bağlantıyla yalnızca okur.
2026-09-17 09:01:14 +00:00
---
2026-06-09 12:54:05 +00:00
## 14. Arka Plan İşleri
2026-06-09 12:54:05 +00:00
İki motor birlikte kullanılır: **Hangfire** (kalıcı iş deposu, `default` ve `platform` kuyrukları)
ve **ABP Background Workers** (yinelenen işler). Yönetim `/admin` altındaki Background Worker
2026-10-19 14:35:08 +00:00
ekranlarından yapılır; yetkiler `Sas.BackgroundWorkers.RecurringJobs` ve `.Jobs`.
2026-06-09 12:54:05 +00:00
| Worker tipi | İşlevi |
2026-06-09 12:54:05 +00:00
| --- | --- |
| `MailQueueWorker` | Mail kuyruğunu işler. |
| `SqlWorker` | Zamanlanmış SQL çalıştırır. |
| `NotificationWorker` | Bildirim kurallarını değerlendirir ve gönderir. |
| `SessionCleanupWorker` | Süresi dolmuş oturumları temizler. |
| `BackupWorker` | Veritabanı yedeği alır ve eski yedekleri temizler. |
| `ProbationReminderWorker` | Deneme süresinin bitmesine 15 gün kalan çalışanlar için `DenemeSuresiBitiyor` bildirim tipinin kurallarını işletir (`Scope: All`, her gün 08:00). |
2026-06-09 12:54:05 +00:00
**Kapsam (`Scope`).** İş kaydı her zaman host tablosundadır (`Sas_H_BackgroundWorker`); işin
**gövdesinin** hangi kiracı bağlamında kaç kez çalışacağını `Scope` belirler
(`WorkerScopeEnum`): `Host` yalnızca host bağlamında bir kez, `Tenant` her kiracı için bir kez,
`All` önce host sonra her kiracı için. **Her kapsam ayrı bir Hangfire işidir**
(`BackgroundWorkerInitializer`): host `{WorkerType}:{Name}`, kiracı `{WorkerType}:{Name}#{TenantId}`.
`PlatformBackgroundWorker.StartAsync` tek kapsam çalıştırır, kilidi de kapsam başına alır — bir
kiracının hatası ya da yavaşlığı diğerlerini durdurmaz ve Jobs ekranı kiracı başına süre, durum ve
hata gösterir (`Tenant` kolonu; ad `Sas_H_BackgroundWorker_JobFlow` içinde iş adının `#` ekinden
`AbpTenants`'a çözülür). Kiracı adı sonradan değişebildiği için iş adına kimlik yazılır.
Kiracı eklenip silindiğinde zamanlama yeniden üretilir (`BackgroundWorkerTenantHandler`).
`BeforeSp`/`AfterSp` de kapsam başına çalışır, çünkü dokundukları veri kiracıya aittir.
Bildirim kuyruğu kiracı tablosunda olduğu için `NotificationWorker` işi `All` kapsamıyla gelir
2026-10-19 14:35:08 +00:00
(`data/Sas.BackgroundWorkers.RecurringJobs.json`).
Hangfire panosu geliştirmede serbest, üretimde `AbpHangfireAuthorizationFilter` ile korumalıdır
(`/hangfire`).
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 15. Raporlama
2026-06-09 12:54:05 +00:00
- **DevExpress Reporting**: Web Document Viewer (`/admin/reports/:report/view/...`) ve
Report Designer (`/admin/reports/:report/design/...`).
- **Rapor deposu**: `ReportTemplate` ve `ReportCategory` varlıkları
2026-10-19 14:35:08 +00:00
(`data/Adm.Reports.{ReportTemplates,Categories}.json`, seed senkronu açık). Tasarımcıda
kaydedilen `.repx` düzenleri `CustomReportStorageWebExtension` ile sunucudaki
`PredefinedReports/Reports` klasörüne yazılır.
- **`DynamicReport` şablonları** (`Raporlar` kategorisi): `HtmlContent` HTML'i A4'e basılır
(`/admin/reports/DynamicReport/view/{ŞablonId}`). Ekran satırından açılınca
(`.../view/{ŞablonId}/{ListFormCode}?recordId=@Id`) `{{AlanAdi}}` yer tutucuları kaydın form
raporundaki metniyle doldurulur (`DynamicFormReport.GetRecordTextsAsync`; ekran okuma yetkisi +
kiracı filtresi, değerler HTML kodlanır). Kiracıda bulunmayan şablon host'tan okunur. MCP
araçları §13.9.
- **Dinamik raporlar** (`HttpApi.Host/PredefinedReports`): ListForm tanımından otomatik üretilen
grid, form ve ağaç raporları — grid durumunu (sütun sırası, gruplama, filtre) rapora yansıtır.
Form raporu (`DynamicFormReport`) alt formları ekrandaki sekme sırasıyla basar: `List` sekmesi grid
alt raporu, `Form` sekmesi (aynı satırı bölen `Id → Id` sekmeleri dahil) form alt raporu olur; iç içe
form sekmeleri en fazla 3 seviye iner.
- Linux konteynerlerde font sorunlarını önlemek için `DevExpressFontConfiguration` devrededir.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 16. Dosya Yönetimi ve CDN
2026-06-09 12:54:05 +00:00
- ABP BlobStoring, dosya sistemi sağlayıcısıyla `App:CdnPath` altına yazar; dosyalar `App:CdnUrl`
üzerinden servis edilir.
- `FileManagementAppService`: listeleme, yükleme, indirme, kopyalama, taşıma, yeniden adlandırma, silme.
- UI: `/admin/files` (`views/admin/files/FileManager.tsx`) — breadcrumb, çoklu yükleme, önizleme.
- Avatar, aktivite görselleri, içe aktarma dosyaları ve veritabanı yedekleri ayrı konteynerlerde tutulur.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 17. Gerçek Zamanlı Özellikler
2026-06-09 12:54:05 +00:00
- **Messenger**: SignalR hub'ı `/messengerhub`. Konuşma ve mesaj varlıkları veritabanında;
Redis etkinse backplane olarak kullanılır. Query string ile gelen `access_token`,
hub bağlantısı için `Authorization` başlığına çevrilir.
- **Video oda**: Oda oluşturma/başlatma/bitirme, katılımcı yönetimi, sohbet, doküman ve ekran
paylaşımı panelleri; TURN sunucusu `configs/deployment/configs/turnserver.conf` ile yapılandırılır.
- **Global arama**: `GlobalSearchAppService` üzerinden çapraz modül arama.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 18. Frontend Mimarisi
2026-06-09 12:54:05 +00:00
### 18.1. Route ve bileşen çözümü
2026-06-09 12:54:05 +00:00
```
Route kayıtları (DB)
├─ componentPath ──► fiziksel view (React.lazy)
└─ CustomComponent ──► @babel/standalone ile runtime derleme ──► ComponentContext
│
DynamicRouter ──► ProtectedRoute ──► PermissionGuard ──► PageContainer
```
2026-06-09 12:54:05 +00:00
`ROUTES_ENUM` (`src/routes/route.constant.ts`) statik yol sözleşmelerini tutar; iş modülleri için
(`supplychain`, `maintenance`, `warehouse`, `projects`, `hr`, `crm`, `mrp`, `accounting`) ayrılmış
yol desenleri de buradadır. Bu alanların ekranları, fiziksel React sayfaları yerine ListForm
konfigürasyonu ile üretilir.
2026-06-09 12:54:05 +00:00
### 18.2. Durum yönetimi
2026-06-09 12:54:05 +00:00
- **easy-peasy** global store: `auth`, `theme`, `locale`, `abpConfig`, `admin`, `client`, `base`.
Oturum ve tercih verileri `localStorage` yerine store üzerinden yönetilir (`stateSync.ts`).
Kenar çubuğu (`abpConfig.menu.mainMenu`) ayrı bir menü isteğiyle değil, `getConfig` içinde
application-configuration'ın `extraProperties.menus.protected` listesi + yüklü dil metinlerinden
kurulur; dil, yetki ya da menü kaydından sonra `getConfig` çağrısı menüyü de tazeler.
- **React Query**: sunucu verisi önbellekleme.
2026-10-17 18:51:10 +00:00
- **Context'ler**: `ComponentContext` (runtime bileşen kaydı), `DemoContext` (demo talebi diyaloğu).
2026-06-09 12:54:05 +00:00
### 18.3. Tema ve lokalizasyon
2026-06-09 12:54:05 +00:00
- Tailwind tabanlı tasarım sistemi (`components/ui`) + DevExtreme temaları; açık/koyu mod ve
tema yapılandırıcı (`ThemeConfigurator`).
- **Varsayılan mod koyudur.** `proxy/theme/theme.config.ts`: `mode` ve `navMode` `dark`, DevExtreme
stili `dx.material.blue.dark.compact`. Kullanıcı tercihi store üzerinden saklanır; bu yalnızca
ilk açılış varsayılanıdır.
- Dil metinleri veritabanından gelir (`Sozsoft.Languages`); dil seçici header'dadır.
Tenant bazlı çeviri geçersiz kılma desteklenir.
2026-10-17 18:51:10 +00:00
- Tarih ve sayı biçimi aktif dilden gelir: editör/kolon `displayFormat` değerleri DevExtreme'in adlı biçimleridir
(`shortDate`, `shortDateShortTime`, `shortTime`; `EditorOptions.Date/DateTime/Time` ↔ `presets.ts` aynası), sunucu
tarafı tek yol `CultureFormat` + `CultureInfo.CurrentCulture`'dır — sabit desen (`dd/MM/yyyy`) ne seed'e ne koda yazılır.
İstemcide kullanıcıya görünen her tarih/sayı `ui/src/utils/localeFormat.ts` üzerinden biçimlenir (`formatLocaleDate/Time/
DateTime/Number/Currency`), DevExtreme arayüz metinleri `configureDevExtreme`'de `loadMessages` ile dile göre yüklenir;
izinli makine biçimleri `ui/README.md` › "Tarih ve sayı biçimi".
- Layout seçenekleri: dikey menü, yatay menü, yığılmış yan menü, mobil navigasyon.
2026-06-09 12:54:05 +00:00
### 18.4. PWA ve sürüm yönetimi
2026-06-09 12:54:05 +00:00
2026-10-05 12:58:59 +00:00
- Service worker kurulumu yalnızca **kabuğu** indirir (index.html + entry JS/CSS + eager vendor
chunk'ları, workbox precache manifest'i); kabuk zorunludur. **Bileşen ısıtması** kurulumdan
ayrıdır ve yalnızca oturum açık kullanıcı içindir: store, application-configuration her
geldiğinde `protected` fiziksel rotalardan kullanıcının yetkili olduklarını
(`PermissionGuard` ile aynı ölçüt) `setWarmupRoutes` ile bildirir, uygulama `ENSURE_WARMUP`
mesajıyla bu rotaları worker'a yollar. Worker build'de üretilen `dist/warmup.json`'daki
`routes` eşlemesinden (view modülü → chunk + statik import zinciri + CSS) yalnızca o dosyaları
indirir. Ziyaretçi, public sayfa ve giriş ekranı hiçbir bileşen indirmez; ekranın içinden lazy
açılan parçalar (diyalog, editör) ve runtime bileşenleri ilk kullanımda runtime cache'e
alınır. Isıtma best-effort'tur (inemeyen dosya atlanır, süre bütçesi aşılırsa kalanlar
runtime cache'e kalır) ve her açılışta eksikleri kaldığı yerden tamamlar. DevExtreme temaları
(~33 MB), font ve görseller runtime cache'tedir.
2026-08-12 19:08:14 +00:00
- `registerType: 'prompt'` — yeni sürüm indirilir, indirme bitince otomatik devreye alınır
(`views/version/swRegistration.ts`, `sw.ts`, `AppUpdateDialog.tsx`).
2026-08-17 08:51:46 +00:00
- **Sürüm bilgisi backend'dedir.** `Sas_H_ChangeLog` tablosu tek kaynaktır; istemci sürüm
2026-08-12 21:26:36 +00:00
numarasını ve son sürümün notlarını `application-configuration` yanıtındaki
`extraProperties.changeLogs` alanından okur (`PlatformApplicationConfigurationContributor`),
2026-08-17 08:51:46 +00:00
tam listeyi ise `/api/app/change-log` endpoint'inden alır. Ayrı bir `version.json` isteği
2026-08-12 21:26:36 +00:00
yoktur. Changelog `/admin/changeLog` ekranında gösterilir; sürüm numarası son görülenden
farklıysa kullanıcı giriş sonrası oraya yönlendirilir (tetikleyici bundle değil, sürümdür —
yalnızca yeni bir git tag'i eklenmiş olsa da çalışır).
2026-08-17 08:51:46 +00:00
- **Sürüm notu akışı — DbMigrator gerektirmez:** `configs/change-logs/generate-change-logs.sh`
deploy sırasında (`deploy/app.sh`) git tag'lerinden `configs/change-logs/change-logs.json`
üretir, dosya API konteynerine `/etc/api/change-logs` altına salt okunur mount edilir ve
`ChangeLogStartupService` uygulama açılışında tabloyu bu dosyayla eşitler. Dosya imaja
2026-08-15 06:30:36 +00:00
**gömülmez**: api build context'i içindeyken her yeni tag Dockerfile'daki `COPY . .`
katmanını geçersiz kılıp imajı gereksiz yere yeniden derletiyordu; artık kod değişmediyse
2026-10-20 10:53:21 +00:00
imaj cache'ten gelir. Yeni sürüm için tag atıp deploy etmek yeterlidir: dosya değiştiyse
`app.sh` API'yi yeniden başlatır, çünkü imajı değişmeyen konteynere `up -d` dokunmaz ve
açılışta okunan dosya aksi hâlde tabloya hiç yansımazdı. Notlar
2026-08-12 21:26:36 +00:00
**annotated** tag mesajından okunur (`git tag -a 1.2.0 -m "..."`), her satır bir madde.
- **Tablo git tag listesinin aynasıdır:** yeni tag eklenir, mesajı düzeltilen tag güncellenir,
2026-08-17 08:51:46 +00:00
git'ten silinen tag tablodan da (hard delete ile) kaldırılır. Bu yüzden `Sas_H_ChangeLog`
2026-08-12 21:26:36 +00:00
elle düzenlenmemelidir — ilk deploy'da geri alınır. Dosya okunamaz veya boşsa senkronizasyon
hiçbir şey yapmaz, tablo boşaltılmaz.
- Deploy algılaması service worker'ın kendi `registration.update()` kontrolüne dayanır:
herhangi bir chunk değiştiğinde entry hash'i, dolayısıyla index.html ve `sw.js` içindeki
precache manifest'i de değişir.
2026-10-17 18:51:10 +00:00
- **Deploy sonrası eksik chunk yoktur.** UI konteyneri nginx ile servis eder
(`ui/Sozsoft.Platform.Ui.Dockerfile`, `ui/nginx/default.conf`): hash'li `assets/` dosyaları
`ui-assets` volume'unda biriktirilir (`ui/nginx/40-archive-assets.sh` açılışta yeni sürümün
dosyalarını ekler, 30 günden eski ve artık hiçbir sürümde olmayanları siler), böylece
deploy'dan önce açılmış sekmeler kendi sürümlerinin chunk'larını bulmaya devam eder.
`index.html`/`sw.js`/`warmup.json`/`manifest.webmanifest` `no-store`, hash'li dosyalar
`immutable` başlığıyla döner. Yine de bir chunk bulunamazsa (`vite:preloadError`) uygulama
kabuk cache'ini silip sayfayı bir kez yeniler; ikinci başarısızlıkta hata sınırı
"uygulama güncellendi, yenileyin" mesajı ve Yenile butonu gösterir
(`swRegistration.isChunkLoadError` / `reloadApp`). Ayrıntı: `ui/README.md`.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 19. API ve Swagger
2026-06-09 12:54:05 +00:00
- Swagger UI, API kökünde yayınlanır; OAuth2 ile "Authorize" düğmesi üzerinden token alınabilir.
- Endpoint'ler ABP **conventional controller** mekanizmasıyla uygulama servislerinden üretilir:
2026-06-09 12:54:05 +00:00
| Namespace | Kök yol |
2026-06-09 12:54:05 +00:00
| --- | --- |
2026-10-19 14:35:08 +00:00
| `Sozsoft.Platform.ListForms.Adm` | `/api/admin/...` |
| Diğer `Sozsoft.Platform.*` servisleri | `/api/app/...` |
| Modül servisleri (Languages, Settings, MailQueue, Notifications, SqlQueryManager) | Modül kendi kök yolunu kullanır |
2026-06-09 12:54:05 +00:00
- DevExpress raporlama controller'ları (`CustomReportDesigner`, `CustomWebDocumentViewer`,
`QueryBuilder`) Swagger dokümanından hariç tutulur.
- Yanıtlar Brotli/Gzip ile sıkıştırılır; `X-Correlation-Id` başlığı istemciye açılır.
2026-10-19 14:35:08 +00:00
- Hata kodları: `PlatformConsts.AppErrorCodes` (`Error:0001` yetkisiz, `Sas.Platform.NoResults2` kayıt yok,
`Error:0003` parametre geçersiz, `Error:0005` iç hata).
2026-06-09 12:54:05 +00:00
**Token örneği** (yer tutucularla):
2026-06-09 12:54:05 +00:00
```http
POST /connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
2026-06-09 12:54:05 +00:00
grant_type=password
&username=<KULLANICI>
&password=<PAROLA>
&client_id=Platform_PublicApi
&scope=offline_access%20Platform
```
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 20. Dağıtım (Deployment)
2026-06-09 12:54:05 +00:00
`configs/deployment` altında üç ortam için compose dosyaları ve numaralandırılmış scriptler bulunur.
2026-09-17 12:24:51 +00:00
Benzer adlı `configs/docker` ile karıştırılmamalı: `docker/` yalnızca **yerel geliştirmede** veri
katmanını (SQL/PostgreSQL/Redis) ayağa kaldırır, `deployment/` ise **sunucudaki** kurulumdur.
2026-06-09 12:54:05 +00:00
| Dosya | Amaç |
2026-06-09 12:54:05 +00:00
| --- | --- |
| `docker-compose-data.yml` | Redis, SQL Server / PostgreSQL, yedek volume'ları |
| `docker-compose-app.yml` / `.dev.yml` / `.production.yml` | API, UI, migrator, nginx |
2026-09-17 12:24:51 +00:00
| `docker-compose-devops.yml` | Forgejo (CI/CD), Rocket.Chat, n8n, Ollama, Open WebUI, coturn |
| `Dockerfile`, `api/*.Dockerfile`, `ui/Sozsoft.Platform.Ui.Dockerfile` | İmaj tanımları |
2026-09-17 12:24:51 +00:00
Servis yapılandırma dosyaları tek klasörde, `configs/deployment/configs/` altındadır — compose
dosyaları buradan mount eder, ikinci bir kopya tutulmaz:
| Dosya | Kullanan servis |
| --- | --- |
| `nginx.conf` | Ters vekil: tüm `*.sozsoft.com` vhost'ları, SSL sonlandırma, `/mcp` bloğu |
| `redis.conf` / `redis.7.conf` | Redis (dağıtık cache, sürüme göre) |
| `turnserver.conf` | coturn — WebRTC TURN/STUN (bkz. §Messenger) |
| `elasticsearch.yml` | Elasticsearch (opsiyonel log/arama katmanı) |
### Alt alan adı → port eşlemesi
`nginx.conf` içindeki vhost'lar host üzerinde yayınlanan portlara bakar; yeni bir servis
eklerken portun boş olduğu buradan doğrulanır.
| Alan adı | Port | Servis |
| --- | --- | --- |
| `devops.sozsoft.com` | 3000 | Forgejo |
| `chat.sozsoft.com` | 3001 | Rocket.Chat |
| `sozsoft.com` + tenant alt alan adları | 3002 | UI (React) |
| `ollama.sozsoft.com` | 3003 | Open WebUI — sohbet arayüzü |
| `ollama.sozsoft.com/ollama-api/` | 11434 | Ollama ham API'si (basic auth) |
| `ai.sozsoft.com` | 5678 | n8n |
| `api.sozsoft.com` | 8080 | API (`/mcp`, `/videoroomhub`, `/messengerhub`, `/notificationhub` dahil) |
2026-09-17 12:24:51 +00:00
| `cdn.sozsoft.com` | 8081 | CDN |
Open WebUI kendi backend'ini `/api/...` altında sunduğu için Ollama'nın ham API'si `/ollama-api/`
önekine alınmıştır; iki uç aynı önekte yaşayamaz.
Basic auth yalnızca `/ollama-api/` bloğundadır — Ollama'nın kendi kimlik doğrulaması yoktur.
Sohbet arayüzünün önüne **konmaz**: Open WebUI socket.io üzerinden WebSocket açar, tarayıcı WS
upgrade isteğine basic auth kimliğini eklemez ve bağlantı 401 döngüsüne girer. Arayüzün güvenlik
sınırı uygulamanın kendi girişidir; bu yüzden kurulumdan hemen sonra **yeni kayıt alımının
kapatılması bir tercih değil, kurulumun parçasıdır** (`Settings → Admin → General → Enable New
Sign Ups`). Daha sıkı bir sınır gerekiyorsa vhost'a `allow`/`deny` ile IP kısıtı eklenir; basic
auth bu arayüz için bir seçenek değildir.
Compose tarafında `DEFAULT_USER_ROLE=pending` ikinci bir emniyet katmanıdır: ilk kaydolan admin
olur, sonrakiler onay bekleyen role düşer. Bu değer ve `ENABLE_SIGNUP` Open WebUI'nin
*PersistentConfig* değişkenlerindendir — yalnızca **ilk açılışta** env'den okunur, sonrasında
veritabanındaki değer geçerlidir; yani env'i sonradan değiştirmek yerine arayüzdeki ayar kullanılır.
GPU olmayan 8 GB'lık DevOps makinesinde Ollama `OLLAMA_MAX_LOADED_MODELS=1` ile sınırlıdır ve
Open WebUI'nin gömme/erişim hattı kapalıdır (`BYPASS_EMBEDDING_AND_RETRIEVAL`): varsayılan RAG
hattı ayrıca bir gömme modeli indirir, bu makinede bellek bütçesi yoktur. Yüklenen dosyanın metni
doğrudan bağlama girer.
2026-06-09 12:54:05 +00:00
Script sırası (`configs/deployment/scripts`):
```
2026-09-17 12:24:51 +00:00
1-devops.sh → DevOps makinesi (Forgejo, Rocket.Chat, n8n, Ollama, Open WebUI)
2-data-postgres.sh /
3-data-sql.sh → Veri katmanı
4-production-build.sh → İmaj build
5-production-deploy.sh → Yayın
6-production-migrator-true.sh → Migration çalıştırma
7-setup_ssl.sh → Sertifika
8-dev-build.sh / 9-dev-deploy.sh / 10-dev-migrator-true.sh → Dev ortamı
```
2026-06-09 12:54:05 +00:00
Migrator servisi `SEED` ortam değişkeni ile çalışır ve compose dosyasında `SEED=${SEED:-true}`
olarak tanımlıdır: değişken verilmezse seed **çalışır**. Seed dosyaları imaja gömülmez; depodaki
`configs/seeds` klasöründen mount edilir (`App:SeedsPath`), böylece bir seed değiştiğinde imaj
build etmek gerekmez.
Ayrıntılı kurulum notları: [`configs/deployment/README.md`](configs/deployment/README.md),
[`Readme_Production.md`](configs/deployment/Readme_Production.md),
[`Readme_Devops.md`](configs/deployment/Readme_Devops.md).
2026-06-09 12:54:05 +00:00
> Bu dokümanlarda geçmişte örnek amaçlı bırakılmış kimlik bilgileri bulunabilir; üretimde
> tümü döndürülmeli ve secret yöneticisine taşınmalıdır.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 21. Geliştirme Standartları ve Sık Kullanılan Komutlar
2026-06-09 12:54:05 +00:00
### 21.1. Çözüm kuralları
2026-06-09 12:54:05 +00:00
1. Her istek şu sırayla değerlendirilir: **(1)** ListForm konfigürasyonu → **(2)** SQL Query
Manager + Custom Endpoint → **(3)** Dynamic Service → **(4)** kod değişikliği (son çare,
gerekçe zorunlu).
2. Standart iş ihtiyaçları için yeni özel React sayfası/bileşeni geliştirilmez.
3. Her öneri tenant ve yetki tasarımını içerir.
4. Platform yetkilendirme desenleri atlanmaz.
5. Secret, tenant id veya bağlantı dizesi koda gömülmez.
2026-06-09 12:54:05 +00:00
### 21.2. Frontend
2026-06-09 12:54:05 +00:00
```powershell
cd ui
npm start # geliştirme sunucusu
npm run build # üretim derlemesi (metadata + version + vite build)
2026-10-16 21:13:31 +00:00
npm run lint # oxlint (.oxlintrc.json)
npm run format # prettier --write + oxlint --fix
```
2026-06-09 12:54:05 +00:00
### 21.3. Backend
2026-06-09 12:54:05 +00:00
```powershell
cd api
dotnet build
dotnet run --project src/Sozsoft.Platform.HttpApi.Host
dotnet format --include .\modules\Sozsoft.Notifications\ --folder
2026-06-09 12:54:05 +00:00
# Kod standardı kapısı (ölü kod / stil analizörleri)
dotnet build Sozsoft.Platform.sln --no-incremental `
-p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true
dotnet format Sozsoft.Platform.sln --diagnostics IDE0005,IDE0161 --severity warn
# Migration (EntityFrameworkCore projesi içinde)
dotnet ef migrations add <Ad>
dotnet ef database update
dotnet ef migrations remove
```
2026-06-09 12:54:05 +00:00
.NET/ABP kod standardı (hedef sürümler, nullable politikası, ölü kod kuralları, modern C#
kullanımı, ABP katman sınırları) bağlayıcı olarak
[`.github/instructions/dotnet.instructions.md`](.github/instructions/dotnet.instructions.md)
dosyasındadır. Standart derlemede zorlanır: `api/Directory.Build.props` analizörleri açar,
`api/.editorconfig` kural şiddetlerini tanımlar; bu iki dosya standardın tek kaynağıdır.
Yeni ABP modülü eklemek için:
`abp new Sozsoft.<Modul> -t module --no-ui -m none --database-provider ef`
2026-06-09 12:54:05 +00:00
### 21.4. Seed dosyaları
2026-06-09 12:54:05 +00:00
İki ayrı seed yüzeyi vardır ve karıştırılmamalıdır.
**1. Depo içi (kod ile taşınan) seed'ler** — `api/src/Sozsoft.Platform.DbMigrator/Migrations/`.
2026-10-19 14:35:08 +00:00
Platformun kendi ekranları buradadır (dil metinleri `configs/seeds/data/Sas.Languages.*.json`); değişiklikleri
derlenip yayınlanır. Yeni bir modülün nasıl kurulacağını öğrenmek için başvurulacak dosyalar:
2026-10-19 14:35:08 +00:00
- `ListFormSeeder_Sas.cs`, `ListFormSeeder_Adm.cs`, `ListFormSeeder_Hr.cs` — menü
2026-09-29 11:46:39 +00:00
modülüne göre ayrılmış ListForm + alan tanımları (İK ekranları `ListFormSeeder_Hr.cs`'te)
- `WizardDataSeeder.cs`, `CrudDataSeeder.cs` — aşağıdaki
runtime seed dosyalarını okuyup uygulayan seeder'lar
- `ListFormSeedDataApplier`, `CrudEndpointSeedApplier` (`Domain/Data/`) — uygulama kuralının tek
yeri; hem migrate/seed akışı hem wizard import'unun kapanışı aynı servisleri çağırır
**2. Runtime'da üretilen seed'ler** — `configs/seeds/`. Wizard, Custom Components ekranı/Visual
Designer, CRUD Endpoint Manager ve `SeedFilePath` tanımlı ListForm ekranları çalışma zamanında
burayı yazar; veritabanı silinip yeniden oluşturulduğunda aynı dosyalar
2026-10-05 12:58:59 +00:00
geri yüklenir. Klasör tektir (host ve kendi veritabanı olan her tenant aynı dosyalarla kurulur)
ve `SeedPathResolver` üzerinden çözülür:
```
configs/seeds/
2026-10-05 12:58:59 +00:00
├── wizard/ # {Ad}.json → WizardDataSeeder
├── crud/ # {EntityName}.json → CrudEndpointSeedApplier
2026-10-07 08:17:46 +00:00
├── docs/modules/ # {Menü}.md → ModuleDocManager (seed edilmez)
2026-10-05 12:58:59 +00:00
├── data/ # {ListFormCode}.json ya da {ListFormCode}/{anahtar}.json → ListFormSeedDataApplier
2026-10-19 14:35:08 +00:00
│ # Sas.Menus.{Routes,Module,Menu}.json — route/modül/menü ağacı
2026-10-05 12:58:59 +00:00
│ # Abp.Identity.Permission{Groups,s}.json — yetki tanımları
2026-10-19 14:35:08 +00:00
│ # Sas.Definitions.{CountryGroup,Country,City,District}.json — adres zinciri
│ # Sas.Definitions.{Currency,ContactTitle,AiBot,ApplicationSuite}.json — para birimi, hitap, AI bot, uygulama paketi; Hr.TerminationReason.json — SGK çıkış kodu (host tablosu, İK menüsü)
│ # Hr.{SkillType,SkillLevel,Skill}.json — İK yetkinlik tanımları
│ # Hr.{Department,JobPosition}.json — İK departman → pozisyon (BranchId ile şubeye bağlı)
│ # Hr.Intranet.*.json — İK › Intranet (duyuru, anket, sosyal akış, etkinlik)
│ # Hr.{CandidateSource,RejectionReason,EvaluationCriterion}.json — İK › Tanımlar altındaki işe alım tanımları
│ # Hr.PerformanceRating.json — İK › Tanımlar › Performans Dereceleri (5 kademeli ölçek)
│ # Adm.Notifications.Notification{Templates,Types,Rules}.json — bildirim şablonu, tipi → kuralı
2026-10-21 20:40:03 +00:00
│ # Sas.Languages.Language.json + Sas.Languages.LanguageText/{CultureName}.json — dil → metin (eksik LanguageKey metinle üretilir)
2026-10-19 14:35:08 +00:00
│ # Sas.SettingDefinitions.json, Sas.BackgroundWorkers.RecurringJobs.json — ayar tanımları, zamanlanmış işler
2026-10-05 12:58:59 +00:00
├── sql/{object,execute}/ # .sql (SQL Server)
│ └── host/{object,execute}/ # yalnızca host veritabanında çalışır
├── postgres/{object,execute}/
│ └── host/{object,execute}/
└── .imports/{importId}/ # wizard import staging + backup (seeder taramalarına girmez)
```
2026-06-09 12:54:05 +00:00
> Bu dosyalar elle de düzenlenebilir; ilgili ekrandan tekrar kaydedildiğinde yeniden üretilirler.
> Wizard dosyası `EditFileName` ile güncellenirse deploy veritabanına dokunmaz: dosyalar cevaptan
> yeniden yazılır (ad değiştiyse eski dosya silinir), önceki sürümün kayıtları `Pending.Replaced`'a
> alınır ve DB Migrate onları kaldırıp ekranı yeniden kurar (`lowcode.instructions.md` §4.0). Wizard'ın ürettiği ekran **ListForm detayından** düzenlendiğinde
2026-10-20 08:52:23 +00:00
> de aynı dosya güncellenir (`WizardSeedSynchronizer`): `Wizard` bölümü veritabanındaki güncel
> ekrandan `RefreshFrom` ile yeniden yazılır.
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 22. Sorun Giderme
2026-06-09 12:54:05 +00:00
| Belirti | Olası neden ve çözüm |
2026-06-09 12:54:05 +00:00
| --- | --- |
2026-10-18 21:47:36 +00:00
| `/api/abp/application-configuration` (ve kurulum dışı her uç) 503 dönüyor | Veritabanına erişilemiyor (`DatabaseReadinessGate`). Bağlantı dizesini ve veri konteynerini kontrol edin; `/api/setup/application-status` ile durumu görün. |
| UI `/setup` ekranına düşüyor | Şema oluşmamış veya bekleyen migration var. Setup ekranından migration'ı tetikleyin ya da DbMigrator'ı çalıştırın. |
| Giriş "UserCannotSignIn..." hatası veriyor | [11.3](#113-giriş-engelleri) tablosuna bakın: IP, çalışma saati, doğrulama, şube/eş zamanlı kullanıcı limiti. |
2026-10-05 14:11:06 +00:00
| Yeni sürüm tarayıcıda görünmüyor | Yeni worker indirmeyi bitirince kendiliğinden devreye girer ve sayfa yenilenir (`swRegistration.ts`). Kurulum takıldıysa sekmeyi tamamen kapatıp açın ya da DevTools › Application › Service Workers'tan eski worker'ı kaldırın. |
| DevExtreme teması yüklenmiyor | `public/css` altındaki tema dosyaları runtime cache'tedir (StaleWhileRevalidate); hard refresh yeterlidir. |
| Hangfire başlangıçta hata veriyor | Bağlantı dizesi boşsa Hangfire kaydı atlanır ama ABP modülleri yüklenir. Bağlantı dizesini doğrulayın. |
| Dinamik servis yayınlanmıyor | Önce `TestCompile` çalıştırın; derleme hatası mesajı UI'da döner. `Publish` için ilgili yetki gerekir. |
| Raporlarda font/karakter bozukluğu (Linux) | `DevExpressFontConfiguration.InstallLinuxFonts` loglarını kontrol edin; konteynerde font paketi eksik olabilir. |
2026-06-09 12:54:05 +00:00
---
2026-06-09 12:54:05 +00:00
## 23. Bilinen Sınırlar ve Notlar
2026-06-09 12:54:05 +00:00
- `route.constant.ts` içindeki `supplychain`, `maintenance`, `warehouse`, `projects`, `crm`,
`mrp`, `accounting` ve `hr` yol sabitleri **yol sözleşmesidir**; bu alanların ekranları fiziksel
React sayfaları olarak değil, ListForm konfigürasyonu ile üretilir. `hr` altında yalnızca
2026-10-19 14:35:08 +00:00
organizasyon şeması (`OrgChart.tsx`, İK menüsü `Hr.OrgChart`) fiziksel bir view'dır.
- Depodaki `appsettings*.json` ve deployment dokümanları geliştirme değerleri/örnek kimlik
bilgileri içerir; üretim öncesi tümü değiştirilmelidir.
- SQL View Designer tek yönlüdür (model → T-SQL). Elle yazılmış ya da tasarımcının kanonik
şekline uymayan bir view tanımı geri okunamaz; dialog ham SQL moduna düşer.
- `api/test/Sozsoft.Platform.EntityFrameworkCore.Tests` şu an SQLite şema oluşturmada
(`nvarchar(max)` → `SQLite Error 1`) kırıktır; bu kırıklık kod standardından önce de vardı ve
ayrı bir iş olarak ele alınmalıdır.
- Rol–yetki eşleşmeleri veritabanında müşteri ortamına göre değişir; buradaki yetki kodları
tanımın kendisidir, atama değil.
---
## Lisans ve Sahiplik
2026-06-09 12:54:05 +00:00
Bu depo Sozsoft'a aittir. DevExpress/DevExtreme bileşenleri ticari lisans gerektirir
(`api/DevExpress_License.txt`).