Seo çalışmaları
5
.github/instructions/ai.instructions.md
vendored
|
|
@ -60,7 +60,8 @@ System nature:
|
||||||
Anything beyond that — a new React component or page, a new toolbox family, core engine changes (ListForm
|
Anything beyond that — a new React component or page, a new toolbox family, core engine changes (ListForm
|
||||||
runtime, seeders, code generator, auth, DataSourceManager, dispatcher/compiler), new ABP
|
runtime, seeders, code generator, auth, DataSourceManager, dispatcher/compiler), new ABP
|
||||||
entity/module/migration, platform table schema changes, new NuGet/npm dependencies, the
|
entity/module/migration, platform table schema changes, new NuGet/npm dependencies, the
|
||||||
`App.Menus.Routes.json` seed data, new external integrations — is **out of frame**: finish everything that
|
`App.Menus.Routes.json` seed data (except the `Seo*` columns of an existing row, which are part
|
||||||
|
of ordinary production — lowcode §0.9), new external integrations — is **out of frame**: finish everything that
|
||||||
fits inside the frame first, then ask the user with a single question (why configuration is
|
fits inside the frame first, then ask the user with a single question (why configuration is
|
||||||
insufficient, the smallest change that works, files touched, rollback) and proceed **only with
|
insufficient, the smallest change that works, files touched, rollback) and proceed **only with
|
||||||
explicit approval**. Approval is per request and does not carry over. Full lists:
|
explicit approval**. Approval is per request and does not carry over. Full lists:
|
||||||
|
|
@ -723,6 +724,8 @@ From the `configs/seeds/host/data/App.Menus.*.json` patterns, AI must define bot
|
||||||
|
|
||||||
- `App.Menus.Routes.json` row:
|
- `App.Menus.Routes.json` row:
|
||||||
- `Id`, `Key`, `Path`, `ComponentPath`, `RouteType`, `Authority`
|
- `Id`, `Key`, `Path`, `ComponentPath`, `RouteType`, `Authority`
|
||||||
|
- for a visitor-facing route, also its `Seo*` columns — there is no separate SEO table
|
||||||
|
(lowcode §0.9)
|
||||||
- `App.Menus.Menu.json` row:
|
- `App.Menus.Menu.json` row:
|
||||||
- `ParentCode`, `Code`, `DisplayName`, `Url`, `Icon`, `RequiredPermissionName`, `Order`
|
- `ParentCode`, `Code`, `DisplayName`, `Url`, `Icon`, `RequiredPermissionName`, `Order`
|
||||||
|
|
||||||
|
|
|
||||||
2
.github/instructions/list.instructions.md
vendored
|
|
@ -28,7 +28,7 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
## Special File Handling Rules
|
## Special File Handling Rules
|
||||||
|
|
||||||
- **configs/seeds/host/data/App.Menus.*.json:**
|
- **configs/seeds/host/data/App.Menus.*.json:**
|
||||||
- Never modify `App.Menus.Routes.json` directly. Only `App.Menus.Module.json` and `App.Menus.Menu.json` can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to those two files as required by platform design.
|
- Never add, remove or re-path a route in `App.Menus.Routes.json`. The only field set you may edit on an existing row is its `Seo*` columns (lowcode §0.9). Only `App.Menus.Module.json` and `App.Menus.Menu.json` can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to those two files as required by platform design.
|
||||||
|
|
||||||
- **LanguagesData.json:**
|
- **LanguagesData.json:**
|
||||||
- When adding a new key, always check if the key already exists. Only add the key if it does not exist to prevent duplicates.
|
- When adding a new key, always check if the key already exists. Only add the key if it does not exist to prevent duplicates.
|
||||||
|
|
|
||||||
|
|
@ -1304,6 +1304,10 @@ yazma, oradan yeni bir Custom Component üret. `Services` sayfası yoktur.
|
||||||
| `/demo` | `views/public/Demo` | `Sas_H_Demo` |
|
| `/demo` | `views/public/Demo` | `Sas_H_Demo` |
|
||||||
| `/access-denied` | `views/AccessDenied` | — |
|
| `/access-denied` | `views/AccessDenied` | — |
|
||||||
|
|
||||||
|
Ziyaretçiye açık her yolun SEO alanları ayrı bir tabloda değil, yolu tanımlayan kaydın
|
||||||
|
üzerindedir: `Route` satırının ya da Custom Component'in `Seo*` kolonları. Listeye yeni bir public
|
||||||
|
yol eklediğinde bu alanları da doldur (lowcode §0.9).
|
||||||
|
|
||||||
**Kimlik akışı** (`routeType: authenticated`): `/login`, `/register`, `/forgot-password`,
|
**Kimlik akışı** (`routeType: authenticated`): `/login`, `/register`, `/forgot-password`,
|
||||||
`/reset-password`, `/confirm`, `/confirm/:userId/:token`, `/extend-login` →
|
`/reset-password`, `/confirm`, `/confirm/:userId/:token`, `/extend-login` →
|
||||||
`views/auth/*`. Bunlara dokunma; parola ve doğrulama akışları ABP tarafındadır.
|
`views/auth/*`. Bunlara dokunma; parola ve doğrulama akışları ABP tarafındadır.
|
||||||
|
|
|
||||||
78
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -53,9 +53,9 @@ configs/seeds/host/
|
||||||
├── postgres/object|execute/… ← aynısının PostgreSQL diyalekti (hedef PG ise)
|
├── postgres/object|execute/… ← aynısının PostgreSQL diyalekti (hedef PG ise)
|
||||||
├── crud/{EntityName}.json ← tablonun REST uçları
|
├── crud/{EntityName}.json ← tablonun REST uçları
|
||||||
├── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları
|
├── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları
|
||||||
└── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel)
|
├── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel)
|
||||||
Custom Component'ler de burada yaşar:
|
│ Custom Component'ler de burada yaşar:
|
||||||
data/App.DeveloperKit.CustomComponents/{Id}.json
|
│ data/App.DeveloperKit.CustomComponents/{Id}.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen aynıdır.
|
Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen aynıdır.
|
||||||
|
|
@ -70,6 +70,7 @@ Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen ayn
|
||||||
| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir |
|
| 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir |
|
||||||
| 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** `SeededAt` damgasını alır |
|
| 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** `SeededAt` damgasını alır |
|
||||||
| 6 | Liste verisi (isteniyorsa) | `data/{ListFormCode}.json` | Ekran ve tablo hazır olmalı; `ListForm.SeedFilePath` bu yolu gösterir |
|
| 6 | Liste verisi (isteniyorsa) | `data/{ListFormCode}.json` | Ekran ve tablo hazır olmalı; `ListForm.SeedFilePath` bu yolu gösterir |
|
||||||
|
| 7 | SEO alanları (public yol ürettiysen) | Yolu tanımlayan satırın kendisi: `data/App.Menus.Routes.json` ya da `data/App.DeveloperKit.CustomComponents/{Id}.json` | Yolu üreten dosya yazıldıktan sonra; ziyaretçiye açık hiçbir yol metasız kalmaz (§0.9) |
|
||||||
|
|
||||||
**`Wizard.SeededAt` bir bağımlılık aracıdır.** Dosya adı tarih taşımaz (`{WizardName}.json`);
|
**`Wizard.SeededAt` bir bağımlılık aracıdır.** Dosya adı tarih taşımaz (`{WizardName}.json`);
|
||||||
`WizardDataSeeder` `wizard/` klasörünü **`Wizard.SeededAt` damgasına göre sıralı** işler (eşitlikte
|
`WizardDataSeeder` `wizard/` klasörünü **`Wizard.SeededAt` damgasına göre sıralı** işler (eşitlikte
|
||||||
|
|
@ -394,7 +395,7 @@ kullanıcıya sorulur ve yalnızca onayla yapılır.** "Yapılamaz" cevabı yokt
|
||||||
|
|
||||||
| Alan | Örnek |
|
| Alan | Örnek |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Seed artefaktları | Wizard, custom component (visual/code modu), `crud/`, SQL nesneleri (tablo/view/SP/fonksiyon), Custom Endpoint, Dynamic Service, BackgroundWorker, bildirim türü/kuralı, dil anahtarı, yetki kaydı |
|
| Seed artefaktları | Wizard, custom component (visual/code modu), `crud/`, SQL nesneleri (tablo/view/SP/fonksiyon), Custom Endpoint, Dynamic Service, BackgroundWorker, bildirim türü/kuralı, dil anahtarı, yetki kaydı, **SEO kaydı** (§0.9) |
|
||||||
| Mevcut bileşeni genişletme | Kullanılan bir `ui` bileşenine **eksik olay / prop / küçük yetenek** ekleme (§7.3 notu) — komponentin %70'i hazır araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır; `componentProps.json` girdisini tamamlama |
|
| Mevcut bileşeni genişletme | Kullanılan bir `ui` bileşenine **eksik olay / prop / küçük yetenek** ekleme (§7.3 notu) — komponentin %70'i hazır araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır; `componentProps.json` girdisini tamamlama |
|
||||||
| Mevcut sözlüğü genişletme | Yeni EditorScript tarifi (`scriptRecipes.ts` ↔ `Domain.Shared/Editors` birlikte), yeni `EditorOptions` anahtarı, yeni `DefaultValue` token'ı — **geriye dönük uyumlu** olmak şartıyla |
|
| Mevcut sözlüğü genişletme | Yeni EditorScript tarifi (`scriptRecipes.ts` ↔ `Domain.Shared/Editors` birlikte), yeni `EditorOptions` anahtarı, yeni `DefaultValue` token'ı — **geriye dönük uyumlu** olmak şartıyla |
|
||||||
| Tasarımcı sözleşmesi | Var olan düğüme prop/varsayılan ekleme, yeni Script Builder tarifi |
|
| Tasarımcı sözleşmesi | Var olan düğüme prop/varsayılan ekleme, yeni Script Builder tarifi |
|
||||||
|
|
@ -407,7 +408,7 @@ kullanıcıya sorulur ve yalnızca onayla yapılır.** "Yapılamaz" cevabı yokt
|
||||||
| Çekirdek motor | ListForm runtime, seeder'lar, `codeGenerator.ts`, yetki/kimlik altyapısı, `DataSourceManager`, dispatcher/derleyici (`CustomEndpointAppService`, `DynamicServiceCompiler`) |
|
| Çekirdek motor | ListForm runtime, seeder'lar, `codeGenerator.ts`, yetki/kimlik altyapısı, `DataSourceManager`, dispatcher/derleyici (`CustomEndpointAppService`, `DynamicServiceCompiler`) |
|
||||||
| Backend şeması | Yeni ABP modülü/entity/migration, platform tablolarında (`{Önek}_{H\|T\|B}_{Ad}` — reference §12.1) kolon değişikliği, `TableNameEnum`/`TableNameResolver` değişikliği, DTO/JSON kolon sözleşmesini **bozan** değişiklik |
|
| Backend şeması | Yeni ABP modülü/entity/migration, platform tablolarında (`{Önek}_{H\|T\|B}_{Ad}` — reference §12.1) kolon değişikliği, `TableNameEnum`/`TableNameResolver` değişikliği, DTO/JSON kolon sözleşmesini **bozan** değişiklik |
|
||||||
| Bağımlılık | Yeni NuGet/npm paketi, sürüm yükseltme |
|
| Bağımlılık | Yeni NuGet/npm paketi, sürüm yükseltme |
|
||||||
| Sabit yapılar | `data/App.Menus.Routes.json`, `PlatformConsts` içindeki mevcut değerlerin değişmesi |
|
| Sabit yapılar | `data/App.Menus.Routes.json` satırlarının eklenmesi/silinmesi/yolunun değişmesi (`Seo*` alanları hariç, §0.9), `PlatformConsts` içindeki mevcut değerlerin değişmesi |
|
||||||
| Dış dünya | Yeni harici entegrasyon, yeni bağlantı tipi, üçüncü taraf servis |
|
| Dış dünya | Yeni harici entegrasyon, yeni bağlantı tipi, üçüncü taraf servis |
|
||||||
|
|
||||||
**Sorma biçimi:** çerçeve içinde yapılabilen her şeyi **önce bitir**, sonra tek soruda
|
**Sorma biçimi:** çerçeve içinde yapılabilen her şeyi **önce bitir**, sonra tek soruda
|
||||||
|
|
@ -416,6 +417,73 @@ hangi değişiklik yeter, hangi dosyalara dokunacak, geri alma nasıl olacak. Se
|
||||||
daima "çerçeve içinde daraltılmış çözüm" alternatifi de bulunsun. Onay gelmeden `api/` ya da
|
daima "çerçeve içinde daraltılmış çözüm" alternatifi de bulunsun. Onay gelmeden `api/` ya da
|
||||||
`ui/` altında çerçeve dışı bir satır yazılmaz; onay bir talebe özeldir, sonraki talebe taşınmaz.
|
`ui/` altında çerçeve dışı bir satır yazılmaz; onay bir talebe özeldir, sonraki talebe taşınmaz.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 0.9 SEO alanları — ziyaretçiye açık her yol için zorunlu
|
||||||
|
|
||||||
|
Ziyaretçiye açık bir yol üreten her iş, o yolun **SEO alanlarını da** doldurur. Bu kullanıcıdan
|
||||||
|
istenen bir bilgi değildir, üretimin parçasıdır: kullanıcı SEO bilgisi girmez, sen yazarsın.
|
||||||
|
Metasız yayınlanan bir sayfa arama sonucunda başlıksız ve açıklamasız görünür; paylaşıldığında
|
||||||
|
da boş bir kart çıkar.
|
||||||
|
|
||||||
|
**Ayrı bir SEO tablosu yoktur.** Meta, yolu tanımlayan kaydın üzerinde yaşar. Bu platformda rota
|
||||||
|
iki yerden doğar, ikisinde de aynı alan kümesi vardır:
|
||||||
|
|
||||||
|
| Yolu tanımlayan | Seed dosyası | Örnek yollar |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Route` satırı (fiziksel React sayfası) | `data/App.Menus.Routes.json` | `/products`, `/blog`, `/demo`, `/checkout` |
|
||||||
|
| Custom Component `RoutePath` | `data/App.DeveloperKit.CustomComponents/{Id}.json` | `/home`, `/about`, `/contact` |
|
||||||
|
|
||||||
|
Aynı yolu ikisi de tanımlıyorsa **bileşen kazanır** — çalışma zamanında da bileşen fiziksel
|
||||||
|
rotayı gölgeler (`SeoAppService.CollectSeoRoutesAsync`). `/admin/` ile başlayan hiçbir yol için
|
||||||
|
alan doldurulmaz; yönetim yüzeyi zaten `noindex` gelir.
|
||||||
|
|
||||||
|
**Ne zaman:** public bir `RoutePath` taşıyan Custom Component, `Pub` modülüne eklenen bir menü,
|
||||||
|
ya da ziyaretçiye açık yeni bir fiziksel sayfa ürettiğinde.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
// data/App.Menus.Routes.json ya da data/App.DeveloperKit.CustomComponents/{Id}.json
|
||||||
|
// içindeki satırın üzerine, var olan alanların yanına:
|
||||||
|
{
|
||||||
|
"Path": "/kurslar", // Custom Component'te: "RoutePath"
|
||||||
|
"SeoTitle": "::App.PublicSeo.Courses.Title", // dil anahtarı ya da düz metin
|
||||||
|
"SeoDescription": "::App.PublicSeo.Courses.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Courses.Keywords",
|
||||||
|
"SeoOgImage": null, // boşsa ayarlardaki varsayılan paylaşım görseli
|
||||||
|
"SeoCanonicalUrl": null, // boşsa site adresi + yol
|
||||||
|
"SeoNoIndex": false,
|
||||||
|
"SeoIncludeInSitemap": true,
|
||||||
|
"SeoChangeFrequency": "weekly",
|
||||||
|
"SeoPriority": 0.7,
|
||||||
|
"SeoStructuredData": null // ham JSON-LD; sayfaya olduğu gibi eklenir
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Kurallar:**
|
||||||
|
|
||||||
|
| Kural | Neden |
|
||||||
|
| --- | --- |
|
||||||
|
| Metin alanları **dil anahtarı** taşır (`::App.PublicSeo.{Sayfa}.{Alan}`), anahtarlar `LanguagesData.json`'a EN + TR yazılır | Kayıt kültür başına çoğalmaz; çeviri dil dosyasında, tek yerde durur (standing default 4) |
|
||||||
|
| `Title` 60, `Description` 160 karakteri aşmaz | Arama sonucunda kırpılır |
|
||||||
|
| Metni uydurma — sayfanın kendi içeriğinden türet | Sayfada yazmayan bir vaat meta'da da yazmamalı |
|
||||||
|
| Kişiye özel / tek kullanımlık akış (`/checkout`, `/payment`, `/success`) → `SeoNoIndex: true`, `SeoIncludeInSitemap: false` | İçeriği olmayan, tekrar ziyaret edilemeyen adres |
|
||||||
|
| Parametreli rota (`/blog/:id`) alan taşımaz | Bir adres değil kalıptır; tek tek adresler sitemap'e veriden üretilir (`SeoAppService`) |
|
||||||
|
| Yönlendiren adres → `SeoCanonicalUrl` hedefi gösterir, `SeoIncludeInSitemap: false` | Aynı içerik iki adreste görünmesin |
|
||||||
|
| **Her satır** `SeoNoIndex` ve `SeoIncludeInSitemap` taşır — ziyaretçiye açık olmayanlar da (`true`/`false`) | Alanlar ekranın alan listesinde; `NOT NULL` bit kolonu satırda yoksa insert düşer. Diğer non-nullable alanlarla (`IsActive`, `IsDisabled`) aynı kural |
|
||||||
|
|
||||||
|
**Genel varsayılanlar kayıtta değil ayardadır:** site adresi, site adı, varsayılan başlık ve
|
||||||
|
açıklama, paylaşım görseli, Twitter hesabı, diller, robots yönergesi, Google doğrulaması,
|
||||||
|
sosyal hesaplar ve sitemap anahtarı `App.SiteManagement.Seo.*` ayarlarındadır
|
||||||
|
(`configs/seeds/host/data/App.SettingDefinitions.json`). Bir değer bütün site için geçerliyse
|
||||||
|
oraya yazılır, her yola tekrarlanmaz. Site kökü (`/`) hiçbir kayıt taşımaz; doğrudan bu
|
||||||
|
varsayılanlardan beslenir.
|
||||||
|
|
||||||
|
**Akış:** ayarlar + iki kaynaktan toplanan yol kayıtları uygulama konfigürasyonunun `seo`
|
||||||
|
alanıyla ziyaretçiye taşınır (`PlatformApplicationConfigurationContributor`), istemcide
|
||||||
|
`SeoHead` sayfanın başlığını, açıklamasını, kanonik adresini, `og:`/`twitter:` kartını ve
|
||||||
|
JSON-LD belgelerini kurar; `::` ile başlayan değerler orada çevrilir (`resolveSeoText`).
|
||||||
|
`sitemap.xml` ve `robots.txt` aynı kaynaktan `SeoController` tarafından üretilir.
|
||||||
|
|
||||||
## 1. Hangi istek → hangi artefakt
|
## 1. Hangi istek → hangi artefakt
|
||||||
|
|
||||||
| İstek | Üretilecek artefakt | Bölüm |
|
| İstek | Üretilecek artefakt | Bölüm |
|
||||||
|
|
|
||||||
42
README.md
|
|
@ -1067,11 +1067,53 @@ hâlde ikinci rotasız bileşen birincisiyle çakışırdı.
|
||||||
`/home`, `/about`, `/contact` bu yolla çalışır (`PublicHomePage`, `PublicAboutPage`,
|
`/home`, `/about`, `/contact` bu yolla çalışır (`PublicHomePage`, `PublicAboutPage`,
|
||||||
`PublicContactPage`); üst ve alt bilgi rotasızdır (`PublicHeader`, `PublicFooter`).
|
`PublicContactPage`); üst ve alt bilgi rotasızdır (`PublicHeader`, `PublicFooter`).
|
||||||
|
|
||||||
|
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`,
|
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ı
|
`/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
|
`PublicAppService`'in `post-list` / `post-by-slug` uçlarından beslenir, yönetimi
|
||||||
`App.BlogManagement.*` ekranlarındadır.
|
`App.BlogManagement.*` ekranlarındadır.
|
||||||
|
|
||||||
|
**SEO.** Ziyaretçiye açık yüzeyin arama motoru sözleşmesi üç parçadır:
|
||||||
|
|
||||||
|
| Parça | Nerede | Ne taşır |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Genel varsayılanlar | `App.SiteManagement.Seo.*` ayarları (Ayarlar ekranı, yetki `App.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, sitemap anahtarı |
|
||||||
|
| 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 yollar elenir. Arama motorları bu iki dosyayı **sitenin** kök dizininde aradığı için UI sunucusu her iki
|
||||||
|
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
|
||||||
|
`SeedSync*` bayrakları açık olduğu için ekrandan girilen değer `data/App.Menus.Routes.json` ya da
|
||||||
|
`data/App.DeveloperKit.CustomComponents/{Id}.json` dosyasına düşer.
|
||||||
|
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:
|
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
|
`views/public/Home|About|Contact|Services`, `views/public/designer/`, bunlara özel istemci
|
||||||
servisleri; sunucuda `Home`/`About`/`Contact`/`Service` entity'leri, `PublicAppService` sayfa
|
servisleri; sunucuda `Home`/`About`/`Contact`/`Service` entity'leri, `PublicAppService` sayfa
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Volo.Abp.Application.Services;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.Public;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public sitenin arama motoru yuzeyi. Tamami anonim erisime aciktir: sozlesme uygulama
|
||||||
|
/// konfigurasyonuyla ziyaretciye tasinir, sitemap ve robots metni ise <c>SeoController</c>
|
||||||
|
/// uzerinden ham metin olarak yayinlanir.
|
||||||
|
/// </summary>
|
||||||
|
public interface ISeoAppService : IApplicationService
|
||||||
|
{
|
||||||
|
Task<SeoConfigDto> GetConfigAsync();
|
||||||
|
|
||||||
|
/// <param name="baseUrl">Ayarda site adresi bos birakildiginda kullanilan istek adresi.</param>
|
||||||
|
Task<string> GetSitemapAsync(string baseUrl);
|
||||||
|
|
||||||
|
Task<string> GetRobotsAsync(string baseUrl);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.Public;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ziyaretciye acik SEO sozlesmesi. Istemci her sayfada bu sozlesmeden kendi metasini kurar:
|
||||||
|
/// once yola karsilik gelen <see cref="Routes"/> kaydi, yoksa buradaki varsayilanlar.
|
||||||
|
/// </summary>
|
||||||
|
public class SeoConfigDto
|
||||||
|
{
|
||||||
|
/// <summary>Kanonik kok adres; bos ise istemci tarayicidaki adresi kullanir.</summary>
|
||||||
|
public string SiteUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SiteName { get; set; } = string.Empty;
|
||||||
|
public string DefaultTitle { get; set; } = string.Empty;
|
||||||
|
public string DefaultDescription { get; set; } = string.Empty;
|
||||||
|
public string DefaultKeywords { get; set; } = string.Empty;
|
||||||
|
public string DefaultImage { get; set; } = string.Empty;
|
||||||
|
public string TwitterSite { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>hreflang baglantilarinin uretildigi kultur listesi.</summary>
|
||||||
|
public List<string> Locales { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>Varsayilan robots yonergesi (<c>index, follow</c>).</summary>
|
||||||
|
public string Robots { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string GoogleSiteVerification { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>Organization yapisal verisinin <c>sameAs</c> alani.</summary>
|
||||||
|
public List<string> SameAs { get; set; } = [];
|
||||||
|
|
||||||
|
public List<SeoRouteDto> Routes { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tek bir yolun SEO sozlesmesi. Ayri bir SEO tablosu yoktur: kayit ya <c>Route</c>
|
||||||
|
/// satirindan ya da rota tasiyan bir Custom Component'ten gelir.
|
||||||
|
/// </summary>
|
||||||
|
public class SeoRouteDto
|
||||||
|
{
|
||||||
|
/// <summary>Uygulama yolu; <c>/blog/*</c> bicimi alt agacin tamamini kapsar.</summary>
|
||||||
|
public string RoutePath { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>Dogrudan metin ya da <c>::Anahtar</c> bicimiyle dil anahtari; cozumlemeyi istemci yapar.</summary>
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Keywords { get; set; }
|
||||||
|
public string? OgImage { get; set; }
|
||||||
|
public string? CanonicalUrl { get; set; }
|
||||||
|
public bool NoIndex { get; set; }
|
||||||
|
public string? StructuredData { get; set; }
|
||||||
|
}
|
||||||
403
api/src/Sozsoft.Platform.Application/Public/SeoAppService.cs
Normal file
|
|
@ -0,0 +1,403 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Sozsoft.Platform.Entities;
|
||||||
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
using Volo.Abp.Settings;
|
||||||
|
using SeoSettings = Sozsoft.Platform.PlatformConsts.AbpSettings.SiteManagement.Seo;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.Public;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public sitenin arama motoru yuzeyi: sayfa metasinin sozlesmesi, sitemap ve robots metni.
|
||||||
|
/// Tamami anonim erisime aciktir.
|
||||||
|
/// </summary>
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class SeoAppService : PlatformAppService, ISeoAppService
|
||||||
|
{
|
||||||
|
/// <summary>Sitemap'e yalnizca ziyaretciye acik yollar girer; yonetim yuzeyi disaridadir.</summary>
|
||||||
|
private const string AdminPathPrefix = "/admin";
|
||||||
|
|
||||||
|
private const string DefaultChangeFrequency = "weekly";
|
||||||
|
|
||||||
|
/// <summary>Route tablosunda ziyaretciye acik satirlarin tipi.</summary>
|
||||||
|
private const string PublicRouteType = "public";
|
||||||
|
|
||||||
|
private readonly ISettingProvider _settingProvider;
|
||||||
|
private readonly IRepository<Menu, Guid> _menuRepository;
|
||||||
|
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
||||||
|
private readonly IRepository<BlogPost, Guid> _postRepository;
|
||||||
|
private readonly IRepository<Route, Guid> _routeRepository;
|
||||||
|
|
||||||
|
public SeoAppService(
|
||||||
|
ISettingProvider settingProvider,
|
||||||
|
IRepository<Menu, Guid> menuRepository,
|
||||||
|
IRepository<CustomComponent, Guid> customComponentRepository,
|
||||||
|
IRepository<BlogPost, Guid> postRepository,
|
||||||
|
IRepository<Route, Guid> routeRepository)
|
||||||
|
{
|
||||||
|
_settingProvider = settingProvider;
|
||||||
|
_menuRepository = menuRepository;
|
||||||
|
_customComponentRepository = customComponentRepository;
|
||||||
|
_postRepository = postRepository;
|
||||||
|
_routeRepository = routeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SeoConfigDto> GetConfigAsync()
|
||||||
|
{
|
||||||
|
var config = new SeoConfigDto
|
||||||
|
{
|
||||||
|
SiteUrl = NormalizeBaseUrl(await GetSettingAsync(SeoSettings.SiteUrl)),
|
||||||
|
SiteName = await GetSettingAsync(SeoSettings.SiteName),
|
||||||
|
DefaultTitle = await GetSettingAsync(SeoSettings.DefaultTitle),
|
||||||
|
DefaultDescription = await GetSettingAsync(SeoSettings.DefaultDescription),
|
||||||
|
DefaultKeywords = await GetSettingAsync(SeoSettings.DefaultKeywords),
|
||||||
|
DefaultImage = await GetSettingAsync(SeoSettings.DefaultImage),
|
||||||
|
TwitterSite = await GetSettingAsync(SeoSettings.TwitterSite),
|
||||||
|
Robots = await GetSettingAsync(SeoSettings.Robots),
|
||||||
|
GoogleSiteVerification = await GetSettingAsync(SeoSettings.GoogleSiteVerification),
|
||||||
|
Locales = SplitList(await GetSettingAsync(SeoSettings.Locales)),
|
||||||
|
SameAs = SplitList(await GetSettingAsync(SeoSettings.OrganizationSameAs)),
|
||||||
|
};
|
||||||
|
|
||||||
|
config.Routes = [.. (await CollectSeoRoutesAsync())
|
||||||
|
.Select(entry => entry.ToDto())
|
||||||
|
.OrderBy(route => route.RoutePath, StringComparer.Ordinal)];
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetSitemapAsync(string baseUrl)
|
||||||
|
{
|
||||||
|
if (!await GetBoolSettingAsync(SeoSettings.SitemapEnabled))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var siteUrl = await ResolveSiteUrlAsync(baseUrl);
|
||||||
|
var excluded = SplitLines(await GetSettingAsync(SeoSettings.SitemapExcludePaths));
|
||||||
|
var entries = await CollectSitemapEntriesAsync(excluded);
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.AppendLine("""<?xml version="1.0" encoding="UTF-8"?>""");
|
||||||
|
builder.AppendLine("""<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">""");
|
||||||
|
|
||||||
|
foreach (var entry in entries)
|
||||||
|
{
|
||||||
|
builder.AppendLine(" <url>");
|
||||||
|
builder.AppendLine($" <loc>{Escape(siteUrl + entry.Path)}</loc>");
|
||||||
|
|
||||||
|
if (entry.LastModified.HasValue)
|
||||||
|
{
|
||||||
|
builder.AppendLine($" <lastmod>{entry.LastModified.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}</lastmod>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.AppendLine($" <changefreq>{Escape(entry.ChangeFrequency)}</changefreq>");
|
||||||
|
builder.AppendLine($" <priority>{entry.Priority.ToString("0.0", CultureInfo.InvariantCulture)}</priority>");
|
||||||
|
builder.AppendLine(" </url>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.AppendLine("</urlset>");
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetRobotsAsync(string baseUrl)
|
||||||
|
{
|
||||||
|
var robots = await GetSettingAsync(SeoSettings.Robots);
|
||||||
|
var siteUrl = await ResolveSiteUrlAsync(baseUrl);
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.AppendLine("User-agent: *");
|
||||||
|
|
||||||
|
// Ayar "dizine ekleme" derse butun site kapatilir; aksi halde yalnizca ziyaretciye
|
||||||
|
// acik olmayan yuzeyler disarida birakilir.
|
||||||
|
if (robots.Contains("noindex", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AppendLine("Disallow: /");
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.AppendLine("Disallow: /admin/");
|
||||||
|
builder.AppendLine("Disallow: /auth/");
|
||||||
|
builder.AppendLine("Disallow: /checkout");
|
||||||
|
builder.AppendLine("Disallow: /payment");
|
||||||
|
builder.AppendLine("Disallow: /success");
|
||||||
|
|
||||||
|
foreach (var path in SplitLines(await GetSettingAsync(SeoSettings.SitemapExcludePaths)))
|
||||||
|
{
|
||||||
|
builder.AppendLine($"Disallow: {path.TrimEnd('*')}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await GetBoolSettingAsync(SeoSettings.SitemapEnabled))
|
||||||
|
{
|
||||||
|
builder.AppendLine();
|
||||||
|
builder.AppendLine($"Sitemap: {siteUrl}/sitemap.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<SitemapEntry>> CollectSitemapEntriesAsync(List<string> excluded)
|
||||||
|
{
|
||||||
|
var entries = new Dictionary<string, SitemapEntry>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
void Add(string? path, DateTime? lastModified, string changeFrequency, decimal priority)
|
||||||
|
{
|
||||||
|
var normalized = NormalizePath(path);
|
||||||
|
if (normalized == null || IsExcluded(normalized, excluded) || entries.ContainsKey(normalized))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries[normalized] = new SitemapEntry(normalized, lastModified, changeFrequency, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ana sayfa menude bir kayit olmayabilir; site kokunun her zaman listede olmasi gerekir.
|
||||||
|
Add("/", null, "daily", 1.0m);
|
||||||
|
|
||||||
|
var menus = await _menuRepository.GetListAsync(
|
||||||
|
menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled);
|
||||||
|
|
||||||
|
foreach (var menu in menus.OrderBy(menu => menu.Order))
|
||||||
|
{
|
||||||
|
Add(menu.Url, null, DefaultChangeFrequency, 0.8m);
|
||||||
|
}
|
||||||
|
|
||||||
|
var components = await _customComponentRepository.GetListAsync(component => component.IsActive);
|
||||||
|
|
||||||
|
foreach (var component in components)
|
||||||
|
{
|
||||||
|
Add(component.RoutePath, component.LastModificationTime ?? component.CreationTime, DefaultChangeFrequency, 0.7m);
|
||||||
|
}
|
||||||
|
|
||||||
|
var posts = await _postRepository.GetListAsync(post => post.IsPublished);
|
||||||
|
|
||||||
|
foreach (var post in posts)
|
||||||
|
{
|
||||||
|
Add($"/blog/{post.Slug}", post.LastModificationTime ?? post.PublishedAt, "monthly", 0.6m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yolu tanimlayan kayitlarin kendi SEO alanlari hem yeni yol ekler hem de
|
||||||
|
// yukaridakilerin sirasini ve sikligini ezer.
|
||||||
|
foreach (var seo in await CollectSeoRoutesAsync())
|
||||||
|
{
|
||||||
|
if (seo.RoutePath.Contains('*'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seo.NoIndex || !seo.IncludeInSitemap)
|
||||||
|
{
|
||||||
|
entries.Remove(seo.RoutePath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries[seo.RoutePath] = new SitemapEntry(
|
||||||
|
seo.RoutePath,
|
||||||
|
seo.LastModified,
|
||||||
|
seo.ChangeFrequency.IsNullOrWhiteSpace() ? DefaultChangeFrequency : seo.ChangeFrequency!,
|
||||||
|
seo.Priority ?? 0.5m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. entries.Values.OrderBy(entry => entry.Path, StringComparer.Ordinal)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Uygulama ici yollari tek bicime indirger; disa donuk ve yonetim adresleri elenir.</summary>
|
||||||
|
private static string? NormalizePath(string? path)
|
||||||
|
{
|
||||||
|
if (path.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = path!.Trim();
|
||||||
|
|
||||||
|
if (value.Contains("://", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value.StartsWith('/'))
|
||||||
|
{
|
||||||
|
value = "/" + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.StartsWith(AdminPathPrefix + "/", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| value.Equals(AdminPathPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parametreli rota (/blog/:id) bir adres degil, bir kaliptir.
|
||||||
|
if (value.Contains(':', StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.Length > 1 ? value.TrimEnd('/') : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsExcluded(string path, List<string> excluded)
|
||||||
|
{
|
||||||
|
return excluded.Exists(rule => rule.EndsWith('*')
|
||||||
|
? path.StartsWith(rule.TrimEnd('*'), StringComparison.OrdinalIgnoreCase)
|
||||||
|
: path.Equals(rule, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ResolveSiteUrlAsync(string baseUrl)
|
||||||
|
{
|
||||||
|
var configured = NormalizeBaseUrl(await GetSettingAsync(SeoSettings.SiteUrl));
|
||||||
|
|
||||||
|
return configured.IsNullOrWhiteSpace() ? NormalizeBaseUrl(baseUrl) : configured;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeBaseUrl(string value)
|
||||||
|
{
|
||||||
|
return value.IsNullOrWhiteSpace() ? string.Empty : value.Trim().TrimEnd('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetSettingAsync(string name)
|
||||||
|
{
|
||||||
|
return await _settingProvider.GetOrNullAsync(name) ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> GetBoolSettingAsync(string name)
|
||||||
|
{
|
||||||
|
var value = await GetSettingAsync(name);
|
||||||
|
|
||||||
|
return value.IsNullOrWhiteSpace() || value.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> SplitList(string value)
|
||||||
|
{
|
||||||
|
return [.. value
|
||||||
|
.Split([',', ';', '\n', '\r'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> SplitLines(string value)
|
||||||
|
{
|
||||||
|
return [.. value
|
||||||
|
.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Escape(string value)
|
||||||
|
{
|
||||||
|
return value
|
||||||
|
.Replace("&", "&", StringComparison.Ordinal)
|
||||||
|
.Replace("<", "<", StringComparison.Ordinal)
|
||||||
|
.Replace(">", ">", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SEO kayitlarini yolu tanimlayan iki kaynaktan toplar: fiziksel sayfalarin <c>Route</c>
|
||||||
|
/// satirlari ve rota tasiyan Custom Component kayitlari. Ayni yolu ikisi de tanimliyorsa
|
||||||
|
/// bilesen kazanir; calisma zamaninda da bilesen fiziksel rotayi golgeler.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<List<SeoEntry>> CollectSeoRoutesAsync()
|
||||||
|
{
|
||||||
|
var entries = new Dictionary<string, SeoEntry>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
void Add(SeoEntry? entry)
|
||||||
|
{
|
||||||
|
if (entry != null)
|
||||||
|
{
|
||||||
|
entries[entry.RoutePath] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var routes = await _routeRepository.GetListAsync(route => route.RouteType == PublicRouteType);
|
||||||
|
|
||||||
|
foreach (var route in routes)
|
||||||
|
{
|
||||||
|
Add(SeoEntry.From(
|
||||||
|
route.Path,
|
||||||
|
route.SeoTitle, route.SeoDescription, route.SeoKeywords, route.SeoOgImage,
|
||||||
|
route.SeoCanonicalUrl, route.SeoNoIndex, route.SeoIncludeInSitemap,
|
||||||
|
route.SeoChangeFrequency, route.SeoPriority, route.SeoStructuredData,
|
||||||
|
route.LastModificationTime ?? route.CreationTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
var components = await _customComponentRepository.GetListAsync(component => component.IsActive);
|
||||||
|
|
||||||
|
foreach (var component in components)
|
||||||
|
{
|
||||||
|
Add(SeoEntry.From(
|
||||||
|
component.RoutePath,
|
||||||
|
component.SeoTitle, component.SeoDescription, component.SeoKeywords, component.SeoOgImage,
|
||||||
|
component.SeoCanonicalUrl, component.SeoNoIndex, component.SeoIncludeInSitemap,
|
||||||
|
component.SeoChangeFrequency, component.SeoPriority, component.SeoStructuredData,
|
||||||
|
component.LastModificationTime ?? component.CreationTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. entries.Values];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Iki kaynagin SEO alanlarini tek bicime indirger.</summary>
|
||||||
|
private sealed record SeoEntry(
|
||||||
|
string RoutePath,
|
||||||
|
string? Title,
|
||||||
|
string? Description,
|
||||||
|
string? Keywords,
|
||||||
|
string? OgImage,
|
||||||
|
string? CanonicalUrl,
|
||||||
|
bool NoIndex,
|
||||||
|
bool IncludeInSitemap,
|
||||||
|
string? ChangeFrequency,
|
||||||
|
decimal? Priority,
|
||||||
|
string? StructuredData,
|
||||||
|
DateTime? LastModified)
|
||||||
|
{
|
||||||
|
/// <summary>Hicbir SEO alani doldurulmamis kayit sozlesmeye girmez; genel varsayilanlar zaten uygulanir.</summary>
|
||||||
|
public static SeoEntry? From(
|
||||||
|
string? routePath,
|
||||||
|
string? title, string? description, string? keywords, string? ogImage,
|
||||||
|
string? canonicalUrl, bool noIndex, bool includeInSitemap,
|
||||||
|
string? changeFrequency, decimal? priority, string? structuredData,
|
||||||
|
DateTime? lastModified)
|
||||||
|
{
|
||||||
|
var normalized = NormalizePath(routePath);
|
||||||
|
if (normalized == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasValue = !title.IsNullOrWhiteSpace()
|
||||||
|
|| !description.IsNullOrWhiteSpace()
|
||||||
|
|| !keywords.IsNullOrWhiteSpace()
|
||||||
|
|| !ogImage.IsNullOrWhiteSpace()
|
||||||
|
|| !canonicalUrl.IsNullOrWhiteSpace()
|
||||||
|
|| !structuredData.IsNullOrWhiteSpace()
|
||||||
|
|| !changeFrequency.IsNullOrWhiteSpace()
|
||||||
|
|| priority.HasValue
|
||||||
|
|| noIndex
|
||||||
|
|| !includeInSitemap;
|
||||||
|
|
||||||
|
return hasValue
|
||||||
|
? new SeoEntry(normalized, title, description, keywords, ogImage, canonicalUrl,
|
||||||
|
noIndex, includeInSitemap, changeFrequency, priority, structuredData, lastModified)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeoRouteDto ToDto()
|
||||||
|
{
|
||||||
|
return new SeoRouteDto
|
||||||
|
{
|
||||||
|
RoutePath = RoutePath,
|
||||||
|
Title = Title,
|
||||||
|
Description = Description,
|
||||||
|
Keywords = Keywords,
|
||||||
|
OgImage = OgImage,
|
||||||
|
CanonicalUrl = CanonicalUrl,
|
||||||
|
NoIndex = NoIndex,
|
||||||
|
StructuredData = StructuredData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed record SitemapEntry(string Path, DateTime? LastModified, string ChangeFrequency, decimal Priority);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"Languages": [
|
"Languages": [
|
||||||
{
|
{
|
||||||
"cultureName": "ar",
|
"cultureName": "ar",
|
||||||
|
|
@ -21972,6 +21972,12 @@
|
||||||
"en": "Zoom out",
|
"en": "Zoom out",
|
||||||
"tr": "Uzaklaştır"
|
"tr": "Uzaklaştır"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Profile.AvatarAlt",
|
||||||
|
"en": "Profile picture",
|
||||||
|
"tr": "Profil fotoğrafı"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.Profile.AllowedImageTypes",
|
"key": "App.Profile.AllowedImageTypes",
|
||||||
|
|
@ -29093,6 +29099,450 @@
|
||||||
"key": "App.StaticLookup.HostAndTenant",
|
"key": "App.StaticLookup.HostAndTenant",
|
||||||
"en": "Host and Tenants",
|
"en": "Host and Tenants",
|
||||||
"tr": "Host ve Kiracılar"
|
"tr": "Host ve Kiracılar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo",
|
||||||
|
"en": "SEO",
|
||||||
|
"tr": "SEO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SiteUrl",
|
||||||
|
"en": "Site Address",
|
||||||
|
"tr": "Site Adresi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SiteUrl.Description",
|
||||||
|
"en": "Canonical base address of the public site, e.g. https://www.example.com",
|
||||||
|
"tr": "Public sitenin canonical kök adresi, örn. https://www.example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SiteName",
|
||||||
|
"en": "Site Name",
|
||||||
|
"tr": "Site Adı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SiteName.Description",
|
||||||
|
"en": "Brand name used in og:site_name and structured data",
|
||||||
|
"tr": "og:site_name ve yapısal veride kullanılan marka adı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultTitle",
|
||||||
|
"en": "Default Title",
|
||||||
|
"tr": "Varsayılan Başlık"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultTitle.Description",
|
||||||
|
"en": "Title used when a page has no title of its own",
|
||||||
|
"tr": "Sayfanın kendi başlığı yoksa kullanılan başlık"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultDescription",
|
||||||
|
"en": "Default Description",
|
||||||
|
"tr": "Varsayılan Açıklama"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultDescription.Description",
|
||||||
|
"en": "Meta description used when a page has none; keep it under 160 characters",
|
||||||
|
"tr": "Sayfanın kendi açıklaması yoksa kullanılan meta açıklama; 160 karakteri aşmayın"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultKeywords",
|
||||||
|
"en": "Default Keywords",
|
||||||
|
"tr": "Varsayılan Anahtar Kelimeler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultKeywords.Description",
|
||||||
|
"en": "Comma separated keywords",
|
||||||
|
"tr": "Virgülle ayrılmış anahtar kelimeler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultImage",
|
||||||
|
"en": "Default Share Image",
|
||||||
|
"tr": "Varsayılan Paylaşım Görseli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.DefaultImage.Description",
|
||||||
|
"en": "Image used in og:image and twitter:image; at least 1200x630 is recommended",
|
||||||
|
"tr": "og:image ve twitter:image için kullanılan görsel; en az 1200x630 önerilir"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.TwitterSite",
|
||||||
|
"en": "Twitter Account",
|
||||||
|
"tr": "Twitter Hesabı"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.TwitterSite.Description",
|
||||||
|
"en": "Account handle written to twitter:site, e.g. @sozsoft",
|
||||||
|
"tr": "twitter:site alanına yazılan hesap, örn. @sozsoft"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.Locales",
|
||||||
|
"en": "Languages",
|
||||||
|
"tr": "Diller"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.Locales.Description",
|
||||||
|
"en": "Comma separated culture codes used to build hreflang links, e.g. tr,en",
|
||||||
|
"tr": "hreflang bağlantılarını üretmek için kullanılan, virgülle ayrılmış kültür kodları, örn. tr,en"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.Robots",
|
||||||
|
"en": "Indexing",
|
||||||
|
"tr": "Dizinleme"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.Robots.Description",
|
||||||
|
"en": "Default robots directive for public pages",
|
||||||
|
"tr": "Public sayfalar için varsayılan robots yönergesi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.Robots.IndexFollow",
|
||||||
|
"en": "Index",
|
||||||
|
"tr": "Dizine ekle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.Robots.NoIndexNoFollow",
|
||||||
|
"en": "Do not index",
|
||||||
|
"tr": "Dizine ekleme"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.GoogleSiteVerification",
|
||||||
|
"en": "Google Site Verification",
|
||||||
|
"tr": "Google Site Doğrulama"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.GoogleSiteVerification.Description",
|
||||||
|
"en": "Content of the google-site-verification meta tag",
|
||||||
|
"tr": "google-site-verification meta etiketinin içeriği"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.OrganizationSameAs",
|
||||||
|
"en": "Social Accounts",
|
||||||
|
"tr": "Sosyal Hesaplar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.OrganizationSameAs.Description",
|
||||||
|
"en": "One social profile address per line; written to the sameAs field of the Organization structured data",
|
||||||
|
"tr": "Her satıra bir sosyal profil adresi; Organization yapısal verisinin sameAs alanına yazılır"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SitemapEnabled",
|
||||||
|
"en": "Publish Sitemap",
|
||||||
|
"tr": "Site Haritası Yayınla"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SitemapEnabled.Description",
|
||||||
|
"en": "Serves /sitemap.xml and announces it in robots.txt",
|
||||||
|
"tr": "/sitemap.xml adresini yayınlar ve robots.txt içinde duyurur"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SitemapExcludePaths",
|
||||||
|
"en": "Paths Excluded From Sitemap",
|
||||||
|
"tr": "Site Haritasına Girmeyecek Yollar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.SiteManagement.Seo.SitemapExcludePaths.Description",
|
||||||
|
"en": "One path per line; a line ending with * excludes the whole subtree",
|
||||||
|
"tr": "Her satıra bir yol; * ile biten satır alt ağacın tamamını dışarıda bırakır"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.Description",
|
||||||
|
"en": "Meta description",
|
||||||
|
"tr": "Meta aciklama"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.Keywords",
|
||||||
|
"en": "Keywords",
|
||||||
|
"tr": "Anahtar Kelimeler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.OgImage",
|
||||||
|
"en": "Share Image",
|
||||||
|
"tr": "Paylasim Gorseli"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.CanonicalUrl",
|
||||||
|
"en": "Canonical Address",
|
||||||
|
"tr": "Kanonik Adres"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.NoIndex",
|
||||||
|
"en": "Do Not Index",
|
||||||
|
"tr": "Dizine Ekleme"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.IncludeInSitemap",
|
||||||
|
"en": "In Sitemap",
|
||||||
|
"tr": "Site Haritasinda"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.ChangeFrequency",
|
||||||
|
"en": "Change Frequency",
|
||||||
|
"tr": "Degisim Sikligi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.StructuredData",
|
||||||
|
"en": "Structured Data",
|
||||||
|
"tr": "Yapisal Veri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Platform.SkipToContent",
|
||||||
|
"en": "Skip to content",
|
||||||
|
"tr": "İçeriğe geç"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo",
|
||||||
|
"en": "SEO",
|
||||||
|
"tr": "SEO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.Title",
|
||||||
|
"en": "Meta title",
|
||||||
|
"tr": "Meta baslik"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Seo.Priority",
|
||||||
|
"en": "Sitemap priority",
|
||||||
|
"tr": "Sitemap onceligi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Home.Title",
|
||||||
|
"en": "Language Course Management Software",
|
||||||
|
"tr": "Dil Kursu Yonetim Yazilimi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Home.Description",
|
||||||
|
"en": "Cloud based management software developed for language schools and courses: student registration, class scheduling, accounting and reporting in one place.",
|
||||||
|
"tr": "Dil okullari ve kurslari icin gelistirilen bulut tabanli yonetim yazilimi: ogrenci kaydi, sinif planlama, muhasebe ve raporlama tek yerde."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Home.Keywords",
|
||||||
|
"en": "language course software, student tracking, course management, education crm",
|
||||||
|
"tr": "dil kursu yazilimi, ogrenci takip, kurs yonetimi, egitim crm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.About.Title",
|
||||||
|
"en": "About Us",
|
||||||
|
"tr": "Hakkimizda"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.About.Description",
|
||||||
|
"en": "We have been developing software for the education sector since 2012. Our team, our products and the approach behind them.",
|
||||||
|
"tr": "2012'den bu yana egitim sektorune yazilim gelistiriyoruz. Ekibimiz, urunlerimiz ve arkasindaki yaklasim."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.About.Keywords",
|
||||||
|
"en": "about us, software company, education software",
|
||||||
|
"tr": "hakkimizda, yazilim firmasi, egitim yazilimi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Contact.Title",
|
||||||
|
"en": "Contact",
|
||||||
|
"tr": "Iletisim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Contact.Description",
|
||||||
|
"en": "Reach us for your questions about our products, pricing and demo requests.",
|
||||||
|
"tr": "Urunlerimiz, fiyatlandirma ve demo talepleriniz icin bize ulasin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Contact.Keywords",
|
||||||
|
"en": "contact, support, demo request",
|
||||||
|
"tr": "iletisim, destek, demo talebi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Blog.Title",
|
||||||
|
"en": "Blog",
|
||||||
|
"tr": "Blog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Blog.Description",
|
||||||
|
"en": "Articles on education technologies, course management and our product updates.",
|
||||||
|
"tr": "Egitim teknolojileri, kurs yonetimi ve urun guncellemelerimiz uzerine yazilar."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Blog.Keywords",
|
||||||
|
"en": "blog, education technology, course management",
|
||||||
|
"tr": "blog, egitim teknolojileri, kurs yonetimi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Products.Title",
|
||||||
|
"en": "Products and Pricing",
|
||||||
|
"tr": "Urunler ve Fiyatlandirma"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Products.Description",
|
||||||
|
"en": "Compare the packages, choose your billing cycle and start right away.",
|
||||||
|
"tr": "Paketleri karsilastirin, faturalama donemini secin ve hemen baslayin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Products.Keywords",
|
||||||
|
"en": "products, pricing, packages, subscription",
|
||||||
|
"tr": "urunler, fiyatlandirma, paketler, abonelik"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Demo.Title",
|
||||||
|
"en": "Request a Free Demo",
|
||||||
|
"tr": "Ucretsiz Demo Talebi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Demo.Description",
|
||||||
|
"en": "See the product live with your own data; we set up the demo environment for you.",
|
||||||
|
"tr": "Urunu kendi verinizle canli gorun; demo ortamini sizin icin kuruyoruz."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Demo.Keywords",
|
||||||
|
"en": "demo, free trial, product demo",
|
||||||
|
"tr": "demo, ucretsiz deneme, urun demosu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Checkout.Title",
|
||||||
|
"en": "Checkout",
|
||||||
|
"tr": "Siparisi Tamamla"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Checkout.Description",
|
||||||
|
"en": "Review the packages in your basket, choose your billing period and complete your order.",
|
||||||
|
"tr": "Sepetinizdeki paketleri gozden gecirin, faturalama doneminizi secin ve siparisinizi tamamlayin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Checkout.Keywords",
|
||||||
|
"en": "checkout, order, basket",
|
||||||
|
"tr": "siparis, odeme, sepet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Payment.Title",
|
||||||
|
"en": "Payment",
|
||||||
|
"tr": "Odeme"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Payment.Description",
|
||||||
|
"en": "Choose your payment method and installment option to complete your order securely.",
|
||||||
|
"tr": "Odeme yonteminizi ve taksit secenegini belirleyerek siparisinizi guvenle tamamlayin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Payment.Keywords",
|
||||||
|
"en": "payment, installment, credit card",
|
||||||
|
"tr": "odeme, taksit, kredi karti"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Success.Title",
|
||||||
|
"en": "Order Received",
|
||||||
|
"tr": "Siparis Alindi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Success.Description",
|
||||||
|
"en": "Your order has been received. The details of your subscription are on this page.",
|
||||||
|
"tr": "Siparisiniz alindi. Aboneliginize dair ayrintilar bu sayfadadir."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.Success.Keywords",
|
||||||
|
"en": "order confirmation, subscription",
|
||||||
|
"tr": "siparis onayi, abonelik"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.AccessDenied.Title",
|
||||||
|
"en": "Access Denied",
|
||||||
|
"tr": "Erisim Reddedildi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicSeo.AccessDenied.Description",
|
||||||
|
"en": "You do not have permission to view this page. Contact your administrator if you need access.",
|
||||||
|
"tr": "Bu sayfayi goruntuleme yetkiniz yok. Erisim gerekiyorsa yoneticinizle iletisime gecin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicProducts.DecreasePeriod",
|
||||||
|
"en": "Decrease period",
|
||||||
|
"tr": "Donemi azalt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicProducts.IncreasePeriod",
|
||||||
|
"en": "Increase period",
|
||||||
|
"tr": "Donemi artir"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicProducts.DecreaseQuantity",
|
||||||
|
"en": "Decrease quantity",
|
||||||
|
"tr": "Adedi azalt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicProducts.IncreaseQuantity",
|
||||||
|
"en": "Increase quantity",
|
||||||
|
"tr": "Adedi artir"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
using Sozsoft.Languages.Languages;
|
||||||
|
using Sozsoft.Platform.Editors;
|
||||||
using Sozsoft.Platform.ListForms;
|
using Sozsoft.Platform.ListForms;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
using static Sozsoft.Platform.PlatformConsts;
|
using static Sozsoft.Platform.PlatformConsts;
|
||||||
|
|
@ -277,4 +280,60 @@ public static class ListFormSeeder_DefaultJsons
|
||||||
Deny = d
|
Deny = d
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bir yolun arama motoru sozlesmesi. Ayri bir SEO tablosu yoktur: alanlar yolu tanimlayan
|
||||||
|
/// kaydin (Route, Custom Component) ekranina eklenir, bu yuzden kume tek yerde durur.
|
||||||
|
/// Metin alanlari dogrudan yazilabilir ya da <c>::Anahtar</c> ile dil anahtari tasiyabilir.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listForm">Alanlarin eklenecegi ekran.</param>
|
||||||
|
/// <param name="editGroupOrderNo">Duzenleme formunda SEO alanlarinin toplandigi grup.</param>
|
||||||
|
/// <param name="editOrderStart">Grup icindeki ilk siranin numarasi.</param>
|
||||||
|
/// <param name="listOrderStart">Liste gorunumundeki ilk sutun numarasi.</param>
|
||||||
|
public static ListFormField[] SeoFields(ListForm listForm, int editGroupOrderNo, int editOrderStart, int listOrderStart)
|
||||||
|
{
|
||||||
|
var editOrder = editOrderStart;
|
||||||
|
var listOrder = listOrderStart;
|
||||||
|
|
||||||
|
ListFormField Field(string fieldName, DbType dbType, string editorType, string caption, string? editorOptions = null)
|
||||||
|
{
|
||||||
|
return new ListFormField
|
||||||
|
{
|
||||||
|
ListFormCode = listForm.ListFormCode,
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
SourceDbType = dbType,
|
||||||
|
FieldName = fieldName,
|
||||||
|
EditGroupOrderNo = editGroupOrderNo,
|
||||||
|
EditOrderNo = editOrder++,
|
||||||
|
EditorType2 = editorType,
|
||||||
|
ColSpan = 1,
|
||||||
|
EditorOptions = editorOptions,
|
||||||
|
CaptionName = caption,
|
||||||
|
Width = 0,
|
||||||
|
ListOrderNo = listOrder++,
|
||||||
|
// SEO alanlari liste gorunumunu sisirir; ekranda varsayilan olarak gizlidir,
|
||||||
|
// duzenleme formundaki SEO grubundan yonetilir.
|
||||||
|
Visible = false,
|
||||||
|
IsActive = true,
|
||||||
|
AllowSearch = false,
|
||||||
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
|
PivotSettingsJson = DefaultPivotSettingsJson,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
[
|
||||||
|
Field("SeoTitle", DbType.String, EditorTypes.dxTextBox, "App.Seo.Title"),
|
||||||
|
Field("SeoDescription", DbType.String, EditorTypes.dxTextArea, "App.Seo.Description", EditorOptions.Multiline(80)),
|
||||||
|
Field("SeoKeywords", DbType.String, EditorTypes.dxTextBox, "App.Seo.Keywords"),
|
||||||
|
Field("SeoOgImage", DbType.String, EditorTypes.dxTextBox, "App.Seo.OgImage"),
|
||||||
|
Field("SeoCanonicalUrl", DbType.String, EditorTypes.dxTextBox, "App.Seo.CanonicalUrl"),
|
||||||
|
Field("SeoNoIndex", DbType.Boolean, EditorTypes.dxCheckBox, "App.Seo.NoIndex"),
|
||||||
|
Field("SeoIncludeInSitemap", DbType.Boolean, EditorTypes.dxCheckBox, "App.Seo.IncludeInSitemap"),
|
||||||
|
Field("SeoChangeFrequency", DbType.String, EditorTypes.dxTextBox, "App.Seo.ChangeFrequency"),
|
||||||
|
Field("SeoPriority", DbType.Decimal, EditorTypes.dxNumberBox, "App.Seo.Priority"),
|
||||||
|
Field("SeoStructuredData", DbType.String, EditorTypes.dxTextArea, "App.Seo.StructuredData", EditorOptions.Multiline(160)),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5709,10 +5709,16 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
{
|
{
|
||||||
new() {
|
new() {
|
||||||
Order = 1, ColCount = 1, ColSpan = 1, ItemType = "group" },
|
Order = 1, ColCount = 1, ColSpan = 1, ItemType = "group" },
|
||||||
|
// SEO alanlari kendi grubunda toplanir; yolun metasi bu kaydin uzerinde yasar.
|
||||||
|
new() {
|
||||||
|
Order = 2, ColCount = 2, ColSpan = 1, ItemType = "group", Caption = "App.Seo" },
|
||||||
}),
|
}),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||||
new() { FieldName = "RouteType", FieldDbType = DbType.String, Value = "public", CustomValueType = FieldCustomValueTypeEnum.Value }
|
new() { FieldName = "RouteType", FieldDbType = DbType.String, Value = "public", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||||
|
// NOT NULL bit kolonlar; form bunlari yazmazsa insert NULL gonderir.
|
||||||
|
new() { FieldName = "SeoNoIndex", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||||
|
new() { FieldName = "SeoIncludeInSitemap", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -5887,7 +5893,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
}),
|
}),
|
||||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
}
|
},
|
||||||
|
.. SeoFields(listForm, editGroupOrderNo: 2, editOrderStart: 1, listOrderStart: 8),
|
||||||
]);
|
]);
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
@ -7074,6 +7081,8 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
||||||
{
|
{
|
||||||
new() { Order = 1, ColCount = 2, ColSpan = 1, ItemType = "group" },
|
new() { Order = 1, ColCount = 2, ColSpan = 1, ItemType = "group" },
|
||||||
|
// SEO alanlari kendi grubunda toplanir; yolun metasi bu kaydin uzerinde yasar.
|
||||||
|
new() { Order = 2, ColCount = 2, ColSpan = 1, ItemType = "group", Caption = "App.Seo" },
|
||||||
}),
|
}),
|
||||||
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||||
new() { FieldName = "CreationTime", FieldDbType = DbType.DateTimeOffset, Value = "@NOW", CustomValueType = FieldCustomValueTypeEnum.CustomKey },
|
new() { FieldName = "CreationTime", FieldDbType = DbType.DateTimeOffset, Value = "@NOW", CustomValueType = FieldCustomValueTypeEnum.CustomKey },
|
||||||
|
|
@ -7084,6 +7093,9 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
}),
|
}),
|
||||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||||
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
new() { FieldName = "IsActive", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||||
|
// NOT NULL bit kolonlar; form bunlari yazmazsa insert NULL gonderir.
|
||||||
|
new() { FieldName = "SeoNoIndex", FieldDbType = DbType.Boolean, Value = "false", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||||
|
new() { FieldName = "SeoIncludeInSitemap", FieldDbType = DbType.Boolean, Value = "true", CustomValueType = FieldCustomValueTypeEnum.Value },
|
||||||
}),
|
}),
|
||||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.CustomComponent)),
|
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.CustomComponent)),
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
|
|
@ -7329,6 +7341,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson
|
PivotSettingsJson = DefaultPivotSettingsJson
|
||||||
},
|
},
|
||||||
|
.. SeoFields(listForm, editGroupOrderNo: 2, editOrderStart: 1, listOrderStart: 10),
|
||||||
]);
|
]);
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
@ -12296,5 +12309,6 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Sozsoft.Languages.Languages;
|
using Sozsoft.Languages.Languages;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
|
@ -258,6 +258,26 @@ public static class PlatformConsts
|
||||||
public const string TimedLoginEmails = Default + ".TimedLoginEmails";
|
public const string TimedLoginEmails = Default + ".TimedLoginEmails";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Public sitenin arama motoru varsayilanlari; tamami istemciye tasinir.</summary>
|
||||||
|
public static class Seo
|
||||||
|
{
|
||||||
|
public const string Default = GroupName + ".Seo";
|
||||||
|
|
||||||
|
public const string SiteUrl = Default + ".SiteUrl";
|
||||||
|
public const string SiteName = Default + ".SiteName";
|
||||||
|
public const string DefaultTitle = Default + ".DefaultTitle";
|
||||||
|
public const string DefaultDescription = Default + ".DefaultDescription";
|
||||||
|
public const string DefaultKeywords = Default + ".DefaultKeywords";
|
||||||
|
public const string DefaultImage = Default + ".DefaultImage";
|
||||||
|
public const string TwitterSite = Default + ".TwitterSite";
|
||||||
|
public const string Locales = Default + ".Locales";
|
||||||
|
public const string Robots = Default + ".Robots";
|
||||||
|
public const string GoogleSiteVerification = Default + ".GoogleSiteVerification";
|
||||||
|
public const string OrganizationSameAs = Default + ".OrganizationSameAs";
|
||||||
|
public const string SitemapEnabled = Default + ".SitemapEnabled";
|
||||||
|
public const string SitemapExcludePaths = Default + ".SitemapExcludePaths";
|
||||||
|
}
|
||||||
|
|
||||||
public static class Theme
|
public static class Theme
|
||||||
{
|
{
|
||||||
public const string Default = GroupName + ".Theme";
|
public const string Default = GroupName + ".Theme";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Enums;
|
namespace Sozsoft.Platform.Enums;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,29 @@ public class CustomComponent : FullAuditedEntity<Guid>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? DataSources { get; set; }
|
public string? DataSources { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arama motoru yuzeyi. Ayri bir SEO tablosu yoktur: meta, yolu tanimlayan kaydin
|
||||||
|
/// uzerinde yasar. Metin alanlari dogrudan yazilabilir ya da <c>::Anahtar</c> bicimiyle
|
||||||
|
/// dil anahtari tasiyabilir; kulture gore cozumlemeyi istemci yapar.
|
||||||
|
/// Rotasiz bilesende (ust bilgi, alt bilgi) bos kalir.
|
||||||
|
/// </summary>
|
||||||
|
public string? SeoTitle { get; set; }
|
||||||
|
|
||||||
|
public string? SeoDescription { get; set; }
|
||||||
|
public string? SeoKeywords { get; set; }
|
||||||
|
public string? SeoOgImage { get; set; }
|
||||||
|
public string? SeoCanonicalUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Dizine eklenmesin; sitemap disinda kalir ve <c>noindex</c> yazilir.</summary>
|
||||||
|
public bool SeoNoIndex { get; set; }
|
||||||
|
|
||||||
|
public bool SeoIncludeInSitemap { get; set; } = true;
|
||||||
|
public string? SeoChangeFrequency { get; set; }
|
||||||
|
public decimal? SeoPriority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Sayfaya gomulen JSON-LD; bos ise yalnizca genel Organization verisi yazilir.</summary>
|
||||||
|
public string? SeoStructuredData { get; set; }
|
||||||
|
|
||||||
public CustomComponent(string name, string? routePath, string code, string? props, string? description, bool isActive, string? dependencies, string? dataSources = null)
|
public CustomComponent(string name, string? routePath, string code, string? props, string? description, bool isActive, string? dependencies, string? dataSources = null)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,28 @@ public class Route : FullAuditedEntity<Guid>
|
||||||
public string RouteType { get; set; }
|
public string RouteType { get; set; }
|
||||||
public string[] Authority { get; set; }
|
public string[] Authority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arama motoru yuzeyi. Ayri bir SEO tablosu yoktur: meta, yolu tanimlayan kaydin
|
||||||
|
/// uzerinde yasar. Metin alanlari dogrudan yazilabilir ya da <c>::Anahtar</c> bicimiyle
|
||||||
|
/// dil anahtari tasiyabilir; kulture gore cozumlemeyi istemci yapar.
|
||||||
|
/// </summary>
|
||||||
|
public string? SeoTitle { get; set; }
|
||||||
|
|
||||||
|
public string? SeoDescription { get; set; }
|
||||||
|
public string? SeoKeywords { get; set; }
|
||||||
|
public string? SeoOgImage { get; set; }
|
||||||
|
public string? SeoCanonicalUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Dizine eklenmesin; sitemap disinda kalir ve <c>noindex</c> yazilir.</summary>
|
||||||
|
public bool SeoNoIndex { get; set; }
|
||||||
|
|
||||||
|
public bool SeoIncludeInSitemap { get; set; } = true;
|
||||||
|
public string? SeoChangeFrequency { get; set; }
|
||||||
|
public decimal? SeoPriority { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Sayfaya gomulen JSON-LD; bos ise yalnizca genel Organization verisi yazilir.</summary>
|
||||||
|
public string? SeoStructuredData { get; set; }
|
||||||
|
|
||||||
public Route(string key, string path, string componentType, string componentPath, string routeType, string[] authority)
|
public Route(string key, string path, string componentType, string componentPath, string routeType, string[] authority)
|
||||||
{
|
{
|
||||||
Key = key;
|
Key = key;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
@ -280,6 +280,17 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.ComponentPath).IsRequired().HasMaxLength(256);
|
b.Property(x => x.ComponentPath).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.RouteType).HasMaxLength(64);
|
b.Property(x => x.RouteType).HasMaxLength(64);
|
||||||
|
|
||||||
|
// SEO metni ya dogrudan yazilir ya da `::Anahtar` bicimiyle dil anahtari tasir.
|
||||||
|
b.Property(x => x.SeoTitle).HasMaxLength(200);
|
||||||
|
b.Property(x => x.SeoDescription).HasMaxLength(500);
|
||||||
|
b.Property(x => x.SeoKeywords).HasMaxLength(300);
|
||||||
|
b.Property(x => x.SeoOgImage).HasMaxLength(500);
|
||||||
|
b.Property(x => x.SeoCanonicalUrl).HasMaxLength(500);
|
||||||
|
b.Property(x => x.SeoChangeFrequency).HasMaxLength(20);
|
||||||
|
b.Property(x => x.SeoPriority).HasPrecision(2, 1);
|
||||||
|
// Sayfaya eklenen JSON-LD belgesinin uzunlugu onceden bilinemez.
|
||||||
|
b.Property(x => x.SeoStructuredData).HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property(x => x.Authority).HasConversion(
|
b.Property(x => x.Authority).HasConversion(
|
||||||
v => string.Join(",", v),
|
v => string.Join(",", v),
|
||||||
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
|
@ -693,6 +704,17 @@ public class PlatformDbContext :
|
||||||
// Data sekmesindeki endpoint listesi; adet ve url uzunlugu onceden bilinemez.
|
// Data sekmesindeki endpoint listesi; adet ve url uzunlugu onceden bilinemez.
|
||||||
b.Property(x => x.DataSources).HasColumnType("nvarchar(max)");
|
b.Property(x => x.DataSources).HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
// SEO metni ya dogrudan yazilir ya da `::Anahtar` bicimiyle dil anahtari tasir.
|
||||||
|
b.Property(x => x.SeoTitle).HasMaxLength(200);
|
||||||
|
b.Property(x => x.SeoDescription).HasMaxLength(500);
|
||||||
|
b.Property(x => x.SeoKeywords).HasMaxLength(300);
|
||||||
|
b.Property(x => x.SeoOgImage).HasMaxLength(500);
|
||||||
|
b.Property(x => x.SeoCanonicalUrl).HasMaxLength(500);
|
||||||
|
b.Property(x => x.SeoChangeFrequency).HasMaxLength(20);
|
||||||
|
b.Property(x => x.SeoPriority).HasPrecision(2, 1);
|
||||||
|
// Sayfaya eklenen JSON-LD belgesinin uzunlugu onceden bilinemez.
|
||||||
|
b.Property(x => x.SeoStructuredData).HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||||
// Rotasi olmayan bilesen (alt bilgi, ust bilgi) `RoutePath` tasimaz; tekil indeks
|
// Rotasi olmayan bilesen (alt bilgi, ust bilgi) `RoutePath` tasimaz; tekil indeks
|
||||||
// yalnizca dolu degerleri kapsar, aksi halde her rotasiz bilesen digeriyle carpisir.
|
// yalnizca dolu degerleri kapsar, aksi halde her rotasiz bilesen digeriyle carpisir.
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Sozsoft.Platform.Migrations
|
namespace Sozsoft.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20260922094111_Initial")]
|
[Migration("20260922201238_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -1956,6 +1956,43 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasMaxLength(512)
|
.HasMaxLength(512)
|
||||||
.HasColumnType("nvarchar(512)");
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoCanonicalUrl")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoChangeFrequency")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoDescription")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoIncludeInSitemap")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoKeywords")
|
||||||
|
.HasMaxLength(300)
|
||||||
|
.HasColumnType("nvarchar(300)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoNoIndex")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoOgImage")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<decimal?>("SeoPriority")
|
||||||
|
.HasPrecision(2, 1)
|
||||||
|
.HasColumnType("decimal(2,1)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoStructuredData")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoTitle")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Name")
|
b.HasIndex("Name")
|
||||||
|
|
@ -4998,6 +5035,43 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
.HasColumnType("nvarchar(64)");
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoCanonicalUrl")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoChangeFrequency")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoDescription")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoIncludeInSitemap")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoKeywords")
|
||||||
|
.HasMaxLength(300)
|
||||||
|
.HasColumnType("nvarchar(300)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoNoIndex")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoOgImage")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<decimal?>("SeoPriority")
|
||||||
|
.HasPrecision(2, 1)
|
||||||
|
.HasColumnType("decimal(2,1)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoStructuredData")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoTitle")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Key")
|
b.HasIndex("Key")
|
||||||
|
|
@ -1187,6 +1187,16 @@ namespace Sozsoft.Platform.Migrations
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
Dependencies = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
Dependencies = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||||
DataSources = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
DataSources = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
SeoTitle = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
SeoDescription = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
SeoKeywords = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
|
||||||
|
SeoOgImage = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
SeoCanonicalUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
SeoNoIndex = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
SeoIncludeInSitemap = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
SeoChangeFrequency = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||||
|
SeoPriority = table.Column<decimal>(type: "decimal(2,1)", precision: 2, scale: 1, nullable: true),
|
||||||
|
SeoStructuredData = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -1689,6 +1699,16 @@ namespace Sozsoft.Platform.Migrations
|
||||||
ComponentPath = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
ComponentPath = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
RouteType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
RouteType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
Authority = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
Authority = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
SeoTitle = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: true),
|
||||||
|
SeoDescription = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
SeoKeywords = table.Column<string>(type: "nvarchar(300)", maxLength: 300, nullable: true),
|
||||||
|
SeoOgImage = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
SeoCanonicalUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||||
|
SeoNoIndex = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
SeoIncludeInSitemap = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
SeoChangeFrequency = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||||
|
SeoPriority = table.Column<decimal>(type: "decimal(2,1)", precision: 2, scale: 1, nullable: true),
|
||||||
|
SeoStructuredData = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -1953,6 +1953,43 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasMaxLength(512)
|
.HasMaxLength(512)
|
||||||
.HasColumnType("nvarchar(512)");
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoCanonicalUrl")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoChangeFrequency")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoDescription")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoIncludeInSitemap")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoKeywords")
|
||||||
|
.HasMaxLength(300)
|
||||||
|
.HasColumnType("nvarchar(300)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoNoIndex")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoOgImage")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<decimal?>("SeoPriority")
|
||||||
|
.HasPrecision(2, 1)
|
||||||
|
.HasColumnType("decimal(2,1)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoStructuredData")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoTitle")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Name")
|
b.HasIndex("Name")
|
||||||
|
|
@ -4995,6 +5032,43 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
.HasColumnType("nvarchar(64)");
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoCanonicalUrl")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoChangeFrequency")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoDescription")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoIncludeInSitemap")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoKeywords")
|
||||||
|
.HasMaxLength(300)
|
||||||
|
.HasColumnType("nvarchar(300)");
|
||||||
|
|
||||||
|
b.Property<bool>("SeoNoIndex")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SeoOgImage")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("nvarchar(500)");
|
||||||
|
|
||||||
|
b.Property<decimal?>("SeoPriority")
|
||||||
|
.HasPrecision(2, 1)
|
||||||
|
.HasColumnType("decimal(2,1)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoStructuredData")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SeoTitle")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Key")
|
b.HasIndex("Key")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Sozsoft.Platform.Public;
|
||||||
|
using Volo.Abp.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arama motorlarinin kok dizinde bekledigi iki metin dosyasi. Adresler API alan adinda
|
||||||
|
/// yayinlanir; site alan adindan servis edilmesi icin ters vekil <c>/sitemap.xml</c> ve
|
||||||
|
/// <c>/robots.txt</c> yollarini buraya yonlendirir.
|
||||||
|
/// </summary>
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class SeoController : AbpController
|
||||||
|
{
|
||||||
|
private readonly ISeoAppService _seoAppService;
|
||||||
|
|
||||||
|
public SeoController(ISeoAppService seoAppService)
|
||||||
|
{
|
||||||
|
_seoAppService = seoAppService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("sitemap.xml")]
|
||||||
|
public async Task<IActionResult> SitemapAsync()
|
||||||
|
{
|
||||||
|
var sitemap = await _seoAppService.GetSitemapAsync(GetRequestBaseUrl());
|
||||||
|
|
||||||
|
// Ayar kapaliyken bos govde yerine 404 donulur; bos bir sitemap arama motoruna
|
||||||
|
// "bu sitede indekslenecek sayfa yok" demektir.
|
||||||
|
return string.IsNullOrWhiteSpace(sitemap)
|
||||||
|
? NotFound()
|
||||||
|
: Content(sitemap, "application/xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("robots.txt")]
|
||||||
|
public async Task<IActionResult> RobotsAsync()
|
||||||
|
{
|
||||||
|
return Content(await _seoAppService.GetRobotsAsync(GetRequestBaseUrl()), "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRequestBaseUrl()
|
||||||
|
{
|
||||||
|
return $"{Request.Scheme}://{Request.Host}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Identity;
|
using Sozsoft.Platform.Identity;
|
||||||
|
using Sozsoft.Platform.Public;
|
||||||
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
|
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
@ -75,6 +76,11 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura
|
||||||
})
|
})
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
|
// Public sayfanin metasi ilk boyamada hazir olmali; ayri bir istek beklenirse
|
||||||
|
// tarayici basligi ve paylasim karti bir kare gec dolar.
|
||||||
|
var seoAppService = context.ServiceProvider.GetRequiredService<ISeoAppService>();
|
||||||
|
context.ApplicationConfiguration.SetProperty("seo", await seoAppService.GetConfigAsync());
|
||||||
|
|
||||||
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
|
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
|
||||||
if (currentUser.Id.HasValue)
|
if (currentUser.Id.HasValue)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
32
claude.md
|
|
@ -48,7 +48,7 @@ options you rejected.
|
||||||
3. Every implementation proposal must include tenant and permission design.
|
3. Every implementation proposal must include tenant and permission design.
|
||||||
4. Never bypass platform authorization patterns.
|
4. Never bypass platform authorization patterns.
|
||||||
5. Never hardcode secrets, tenant IDs, or connection strings.
|
5. Never hardcode secrets, tenant IDs, or connection strings.
|
||||||
6. Never step outside the framework without explicit approval (standing default 10).
|
6. Never step outside the framework without explicit approval (standing default 11).
|
||||||
|
|
||||||
Exception:
|
Exception:
|
||||||
|
|
||||||
|
|
@ -138,11 +138,17 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
görsel Tur 2'nin çoğunu cevaplar, yalnızca görselden okunamayanlar sorulur.
|
görsel Tur 2'nin çoğunu cevaplar, yalnızca görselden okunamayanlar sorulur.
|
||||||
Bu kural yalnızca **yeni üretim** içindir; düzeltme/alan ekleme/debug doğrudan yapılır.
|
Bu kural yalnızca **yeni üretim** içindir; düzeltme/alan ekleme/debug doğrudan yapılır.
|
||||||
Ayrıntı: `lowcode.instructions.md` §0.6–§0.7.
|
Ayrıntı: `lowcode.instructions.md` §0.6–§0.7.
|
||||||
7. **Ekran içi hesap/koşul ihtiyacı** → önce `EditorScript` (Script Builder tarifleri), sonra
|
7. **Ziyaretçiye açık yeni yol** (public `RoutePath` taşıyan custom component, `Pub` menüsü,
|
||||||
|
yeni public sayfa) → SEO alanları da doldurulur. Ayrı bir SEO tablosu yoktur: meta, yolu
|
||||||
|
tanımlayan kaydın `Seo*` kolonlarındadır — `data/App.Menus.Routes.json` ya da
|
||||||
|
`data/App.DeveloperKit.CustomComponents/{Id}.json`. Metin `::Anahtar` ile dil anahtarı taşır.
|
||||||
|
Kullanıcı SEO bilgisi girmez; üreten yazar. Genel varsayılanlar kayıtta değil
|
||||||
|
`App.SiteManagement.Seo.*` ayarlarındadır. Ayrıntı: `lowcode.instructions.md` §0.9.
|
||||||
|
8. **Ekran içi hesap/koşul ihtiyacı** → önce `EditorScript` (Script Builder tarifleri), sonra
|
||||||
`EditorOptions`; kod yazmak son çare.
|
`EditorOptions`; kod yazmak son çare.
|
||||||
8. **SQL** → her zaman parametreli. String birleştirme ile sorgu kurma.
|
9. **SQL** → her zaman parametreli. String birleştirme ile sorgu kurma.
|
||||||
9. **Silme** → `DeleteCommand = DefaultDeleteCommand("{Tablo}")`; ham SQL string'i yazma.
|
10. **Silme** → `DeleteCommand = DefaultDeleteCommand("{Tablo}")`; ham SQL string'i yazma.
|
||||||
10. **Çerçeve sınırı** → uygulama kendini kendi mekanizmalarıyla geliştirir (seed artefaktları;
|
11. **Çerçeve sınırı** → uygulama kendini kendi mekanizmalarıyla geliştirir (seed artefaktları;
|
||||||
var olan `ui` bileşenine eksik olay/prop/küçük yetenek ekleme — komponentin %70'i hazır
|
var olan `ui` bileşenine eksik olay/prop/küçük yetenek ekleme — komponentin %70'i hazır
|
||||||
araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır, yeni bileşen yazılmaz; geriye dönük
|
araçlarla kuruluyorsa kalanı bu eklemelerle tamamlanır, yeni bileşen yazılmaz; geriye dönük
|
||||||
uyumlu tarif/seçenek/token ekleme).
|
uyumlu tarif/seçenek/token ekleme).
|
||||||
|
|
@ -234,6 +240,18 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
duyurulur (`INotificationRealtimeNotifier`; sözleşme bildirim modülünün Domain'inde, SignalR
|
duyurulur (`INotificationRealtimeNotifier`; sözleşme bildirim modülünün Domain'inde, SignalR
|
||||||
gerçeklemesi barındıran uygulamada — modül katmanı ASP.NET Core'a bağlanmaz) — duyuru en iyi çabadır, istemcideki yedek yoklama kaldırılmaz.
|
gerçeklemesi barındıran uygulamada — modül katmanı ASP.NET Core'a bağlanmaz) — duyuru en iyi çabadır, istemcideki yedek yoklama kaldırılmaz.
|
||||||
Ayrıntı: `README.md` §13.3–§13.5.
|
Ayrıntı: `README.md` §13.3–§13.5.
|
||||||
|
- **SEO** → **ayrı bir SEO tablosu yoktur**; meta, yolu tanımlayan kaydın üzerinde yaşar. Üç
|
||||||
|
parça birlikte çalışır: genel varsayılanlar `App.SiteManagement.Seo.*` ayarları, yol bazlı
|
||||||
|
alanlar `Route.Seo*` ve `CustomComponent.Seo*` (ikisinin de `SeedSync*` bayrakları açık),
|
||||||
|
istemci tarafı `SeoHead`. Üçü de uygulama konfigürasyonunun `seo` alanından beslenir
|
||||||
|
(`PlatformApplicationConfigurationContributor` → `SeoAppService.GetConfigAsync`); `sitemap.xml`
|
||||||
|
ve `robots.txt` aynı servisten `SeoController` ile yayınlanır. Aynı yolu iki kaynak da
|
||||||
|
tanımlıyorsa bileşen kazanır — çalışma zamanında da bileşen fiziksel rotayı gölgeler.
|
||||||
|
**Ziyaretçiye açık yeni bir yol üreten her iş SEO alanlarını da doldurur** (standing default 7).
|
||||||
|
Sitemap'e giren kaynaklar (`Pub` menüleri, aktif custom component rotaları, yayınlanmış blog
|
||||||
|
yazıları) `SeoAppService` içindedir; yeni bir public kaynak türü eklenirse toplayıcıya da
|
||||||
|
eklenir, yoksa sayfa sitemap'te görünmez. Yeni bir meta alanı = iki entity + `SeoRouteDto` +
|
||||||
|
`SeoFields` (seeder) + `SeoHead` birlikte.
|
||||||
- **Yeni bir dil anahtarı** → `LanguagesData.json`; eklemeden önce anahtarın zaten var olup
|
- **Yeni bir dil anahtarı** → `LanguagesData.json`; eklemeden önce anahtarın zaten var olup
|
||||||
olmadığını kontrol et.
|
olmadığını kontrol et.
|
||||||
- **Custom component sınıfları** → bileşen kodu veritabanında yaşar, Tailwind derlemede yalnızca
|
- **Custom component sınıfları** → bileşen kodu veritabanında yaşar, Tailwind derlemede yalnızca
|
||||||
|
|
@ -308,7 +326,9 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
ve zamanlanmış işler (`App.BackgroundWorkers.RecurringJobs.json`) da buradadır; `HostData.json`
|
ve zamanlanmış işler (`App.BackgroundWorkers.RecurringJobs.json`) da buradadır; `HostData.json`
|
||||||
kaldırıldı, `HostDataSeeder` yalnızca bağlantı cümlesi appsettings'ten gelen varsayılan veri kaynağını kurar. Dosyalar ekranın
|
kaldırıldı, `HostDataSeeder` yalnızca bağlantı cümlesi appsettings'ten gelen varsayılan veri kaynağını kurar. Dosyalar ekranın
|
||||||
anahtarını (`KeyFieldName`) kullanır; `Guid` anahtarlı satırın `Id`'si iş anahtarından türetilmiş
|
anahtarını (`KeyFieldName`) kullanır; `Guid` anahtarlı satırın `Id`'si iş anahtarından türetilmiş
|
||||||
sabit bir GUID'dir, satır eklerken aynı kuralla üret. `App.Menus.Routes.json` dosyasına dokunma.
|
sabit bir GUID'dir, satır eklerken aynı kuralla üret. `App.Menus.Routes.json` dosyasına
|
||||||
|
yalnızca var olan satırın `Seo*` alanları için dokunulur (standing default 7); rota eklemek,
|
||||||
|
silmek ya da yolunu değiştirmek çerçeve dışıdır.
|
||||||
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
|
- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`);
|
||||||
`App.Public.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
|
`App.Public.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de
|
||||||
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
`PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır
|
||||||
|
|
|
||||||
|
|
@ -3774,6 +3774,16 @@
|
||||||
"MultiTenancySide": 2,
|
"MultiTenancySide": 2,
|
||||||
"Modules": "Sas|Adm"
|
"Modules": "Sas|Adm"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"GroupName": "App.Administration",
|
||||||
|
"Name": "App.SiteManagement.Seo",
|
||||||
|
"ParentName": "App.SiteManagement",
|
||||||
|
"DisplayName": "App.SiteManagement.Seo",
|
||||||
|
"Order": 4,
|
||||||
|
"IsEnabled": true,
|
||||||
|
"MultiTenancySide": 2,
|
||||||
|
"Modules": "Sas|Adm"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"GroupName": "App.Administration",
|
"GroupName": "App.Administration",
|
||||||
"Name": "App.Sender",
|
"Name": "App.Sender",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
"Title": "App.PostsAi.AiTitle",
|
"Title": "App.PostsAi.AiTitle",
|
||||||
"Slug": "ai-ve-gelecegi",
|
"Slug": "ai-ve-gelecegi",
|
||||||
"Summary": "App.PostsAi.Excerpt",
|
"Summary": "App.PostsAi.Excerpt",
|
||||||
"CoverImage": "https://images.pexels.com/photos/8386434/pexels-photo-8386434.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
"CoverImage": "/img/blog/ai-ve-gelecegi.webp",
|
||||||
"ReadTime": "5 dk",
|
"ReadTime": "5 dk",
|
||||||
"CategoryId": "20294789-3472-5326-843d-4c80a9a0b740",
|
"CategoryId": "20294789-3472-5326-843d-4c80a9a0b740",
|
||||||
"Author": "system@sozsoft.com",
|
"Author": "system@sozsoft.com",
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
"Title": "App.PostsWeb.WebTitle",
|
"Title": "App.PostsWeb.WebTitle",
|
||||||
"Slug": "web-gelistirmede-son-trendler",
|
"Slug": "web-gelistirmede-son-trendler",
|
||||||
"Summary": "App.PostsWeb.Excerpt",
|
"Summary": "App.PostsWeb.Excerpt",
|
||||||
"CoverImage": "https://images.pexels.com/photos/11035471/pexels-photo-11035471.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
"CoverImage": "/img/blog/web-gelistirmede-son-trendler.webp",
|
||||||
"ReadTime": "7 dk",
|
"ReadTime": "7 dk",
|
||||||
"CategoryId": "29997b64-a21d-5e04-a392-d6246c6cf766",
|
"CategoryId": "29997b64-a21d-5e04-a392-d6246c6cf766",
|
||||||
"Author": "system@sozsoft.com",
|
"Author": "system@sozsoft.com",
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
"Title": "App.PostsSecurity.SecurityTitle",
|
"Title": "App.PostsSecurity.SecurityTitle",
|
||||||
"Slug": "siber-guvenlik-tehditleri-ve-korunma-yollari",
|
"Slug": "siber-guvenlik-tehditleri-ve-korunma-yollari",
|
||||||
"Summary": "App.PostsSecurity.Excerpt",
|
"Summary": "App.PostsSecurity.Excerpt",
|
||||||
"CoverImage": "https://images.pexels.com/photos/5380642/pexels-photo-5380642.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
"CoverImage": "/img/blog/siber-guvenlik-tehditleri-ve-korunma-yollari.webp",
|
||||||
"ReadTime": "6 dk",
|
"ReadTime": "6 dk",
|
||||||
"CategoryId": "748a96c1-eba8-53a7-9c3d-7317f12ec627",
|
"CategoryId": "748a96c1-eba8-53a7-9c3d-7317f12ec627",
|
||||||
"Author": "system@sozsoft.com",
|
"Author": "system@sozsoft.com",
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
"Title": "App.PostsMobile.MobileTitle",
|
"Title": "App.PostsMobile.MobileTitle",
|
||||||
"Slug": "mobil-uygulama-gelistirmede-cross-platform-cozumler",
|
"Slug": "mobil-uygulama-gelistirmede-cross-platform-cozumler",
|
||||||
"Summary": "App.PostsMobile.Excerpt",
|
"Summary": "App.PostsMobile.Excerpt",
|
||||||
"CoverImage": "https://images.pexels.com/photos/13017583/pexels-photo-13017583.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
"CoverImage": "/img/blog/mobil-uygulama-gelistirmede-cross-platform-cozumler.webp",
|
||||||
"ReadTime": "4 dk",
|
"ReadTime": "4 dk",
|
||||||
"CategoryId": "f282699d-8dda-5d0a-bd11-9e9b40a10161",
|
"CategoryId": "f282699d-8dda-5d0a-bd11-9e9b40a10161",
|
||||||
"Author": "system@sozsoft.com",
|
"Author": "system@sozsoft.com",
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
"Title": "App.PostsDatabase.DatabaseTitle",
|
"Title": "App.PostsDatabase.DatabaseTitle",
|
||||||
"Slug": "veritabani-yonetim-sistemleri-karsilastirmasi",
|
"Slug": "veritabani-yonetim-sistemleri-karsilastirmasi",
|
||||||
"Summary": "App.PostsDatabase.Excerpt",
|
"Summary": "App.PostsDatabase.Excerpt",
|
||||||
"CoverImage": "https://images.pexels.com/photos/325229/pexels-photo-325229.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
"CoverImage": "/img/blog/veritabani-yonetim-sistemleri-karsilastirmasi.webp",
|
||||||
"ReadTime": "8 dk",
|
"ReadTime": "8 dk",
|
||||||
"CategoryId": "63dcbcec-e47f-5859-9de0-5bd46106ffb5",
|
"CategoryId": "63dcbcec-e47f-5859-9de0-5bd46106ffb5",
|
||||||
"Author": "system@sozsoft.com",
|
"Author": "system@sozsoft.com",
|
||||||
|
|
@ -93,7 +93,7 @@
|
||||||
"Title": "App.PostsDigital.DigitalTitle",
|
"Title": "App.PostsDigital.DigitalTitle",
|
||||||
"Slug": "dijital-pazarlamada-veri-analizi",
|
"Slug": "dijital-pazarlamada-veri-analizi",
|
||||||
"Summary": "App.PostsDigital.Excerpt",
|
"Summary": "App.PostsDigital.Excerpt",
|
||||||
"CoverImage": "https://images.pexels.com/photos/7681091/pexels-photo-7681091.jpeg?auto=compress&cs=tinysrgb&w=1920",
|
"CoverImage": "/img/blog/dijital-pazarlamada-veri-analizi.webp",
|
||||||
"ReadTime": "6 dk",
|
"ReadTime": "6 dk",
|
||||||
"CategoryId": "89a1ed95-dbf9-54f1-a5a8-41dddae7a9d2",
|
"CategoryId": "89a1ed95-dbf9-54f1-a5a8-41dddae7a9d2",
|
||||||
"Author": "system@sozsoft.com",
|
"Author": "system@sozsoft.com",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,17 @@
|
||||||
"Dependencies": [],
|
"Dependencies": [],
|
||||||
"Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%7D__*/\nconst NewComponent = () => {\n\n\n return (\n <>\n\n </>\n )\n}\n\nexport default NewComponent",
|
"Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%7D__*/\nconst NewComponent = () => {\n\n\n return (\n <>\n\n </>\n )\n}\n\nexport default NewComponent",
|
||||||
"Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[]}}",
|
"Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[]}}",
|
||||||
"DataSources": []
|
"DataSources": [],
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,17 @@
|
||||||
],
|
],
|
||||||
"Code": "const RoleListComponent = ({\n title = \"AbpRoles\"\n}) => {\n return (\n <DynamicEntityComponent id=\"c_mdljvvmq_fno52v\" title={title} />\n );\n};\n\nexport default RoleListComponent;",
|
"Code": "const RoleListComponent = ({\n title = \"AbpRoles\"\n}) => {\n return (\n <DynamicEntityComponent id=\"c_mdljvvmq_fno52v\" title={title} />\n );\n};\n\nexport default RoleListComponent;",
|
||||||
"Props": null,
|
"Props": null,
|
||||||
"DataSources": []
|
"DataSources": [],
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,17 @@
|
||||||
"Props": null,
|
"Props": null,
|
||||||
"DataSources": "",
|
"DataSources": "",
|
||||||
"LastModificationTime": null,
|
"LastModificationTime": null,
|
||||||
"CreationTime": "2026-09-05T17:22:11"
|
"CreationTime": "2026-09-05T17:22:11",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -11,7 +11,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/Products",
|
"ComponentPath": "@/views/public/Products",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.Products.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.Products.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Products.Keywords",
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": false,
|
||||||
|
"SeoIncludeInSitemap": true,
|
||||||
|
"SeoChangeFrequency": "weekly",
|
||||||
|
"SeoPriority": 0.9,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "5c071295-f626-5e41-93fc-b4462a7a0528",
|
"Id": "5c071295-f626-5e41-93fc-b4462a7a0528",
|
||||||
|
|
@ -20,7 +30,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/Checkout",
|
"ComponentPath": "@/views/public/Checkout",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.Checkout.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.Checkout.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Checkout.Keywords",
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "f88f7222-42e6-5bef-b2aa-a8db81d11383",
|
"Id": "f88f7222-42e6-5bef-b2aa-a8db81d11383",
|
||||||
|
|
@ -29,7 +49,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/Payment",
|
"ComponentPath": "@/views/public/Payment",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.Payment.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.Payment.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Payment.Keywords",
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "c4c17ec7-8423-513a-863a-6065816efe0a",
|
"Id": "c4c17ec7-8423-513a-863a-6065816efe0a",
|
||||||
|
|
@ -38,7 +68,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/Success",
|
"ComponentPath": "@/views/public/Success",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.Success.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.Success.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Success.Keywords",
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "d3529321-2f70-5965-8e2d-7e4dc9e56dca",
|
"Id": "d3529321-2f70-5965-8e2d-7e4dc9e56dca",
|
||||||
|
|
@ -47,7 +87,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/Blog",
|
"ComponentPath": "@/views/public/Blog",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.Blog.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.Blog.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Blog.Keywords",
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": false,
|
||||||
|
"SeoIncludeInSitemap": true,
|
||||||
|
"SeoChangeFrequency": "daily",
|
||||||
|
"SeoPriority": 0.8,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "037dc714-77fc-5b0c-840c-0cdeca22df19",
|
"Id": "037dc714-77fc-5b0c-840c-0cdeca22df19",
|
||||||
|
|
@ -56,7 +106,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/BlogDetail",
|
"ComponentPath": "@/views/public/BlogDetail",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "d170f3e3-b0bd-5ed0-8394-601b51f5666f",
|
"Id": "d170f3e3-b0bd-5ed0-8394-601b51f5666f",
|
||||||
|
|
@ -65,7 +125,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/public/Demo",
|
"ComponentPath": "@/views/public/Demo",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.Demo.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.Demo.Description",
|
||||||
|
"SeoKeywords": "::App.PublicSeo.Demo.Keywords",
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": false,
|
||||||
|
"SeoIncludeInSitemap": true,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": 0.6,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "e4d6f040-ad8a-523f-bd4f-211329c651cf",
|
"Id": "e4d6f040-ad8a-523f-bd4f-211329c651cf",
|
||||||
|
|
@ -74,7 +144,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/AccessDenied",
|
"ComponentPath": "@/views/AccessDenied",
|
||||||
"RouteType": "public",
|
"RouteType": "public",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": "::App.PublicSeo.AccessDenied.Title",
|
||||||
|
"SeoDescription": "::App.PublicSeo.AccessDenied.Description",
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "bf6d30a3-03cf-5475-9daa-730023459bb0",
|
"Id": "bf6d30a3-03cf-5475-9daa-730023459bb0",
|
||||||
|
|
@ -83,7 +163,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/Login",
|
"ComponentPath": "@/views/auth/Login",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "df9b57fd-37f7-5377-b84c-70257295261a",
|
"Id": "df9b57fd-37f7-5377-b84c-70257295261a",
|
||||||
|
|
@ -92,7 +182,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/Register",
|
"ComponentPath": "@/views/auth/Register",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "bb282d0d-062b-51b7-87d7-22545c6f0c4d",
|
"Id": "bb282d0d-062b-51b7-87d7-22545c6f0c4d",
|
||||||
|
|
@ -101,7 +201,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/ForgotPassword",
|
"ComponentPath": "@/views/auth/ForgotPassword",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "7be57172-a75d-5f03-8a7a-566523dc5acc",
|
"Id": "7be57172-a75d-5f03-8a7a-566523dc5acc",
|
||||||
|
|
@ -110,7 +220,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/ResetPassword",
|
"ComponentPath": "@/views/auth/ResetPassword",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "2e78c334-4e75-586b-9c14-f5b5169bd895",
|
"Id": "2e78c334-4e75-586b-9c14-f5b5169bd895",
|
||||||
|
|
@ -119,7 +239,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/SendConfirmationCode",
|
"ComponentPath": "@/views/auth/SendConfirmationCode",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "d5f0773b-dece-5b55-beea-db2547ff9293",
|
"Id": "d5f0773b-dece-5b55-beea-db2547ff9293",
|
||||||
|
|
@ -128,7 +258,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/ExtendLogin",
|
"ComponentPath": "@/views/auth/ExtendLogin",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "f639b8fd-a462-5af5-b230-d2e5bd8a8eaf",
|
"Id": "f639b8fd-a462-5af5-b230-d2e5bd8a8eaf",
|
||||||
|
|
@ -137,7 +277,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/auth/VerifyConfirmationCode",
|
"ComponentPath": "@/views/auth/VerifyConfirmationCode",
|
||||||
"RouteType": "authenticated",
|
"RouteType": "authenticated",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "56ecf681-cf6b-5968-9acc-754e3026231c",
|
"Id": "56ecf681-cf6b-5968-9acc-754e3026231c",
|
||||||
|
|
@ -146,7 +296,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/Dashboard",
|
"ComponentPath": "@/views/Dashboard",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "f5d4a187-ef76-5010-bb2f-14844722875d",
|
"Id": "f5d4a187-ef76-5010-bb2f-14844722875d",
|
||||||
|
|
@ -155,7 +315,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/menu/MenuManager",
|
"ComponentPath": "@/views/menu/MenuManager",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Menus.Manager"
|
"Authority": "App.Menus.Manager",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "43285584-09c7-52cb-83ae-a35cb69bfced",
|
"Id": "43285584-09c7-52cb-83ae-a35cb69bfced",
|
||||||
|
|
@ -164,7 +334,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/listForm/wizard/Wizard",
|
"ComponentPath": "@/views/admin/listForm/wizard/Wizard",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Listforms.Wizard"
|
"Authority": "App.Listforms.Wizard",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "e87db52d-25cd-5c0f-9aa8-01dc2dc5b032",
|
"Id": "e87db52d-25cd-5c0f-9aa8-01dc2dc5b032",
|
||||||
|
|
@ -173,7 +353,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/listForm/wizard/WizardFileManager",
|
"ComponentPath": "@/views/admin/listForm/wizard/WizardFileManager",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Listforms.Wizard"
|
"Authority": "App.Listforms.Wizard",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "8ce5dfc6-8795-5d5e-b815-447f08292900",
|
"Id": "8ce5dfc6-8795-5d5e-b815-447f08292900",
|
||||||
|
|
@ -182,7 +372,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/listForm/edit/FormEdit",
|
"ComponentPath": "@/views/admin/listForm/edit/FormEdit",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "4e380430-9661-55ff-9c6b-270823b14401",
|
"Id": "4e380430-9661-55ff-9c6b-270823b14401",
|
||||||
|
|
@ -191,7 +391,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/ai/Assistant",
|
"ComponentPath": "@/views/ai/Assistant",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Definitions.AiBot.Asistant"
|
"Authority": "App.Definitions.AiBot.Asistant",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "ad21c21d-84c2-5f73-9eff-5bdd4d6754eb",
|
"Id": "ad21c21d-84c2-5f73-9eff-5bdd4d6754eb",
|
||||||
|
|
@ -200,7 +410,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/profile/Profile",
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "43df2816-075a-5fb0-8d75-07041acedf60",
|
"Id": "43df2816-075a-5fb0-8d75-07041acedf60",
|
||||||
|
|
@ -209,7 +429,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/profile/Profile",
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "5bf4ec7d-09fa-57bc-a832-915f0bd9a424",
|
"Id": "5bf4ec7d-09fa-57bc-a832-915f0bd9a424",
|
||||||
|
|
@ -218,7 +448,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/profile/Profile",
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "c64d7bd0-e884-55a6-915a-7a43e0c5e684",
|
"Id": "c64d7bd0-e884-55a6-915a-7a43e0c5e684",
|
||||||
|
|
@ -227,7 +467,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/activityLog/ActivityLog",
|
"ComponentPath": "@/views/admin/activityLog/ActivityLog",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "012f714f-fb86-5292-a2f1-c11c4bf88a06",
|
"Id": "012f714f-fb86-5292-a2f1-c11c4bf88a06",
|
||||||
|
|
@ -236,7 +486,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/version/ChangeLog",
|
"ComponentPath": "@/views/version/ChangeLog",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "507adc62-0ffd-51f0-a9d2-165eccbfc225",
|
"Id": "507adc62-0ffd-51f0-a9d2-165eccbfc225",
|
||||||
|
|
@ -245,7 +505,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/settings/Settings",
|
"ComponentPath": "@/views/settings/Settings",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Setting"
|
"Authority": "App.Setting",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "9cae5316-b620-5100-9726-bd638cd71517",
|
"Id": "9cae5316-b620-5100-9726-bd638cd71517",
|
||||||
|
|
@ -254,7 +524,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/user-management/Details",
|
"ComponentPath": "@/views/admin/user-management/Details",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "AbpIdentity.Users.Update"
|
"Authority": "AbpIdentity.Users.Update",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "f44286f9-e9d8-5478-80c1-3643eb848078",
|
"Id": "f44286f9-e9d8-5478-80c1-3643eb848078",
|
||||||
|
|
@ -263,7 +543,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/organization-unit/OrganizationUnits",
|
"ComponentPath": "@/views/admin/organization-unit/OrganizationUnits",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "Abp.Identity.OrganizationUnits"
|
"Authority": "Abp.Identity.OrganizationUnits",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "dc18c096-b996-51f6-b774-14a90e74f137",
|
"Id": "dc18c096-b996-51f6-b774-14a90e74f137",
|
||||||
|
|
@ -272,7 +562,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/hr/OrgChart",
|
"ComponentPath": "@/views/admin/hr/OrgChart",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Definitions.Department"
|
"Authority": "App.Definitions.Department",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "53d4baee-8360-523a-8470-26eaaaa54371",
|
"Id": "53d4baee-8360-523a-8470-26eaaaa54371",
|
||||||
|
|
@ -281,7 +581,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/forum/Forum",
|
"ComponentPath": "@/views/forum/Forum",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.ForumManagement.Publish"
|
"Authority": "App.ForumManagement.Publish",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "094842eb-fc95-51ea-bd57-6284f377c199",
|
"Id": "094842eb-fc95-51ea-bd57-6284f377c199",
|
||||||
|
|
@ -290,7 +600,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/list/List",
|
"ComponentPath": "@/views/list/List",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "c2230e57-183d-56b5-8e7b-e6fde917b335",
|
"Id": "c2230e57-183d-56b5-8e7b-e6fde917b335",
|
||||||
|
|
@ -299,7 +619,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/form/FormNew",
|
"ComponentPath": "@/views/form/FormNew",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "62e585e8-7393-5749-8281-ba94f7fdd43e",
|
"Id": "62e585e8-7393-5749-8281-ba94f7fdd43e",
|
||||||
|
|
@ -308,7 +638,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/form/FormView",
|
"ComponentPath": "@/views/form/FormView",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "fddafca0-a6ad-51fb-85b0-ccdf1cfed1d6",
|
"Id": "fddafca0-a6ad-51fb-85b0-ccdf1cfed1d6",
|
||||||
|
|
@ -317,7 +657,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/form/FormEdit",
|
"ComponentPath": "@/views/form/FormEdit",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "35d0b346-fe04-5a5a-896f-5e2b87e90bc3",
|
"Id": "35d0b346-fe04-5a5a-896f-5e2b87e90bc3",
|
||||||
|
|
@ -326,7 +676,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/list/Chart",
|
"ComponentPath": "@/views/list/Chart",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "76a3f75e-11db-560a-b749-989f2ca579d7",
|
"Id": "76a3f75e-11db-560a-b749-989f2ca579d7",
|
||||||
|
|
@ -335,7 +695,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/developerKit/SqlQueryManager",
|
"ComponentPath": "@/views/developerKit/SqlQueryManager",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.SqlQueryManager"
|
"Authority": "App.SqlQueryManager",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "08891b2a-5d2e-54c5-b12a-e0e8c2547b55",
|
"Id": "08891b2a-5d2e-54c5-b12a-e0e8c2547b55",
|
||||||
|
|
@ -344,7 +714,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/developerKit/ComponentCodeLayout",
|
"ComponentPath": "@/views/developerKit/ComponentCodeLayout",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.DeveloperKit.CustomComponents"
|
"Authority": "App.DeveloperKit.CustomComponents",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "cdbfdea5-ea04-5537-bb5c-1dd964c675fe",
|
"Id": "cdbfdea5-ea04-5537-bb5c-1dd964c675fe",
|
||||||
|
|
@ -353,7 +733,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/intranet/documents/IntranetDocuments",
|
"ComponentPath": "@/views/intranet/documents/IntranetDocuments",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Intranet.Document"
|
"Authority": "App.Intranet.Document",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "ea5acaee-a479-5daf-b4e6-2999578fd78d",
|
"Id": "ea5acaee-a479-5daf-b4e6-2999578fd78d",
|
||||||
|
|
@ -362,7 +752,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/files/FileManager",
|
"ComponentPath": "@/views/admin/files/FileManager",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Files"
|
"Authority": "App.Files",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "c074e5cc-ab9c-51e9-a89c-d9f662316ab5",
|
"Id": "c074e5cc-ab9c-51e9-a89c-d9f662316ab5",
|
||||||
|
|
@ -371,7 +771,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/report/DevexpressReportViewer",
|
"ComponentPath": "@/views/report/DevexpressReportViewer",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "57520752-8f21-5d81-aaf7-fa8089b83e1c",
|
"Id": "57520752-8f21-5d81-aaf7-fa8089b83e1c",
|
||||||
|
|
@ -380,7 +790,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/report/DevexpressReportDesigner",
|
"ComponentPath": "@/views/report/DevexpressReportDesigner",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": ""
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "16917407-a912-53ab-ab96-d14e7ef6abd2",
|
"Id": "16917407-a912-53ab-ab96-d14e7ef6abd2",
|
||||||
|
|
@ -389,7 +809,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/videoroom/Dashboard",
|
"ComponentPath": "@/views/admin/videoroom/Dashboard",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Videoroom"
|
"Authority": "App.Videoroom",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "7cfef810-4738-577e-88aa-ca32f9d15ccc",
|
"Id": "7cfef810-4738-577e-88aa-ca32f9d15ccc",
|
||||||
|
|
@ -398,7 +828,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/videoroom/RoomList",
|
"ComponentPath": "@/views/admin/videoroom/RoomList",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Videoroom.List"
|
"Authority": "App.Videoroom.List",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "b6af134c-95b8-5453-915a-4cf5d84cb600",
|
"Id": "b6af134c-95b8-5453-915a-4cf5d84cb600",
|
||||||
|
|
@ -407,7 +847,17 @@
|
||||||
"ComponentType": "normal",
|
"ComponentType": "normal",
|
||||||
"ComponentPath": "@/views/admin/videoroom/RoomDetail",
|
"ComponentPath": "@/views/admin/videoroom/RoomDetail",
|
||||||
"RouteType": "protected",
|
"RouteType": "protected",
|
||||||
"Authority": "App.Videoroom.Detail"
|
"Authority": "App.Videoroom.Detail",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
"MonthlyPrice": 850,
|
"MonthlyPrice": 850,
|
||||||
"YearlyPrice": 3400,
|
"YearlyPrice": 3400,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/1181673/pexels-photo-1181673.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/branch-hosting.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "3ced7419-b72a-5bd5-b587-b76cc9778460",
|
"Id": "3ced7419-b72a-5bd5-b587-b76cc9778460",
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
"MonthlyPrice": 2100,
|
"MonthlyPrice": 2100,
|
||||||
"YearlyPrice": 2100,
|
"YearlyPrice": 2100,
|
||||||
"IsQuantityBased": false,
|
"IsQuantityBased": false,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/442150/pexels-photo-442150.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/backup-service.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "22d113c5-94ad-5002-8e1f-79a622ca1ac6",
|
"Id": "22d113c5-94ad-5002-8e1f-79a622ca1ac6",
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
"MonthlyPrice": 5000,
|
"MonthlyPrice": 5000,
|
||||||
"YearlyPrice": 5000,
|
"YearlyPrice": 5000,
|
||||||
"IsQuantityBased": false,
|
"IsQuantityBased": false,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/3184465/pexels-photo-3184465.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/remote-support-contract.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "39999ed6-7115-5816-bd32-5a786d68e356",
|
"Id": "39999ed6-7115-5816-bd32-5a786d68e356",
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
"MonthlyPrice": 900,
|
"MonthlyPrice": 900,
|
||||||
"YearlyPrice": 3500,
|
"YearlyPrice": 3500,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/3184360/pexels-photo-3184360.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/user-license.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "0c927e71-0c51-5a2d-899c-c22302b563a0",
|
"Id": "0c927e71-0c51-5a2d-899c-c22302b563a0",
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"MonthlyPrice": 3700,
|
"MonthlyPrice": 3700,
|
||||||
"YearlyPrice": 3700,
|
"YearlyPrice": 3700,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/3184339/pexels-photo-3184339.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/remote-branch-training.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "37d1aefa-cbfe-5251-9525-ca17becb596f",
|
"Id": "37d1aefa-cbfe-5251-9525-ca17becb596f",
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
"MonthlyPrice": 2000,
|
"MonthlyPrice": 2000,
|
||||||
"YearlyPrice": 2000,
|
"YearlyPrice": 2000,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/3184291/pexels-photo-3184291.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/extra-hourly-service.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "591e9307-c825-521e-bf06-d6f647708ca4",
|
"Id": "591e9307-c825-521e-bf06-d6f647708ca4",
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
"MonthlyPrice": 750,
|
"MonthlyPrice": 750,
|
||||||
"YearlyPrice": 750,
|
"YearlyPrice": 750,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/sms5k.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "8ff856d2-30d5-5f5e-b38e-f6cebee76421",
|
"Id": "8ff856d2-30d5-5f5e-b38e-f6cebee76421",
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
"MonthlyPrice": 1350,
|
"MonthlyPrice": 1350,
|
||||||
"YearlyPrice": 1350,
|
"YearlyPrice": 1350,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/sms5k.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "bfa0964b-e8cb-5739-8b4a-d44773553f0a",
|
"Id": "bfa0964b-e8cb-5739-8b4a-d44773553f0a",
|
||||||
|
|
@ -100,7 +100,7 @@
|
||||||
"MonthlyPrice": 2900,
|
"MonthlyPrice": 2900,
|
||||||
"YearlyPrice": 2900,
|
"YearlyPrice": 2900,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/sms5k.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "2b675f5f-320f-5424-8393-3dc0363ad27e",
|
"Id": "2b675f5f-320f-5424-8393-3dc0363ad27e",
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
"MonthlyPrice": 5100,
|
"MonthlyPrice": 5100,
|
||||||
"YearlyPrice": 5100,
|
"YearlyPrice": 5100,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/sms5k.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "730c2dbf-c700-506d-b216-a169cbd85a15",
|
"Id": "730c2dbf-c700-506d-b216-a169cbd85a15",
|
||||||
|
|
@ -122,7 +122,7 @@
|
||||||
"MonthlyPrice": 8800,
|
"MonthlyPrice": 8800,
|
||||||
"YearlyPrice": 8800,
|
"YearlyPrice": 8800,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/404280/pexels-photo-404280.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/sms5k.webp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": "53ae0678-3c01-5887-89e9-12a45bcc188f",
|
"Id": "53ae0678-3c01-5887-89e9-12a45bcc188f",
|
||||||
|
|
@ -133,7 +133,7 @@
|
||||||
"MonthlyPrice": 880,
|
"MonthlyPrice": 880,
|
||||||
"YearlyPrice": 880,
|
"YearlyPrice": 880,
|
||||||
"IsQuantityBased": true,
|
"IsQuantityBased": true,
|
||||||
"ImageUrl": "https://images.pexels.com/photos/1591061/pexels-photo-1591061.jpeg?auto=compress&cs=tinysrgb&w=300"
|
"ImageUrl": "/img/products/sms-blocking.webp"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1005,6 +1005,227 @@
|
||||||
"IsVisibleToClients": true,
|
"IsVisibleToClients": true,
|
||||||
"IsInherited": true,
|
"IsInherited": true,
|
||||||
"IsEncrypted": false
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "e5b8222e-664c-564b-b853-d360e9a64f4c",
|
||||||
|
"Code": "App.SiteManagement.Seo.SiteUrl",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 1,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.SiteUrl",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.SiteUrl.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "8001a080-ff59-5bf6-9da9-5aebc2abd41b",
|
||||||
|
"Code": "App.SiteManagement.Seo.SiteName",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 2,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.SiteName",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.SiteName.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "Sözsoft",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "5e336aa9-0a1a-56d2-8f9a-5a899ee343d2",
|
||||||
|
"Code": "App.SiteManagement.Seo.DefaultTitle",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 3,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.DefaultTitle",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.DefaultTitle.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "Sözsoft",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "a80126cc-116e-5eaa-b31a-819738b6272d",
|
||||||
|
"Code": "App.SiteManagement.Seo.DefaultDescription",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 4,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.DefaultDescription",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.DefaultDescription.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Memo",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "929037b4-29dd-5e4f-8004-180dae435c3c",
|
||||||
|
"Code": "App.SiteManagement.Seo.DefaultKeywords",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 5,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.DefaultKeywords",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.DefaultKeywords.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "39a4b2a8-9809-5cb8-999c-738fe94a44c7",
|
||||||
|
"Code": "App.SiteManagement.Seo.DefaultImage",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 6,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.DefaultImage",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.DefaultImage.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "/img/logo/logo-512.png",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "7ce5fce9-deee-5170-9ac5-b4e1f4aefa1c",
|
||||||
|
"Code": "App.SiteManagement.Seo.TwitterSite",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 7,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.TwitterSite",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.TwitterSite.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "787c036d-e549-5207-b6c3-f4a34b7fdf6e",
|
||||||
|
"Code": "App.SiteManagement.Seo.Locales",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 8,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.Locales",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.Locales.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "tr,en",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "4e2fd424-301c-55e6-b494-b9190759fe76",
|
||||||
|
"Code": "App.SiteManagement.Seo.Robots",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 9,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.Robots",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.Robots.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "index, follow",
|
||||||
|
"DataType": "List",
|
||||||
|
"SelectOptions": "{\"index, follow\":\"App.SiteManagement.Seo.Robots.IndexFollow\",\"noindex, nofollow\":\"App.SiteManagement.Seo.Robots.NoIndexNoFollow\"}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "5859839b-32aa-579e-8d2a-290800bbb865",
|
||||||
|
"Code": "App.SiteManagement.Seo.GoogleSiteVerification",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 10,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.GoogleSiteVerification",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.GoogleSiteVerification.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Text",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "c20e0c12-10bf-5cb0-99f5-eb3825276694",
|
||||||
|
"Code": "App.SiteManagement.Seo.OrganizationSameAs",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 11,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.OrganizationSameAs",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.OrganizationSameAs.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Memo",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "2b871f1c-bd20-58eb-85d3-012e5dd835be",
|
||||||
|
"Code": "App.SiteManagement.Seo.SitemapEnabled",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 12,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.SitemapEnabled",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.SitemapEnabled.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "true",
|
||||||
|
"DataType": "Bool",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": true,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "43c3ab04-da10-5880-86c5-e1b32099228a",
|
||||||
|
"Code": "App.SiteManagement.Seo.SitemapExcludePaths",
|
||||||
|
"MainGroupKey": "App.SiteManagement",
|
||||||
|
"SubGroupKey": "App.SiteManagement.Seo",
|
||||||
|
"Order": 13,
|
||||||
|
"NameKey": "App.SiteManagement.Seo.SitemapExcludePaths",
|
||||||
|
"DescriptionKey": "App.SiteManagement.Seo.SitemapExcludePaths.Description",
|
||||||
|
"RequiredPermissionName": "App.SiteManagement.Seo",
|
||||||
|
"Providers": "G|D",
|
||||||
|
"DefaultValue": "",
|
||||||
|
"DataType": "Memo",
|
||||||
|
"SelectOptions": "{}",
|
||||||
|
"IsVisibleToClients": false,
|
||||||
|
"IsInherited": false,
|
||||||
|
"IsEncrypted": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ export default [
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
typescript: { project: './tsconfig.eslint.json', alwaysTryTypes: true },
|
typescript: { project: './tsconfig.eslint.json', alwaysTryTypes: true },
|
||||||
},
|
},
|
||||||
|
// Vite'in sanal modulleri diskte yoktur; cozucu onlari bulamaz, tipleri
|
||||||
|
// `vite-env.d.ts` uzerinden gelir.
|
||||||
|
'import/core-modules': ['virtual:pwa-register'],
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
...reactPlugin.configs.recommended.rules,
|
...reactPlugin.configs.recommended.rules,
|
||||||
|
|
@ -83,4 +86,17 @@ export default [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
prettier,
|
prettier,
|
||||||
|
// Derleme araclari Node'da CommonJS olarak calisir: tarayici globalleri yerine Node
|
||||||
|
// globalleri gecerlidir ve Tailwind eklentisi `require` ile yuklenir (tailwind.config.ts
|
||||||
|
// Node tarafindan okunur, bundle'a girmez).
|
||||||
|
{
|
||||||
|
files: ['tailwind.config.ts', 'postcss.config.cjs', 'twSafelistGenerator/**/*.js'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: globals.node,
|
||||||
|
sourceType: 'commonjs',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,27 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<!--
|
||||||
<meta name="description" content="Sözsoft" />
|
Tema rengi sistem temasina gore ayrilir; tek deger koyu modda tarayici cubugunu
|
||||||
|
acik birakip sayfanin ustunde beyaz bir serit gosteriyordu.
|
||||||
|
-->
|
||||||
|
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
|
||||||
|
<meta name="theme-color" content="#111827" media="(prefers-color-scheme: dark)" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Buradaki meta yalnizca ilk boyamanin ve JavaScript calistirmayan istemcilerin
|
||||||
|
gordugu varsayilandir. Gercek deger uygulama konfigurasyonundaki SEO sozlesmesinden
|
||||||
|
gelir ve `SeoHead` tarafindan yazilir (bkz. components/shared/SeoHead.tsx).
|
||||||
|
-->
|
||||||
<title>Sözsoft</title>
|
<title>Sözsoft</title>
|
||||||
|
<meta name="description" content="Sözsoft" />
|
||||||
|
<meta name="robots" content="index, follow" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:site_name" content="Sözsoft" />
|
||||||
|
<meta property="og:title" content="Sözsoft" />
|
||||||
|
<meta property="og:description" content="Sözsoft" />
|
||||||
|
<meta property="og:image" content="/img/logo/logo-512.png" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
BIN
ui/public/img/blog/ai-ve-gelecegi.webp
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
ui/public/img/blog/dijital-pazarlamada-veri-analizi.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
BIN
ui/public/img/blog/web-gelistirmede-son-trendler.webp
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
ui/public/img/hero/about.webp
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
ui/public/img/hero/blog.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
ui/public/img/hero/contact.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
ui/public/img/hero/home.webp
Normal file
|
After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
BIN
ui/public/img/products/backup-service.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
ui/public/img/products/branch-hosting.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |