<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Writings &amp; thoughts RSS feed by Furkan Ünsalan</title>
        <link>https://furkanunsalan.dev/writing</link>
        <description>Stay up to date with my latest writings and thoughts</description>
        <lastBuildDate>Tue, 23 Jun 2026 09:40:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Next.js RSS Generator</generator>
        <language>en</language>
        <copyright>All rights reserved 2026, Furkan Ünsalan</copyright>
        
        <item>
            <title><![CDATA[A casual friday office day @ Teachfluence]]></title>
            <link>https://furkanunsalan.dev/writing#t-4</link>
            <guid isPermaLink="false">thought-4</guid>
            <pubDate>Fri, 05 Jun 2026 21:25:51 GMT</pubDate>
            <content:encoded><![CDATA[<article><p>A casual friday office day @ Teachfluence</p></article><p><img src="https://furkanunsalan.dev/api/img/thoughts/QM1Ta1B--img_8948.jpg" alt="" /></p><p><img src="https://furkanunsalan.dev/api/img/thoughts/Av-8H113-img_8949.webp" alt="" /></p>]]></content:encoded>
            
        </item>
        <item>
            <title><![CDATA[Contentful ile Blog Sistemi Yönetmek]]></title>
            <link>https://furkanunsalan.dev/writing/contentful-blog</link>
            <guid isPermaLink="false">contentful-blog</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<article><p>Bu rehberde <a href="https://kommunity.com/devmultigroup">Developer MultiGroup</a> bünyesinde yeni başlattığımız <a href="https://kommunity.com/devmultigroup/events/android-blast-off-jetpack-compose-bootcamp-18857d79">Android Blast Off</a> bootcamp serimizin <a href="https://android-blast-off.devmultigroup.com/">websitesine</a> nasıl Contentful ve ve Next.js kullanarak çok basit bir şekilde bir blog sistemi kurduğumu anlatıyor olacağım. Bu yazı biraz da kendi yaptıklarımı pekiştirmek adına yazdığım bir yazı olacak; umarım beğenirsiniz, şimdiden iyi okumalar!</p><h3>Peki Nedir Bu Contentful?</h3><blockquote><p>Content that scales.</p></blockquote><p>Contentful, özünde modern web siteleri ve dijital ürünler için geliştirilmiş güçlü bir içerik yönetim sistemi (CMS). Geleneksel CMS’lerden farklı olarak Contentful, “Headless CMS” kategorisinde yer alıyor. Bu terimi basitçe ifade etmek gerekirse, Contentful içeriğinizi oluşturmanızı ve yönetmenizi sağlarken, bu içeriğin nasıl ve nerede görüntüleneceği konusunda size tam bir özgürlük sunar.</p><p>Contentful’un en büyük avantajlarından biri, içeriklerinizi bir kez oluşturup farklı platformlarda (web sitesi, mobil uygulama, akıllı cihazlar vb.) kullanabilmenizdir. API odaklı yapısı sayesinde, geliştirdiğiniz her türlü uygulama için içeriklerinizi kolayca entegre edebilirsiniz.</p><p>Contentful’u diğer CMS’lerden ayıran özellikler:</p><ul><li><strong>Esnek İçerik Modelleme</strong>: Kendi içerik modellerinizi istediğiniz şekilde oluşturabilirsiniz.</li><li><strong>API Öncelikli Yaklaşım</strong>: RESTful API ve GraphQL API seçenekleriyle içeriklerinize kolayca erişim sağlayabilirsiniz.</li><li><strong>Çoklu Dil Desteği</strong>: Projelerinizi birden fazla dilde yönetebilirsiniz.</li><li><strong>Medya Yönetimi</strong>: Görsel ve dosyalarınızı organize bir şekilde saklayabilir ve kullanabilirsiniz.</li><li><strong>İş Akışı Yönetimi</strong>: İçerik oluşturma ve yayınlama süreçlerinizi optimize edebilirsiniz.</li></ul><h4>Hesap Oluşturma</h4><p>Contentful’u kullanmaya başlamak için öncelikle bir hesap oluşturmanız gerekiyor. İşte adım adım hesap oluşturma süreci:</p><ol><li><a href="https://www.contentful.com/">Contentful web sitesine</a> gidin.</li><li>Sağ üst köşedeki “Sign up” butonuna tıklayın.</li><li>Hesabınızı oluşturmak için:</li></ol><ul><li>E-posta adresinizi girin</li><li>Bir şifre oluşturun</li><li>Ya da Google, GitHub veya Apple hesabınız ile oturum açmayı tercih edin</li></ul><p>Kayıt işlemini tamamladıktan sonra, bir onay e-postası alacaksınız. E-postadaki bağlantıya tıklayarak hesabınızı doğrulayın.</p><p>Hesabınız aktif olduktan sonra, Contentful size bir organizasyon ve ilk space’inizi oluşturmanızı önerecektir. Space, içeriklerinizi ve ayarlarınızı tutacağınız ana konteynerdir.</p><p><strong>Space oluştururken:</strong></p><ul><li>Space adı belirleyin</li><li>Hangi amaçla kullanacağınızı seçin (blog, e-ticaret, kişisel proje vb.)</li><li>Örnek içerik şablonlarından birini seçebilir veya sıfırdan başlayabilirsiniz</li></ul><p>Ücretsiz planda, sınırlı sayıda içerik ve dosya yükleyebileceğiniz bir yapı mevcut. Ancak bu limitler gördüğüm kadarıyla çoğu kişisel proje ve küçük işletmeler/ekipler için yeterli olacaktır.</p><h4>Content Type Belirleme</h4><p>Contentful’da içerik modelleme, “Content Types” (İçerik Tipleri) adı verilen yapılar ile gerçekleştirilir. Content Type, içeriğinizin yapısını belirleyen bir şablondur. Örneğin; bir blog yazısı, bir ürün sayfası veya bir ekip üyesi profili için farklı Content Type’lar oluşturabilirsiniz.</p><p><strong>İçerik tipinizi oluşturmak için:</strong></p><ol><li>Sol menüden <strong>“Content model”</strong> bölümüne gidin.</li><li><strong>“Add content type”</strong> butonuna tıklayın.</li><li>İçerik tipinize bir isim verin (örneğin <strong>“Blog Post”</strong>).</li><li>İsterseniz bir açıklama ekleyin ve API identifier’ı düzenleyin.</li><li><strong>“Create”</strong> butonuna tıklayın.</li></ol><p>Her Content Type’a <strong>alanlar (fields)</strong> ekleyerek yapısını belirlemeniz gerekir. Örneğin, haydi bizim Android Blast-Off için oluşturduğumuz Content Type’a göz atalım:</p><ul><li><strong>Title</strong> (Short Text tipi)</li><li><strong>Slug</strong> (Short Text tipi)</li><li><strong>Writers</strong> (Short Text tipi)</li><li><strong>Description</strong> (Short Text tipi)</li><li><strong>Body</strong> (Rich Text tipi)</li></ul><p><strong>Yeni bir alan eklemek için:</strong></p><ol><li>“Add field” butonuna tıklayın.</li><li>Alan tipini seçin (Text, Number, Date, Media, Location, Reference vb.).</li><li>Alana bir isim verin.</li><li>Gerekli diğer ayarları yapın (zorunlu alan mı, görünürlük ayarları, yardımcı metin vb.).</li><li>“Create and configure” butonuna tıklayın.</li><li>Ek alan ayarlarını yapılandırın (karakter limiti, format vb.)</li><li>“Confirm” butonuna tıklayın.</li></ol><p>Content Type Alan Seçenekleri</p><p>Alan tipleri çok çeşitlidir:</p><ul><li><strong>Text</strong>: Kısa veya uzun metinler için</li><li><strong>Rich Text</strong>: Biçimlendirilmiş içerik için (kalın, italik, listeler, bağlantılar vb.)</li><li><strong>Number</strong>: Sayısal değerler için</li><li><strong>Date</strong>: Tarih ve saat bilgileri için</li><li><strong>Location</strong>: Coğrafi konum bilgileri için</li><li><strong>Media</strong>: Görsel, video ve diğer dosyalar için</li><li><strong>Boolean</strong>: Evet/Hayır seçenekleri için</li><li><strong>JSON Object</strong>: Karmaşık veri yapıları için</li><li><strong>Reference</strong>: Diğer içerik öğelerine bağlantılar için</li></ul><p>Contentful’un en beğendiğim yanlarından biri, Content Type’larınızın içeriğini “Zorunlu Alan” olmasından tutun Rich Text için izin verilen yazı türlerine ( H1, H2, <strong>B</strong>, *i *) kadar çoğu detayı ihtiyacınıza göre düzenlebilmeniz.</p><h4>İçerikleri Ekleme</h4><p>Content Type’larınızı oluşturduktan sonra, artık gerçek içerikler ekleyebilirsiniz. Contentful’da her bir içerik öğesine “Entry” adı verilir.</p><p>Yeni bir içerik eklemek için:</p><ol><li>Sol menüden “Content” bölümüne gidin.</li><li>“Add entry” butonuna tıklayın.</li><li>Oluşturduğunuz Content Type’lardan birini seçin (örneğin “Blog Post”).</li><li>İçeriğiniz için gerekli tüm alanları doldurun.</li><li>Sağ üst köşedeki “Publish” butonuna tıklayarak içeriğinizi yayınlayın.</li></ol><p>İçerik oluştururken dikkat edilmesi gereken önemli noktalar:</p><ul><li><strong>Taslak ve Yayınlama</strong>: İçeriklerinizi taslak olarak kaydedebilir ve daha sonra yayınlayabilirsiniz.</li><li><strong>Versiyonlama</strong>: Contentful, içeriklerinizin tüm versiyonlarını saklar, böylece gerektiğinde önceki bir versiyona dönebilirsiniz.</li><li><strong>İş Akışı</strong>: Büyük ekipler için, içerik durumu ve onay süreçleri oluşturabilirsiniz.</li><li><strong>Zamanlanmış Yayınlama</strong>: İçeriklerinizin belirli bir tarih ve saatte yayınlanmasını sağlayabilirsiniz (ücretli planlarda).</li></ul><p>İçeriklerinizi düzenlemek için, “Content” bölümünden ilgili içeriğe tıklayın. Az önceki görseldekiyle aynı arayüz ile karşılacaksınız. Buradan değişikliklerinizi yaptıktan sonra, “Publish” butonuna tıklayarak güncellemeleri yayınlayın.</p><h4>Blog’lara Fotoğraf Ekleme</h4><p>Contentful’da blog yazılarınıza fotoğraf eklemek oldukça kolaydır. Bunun için iki yöntem bulunmaktadır:</p><h4>1. Media Alanı Kullanarak Fotoğraf Ekleme</h4><p>Eğer Content Type’ınızda bir “Media” alanı varsa (örneğin, blog yazısının kapak görseli için), bu adımları izleyin:</p><ol><li>İçeriğinizi düzenlerken, Media alanına tıklayın.</li><li>“Add media” butonuna tıklayın.</li><li>Daha önce yüklediğiniz bir görseli seçebilir veya yeni bir görsel/görseller yükleyebilirsiniz.</li><li>Yeni bir görsel yüklemek için “Upload” seçeneğini kullanın.</li><li>Bilgisayarınızdan bir görsel seçin ve “Open” butonuna tıklayın.</li><li>Görseliniz için başlık ve açıklama ekleyin (opsiyonel).</li><li>“Publish” butonuna tıklayarak görseli yayınlayın.</li></ol><h4>2. Rich Text Alanı İçine Fotoğraf Ekleme</h4><p>Rich Text alanı içindeki içeriğinize fotoğraf eklemek için:</p><ol><li>Rich Text düzenleyicide, görseli eklemek istediğiniz konumu seçin.</li><li>Araç çubuğundaki “Embed” butonuna tıklayın.</li><li>“Asset” seçeneğini tıklayın.</li><li>Daha önce yüklediğiniz bir görseli seçebilir veya yeni bir görsel yükleyebilirsiniz.</li></ol><h4>Fotoğraf Yönetimi İpuçları</h4><p>Contentful’da fotoğraflarınızı daha etkili bir şekilde yönetmek için:</p><ul><li><strong>Medya Kütüphanesi</strong>: Tüm medya dosyalarınızı “Media” bölümünden yönetebilirsiniz.</li><li><strong>Dosya Organizasyonu</strong>: Klasörler oluşturarak medya dosyalarınızı organize edebilirsiniz.</li><li><strong>Görsel Optimizasyonu</strong>: Contentful, görselleri otomatik olarak optimize eder ve farklı boyutlarda sunar.</li><li><strong>Alt Metin</strong>: Erişilebilirlik için görsellere alt metin eklemeyi unutmayın.</li><li><strong>Görsel İşleme</strong>: Contentful’ın API’si aracılığıyla görseller üzerinde crop, resize gibi işlemler gerçekleştirebilirsiniz.</li></ul><p>Contentful’un en güçlü yanlarından biri, görsellerinizin otomatik olarak optimize edilmesi ve farklı ekran boyutları için hazırlanmasıdır. URL parametreleri kullanarak görsellerin boyutu, kalitesi ve formatı gibi özelliklerini dinamik olarak değiştirebilirsiniz.</p><p>Örneğin:</p><p>https://images.ctfassets.net/your-space-id/your-image-id/your-image-filename.jpg?w=800&amp;h=600&amp;fit=fill</p><p>Bu URL, görseli 800x600 piksel boyutuna getirir ve içeriği tam dolduracak şekilde yeniden boyutlandırır.</p><h3>Haydi Next.js ile sitemize bağlayalım!</h3><p>Buraya kadar biraz daha Contenful platformuna alıştık ve Content Type oluşturmaktan yeni bir gönderi paylaşmaya çoğu şeye değindik. Buradan sonrasında biraz daha tekniğe girerek Contentful hesabımızı bir Next.js uygulamasına nasıl bağlayabileceğimize bakacağız.</p><p>Burdan sonra kullanılacak kod örnekleri <a href="https://android-blast-off.devmultigroup.com/">Android Blast Off sitemizin</a> <a href="https://github.com/Developer-MultiGroup/android-blast-off-website">Github Repository’sinde</a> açık olarak bulunabilir. Oraya uğrarsanız bir yıldızınızı da alırsak çok seviniriz :) Haydi başlayalım!</p><h4>Contentful Bağlantısının Yapılışı</h4><p>Öncelikle Contentful SDK’sını projemize eklemeliyiz.</p><p>npm install contentful</p><p>Sonrasında ise bir Contentful.ts dosyası oluşturarak Contentful istemcimizi yapılandıralım:</p><p>import { createClient, Entry } from &quot;contentful&quot;; import { BlogPostSkeleton } from &quot;@/types/contentful&quot;;</p><p>const client = createClient({ space: process.env.CONTENTFUL_SPACE_ID || &quot;&quot;, accessToken: process.env.CONTENTFUL_ACCESS_TOKEN || &quot;&quot;, });</p><p><a href="https://github.com/Developer-MultiGroup/android-blast-off-website/blob/main/src/lib/contentful.ts">Contentful İstemcisi Dosyamız</a></p><p>Gördüğünüz üzeri bu dosya bazı gücenlik bilgileri (environment variable) içeriyor; bu değişkenlerden “Space ID” değişkeninizi projenizin genel ayarlar sayfasından, “Access Token” değişkeninizi ise “API Keys” sekmesinden oluşturabilirsiniz.</p><h4>Blogları Listeleyelim</h4><p>Haydi şimdi blogları listeleyeceğimiz sayfafı ve gerekli fonksyionları yazalım. Öncelikle az önce oluşturduğumuz <strong>“contentful.ts”</strong> dosyasımza “***getBlogPosts” ***adında bir fonksiyon eklemeliyiz.</p><p>// src/lib/contentful.ts</p><p>export const getBlogPosts = async (): Promise&lt;Entry&lt;BlogPostSkeleton&gt;[]&gt; =&gt; { const res = await client.getEntries&lt;BlogPostSkeleton&gt;({ content_type: &quot;dmgBlog&quot;, // Bu kısmı kendi Content Type'ınız ile değiştirmelisiniz });</p><p>console.log(res.items);</p><p>return res.items; };</p><p>Bu fonksyionu yazdıktan sonra ise bu blogların hepsini bir sayfada listelemeliyiz. Bunun için <strong>“src/app/blog”</strong> dizinine bir sayfa oluşturalım.</p><p>// src/app/blog/page.tsx</p><p>import { getBlogPosts } from &quot;@/lib/contentful&quot;; import Link from &quot;next/link&quot;; import { Card, CardContent, CardFooter, CardHeader, CardTitle, } from &quot;@/components/ui/card&quot;; import { Button } from &quot;@/components/ui/button&quot;;</p><p>export const revalidate = 60; // ISR</p><p>export default async function BlogIndexPage() { const posts = await getBlogPosts();</p><p>return ( &lt;div className=&quot;min-h-screen flex flex-col font-montserrat-semi&quot;&gt; {/* Fixed top spacing */} &lt;div className=&quot;h-[20vh]&quot;&gt;&lt;/div&gt;</p><p>{/* Content container */} &lt;div className=&quot;w-11/12 md:w-4/5 max-w-6xl mx-auto&quot;&gt; &lt;header className=&quot;mb-12 md:mb-16 text-center&quot;&gt; &lt;h1 className=&quot;text-3xl md:text-4xl font-bold mb-5 text-white&quot;&gt; Blog{&quot; &quot;} &lt;span className=&quot;bg-gradient-to-r text-secondary&quot;&gt;Yazıları&lt;/span&gt; &lt;/h1&gt; &lt;p className=&quot;text-gray-400 mb-8 max-w-2xl mx-auto text-sm md:text-base&quot;&gt; Android Blast-Off hakkında yazdığımız bütün içerikler bir arada! Haydi sen de hemen okumaya başla. &lt;/p&gt; &lt;/header&gt;</p><p>{/* Blog posts grid <em>/}</em> <em>&lt;div className=&quot;grid grid-cols-1 md:grid-cols-2 gap-8 mb-12&quot;&gt;</em> <em>{posts.map((post) =&gt; (</em> <em>&lt;Card</em> <em>key={post.sys.id}</em> <em>className=&quot;mb-6 bg-gray-800/70 backdrop-blur-sm border border-gray-600/30 hover:border-accent hover:bg-gray-800/80 hover:shadow-lg hover:shadow-gray-900/20 transition-all duration-300 group overflow-hidden rounded-xl w-full&quot;</em> <em>&gt;</em> <em>&lt;CardHeader&gt;</em> <em>&lt;p className=&quot;text-secondary text-sm mb-4&quot;&gt;</em> <em>{new Date(post.sys.createdAt).toLocaleDateString(&quot;tr-TR&quot;, {</em> <em>year: &quot;numeric&quot;,</em> <em>month: &quot;long&quot;,</em> <em>day: &quot;numeric&quot;,</em> <em>})}</em> <em>&lt;/p&gt;</em> <em>&lt;CardTitle className=&quot;text-white group-hover:text-white/90 transition-colors text-xl md:text-2xl font-bold&quot;&gt;</em> <em>{String(post.fields.title) || &quot;Untitled Post&quot;}</em> <em>&lt;/CardTitle&gt;</em> <em>{/</em> Writers section */} {Array.isArray(post.fields.writers) &amp;&amp; post.fields.writers.length &gt; 0 &amp;&amp; ( &lt;div className=&quot;text-accent text-sm mt-2&quot;&gt; {post.fields.writers.length === 1 ? &quot;Yazar: &quot; : &quot;Yazarlar: &quot;} {post.fields.writers.join(&quot;, &quot;)} &lt;/div&gt; )} &lt;/CardHeader&gt;</p><p>&lt;CardContent&gt; &lt;p className=&quot;text-white/90 text-sm md:text-base&quot;&gt; {typeof post.fields.description === &quot;string&quot; ? post.fields.description : &quot;Read this interesting blog post to learn more...&quot;} &lt;/p&gt; &lt;/CardContent&gt;</p><p>&lt;CardFooter className=&quot;flex justify-between items-center pt-2&quot;&gt; &lt;Link href={<code>/blog/${post.fields.slug}</code>} passHref&gt; &lt;Button className=&quot;bg-gray-700/90 hover:bg-gray-600/90 text-white font-medium shadow-md hover:cursor-pointer hover:shadow-lg transition-all duration-200 border border-gray-500/30&quot; aria-label={<code>Read ${                       String(post.fields.title) || &quot;Untitled Post&quot;                     }</code>}</p><p>Yazıyı Oku &lt;/Button&gt; &lt;/Link&gt; &lt;/CardFooter&gt; &lt;/Card&gt; ))} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; ); }</p><p><a href="https://github.com/Developer-MultiGroup/android-blast-off-website/blob/main/src/app/blog/page.tsx">Bloglarımız Listelediğimiz Sayfamız</a></p><p>Bu sayfa Contentful’daki tüm yazılarımızı dinamik bir Card komponentine dağıtarak her detayı kullanıcılarımıza gösterdiğimizden emin olmamızı sağlıyor. Burayı istediğiniz gibi şekillendirebilirsiniz.</p><p>Artık bu fonksiyonumuz ve sayfamız sayesinde belirli bir Content Type’a ait bütün yazılarınıza ulaşabileceğiniz bir sayfanız var.</p><h4>Blog Sayfası ve Ayarlamalar</h4><p>Evet artık bütün bloglarımızı listelediğimiz bir sayfamız olduğuna göre, şimdi ise bu blogları okunabilir bir şekilde kullanıcılara göstermeliyiz.</p><p>Bunun için yapmamız gereken ilk şey tekil blog yazılarının içeriklerini çekmek. Tekrardan “contentful.ts” dosyamıza gelerek bu sefer de “getPostsBySlug” adında bir fonksiyon yazmalıyız.</p><p>export const getPostBySlug = async ( slug: string ): Promise&lt;Entry&lt;BlogPostSkeleton&gt; | null&gt; =&gt; { const res = await client.getEntries&lt;BlogPostSkeleton&gt;({ content_type: &quot;dmgBlog&quot;, // 👇 safely assert it as any &quot;fields.slug&quot;: slug, locale: 'en-US', } as any); // acceptable in this specific case</p><p>return res.items[0] ?? null; };</p><p>Bu fonksiyon bizim oluşturduğumuz her Entry için gerekli alanları çekmemizi sağlıyor.</p><p>Son adım olarak ise “src/app/blog/[slug]” dizini içeriinde bir sayfa oluşturarak çektiğimiz içerikleri uygun yerlere yazmamız gerekiyor.</p><pre>
// src/app/blog/[slug]/page.tsx

import { getPostBySlug } from &quot;@/lib/contentful&quot;;
import { notFound } from &quot;next/navigation&quot;;
import { documentToReactComponents } from &quot;@contentful/rich-text-react-renderer&quot;;
import { BLOCKS, INLINES, MARKS, Document } from &quot;@contentful/rich-text-types&quot;;
import Image from &quot;next/image&quot;;

interface BlogPostPageProps {
  params: Promise&lt;{
    slug: string;
  }&gt;;
}

export const revalidate = 60;

// Helper function to extract text from Contentful rich text document
function extractTextFromRichText(node: any): string {
  if (!node) return '';
  if (typeof node === 'string') return node;
  if (Array.isArray(node)) return node.map(extractTextFromRichText).join(' ');
  if (node.nodeType === 'text') return node.value;
  if (node.content) return extractTextFromRichText(node.content);
  return '';
}

export default async function BlogPostPage({ params }: BlogPostPageProps) {
  const { slug } = await params;
  const post = await getPostBySlug(slug);
  if (!post) return notFound();

  const { title, body, writers } = post.fields;
  const createdAt = post.sys.createdAt;
  const formattedDate = new Date(createdAt).toLocaleDateString(&quot;tr-TR&quot;, {
    day: &quot;numeric&quot;,
    month: &quot;long&quot;,
    year: &quot;numeric&quot;,
  });

  // Calculate reading time
  let readingTime = 1;
  if (body) {
    const text = extractTextFromRichText(body);
    const wordCount = text.trim().split(/\s+/).length;
    readingTime = Math.max(1, Math.ceil(wordCount / 200));
  }

  const isValidBody =
    typeof body === &quot;object&quot; &amp;&amp;
    body !== null &amp;&amp;
    &quot;nodeType&quot; in body &amp;&amp;
    body.nodeType === &quot;document&quot; &amp;&amp;
    Array.isArray(body.content);

  // Define rendering options for the rich text
  const options = {
    renderMark: {
      [MARKS.BOLD]: (text: React.ReactNode) =&gt; &lt;strong&gt;{text}&lt;/strong&gt;,
      [MARKS.ITALIC]: (text: React.ReactNode) =&gt; &lt;em&gt;{text}&lt;/em&gt;,
      [MARKS.UNDERLINE]: (text: React.ReactNode) =&gt; &lt;u&gt;{text}&lt;/u&gt;,
      [MARKS.CODE]: (text: React.ReactNode) =&gt; (
        &lt;code className=&quot;bg-gray-800 px-1 py-0.5 rounded&quot;&gt;{text}&lt;/code&gt;
      ),
    },
    renderNode: {
      [BLOCKS.PARAGRAPH]: (node: any, children: React.ReactNode) =&gt; (
        &lt;p className=&quot;mb-4&quot;&gt;{children}&lt;/p&gt;
      ),
      [BLOCKS.HEADING_1]: (node: any, children: React.ReactNode) =&gt; (
        &lt;h1 className=&quot;text-3xl font-bold mt-8 mb-4&quot;&gt;{children}&lt;/h1&gt;
      ),
      [BLOCKS.HEADING_2]: (node: any, children: React.ReactNode) =&gt; (
        &lt;h2 className=&quot;text-2xl font-bold mt-8 mb-3&quot;&gt;{children}&lt;/h2&gt;
      ),
      [BLOCKS.HEADING_3]: (node: any, children: React.ReactNode) =&gt; (
        &lt;h3 className=&quot;text-xl font-bold mt-6 mb-3&quot;&gt;{children}&lt;/h3&gt;
      ),
      [BLOCKS.UL_LIST]: (node: any, children: React.ReactNode) =&gt; (
        &lt;ul className=&quot;list-disc pl-6 mb-4&quot;&gt;{children}&lt;/ul&gt;
      ),
      [BLOCKS.OL_LIST]: (node: any, children: React.ReactNode) =&gt; (
        &lt;ol className=&quot;list-decimal pl-6 mb-4&quot;&gt;{children}&lt;/ol&gt;
      ),
      [BLOCKS.LIST_ITEM]: (node: any, children: React.ReactNode) =&gt; (
        &lt;li className=&quot;mb-1&quot;&gt;{children}&lt;/li&gt;
      ),
      [BLOCKS.QUOTE]: (node: any, children: React.ReactNode) =&gt; (
        &lt;blockquote className=&quot;border-l-4 border-accent pl-4 italic my-4&quot;&gt;
          {children}
        &lt;/blockquote&gt;
      ),
      [BLOCKS.HR]: () =&gt; &lt;hr className=&quot;my-8 border-gray-600&quot; /&gt;,
      [BLOCKS.EMBEDDED_ASSET]: (node: any) =&gt; {
        const asset = node.data.target;
        if (!asset || !asset.fields) return null;

        const { title, description, file } = asset.fields;
        if (!file || !file.url) return null;

        const { url, details } = file;
        const { height, width } = details?.image || { height: 400, width: 600 };

        return (
          &lt;div className=&quot;my-6&quot;&gt;
            &lt;Image
              src={`https:${url}`}
              alt={description || title || &quot;Blog image&quot;}
              width={width}
              height={height}
              className=&quot;mx-auto rounded-lg&quot;
              priority={false}
            /&gt;
            {description &amp;&amp; (
              &lt;p className=&quot;text-sm text-center text-gray-400 mt-1&quot;&gt;
                {description}
              &lt;/p&gt;
            )}
          &lt;/div&gt;
        );
      },
      [INLINES.HYPERLINK]: (node: any, children: React.ReactNode) =&gt; (
        &lt;a
          href={node.data.uri}
          className=&quot;text-blue-400 hover:underline&quot;
          target=&quot;_blank&quot;
          rel=&quot;noopener noreferrer&quot;
        &gt;
          {children}
        &lt;/a&gt;
      ),
    },
  };

  return (
    &lt;main className=&quot;min-h-screen px-6 pt-[25vh] pb-12 font-montserrat-mid text-gray-300&quot;&gt;
      &lt;div className=&quot;max-w-3xl mx-auto&quot;&gt;
        {/* Authors */}
        {writers &amp;&amp; Array.isArray(writers) &amp;&amp; writers.length &gt; 0 &amp;&amp; (
          &lt;p className=&quot;text-sm mb-2 text-accent text-left md:text-center&quot;&gt;
            {writers.length === 1 ? 'Yazar:' : 'Yazarlar:'} {writers.join(', ')}
          &lt;/p&gt;
        )}
        &lt;p className=&quot;text-sm mb-5 text-accent text-left md:text-center&quot;&gt;
          {formattedDate} &lt;span className=&quot;mx-2&quot;&gt;•&lt;/span&gt; {readingTime} dk okuma süresi
        &lt;/p&gt;
        &lt;h1 className=&quot;text-4xl font-extrabold font-montserrat text-secondary mb-15 text-left md:text-center&quot;&gt;
          {typeof title === &quot;string&quot; ? title : &quot;Untitled&quot;}
        &lt;/h1&gt;
        
        &lt;article className=&quot;prose prose-lg max-w-none text-white&quot;&gt;
          {isValidBody ? (
            documentToReactComponents(body as unknown as Document, options)
          ) : (
            &lt;p className=&quot;text-red-500&quot;&gt;Blog içeriği geçersiz veya eksik.&lt;/p&gt;
          )}
        &lt;/article&gt;
      &lt;/div&gt;
    &lt;/main&gt;
  );
}

</pre><p><a href="https://github.com/Developer-MultiGroup/android-blast-off-website/blob/main/src/app/blog/%5Bslug%5D/page.tsx">Tekil Blog Sayfamız</a></p><p>Bu dosyada Contentful’dan gelen Rich Text kısımları gibi alanların hangi birimlerinin (H1, Italic, Markdown vs. ) nasıl görünmesini istediğinizi ayarlayabilir ve kendinize göre değiştirebilirsiniz.</p><p>Tüm bunları yaptıktan sonra aşağıdaki gibi 2 sayfaya sahip olmalısınız.</p><p>Bloglar SayfamızTekil Blog Yazılarımız</p><h3>Teşekkürler</h3><p>Ve işte Contentful ile bir blog sistemi kurmak aslında bu kadar kolay, burdan sonra sayfalarınızı istediğiniz gibi kişiselleştirebilir ve yazılarınızı kolaylıkla Contentful üzerinden paylaşabilirsiniz. Buraya kadar okuduğunuz için teşekkür ederim, bir sonraki yazılardan haberdar olmak için takip etmeyi unutmayın.</p></article>]]></content:encoded>
            <category>Türkçe</category><category>Web Dev</category>
        </item>
        <item>
            <title><![CDATA[Clean Code 101: Neden ve Nasıl Temiz Kod Yazmalıyız]]></title>
            <link>https://furkanunsalan.dev/writing/clean-code-101</link>
            <guid isPermaLink="false">clean-code-101</guid>
            <pubDate>Sun, 11 May 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<article><p>Bu yazıda, “Temiz Kod” yazmanın genel prensiplerinden bazılarına göz atıyor olacağız. İlerleyen bölümlerdeki kod örnekleri TypeScript ile yazıldı fakat bu prensipleri çoğu dil için benzer şekillerde uygulayabilirsiniz.</p><p>Bu yazıyı <a href="https://medium.com/u/55dc97852967">Catalina Turlea</a>’nın <a href="https://medium.com/@catalinaturlea/how-i-do-code-review-caa0e5828d8e">“The Checklist of My Code Review”</a> başlıklı içeriğinden ilham alarak ve kendi yorumlarımı da katarak Türkçeye kazandırmak amacıyla kaleme aldım. Bu sürece vesile olan <a href="https://medium.com/u/b17afd14b562">Göker Güner</a>’e de ayrıca teşekkür ederim.</p><p>“Clean Code” hakkında daha fazla kaynağa göz atmak isterseniz aşağıdakilere bakmayı unutmayın;</p><ul><li><a href="https://medium.com/u/e9d7d248e3fd">Mert Senkaya</a>’nın <a href="https://medium.com/@mertsenkaya/swift-d%C3%BCnyas%C4%B1nda-temiz-kod-pratikleri-1-69242625257b">Swift detaylarına girerek yazdığı yazıya</a></li><li>Fatih Güzel’in <a href="https://www.youtube.com/watch?v=8Ff321ezAXY"><em><strong>“ilke #2 — clean code”</strong></em></a> isimli videosuna</li><li>Robert C. Martin’in Clean Code isimli kitabı</li></ul><h3><strong>1. Kendinizi tekrar etmeyin — DRY (Don’t Repeat Yourself) Prensibi</strong></h3><p>Aşağıdaki kod parçasını bir göz atalım:</p><p><code>// Array'imizin boş olmadığını düşünelim if (array.length === 1) {    submitFormWithValues(&quot;Tek değer seçildi&quot;, array); } else {    submitFormWithValues(array.length + &quot; değer seçildi&quot;, array); }</code></p><p>Bu ilk başta normal gibi görünebilir, ancak daha yakından bakarsanız, fonksiyonun 2 çağrısı arasındaki tek fark bir parametre olmasına rağmen, bu fonksiyon 2 kez çağrılmaktadır.</p><p>Örneğin, fonksiyonun adını değiştirirsek veya üçüncü bir parametre eklersek, kodunuzda yineleme olduğu için kodu 2 yerde değiştirmeniz gerekecektir.</p><p>Bu durum aşağıdaki gibi bir yaklaşım ile önlenebilir:</p><p>let string = array.length === 1 ? &quot;Tek değer seçildi&quot; : array.length + &quot; değer seçildi&quot;; // Now we can call the method only once submitFormWithValues(string, array);</p><p>Artık fonksiyonun değişmesi durumunda, kodu yalnızca tek bir yere uyarlamamız gerekecektir — artık tekrarlama yok.</p><blockquote><p>Bunun için pek çok örnek verilebilir, ancak temel olarak bir fonksiyonda veya dosyada aynı şeyleri birden fazla gördüğünüzde, değişken kısmı olabildiğince azaltarak yinelenen kodu nasıl kaldırabileceğinizi kendinize sorun.</p></blockquote><h3>2. Boolean değerler döndürmek</h3><p>function isEmpty(array: string[]): boolean { if (array.length === 0) { return true; } else { return false; } }</p><p>Boolean değerler döndürdüğünüz durumlarda kodunuza biraz daha dikkatli bakarsanız genellikle işinize yarayacak daha basit bir yol olduğunu görebilirsiniz</p><p>function isEmpty(array: string[]): boolean { return array.length === 0; }</p><p>Aynı durum nesneler üzerindeki boolean özellikleri hesaplarken de geçerlidir. Mesela aşağıdaki gibi bir kod bloğunuz olduğunda:</p><p>function hideHeaderIfEmptyTableView(): void { if (datasource.length === 0) { header.hidden = true; } else { header.hidden = false; } }</p><p>Bu şekilde uzun bir blok yazmak yerine aşağıdaki gibi boolean değerleri ayarladığınız (veya döndürdüğünüz) bir if durumunuz varsa bu değerleri direkt olarak kullanabilirsiniz.</p><p>function hideHeaderIfEmptyTableView(): void { header.hidden = (datasource.length === 0); }</p><h3>3. Girinti seviyesini azaltın ve erken çıkışları (return) kullanın</h3><p>Kodunuzda ne kadar çok girinti seviyesi varsa, okunması ve anlaşılması o kadar zorlaşır.</p><blockquote><p>Beynimiz aynı anda yalnızca sınırlı sayıda koşulu tutabilir, bu nedenle ne kadar çok döngüye sahip olursanız, beyninizin bunu anlamlandırması için o kadar fazla çaba gerekir.</p></blockquote><p>Bu bir nevi erken çıkış kuralı ile aynı konsepti temsil eder. Tüm kodunuzu bir if bloğuna sarmak yerine (tüm kod girintilidir), önce döngünün yürütülmediği koşulu ekleyebilir ve bu durumda sadece geri dönebilirsiniz.</p><p>Aslında oldukça basit, aşağıdaki kod bloğu yerine:</p><p>function myMethod(elements: string[]): void { if (elements.length &gt; 0) { for (const element of elements) { // bir takım işlemler } } }</p><p>Daha ayrık bir yaklaşım gösterebilirsiniz:</p><p>function myMethod(elements: string[]): void { if (elements.length === 0) { return; }</p><p>for (const element of elements) { // bir takım işlemler } }</p><p>Siz de takdir edersiniz ki bu biraz zevk ve stil meselesidir, ancak takip etmenin çok daha kolay olduğunu ve kontrol akışının biraz daha doğrusal olduğunu düşünmek bence yanlış olmaz.</p><h3>4. “Utangaç” Kod</h3><p>The Pragmatic Programmer’da belirtildiği gibi, kodunuz her zaman “utangaç kod” olmalıdır.</p><p>Her şeyi mümkün olan en küçük kapsamla yazın ve kapsamı yalnızca gerçekten ihtiyacınız varsa artırın — örneğin, her şey private olarak başlayın; özellikleriniz, yöntemleriniz, sınıflarınız...</p><p>Bu kuralın geçerli olduğu bir başka yol da nesnelerinizin sahip olduğu özelliklerin açıklanma düzeyidir. Diyelim ki içinde bir resim içeren özel bir UI bileşeniniz var ve bu resim bileşeninizin dışından da ayarlanabilmelidir.</p><p>Görüntüyü bir okuma/yazma özelliği olarak göstermek yerine, sınıfınızın içinde ilgili özelliği güncellemek için bir yöntem sağlayabilirsiniz. Görüntü yalnızca salt okunur olarak gösterilecektir ve ayrıca özelliğinizde ayarlanan değerler üzerinde daha fazla kontrole sahip olabilirsiniz.</p><h3>5. Boş fonksiyonları ve kullanılmayan oluşturulmuş kodları kaldırın</h3><blockquote><p>En iyi kod, varolmayan koddur.</p></blockquote><p>Kullanmadığınız tüm kodları (IDE tarafından oluşturulmuş olsa bile) silmeyi alışkanlık haline getirin. Boş metotları, kullanılmayan değişkenleri, içe aktarmaları, modası geçmiş yorumları projenizde bırakmayın.</p><p>Kod silmek, bir geliştirici olarak en sevdiğiniz etkinliklerden biri olmalıdır çünkü aslında projenizin daha temiz ve minimal bir hale gelmesine yardımcı olur.</p><blockquote><p>“Mükemmelliğe eklenecek bir şey kalmadığında değil, çıkarılacak bir şey kalmadığında ulaşılır” — Antoine de Saint-Exupery</p></blockquote><p>Aynı şey yorumlanmış kodlar için de geçerlidir. Orada öylece bırakmayın. Zaten kaynak kontrol aracınızdaki (git vs.) tüm geçmişe sahipsiniz, bu yüzden kaldırın. Eğer tekrar ihtiyacınız olursa, dosyanızı geri döndürebilirsiniz — her ne kadar benim deneyimlerime göre, emin olmadığınız kodlara nadiren geri dönersiniz (ilk etapta yorumlamaya karar vermenizin nedeni bu olabilir).</p><h3>Dikkate alınması gereken diğer hususlar</h3><p>Kodunuzu gözden geçirirken şunu her zaman aklınızda bulundurmalısınız:</p><ul><li>Sabit kodlanmış değerler: Öncelikle kendinize şunu sormanız gerekir: Statik değeri kaldırmanın bir yolu yok mu? String, float, ints ne olursa olsun. Eğer cevap evet ise, devam edin ve daha iyi hale getirin :) Eğer ondan kurtulmanın bir yolu yoksa, onu statik bir değişkende saklayın (ya da kullandığınız dilde formatı her ne ise).</li></ul><p>İsim, değere <em><strong>defaultImageWidth</strong></em> veya <em><strong>controllerIdentifier</strong></em> gibi bir bağlam sağlayacak ve bir süre sonra koda geri döndüğünüzde ne anlama gelmesi gerektiğini anlamanızı kolaylaştıracaktır. Bir başka faydası da, deneyimlerime göre, bazen bu değerlerin daha fazla yerde tekrar tekrar ortaya çıkmasıdır. Bunları bir değişkende saklamadan, değeriniz 3'ten 4'e değiştiğinde kodu her yerde değiştirmeniz gerekirdi — tekrarlanan bir koda sahip olurdunuz.</p><ul><li><strong>Ekip standartları ve yönergeleri</strong>  — her zaman standartların herkes tarafından takip edildiğinden emin olun ve kod incelemenizde bazı şeylerin gözden kaçmasına izin vermeyin. Bu, değişkenleri adlandırmanın tutarlı yollarını, kod biçimlendirmesini, ekibinizde üzerinde anlaşmaya varılan diliniz için en iyi uygulamaları, adların, yöntemlerin ve kararların herkes için aynı anlama gelmesine dikkat etmeyi içermelidir.</li><li><strong>Hatalarınızı kaydedin</strong>  — belki barizdir, ancak özellikle kodunuz zaten üretimde olmadığı sürece bunun değerini göremeyeceğiniz için bahsetmeye değer olduğunu düşünüyorum (o zaman değişiklik yapmak biraz zor, en azından mobil uygulamalar için). Kodunuz geçersiz bir duruma ulaştığında veya bazı özellikler yanlış yapılandırıldığında, bazı şeylerin imkansız olduğunu düşünseniz bile, hatayı günlüğe kaydettiğinizden ve mesaja mümkün olduğunca fazla bilgi eklediğinizden emin olun. Bunun kaç kez işe yarayacağına inanamayacaksınız.</li></ul><p>Bu yazıda sizlere çoğu yazılım dilinde karşılabileceğiniz, bazıları bariz bazıları ise biraz daha zor fark edilebilen <em><strong>“Clean Code”</strong></em> prensiplerinden TypeScript örnekleri ile bahsetmeye çalıştım, umarım sizin için faydalı olmuştur.</p></article>]]></content:encoded>
            <category>Clean Code</category><category>Türkçe</category><category>Development</category>
        </item>
        <item>
            <title><![CDATA[My Takeaways from Ali Abdaal's Feel Good Productivity]]></title>
            <link>https://furkanunsalan.dev/writing/fgp-takeaways</link>
            <guid isPermaLink="false">fgp-takeaways</guid>
            <pubDate>Wed, 05 Feb 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<article><p>I've recently read Ali Abdaal's New York Times Best Seller 'Feel Good productivity' and I've taken very nice lessons for myself while reading it. Here are the most impactful highlights I noted down, I hope you can take something for yourself from my notes.</p><h2>Part 1: Energise</h2><h3>Chapter 1: Play &gt; Page 35</h3><p>Mihaly Csikszentmihalyi (pronounced ‘chick-sent-me-hi’),</p><h2>Part 2: Unblock</h2><h3>Chapter 6: Get Started &gt; Page 149</h3><p>r/ GetMotivatedBuddies</p><h3>Chapter 6: Get Started &gt; Page 151</h3><p>I find that the best accountability buddies meet five criteria: being disciplined (they must stick to what you’ve agreed to), challenging (they know what it means to help you move on to the next level), patient (they don’t jump to conclusions or rush you into making decisions), supportive (they’re there with words of encouragement) and constructive (they must know how to give you honest feedback and constructive criticism).</p><h2>Part 3: Sustain</h2><h3>Chapter 7: Conserve &gt; Page 158</h3><p>Finally, there are burnouts that relate to doing the wrong stuff. Your mood is suffering because of the weeks, years or decades when you’ve put all your efforts into something that doesn’t bring you joy or meaning, and it has worn you down. You’ve been using your energy in the wrong way. I call these misalignment burnouts.</p><h3>Chapter 7: Conserve &gt; Page 160</h3><p>We accept more work than we can do, and fail to take the breaks in our working day that we require. We sprint all the time.</p><h3>Chapter 7: Conserve &gt; Page 161</h3><p>Jobs’ message was clear: no was just as important as yes. ‘I’m actually as proud of the things we haven’t done as the things I have done,’ Jobs said.</p><h3>Chapter 7: Conserve &gt; Page 162</h3><p>we say yes to things in the present, but in the long term, they’re going to grind us down.</p><h3>Chapter 7: Conserve &gt; Page 164</h3><p>If it isn’t a ‘hell yeah’, it’s not worth doing.</p><h3>Chapter 7: Conserve &gt; Page 165</h3><p>‘Would I be excited about this commitment if it was happening tomorrow? Or am I only thinking about saying “yes” to it because it’s easier to make it a problem for my future self?’</p><h3>Chapter 7: Conserve &gt; Page 167</h3><p>So the goal is to spend most of our time focused on just one task–but not beat ourselves up if we occasionally lose concentration.</p><h3>Chapter 7: Conserve &gt; Page 170</h3><p>In most aspects of our lives, if things go slightly wrong we don’t let ourselves get blown entirely off course.</p><h3>Chapter 7: Conserve &gt; Page 170</h3><p>‘I’ve spent five minutes on social media; I might as well continue to do so for the next three hours.’</p><h3>Chapter 7: Conserve &gt; Page 171</h3><p>‘If you haven’t managed to get deep into the practice, that’s ok. Don’t worry. You can simply begin again.’</p><h3>Chapter 7: Conserve &gt; Page 175</h3><p>Remember Dr Adcock. Even if you’re in the business of saving lives, breaks aren’t a special treat. They’re an absolute necessity.</p><h3>Chapter 8: Recharge &gt; Page 178</h3><p>‘doomscrolling’.</p><h3>Chapter 8: Recharge &gt; Page 182</h3><p>quintessential</p><h3>Chapter 8: Recharge &gt; Page 182</h3><p>Hobbies are the first way we can integrate CALM activities into our lives.</p><h3>Chapter 8: Recharge &gt; Page 191</h3><p>Well, the simplest way is to actively schedule moments of ‘nothingness’ into your week. Some nights, you don’t need to go for a walk or paint a picture. Some nights, you should simply let yourself zone out.</p><h3>Chapter 8: Recharge &gt; Page 193</h3><p>The Reitoff principle is the idea that we should grant ourselves permission to write off a day and intentionally step away from achieving anything. For many of us, the challenge of rest lies in the act of stepping back from the things we think we should be doing. We’re conditioned to value self-control, grit and persistence. We equate rest with laziness, weakness or failure.</p><h3>Chapter 8: Recharge &gt; Page 193</h3><p>this short-term ‘unproductiveness’ gives me time to reset and recharge.</p><h3>Chapter 8: Recharge &gt; Page 193</h3><p>By doing less today, you can do more of what matters tomorrow.</p><h3>Chapter 8: Recharge &gt; Page 194</h3><p>Find an activity or project that makes you feel Competent, Autonomous, Liberated and Mellow.</p><h3>Chapter 9: Align &gt; Page 199</h3><p>External motivation is the form of extrinsic motivation that’s the least autonomous; instead of us being motivated by any kind of internal force, we’re being controlled by the opinions, rules and rewards offered by others.</p><h3>Chapter 9: Align &gt; Page 201</h3><p>when we think about death, we get a clearer view of life.</p><h3>Chapter 9: Align &gt; Page 205</h3><p>It promised to turn my vague ideas about what I wanted into a clear picture underpinned by evidence. The approach was called the ‘odyssey plan’.</p><h3>Chapter 9: Align &gt; Page 205</h3><p>But Burnett’s design mindset offers an unusual way to answer the question. He invites you to reflect on:</p><h3>Chapter 9: Align &gt; Page 206</h3><p>The odyssey plan inspired me to focus on growing my business,</p><h3>Chapter 9: Align &gt; Page 209</h3><p>The wheel of life, Dr Lillicrap explained, was a coaching framework we could use to define success for ourselves. You start by drawing a circle and slicing it up into nine segments. Around the edges of each spoke of the wheel, you write down the major areas of your life. Below are the ones that Dr Lillicrap recommended as a starting point, although you could also come up with your own. We’ve got three for Health (Body, Mind and Soul); three for Work (Mission, Money, Growth) and three for Relationships (Family, Romance, Friends).</p><h3>Chapter 9: Align &gt; Page 211</h3><p>the ‘12-month celebration’. This is my favourite method to convert dreams into actions. The idea is simple. Imagine it’s twelve months from now and you’re having dinner with your best friend. You’re celebrating how much progress you’ve made in the areas of life that are important to you over the last year. Look back over the values that you identified in the wheel of life. Now, write down what you’d want to tell your best friend about your progress in each of them.</p><h3>Chapter 9: Align &gt; Page 212</h3><p>Ask yourself: ‘If I was to make the 12-month celebration a reality, what would I need to do over the next year to get there?</p><h3>Chapter 9: Align &gt; Page 214</h3><p>My favourite way to integrate long-term values into day-to-day decisions draws upon a simple fact: short-term targets feel much easier to reach than long-term ones.</p><h3>Chapter 9: Align &gt; Page 214</h3><p>‘Proximal goals hadn’t just helped these children solve problems–they’d changed the way they looked at math.’</p><h3>Chapter 9: Align &gt; Page 215</h3><p>Personally, I have my 12-month celebration saved in a Google Doc, bookmarked on my computer’s web browser. Whenever I sit down to begin work, I open up that Google Doc and scan through it to remind myself what my 12-month celebration looks like. Then, under each of the areas of health, work and relationships, I choose one subcategory to focus on. Here’s what my three alignment quests looked like this morning:</p><h3>Chapter 9: Align &gt; Page 215</h3><p>H–Gym session 15.30–16: 30 W–Make progress in writing Chapter 9 R–Call Nani (my grandma) Last Word: Think Like a Productivity Scientist</p><h3>Chapter 9: Align &gt; Page 220</h3><p>At the time, I was getting all the basic tactics wrong. Instead of viewing productivity in terms of what made me feel good, I was viewing it in terms of discipline: how much pressure I could pile on myself to just do more. Instead of trying to integrate play, power and people into every ward round, I was catastrophising about my sense of boredom, powerlessness and loneliness</p></article>]]></content:encoded>
            <category>Self Improvement</category><category>Books</category><category>English</category>
        </item>
        <item>
            <title><![CDATA[Connect Your Docker Apps to a Domain]]></title>
            <link>https://furkanunsalan.dev/writing/docker-to-domain</link>
            <guid isPermaLink="false">docker-to-domain</guid>
            <pubDate>Tue, 22 Oct 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<article><h2>Introduction</h2><p>This has been something I really struggled when I first try to setup a config for accessing my docker containers using my domain and at the time I couldn't find a proper document that I can understand so I told myself why not write it yourself so here it is.</p><h2>Update Your System</h2><p>As a classic, update your system and later install NGINX to your machine.</p><p>sudo apt update sudo apt install nginx</p><h2>NGINX Setup</h2><p>After that, go to the following directory and create a config file for your application</p><p>cd /etc/nginx/sites-available/ sudo nano /etc/nginx/sites-available/my-app</p><p>Later, add the following to your config file with the correct information</p><p>server { listen 80; server_name my-app.your-domain.com;</p><p>location / { proxy_pass http://localhost:port-of-your-app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }</p><p>You can check the ports of your docker applications using <code>docker ps</code> inside your machine.</p><p>Then create a symlink from the file you created to the folder where NGINX holds enabled sites.</p><p>sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/</p><p>after that test your config file and restart your nginx instance</p><p>sudo nginx -t sudo systemctl restart nginx</p><h2>Set DNS Records</h2><p>For every app you create with the statements below here, you need to set separate DNS records in your domain provider.</p><p>Here is how you can do it:</p><ol><li>Go to your domain provider</li><li>Open your domain settings and find DNS Records</li><li>Create a new &quot;A&quot; record with the following configs: - Name (Subdomain): the name you used in the creation process. (e.g. my-app)</li></ol><ul><li>Value: Public IP Address of your server (where you run your application)</li></ul><p>You can check if the DNS Records have been changed using <a href="https://dnschecker.org/">this tool</a>. It should show the IP Address of your server when you input your subdomain (my-app.yourdomain.com) if you have done everything correctly.</p><h2>SSL Certification</h2><p>sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d my-app.your-domain.com</p><h2>Additional Commands</h2><h4>List the symlinks (enabled NGINX instances) created for your applications:</h4><p>ls -l /etc/nginx/sites-enabled/</p><h3>Example output:</h3><p>/etc/nginx/sites-enabled/my-app -&gt; /etc/nginx/sites-available/my-app</p><h4>List the available sites for NGINX:</h4><p>ls /etc/nginx/sites-available/</p><h2>Side Notes:</h2><ol><li>In case you fuck-up in the process ( like I did :D )  <a href="https://gist.github.com/xameeramir/a5cb675fb6a6a64098365e89a239541d">here</a>  you can find the default enabled site config file for NGINX. Located under</li></ol><p>/etc/nginx/sites-enabled/default</p><ol><li>You should keep the places where &quot;my-app&quot; is written same, so for example if you are setting up a config for uptime kuma everywhere you should use &quot;kuma&quot; (name depends on you). If you change any of those to a different string it will not work as I experienced.</li></ol></article>]]></content:encoded>
            <category>Tech</category><category>Self Hosting</category><category>English</category>
        </item>
    </channel>
</rss>